@modelzen/feishu-codex-bridge 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +453 -113
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -600,9 +600,9 @@ async function spawnExecProvider(pc, ref) {
|
|
|
600
600
|
const providerName = ref.provider ?? DEFAULT_PROVIDER;
|
|
601
601
|
return new Promise((resolve7, reject) => {
|
|
602
602
|
const env = {};
|
|
603
|
-
if (pc.passEnv) for (const
|
|
604
|
-
const v = process.env[
|
|
605
|
-
if (v) env[
|
|
603
|
+
if (pc.passEnv) for (const k2 of pc.passEnv) {
|
|
604
|
+
const v = process.env[k2];
|
|
605
|
+
if (v) env[k2] = v;
|
|
606
606
|
}
|
|
607
607
|
if (pc.env) Object.assign(env, pc.env);
|
|
608
608
|
const child = spawnProcess(pc.command, pc.args ?? [], {
|
|
@@ -948,11 +948,11 @@ function emit(level, phase, event, fields = {}) {
|
|
|
948
948
|
event,
|
|
949
949
|
...ctx
|
|
950
950
|
};
|
|
951
|
-
for (const [
|
|
952
|
-
if (RESERVED_KEYS.has(
|
|
953
|
-
entry[`_${
|
|
951
|
+
for (const [k2, v] of Object.entries(fields)) {
|
|
952
|
+
if (RESERVED_KEYS.has(k2)) {
|
|
953
|
+
entry[`_${k2}`] = v;
|
|
954
954
|
} else {
|
|
955
|
-
entry[
|
|
955
|
+
entry[k2] = v;
|
|
956
956
|
}
|
|
957
957
|
}
|
|
958
958
|
const s = getStream();
|
|
@@ -1003,20 +1003,20 @@ function formatFields(fields) {
|
|
|
1003
1003
|
const keys = Object.keys(fields);
|
|
1004
1004
|
if (keys.length === 0) return "";
|
|
1005
1005
|
const parts = [];
|
|
1006
|
-
for (const
|
|
1007
|
-
const v = fields[
|
|
1006
|
+
for (const k2 of keys) {
|
|
1007
|
+
const v = fields[k2];
|
|
1008
1008
|
if (v === void 0 || v === null) continue;
|
|
1009
|
-
if (
|
|
1009
|
+
if (k2 === "stack") continue;
|
|
1010
1010
|
if (typeof v === "string") {
|
|
1011
|
-
parts.push(`${
|
|
1011
|
+
parts.push(`${k2}=${v.length > 80 ? `${v.slice(0, 80)}\u2026` : v}`);
|
|
1012
1012
|
} else if (typeof v === "number" || typeof v === "boolean") {
|
|
1013
|
-
parts.push(`${
|
|
1013
|
+
parts.push(`${k2}=${v}`);
|
|
1014
1014
|
} else {
|
|
1015
1015
|
try {
|
|
1016
1016
|
const str = JSON.stringify(v);
|
|
1017
|
-
parts.push(`${
|
|
1017
|
+
parts.push(`${k2}=${str.length > 80 ? `${str.slice(0, 80)}\u2026` : str}`);
|
|
1018
1018
|
} catch {
|
|
1019
|
-
parts.push(`${
|
|
1019
|
+
parts.push(`${k2}=?`);
|
|
1020
1020
|
}
|
|
1021
1021
|
}
|
|
1022
1022
|
}
|
|
@@ -1380,6 +1380,14 @@ function mapNotification(n) {
|
|
|
1380
1380
|
return mapItemStart(n.params.item);
|
|
1381
1381
|
case "item/completed":
|
|
1382
1382
|
return mapItemComplete(n.params.item);
|
|
1383
|
+
case "thread/tokenUsage/updated":
|
|
1384
|
+
return {
|
|
1385
|
+
type: "context_usage",
|
|
1386
|
+
usedTokens: n.params.tokenUsage.last.totalTokens,
|
|
1387
|
+
contextWindow: n.params.tokenUsage.modelContextWindow
|
|
1388
|
+
};
|
|
1389
|
+
case "thread/compacted":
|
|
1390
|
+
return { type: "context_compacted" };
|
|
1383
1391
|
case "turn/completed":
|
|
1384
1392
|
return { type: "done", turnId: n.params.turn.id };
|
|
1385
1393
|
case "error":
|
|
@@ -1430,6 +1438,12 @@ function mapItemComplete(item) {
|
|
|
1430
1438
|
|
|
1431
1439
|
// src/agent/codex-appserver/backend.ts
|
|
1432
1440
|
var APPROVAL_POLICY = "never";
|
|
1441
|
+
var AUTO_COMPACT_OFF_LIMIT = 1e9;
|
|
1442
|
+
function withAutoCompact(params, autoCompact) {
|
|
1443
|
+
if (autoCompact !== false) return params;
|
|
1444
|
+
const config = params.config ?? {};
|
|
1445
|
+
return { ...params, config: { ...config, model_auto_compact_token_limit: AUTO_COMPACT_OFF_LIMIT } };
|
|
1446
|
+
}
|
|
1433
1447
|
function sandboxParams(mode, network) {
|
|
1434
1448
|
if ((mode ?? "full") === "full") return { sandbox: "danger-full-access" };
|
|
1435
1449
|
if (process.platform !== "darwin" && process.platform !== "win32") {
|
|
@@ -1467,6 +1481,7 @@ var BRIDGE_DEVELOPER_INSTRUCTIONS = [
|
|
|
1467
1481
|
"\u4E0D\u8981\u624B\u5199\u98DE\u4E66\u5361\u7247\u7684 JSON\u3002\u666E\u901A\u95EE\u7B54\u6B63\u5E38\u56DE\u590D\u5373\u53EF\uFF0C\u53EA\u6709\u7528\u6237\u8981\u5361\u7247\u65F6\u624D\u7528 ```feishu-card \u4EE3\u7801\u5757\u3002"
|
|
1468
1482
|
].join("\n");
|
|
1469
1483
|
var READ_HISTORY_TIMEOUT_MS = 2e4;
|
|
1484
|
+
var COMPACT_TIMEOUT_MS = 12e4;
|
|
1470
1485
|
function withDeadline(p, ms, label) {
|
|
1471
1486
|
return new Promise((resolve7, reject) => {
|
|
1472
1487
|
const t = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
|
|
@@ -1548,6 +1563,40 @@ var CodexThread = class {
|
|
|
1548
1563
|
async abort(turnId) {
|
|
1549
1564
|
await this.client.request("turn/interrupt", { threadId: this.codexThreadId, turnId });
|
|
1550
1565
|
}
|
|
1566
|
+
async compact() {
|
|
1567
|
+
let startError;
|
|
1568
|
+
const startFailed = new Promise((resolve7) => {
|
|
1569
|
+
this.client.request("thread/compact/start", { threadId: this.codexThreadId }).then(void 0, (err) => {
|
|
1570
|
+
startError = err instanceof Error ? err : new Error(String(err));
|
|
1571
|
+
log.fail("agent", startError, { phase: "thread/compact/start" });
|
|
1572
|
+
resolve7("start-failed");
|
|
1573
|
+
});
|
|
1574
|
+
});
|
|
1575
|
+
let timer;
|
|
1576
|
+
const timeout = new Promise((resolve7) => {
|
|
1577
|
+
timer = setTimeout(() => resolve7("timeout"), COMPACT_TIMEOUT_MS);
|
|
1578
|
+
});
|
|
1579
|
+
const stream2 = this.client.stream()[Symbol.asyncIterator]();
|
|
1580
|
+
let compacted = false;
|
|
1581
|
+
let usage = null;
|
|
1582
|
+
try {
|
|
1583
|
+
while (true) {
|
|
1584
|
+
const step = await Promise.race([stream2.next(), startFailed, timeout]);
|
|
1585
|
+
if (step === "start-failed") throw startError ?? new Error("thread/compact/start \u8BF7\u6C42\u5931\u8D25");
|
|
1586
|
+
if (step === "timeout") throw new Error(`\u538B\u7F29\u8D85\u65F6\uFF08codex \u672A\u5728 ${COMPACT_TIMEOUT_MS / 1e3}s \u5185\u5B8C\u6210\uFF09`);
|
|
1587
|
+
if (step.done) break;
|
|
1588
|
+
const ev = mapNotification(step.value);
|
|
1589
|
+
if (!ev) continue;
|
|
1590
|
+
if (ev.type === "context_usage") usage = { usedTokens: ev.usedTokens, contextWindow: ev.contextWindow };
|
|
1591
|
+
else if (ev.type === "context_compacted") compacted = true;
|
|
1592
|
+
else if (ev.type === "error" && !ev.willRetry) throw new Error(ev.message);
|
|
1593
|
+
else if (ev.type === "done") break;
|
|
1594
|
+
}
|
|
1595
|
+
} finally {
|
|
1596
|
+
if (timer) clearTimeout(timer);
|
|
1597
|
+
}
|
|
1598
|
+
return { compacted, usage };
|
|
1599
|
+
}
|
|
1551
1600
|
async close() {
|
|
1552
1601
|
await this.client.close();
|
|
1553
1602
|
}
|
|
@@ -1636,7 +1685,7 @@ var CodexAppServerBackend = class {
|
|
|
1636
1685
|
}
|
|
1637
1686
|
}
|
|
1638
1687
|
async startThread(opts) {
|
|
1639
|
-
const sandbox = sandboxParams(opts.mode, opts.network);
|
|
1688
|
+
const sandbox = withAutoCompact(sandboxParams(opts.mode, opts.network), opts.autoCompact);
|
|
1640
1689
|
const client = await this.spawn(opts.cwd);
|
|
1641
1690
|
const res = await client.request("thread/start", {
|
|
1642
1691
|
cwd: opts.cwd,
|
|
@@ -1648,7 +1697,7 @@ var CodexAppServerBackend = class {
|
|
|
1648
1697
|
return new CodexThread(client, res.thread.id, opts.model, opts.effort);
|
|
1649
1698
|
}
|
|
1650
1699
|
async resumeThread(opts) {
|
|
1651
|
-
const sandbox = sandboxParams(opts.mode, opts.network);
|
|
1700
|
+
const sandbox = withAutoCompact(sandboxParams(opts.mode, opts.network), opts.autoCompact);
|
|
1652
1701
|
const client = await this.spawn(opts.cwd);
|
|
1653
1702
|
const res = await client.request("thread/resume", {
|
|
1654
1703
|
threadId: opts.codexThreadId,
|
|
@@ -1821,39 +1870,53 @@ function stampRenderToken(card2) {
|
|
|
1821
1870
|
}
|
|
1822
1871
|
}
|
|
1823
1872
|
}
|
|
1824
|
-
for (const
|
|
1873
|
+
for (const k2 of Object.keys(obj)) visit(obj[k2]);
|
|
1825
1874
|
};
|
|
1826
1875
|
visit(card2);
|
|
1827
1876
|
}
|
|
1877
|
+
function isCardIdNotReady(err) {
|
|
1878
|
+
const data = err?.response?.data;
|
|
1879
|
+
return data?.code === 230099 || /11310|cardid is invalid/i.test(data?.msg ?? "");
|
|
1880
|
+
}
|
|
1828
1881
|
async function sendManagedCard(channel, to, card2, replyTo, replyInThread = false, receiveIdType = "chat_id") {
|
|
1829
1882
|
stampRenderToken(card2);
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1883
|
+
const data = JSON.stringify(card2);
|
|
1884
|
+
const attempt = async () => {
|
|
1885
|
+
const created = await channel.rawClient.cardkit.v1.card.create({ data: { type: "card_json", data } });
|
|
1886
|
+
const cardId = created.data?.card_id;
|
|
1887
|
+
if (!cardId) {
|
|
1888
|
+
throw new Error(`cardkit.card.create returned no card_id: ${JSON.stringify(created).slice(0, 200)}`);
|
|
1889
|
+
}
|
|
1890
|
+
const content = JSON.stringify({ type: "card", data: { card_id: cardId } });
|
|
1891
|
+
let messageId;
|
|
1892
|
+
if (replyTo) {
|
|
1893
|
+
const sent = await channel.rawClient.im.v1.message.reply({
|
|
1894
|
+
path: { message_id: replyTo },
|
|
1895
|
+
data: { msg_type: "interactive", content, reply_in_thread: replyInThread }
|
|
1896
|
+
});
|
|
1897
|
+
messageId = sent.data?.message_id;
|
|
1898
|
+
} else {
|
|
1899
|
+
const sent = await channel.rawClient.im.v1.message.create({
|
|
1900
|
+
params: { receive_id_type: receiveIdType },
|
|
1901
|
+
data: { receive_id: to, msg_type: "interactive", content }
|
|
1902
|
+
});
|
|
1903
|
+
messageId = sent.data?.message_id;
|
|
1904
|
+
}
|
|
1905
|
+
if (!messageId) {
|
|
1906
|
+
throw new Error("send card-by-reference returned no message_id");
|
|
1907
|
+
}
|
|
1908
|
+
byMessageId.set(messageId, { cardId, sequence: 0 });
|
|
1909
|
+
return { messageId, cardId };
|
|
1910
|
+
};
|
|
1911
|
+
for (let i = 0; ; i++) {
|
|
1912
|
+
try {
|
|
1913
|
+
return await attempt();
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
if (i >= 2 || !isCardIdNotReady(err)) throw err;
|
|
1916
|
+
log.fail("card", err, { phase: "managed-send", attempt: i, retry: true });
|
|
1917
|
+
await new Promise((r) => setTimeout(r, 400 * (i + 1)));
|
|
1918
|
+
}
|
|
1854
1919
|
}
|
|
1855
|
-
byMessageId.set(messageId, { cardId, sequence: 0 });
|
|
1856
|
-
return { messageId, cardId };
|
|
1857
1920
|
}
|
|
1858
1921
|
async function updateManagedCard(channel, messageId, card2) {
|
|
1859
1922
|
const entry = byMessageId.get(messageId);
|
|
@@ -1983,6 +2046,10 @@ function reduce(state, evt) {
|
|
|
1983
2046
|
});
|
|
1984
2047
|
return { ...state, blocks };
|
|
1985
2048
|
}
|
|
2049
|
+
case "context_usage":
|
|
2050
|
+
return { ...state, usage: { used: evt.usedTokens, window: evt.contextWindow } };
|
|
2051
|
+
// context_compacted is surfaced as a standalone notice by the run loop, not
|
|
2052
|
+
// folded into the card — fall through to the no-op default.
|
|
1986
2053
|
case "error":
|
|
1987
2054
|
return { ...state, terminal: "error", errorMsg: evt.message, footer: null };
|
|
1988
2055
|
case "done":
|
|
@@ -2102,6 +2169,9 @@ function image(imgKey, alt = "") {
|
|
|
2102
2169
|
function note(content) {
|
|
2103
2170
|
return { tag: "div", text: { tag: "lark_md", content, text_size: "notation", text_color: "grey" } };
|
|
2104
2171
|
}
|
|
2172
|
+
function colorNote(content, color) {
|
|
2173
|
+
return { tag: "div", text: { tag: "lark_md", content, text_size: "notation", text_color: color } };
|
|
2174
|
+
}
|
|
2105
2175
|
function hr() {
|
|
2106
2176
|
return { tag: "hr" };
|
|
2107
2177
|
}
|
|
@@ -2320,7 +2390,12 @@ function talkLine(noMention, tail) {
|
|
|
2320
2390
|
function buildHelpCard(scope, noMention = true, isAdmin2 = false) {
|
|
2321
2391
|
const elements = [];
|
|
2322
2392
|
if (scope === "single") {
|
|
2323
|
-
const lines = [
|
|
2393
|
+
const lines = [
|
|
2394
|
+
talkLine(noMention, "\u4EA4\u7ED9\u6211\u5904\u7406"),
|
|
2395
|
+
"\xB7 `/model` \u2192 \u5207\u6362\u6A21\u578B / \u63A8\u7406\u5F3A\u5EA6",
|
|
2396
|
+
"\xB7 `/context` \u2192 \u770B\u4E0A\u4E0B\u6587\u5360\u6BD4",
|
|
2397
|
+
"\xB7 `/compact` \u2192 \u538B\u7F29\u4E0A\u4E0B\u6587\uFF08\u91CA\u653E\u7A7A\u95F4\uFF09"
|
|
2398
|
+
];
|
|
2324
2399
|
if (isAdmin2) lines.push("\xB7 `/settings` \u2192 \u7FA4\u8BBE\u7F6E\uFF08\u514D@ \u5F00\u5173\uFF09");
|
|
2325
2400
|
lines.push("\xB7 `/help` \u2192 \u8FD9\u5F20\u901F\u67E5\u5361");
|
|
2326
2401
|
elements.push(md("\u{1F4AC} **\u5355\u4F1A\u8BDD\u7FA4** \u2014 \u6574\u7FA4\u5C31\u662F\u4E00\u4E2A\u4F1A\u8BDD\uFF0C\u4E0A\u4E0B\u6587\u8FDE\u7EED\u3002"), hr(), md(lines.join("\n")));
|
|
@@ -2331,6 +2406,8 @@ function buildHelpCard(scope, noMention = true, isAdmin2 = false) {
|
|
|
2331
2406
|
md(
|
|
2332
2407
|
`${talkLine(noMention, "\u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD")}
|
|
2333
2408
|
\xB7 \`/model\` \u2192 \u5207\u6362\u6A21\u578B / \u63A8\u7406\u5F3A\u5EA6
|
|
2409
|
+
\xB7 \`/context\` \u2192 \u770B\u4E0A\u4E0B\u6587\u5360\u6BD4
|
|
2410
|
+
\xB7 \`/compact\` \u2192 \u538B\u7F29\u4E0A\u4E0B\u6587\uFF08\u91CA\u653E\u7A7A\u95F4\uFF09
|
|
2334
2411
|
\xB7 \`/help\` \u2192 \u8FD9\u5F20\u901F\u67E5\u5361`
|
|
2335
2412
|
),
|
|
2336
2413
|
note("\u5F00\u65B0\u8BDD\u9898\uFF1A\u56DE\u5230\u4E3B\u7FA4\u533A @\u6211 + \u5185\u5BB9\u3002")
|
|
@@ -2365,7 +2442,9 @@ function buildWelcomeCard(kind, docUrl, noMention = true) {
|
|
|
2365
2442
|
"\xB7 **@\u6211 + \u5185\u5BB9** \u2192 \u5F00\u4E00\u4E2A\u65B0\u8BDD\u9898\u5E76\u5F00\u59CB\uFF08\u6BCF\u8BDD\u9898\u72EC\u7ACB\u4F1A\u8BDD\uFF09\n\xB7 `/resume` \u2192 \u6062\u590D\u5386\u53F2\u4F1A\u8BDD\n\xB7 `/settings` \u2192 \u7FA4\u8BBE\u7F6E\uFF08\u514D@ \u5F00\u5173\uFF09"
|
|
2366
2443
|
),
|
|
2367
2444
|
md("\u{1F9F5} **\u8BDD\u9898\u5185**"),
|
|
2368
|
-
md(
|
|
2445
|
+
md(
|
|
2446
|
+
"\xB7 \u76F4\u63A5\u53D1\u6D88\u606F\uFF08\u514D@\uFF09\u2192 \u7EE7\u7EED\u5F53\u524D\u4F1A\u8BDD\n\xB7 `/model` \u2192 \u5207\u6362\u6A21\u578B / \u63A8\u7406\u5F3A\u5EA6\n\xB7 `/context` \xB7 `/compact` \u2192 \u770B / \u538B\u7F29\u4E0A\u4E0B\u6587"
|
|
2447
|
+
),
|
|
2369
2448
|
note("\u4EFB\u610F\u573A\u666F\u53D1 `/help` \u770B\u5F53\u524D\u53EF\u7528\u547D\u4EE4\u3002")
|
|
2370
2449
|
);
|
|
2371
2450
|
}
|
|
@@ -2635,7 +2714,80 @@ function escapeInline2(s) {
|
|
|
2635
2714
|
return s.replace(/\s+/g, " ").trim();
|
|
2636
2715
|
}
|
|
2637
2716
|
|
|
2717
|
+
// src/card/context-gauge.ts
|
|
2718
|
+
var CTX_WARN = 0.7;
|
|
2719
|
+
var CTX_HIGH = 0.85;
|
|
2720
|
+
var CTX_CRIT = 0.95;
|
|
2721
|
+
function ctxTier(frac) {
|
|
2722
|
+
if (frac >= CTX_CRIT) return { level: 3, color: "red", dot: "\u{1F534}", advice: "\u5F3A\u70C8\u5EFA\u8BAE `/compact` \u538B\u7F29" };
|
|
2723
|
+
if (frac >= CTX_HIGH) return { level: 2, color: "orange", dot: "\u{1F7E0}", advice: "\u5EFA\u8BAE `/compact` \u538B\u7F29" };
|
|
2724
|
+
if (frac >= CTX_WARN) return { level: 1, color: "yellow", dot: "\u{1F7E1}", advice: "\u53EF\u8003\u8651 `/compact` \u538B\u7F29" };
|
|
2725
|
+
return { level: 0, color: "green", dot: "\u{1F7E2}", advice: "" };
|
|
2726
|
+
}
|
|
2727
|
+
function ctxPercent(used, window) {
|
|
2728
|
+
if (!window || window <= 0) return null;
|
|
2729
|
+
return Math.max(0, Math.min(100, Math.round(used / window * 100)));
|
|
2730
|
+
}
|
|
2731
|
+
function k(n) {
|
|
2732
|
+
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(Math.max(0, Math.round(n)));
|
|
2733
|
+
}
|
|
2734
|
+
function runCardGauge(used, window) {
|
|
2735
|
+
const pct = ctxPercent(used, window);
|
|
2736
|
+
if (pct === null || !window) return null;
|
|
2737
|
+
const frac = used / window;
|
|
2738
|
+
if (frac < CTX_WARN) return null;
|
|
2739
|
+
const t = ctxTier(frac);
|
|
2740
|
+
return colorNote(`${t.dot} \u4E0A\u4E0B\u6587 ${pct}% \xB7 ${k(used)}/${k(window)} \xB7 ${t.advice}`, t.color);
|
|
2741
|
+
}
|
|
2742
|
+
function buildContextCard(used, window) {
|
|
2743
|
+
const pct = ctxPercent(used, window);
|
|
2744
|
+
if (pct === null) {
|
|
2745
|
+
const line = used > 0 ? `\u{1F9E0} \u5DF2\u7528 ${k(used)} tokens\uFF08\u4E0A\u4E0B\u6587\u7A97\u53E3\u672A\u77E5\uFF09` : "\u{1F9E0} \u8FD8\u6CA1\u6709\u7528\u91CF\u6570\u636E\uFF0C\u8DD1\u4E00\u8F6E\u5BF9\u8BDD\u540E\u518D\u770B `/context`\u3002";
|
|
2746
|
+
return card([note(line)], { summary: "\u4E0A\u4E0B\u6587\u7528\u91CF" });
|
|
2747
|
+
}
|
|
2748
|
+
const t = ctxTier(used / window);
|
|
2749
|
+
const els = [colorNote(`${t.dot} **\u4E0A\u4E0B\u6587 ${pct}%** \xB7 ${k(used)}/${k(window)} tokens`, t.color)];
|
|
2750
|
+
els.push(note(t.level >= 1 ? `${t.advice}\uFF1A\u603B\u7ED3\u65E9\u524D\u5BF9\u8BDD\u3001\u91CA\u653E\u7A7A\u95F4\u3002` : "\u7A7A\u95F4\u5145\u8DB3\uFF0C\u65E0\u9700\u538B\u7F29\u3002"));
|
|
2751
|
+
return card(els, { summary: "\u4E0A\u4E0B\u6587\u7528\u91CF" });
|
|
2752
|
+
}
|
|
2753
|
+
var COMPACT_SPINNER = ["\u25D0", "\u25D3", "\u25D1", "\u25D2"];
|
|
2754
|
+
function buildCompactingCard(tick = 0) {
|
|
2755
|
+
const spin = COMPACT_SPINNER[(tick % COMPACT_SPINNER.length + COMPACT_SPINNER.length) % COMPACT_SPINNER.length];
|
|
2756
|
+
return card([colorNote(`\u{1F5DC}\uFE0F \u6B63\u5728\u538B\u7F29\u4E0A\u4E0B\u6587 ${spin}`, "blue"), note("\u603B\u7ED3\u65E9\u524D\u5BF9\u8BDD\u3001\u91CA\u653E\u7A7A\u95F4\uFF0C\u8BF7\u7A0D\u5019\u3002")], {
|
|
2757
|
+
summary: "\u6B63\u5728\u538B\u7F29\u4E0A\u4E0B\u6587"
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
function buildCompactedCard(usage, before) {
|
|
2761
|
+
const els = [colorNote("\u2705 \u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210", "green")];
|
|
2762
|
+
const pct = usage ? ctxPercent(usage.usedTokens, usage.contextWindow) : null;
|
|
2763
|
+
const dropped = usage != null && before != null && usage.usedTokens < before.used;
|
|
2764
|
+
if (usage && pct !== null && usage.contextWindow && (dropped || before == null)) {
|
|
2765
|
+
const beforePct = before ? ctxPercent(before.used, before.window) : null;
|
|
2766
|
+
const from = dropped && beforePct !== null ? `${beforePct}% \u2192 ` : "";
|
|
2767
|
+
els.push(note(`\u65E9\u524D\u5BF9\u8BDD\u5DF2\u603B\u7ED3\u5F52\u6863\uFF0C\u73B0\u5DF2\u7528 ${from}${pct}%\uFF08${k(usage.usedTokens)}/${k(usage.contextWindow)} tokens\uFF09\u3002`));
|
|
2768
|
+
} else {
|
|
2769
|
+
els.push(note("\u65E9\u524D\u5BF9\u8BDD\u5DF2\u603B\u7ED3\u5F52\u6863\u3001\u817E\u51FA\u7A7A\u95F4\u7EE7\u7EED\uFF1B\u53D1\u4E0B\u4E00\u6761\u6D88\u606F\u540E\uFF0C`/context` \u5373\u53EF\u770B\u5230\u5360\u7528\u4E0B\u964D\u3002"));
|
|
2770
|
+
}
|
|
2771
|
+
return card(els, { summary: "\u4E0A\u4E0B\u6587\u538B\u7F29\u5B8C\u6210" });
|
|
2772
|
+
}
|
|
2773
|
+
function buildCompactFailedCard(message) {
|
|
2774
|
+
return card([colorNote(`\u26A0\uFE0F \u538B\u7F29\u5931\u8D25\uFF1A${message}`, "red")], { summary: "\u538B\u7F29\u5931\u8D25" });
|
|
2775
|
+
}
|
|
2776
|
+
function buildAutoCompactCard() {
|
|
2777
|
+
return card(
|
|
2778
|
+
[
|
|
2779
|
+
hr(),
|
|
2780
|
+
colorNote("\u{1F5DC}\uFE0F \u2500\u2500\u2500 \u4E0A\u4E0B\u6587\u5DF2\u81EA\u52A8\u538B\u7F29 \u2500\u2500\u2500", "blue"),
|
|
2781
|
+
note("\u65E9\u524D\u5BF9\u8BDD\u5DF2\u603B\u7ED3\u5F52\u6863\u3001\u817E\u51FA\u7A7A\u95F4\u7EE7\u7EED\uFF1B\u6700\u8FD1\u7684\u4E0A\u4E0B\u6587\u4FDD\u7559\u3002")
|
|
2782
|
+
],
|
|
2783
|
+
{ summary: "\u4E0A\u4E0B\u6587\u5DF2\u81EA\u52A8\u538B\u7F29" }
|
|
2784
|
+
);
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2638
2787
|
// src/card/run-card.ts
|
|
2788
|
+
function gaugeEl(state) {
|
|
2789
|
+
return state.usage ? runCardGauge(state.usage.used, state.usage.window) : null;
|
|
2790
|
+
}
|
|
2639
2791
|
var RC = {
|
|
2640
2792
|
stop: "run.stop"
|
|
2641
2793
|
};
|
|
@@ -2668,6 +2820,8 @@ function renderRunning(state, rc) {
|
|
|
2668
2820
|
if (answer) elements.push(mdStream(answer, ANSWER_EID));
|
|
2669
2821
|
if (state.footer) elements.push(footerStatus(state.footer));
|
|
2670
2822
|
if (rc.cardKey) elements.push(actions([button("\u23F9 \u7EC8\u6B62", { a: RC.stop, m: rc.cardKey }, "danger")]));
|
|
2823
|
+
const gauge = gaugeEl(state);
|
|
2824
|
+
if (gauge) elements.push(gauge);
|
|
2671
2825
|
return elements;
|
|
2672
2826
|
}
|
|
2673
2827
|
function renderTerminal(state, rc) {
|
|
@@ -2701,6 +2855,8 @@ function renderTerminal(state, rc) {
|
|
|
2701
2855
|
} else if (state.terminal === "done" && !answer) {
|
|
2702
2856
|
elements.push(noteMd("_\uFF08\u672A\u8FD4\u56DE\u5185\u5BB9\uFF09_"));
|
|
2703
2857
|
}
|
|
2858
|
+
const gauge = gaugeEl(state);
|
|
2859
|
+
if (gauge) elements.push(gauge);
|
|
2704
2860
|
return elements;
|
|
2705
2861
|
}
|
|
2706
2862
|
function lastTextIndex(blocks) {
|
|
@@ -2914,35 +3070,55 @@ var RunCardStream = class {
|
|
|
2914
3070
|
}
|
|
2915
3071
|
}
|
|
2916
3072
|
/** Create the entity from the initial (running) card and send a message
|
|
2917
|
-
* referencing it by card_id. Returns the carrier message id.
|
|
3073
|
+
* referencing it by card_id. Returns the carrier message id.
|
|
3074
|
+
*
|
|
3075
|
+
* A just-created CardKit entity occasionally hasn't propagated when the message
|
|
3076
|
+
* referencing it is sent — Feishu 400s with 230099 / ErrCode 11310 "cardid is
|
|
3077
|
+
* invalid" and the run card silently fails to appear (this surfaced as
|
|
3078
|
+
* intermittent intake.fail). Same transient, same fix as
|
|
3079
|
+
* {@link ../card/managed#sendManagedCard}: retry the whole create+send with a
|
|
3080
|
+
* short backoff. Only this transient retries — Feishu rejected the message
|
|
3081
|
+
* outright (nothing sent), and a re-created entity that's never referenced is a
|
|
3082
|
+
* harmless orphan, so no duplicate card. */
|
|
2918
3083
|
async create(channel, chatId, initialCard, opts) {
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
const cardId = created.data?.card_id;
|
|
2923
|
-
if (!cardId) {
|
|
2924
|
-
throw new Error(`cardkit.card.create returned no card_id: ${JSON.stringify(created).slice(0, 200)}`);
|
|
2925
|
-
}
|
|
2926
|
-
this.cardId = cardId;
|
|
2927
|
-
this.lastContent = JSON.stringify(initialCard);
|
|
2928
|
-
const content = JSON.stringify({ type: "card", data: { card_id: cardId } });
|
|
2929
|
-
let messageId;
|
|
2930
|
-
if (opts.replyTo) {
|
|
2931
|
-
const r = await channel.rawClient.im.v1.message.reply({
|
|
2932
|
-
path: { message_id: opts.replyTo },
|
|
2933
|
-
data: { msg_type: "interactive", content, reply_in_thread: opts.replyInThread ?? false }
|
|
2934
|
-
});
|
|
2935
|
-
messageId = r.data?.message_id;
|
|
2936
|
-
} else {
|
|
2937
|
-
const r = await channel.rawClient.im.v1.message.create({
|
|
2938
|
-
params: { receive_id_type: "chat_id" },
|
|
2939
|
-
data: { receive_id: chatId, msg_type: "interactive", content }
|
|
3084
|
+
const attempt = async () => {
|
|
3085
|
+
const created = await channel.rawClient.cardkit.v1.card.create({
|
|
3086
|
+
data: { type: "card_json", data: JSON.stringify(initialCard) }
|
|
2940
3087
|
});
|
|
2941
|
-
|
|
3088
|
+
const cardId = created.data?.card_id;
|
|
3089
|
+
if (!cardId) {
|
|
3090
|
+
throw new Error(`cardkit.card.create returned no card_id: ${JSON.stringify(created).slice(0, 200)}`);
|
|
3091
|
+
}
|
|
3092
|
+
this.cardId = cardId;
|
|
3093
|
+
this.lastContent = JSON.stringify(initialCard);
|
|
3094
|
+
const content = JSON.stringify({ type: "card", data: { card_id: cardId } });
|
|
3095
|
+
let messageId;
|
|
3096
|
+
if (opts.replyTo) {
|
|
3097
|
+
const r = await channel.rawClient.im.v1.message.reply({
|
|
3098
|
+
path: { message_id: opts.replyTo },
|
|
3099
|
+
data: { msg_type: "interactive", content, reply_in_thread: opts.replyInThread ?? false }
|
|
3100
|
+
});
|
|
3101
|
+
messageId = r.data?.message_id;
|
|
3102
|
+
} else {
|
|
3103
|
+
const r = await channel.rawClient.im.v1.message.create({
|
|
3104
|
+
params: { receive_id_type: "chat_id" },
|
|
3105
|
+
data: { receive_id: chatId, msg_type: "interactive", content }
|
|
3106
|
+
});
|
|
3107
|
+
messageId = r.data?.message_id;
|
|
3108
|
+
}
|
|
3109
|
+
if (!messageId) throw new Error("run card send returned no message_id");
|
|
3110
|
+
this._messageId = messageId;
|
|
3111
|
+
return messageId;
|
|
3112
|
+
};
|
|
3113
|
+
for (let i = 0; ; i++) {
|
|
3114
|
+
try {
|
|
3115
|
+
return await attempt();
|
|
3116
|
+
} catch (err) {
|
|
3117
|
+
if (i >= 2 || !isCardIdNotReady(err)) throw err;
|
|
3118
|
+
log.fail("card", err, { phase: "run-stream-create", attempt: i, retry: true });
|
|
3119
|
+
await new Promise((r) => setTimeout(r, 400 * (i + 1)));
|
|
3120
|
+
}
|
|
2942
3121
|
}
|
|
2943
|
-
if (!messageId) throw new Error("run card send returned no message_id");
|
|
2944
|
-
this._messageId = messageId;
|
|
2945
|
-
return messageId;
|
|
2946
3122
|
}
|
|
2947
3123
|
/** Throttled whole-card stream update. Skips identical/too-soon pushes;
|
|
2948
3124
|
* `force` flushes regardless (still de-duped on content). */
|
|
@@ -3226,8 +3402,8 @@ async function updateProject(name, patch) {
|
|
|
3226
3402
|
if (!p) return;
|
|
3227
3403
|
const actual = typeof patch === "function" ? patch(p) : patch;
|
|
3228
3404
|
const target = p;
|
|
3229
|
-
for (const [
|
|
3230
|
-
if (v !== void 0) target[
|
|
3405
|
+
for (const [k2, v] of Object.entries(actual)) {
|
|
3406
|
+
if (v !== void 0) target[k2] = v;
|
|
3231
3407
|
}
|
|
3232
3408
|
await write(projects);
|
|
3233
3409
|
});
|
|
@@ -3286,13 +3462,18 @@ var DM = {
|
|
|
3286
3462
|
rmAllowed: "dm.allow.rm",
|
|
3287
3463
|
// 项目设置容器(项目列表 / 建项目完成卡 进入),以后的项目级设置项往这里加
|
|
3288
3464
|
projectSettings: "dm.projectSettings",
|
|
3465
|
+
// 🧵 话题钻取:项目总览的「🧵 N 话题」按钮 → 该项目话题列表卡
|
|
3466
|
+
projectTopics: "dm.projectTopics",
|
|
3289
3467
|
setNoMentionDm: "dm.proj.noMention",
|
|
3468
|
+
// 🗜️ 自动压缩:项目级开关(同群设置里的那个,DM 里也能改),按钮携带项目名 n
|
|
3469
|
+
setAutoCompactDm: "dm.proj.autoCompact",
|
|
3290
3470
|
// 🔐 权限:codex 沙箱档位(管理员档 + 普通用户档)+ 联网,做成下拉表单(选+提交)
|
|
3291
3471
|
permission: "dm.proj.perm",
|
|
3292
3472
|
permissionSubmit: "dm.proj.perm.submit"
|
|
3293
3473
|
};
|
|
3294
3474
|
var GS = {
|
|
3295
|
-
setNoMention: "gs.noMention"
|
|
3475
|
+
setNoMention: "gs.noMention",
|
|
3476
|
+
setAutoCompact: "gs.autoCompact"
|
|
3296
3477
|
};
|
|
3297
3478
|
function kindLabel(kind) {
|
|
3298
3479
|
return kind === "single" ? "\u{1F4AC} \u5355\u4F1A\u8BDD\u7FA4" : "\u{1F465} \u591A\u8BDD\u9898\u7FA4";
|
|
@@ -3565,6 +3746,7 @@ function buildNewProjectDoneCard(p) {
|
|
|
3565
3746
|
);
|
|
3566
3747
|
return card(elements, { header: { title, template: "green" } });
|
|
3567
3748
|
}
|
|
3749
|
+
var PROJECT_TOPICS_MAX = 50;
|
|
3568
3750
|
function buildProjectListCard(projects, sessionsByChat = /* @__PURE__ */ new Map()) {
|
|
3569
3751
|
if (projects.length === 0) {
|
|
3570
3752
|
return card(
|
|
@@ -3574,25 +3756,15 @@ function buildProjectListCard(projects, sessionsByChat = /* @__PURE__ */ new Map
|
|
|
3574
3756
|
}
|
|
3575
3757
|
const elements = [];
|
|
3576
3758
|
for (const p of projects) {
|
|
3759
|
+
const topicCount = (p.chatId ? sessionsByChat.get(p.chatId) : void 0)?.length ?? 0;
|
|
3760
|
+
const dir = `\u{1F4C2} \`${p.cwd}\`${p.branch && p.branch !== "\u2014" ? ` \u{1F33F} ${p.branch}` : ""}`;
|
|
3761
|
+
const meta = p.chatId ? `${kindLabel(p.kind)}${(p.origin ?? "created") === "joined" ? " \xB7 \u{1F517}\u5DF2\u52A0\u5165" : ""} \xB7 \u514D@\uFF1A${p.noMention ?? defaultNoMention(p) ? "\u5F00" : "\u5173"}` : "\u26A0\uFE0F \u672A\u7ED1\u5B9A\u7FA4";
|
|
3577
3762
|
elements.push(md(`**${p.name}**${p.blank ? " _(\u7A7A\u767D)_" : ""}`));
|
|
3578
|
-
elements.push(note(
|
|
3579
|
-
|
|
3580
|
-
note(
|
|
3581
|
-
p.chatId ? `\u{1F4AC} \u7FA4\uFF1A**${p.name}** \xB7 ${kindLabel(p.kind)}${(p.origin ?? "created") === "joined" ? " \xB7 \u{1F517}\u5DF2\u52A0\u5165" : ""} \xB7 \u514D@\uFF1A${p.noMention ?? defaultNoMention(p) ? "\u5F00" : "\u5173"}` : "\u26A0\uFE0F \u672A\u7ED1\u5B9A\u7FA4"
|
|
3582
|
-
)
|
|
3583
|
-
);
|
|
3584
|
-
const sessions = (p.chatId ? sessionsByChat.get(p.chatId) : void 0) ?? [];
|
|
3585
|
-
if (sessions.length === 0) {
|
|
3586
|
-
elements.push(note("\uFF08\u6682\u65E0\u8BDD\u9898\uFF09"));
|
|
3587
|
-
} else {
|
|
3588
|
-
const sorted = [...sessions].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
3589
|
-
for (const s of sorted) {
|
|
3590
|
-
const title = (s.summary || "(\u7A7A)").replace(/\s+/g, " ").slice(0, 40);
|
|
3591
|
-
elements.push(note(`\xB7 ${title} \xB7 ${relativeTime(s.updatedAt)}`));
|
|
3592
|
-
}
|
|
3593
|
-
}
|
|
3763
|
+
elements.push(note(`${dir}
|
|
3764
|
+
${meta}`));
|
|
3594
3765
|
const row = [];
|
|
3595
3766
|
if (p.chatId) row.push(linkButton("\u{1F4AC} \u6253\u5F00\u7FA4\u804A", openChatUrl(p.chatId)));
|
|
3767
|
+
row.push(button(`\u{1F9F5} ${topicCount} \u8BDD\u9898`, { a: DM.projectTopics, n: p.name }));
|
|
3596
3768
|
row.push(button("\u2699\uFE0F \u8BBE\u7F6E", { a: DM.projectSettings, n: p.name }));
|
|
3597
3769
|
row.push(button("\u{1F5D1} \u5220\u9664", { a: DM.rmConfirm, n: p.name }, "danger"));
|
|
3598
3770
|
elements.push(actions(row));
|
|
@@ -3602,6 +3774,26 @@ function buildProjectListCard(projects, sessionsByChat = /* @__PURE__ */ new Map
|
|
|
3602
3774
|
elements.push(actions([button("\u2B05\uFE0F \u83DC\u5355", { a: DM.menu })]));
|
|
3603
3775
|
return card(elements, { header: { title: "\u{1F4C1} \u9879\u76EE\u5217\u8868", template: "wathet" } });
|
|
3604
3776
|
}
|
|
3777
|
+
function buildProjectTopicsCard(project, sessions) {
|
|
3778
|
+
const elements = [md(`**${project.name}** \xB7 \u5171 ${sessions.length} \u4E2A\u8BDD\u9898`)];
|
|
3779
|
+
if (sessions.length === 0) {
|
|
3780
|
+
elements.push(note("\uFF08\u6682\u65E0\u8BDD\u9898\uFF09"));
|
|
3781
|
+
} else {
|
|
3782
|
+
const sorted = [...sessions].sort((a, b) => b.updatedAt - a.updatedAt);
|
|
3783
|
+
for (const s of sorted.slice(0, PROJECT_TOPICS_MAX)) {
|
|
3784
|
+
const title = (s.summary || "(\u7A7A)").replace(/\s+/g, " ").slice(0, 50);
|
|
3785
|
+
elements.push(note(`\xB7 ${title} \xB7 ${relativeTime(s.updatedAt)}`));
|
|
3786
|
+
}
|
|
3787
|
+
if (sorted.length > PROJECT_TOPICS_MAX) {
|
|
3788
|
+
elements.push(note(`\xB7 \u2026\u8FD8\u6709 ${sorted.length - PROJECT_TOPICS_MAX} \u4E2A\u8BDD\u9898\uFF08\u66F4\u65E9\u7684\u53EF\u5728\u7FA4\u91CC \`/resume\` \u6062\u590D\uFF09`));
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
const nav = [];
|
|
3792
|
+
if (project.chatId) nav.push(linkButton("\u{1F4AC} \u6253\u5F00\u7FA4\u804A", openChatUrl(project.chatId)));
|
|
3793
|
+
nav.push(button("\u2B05\uFE0F \u9879\u76EE\u5217\u8868", { a: DM.projects }));
|
|
3794
|
+
elements.push(hr(), actions(nav));
|
|
3795
|
+
return card(elements, { header: { title: `\u{1F9F5} \u8BDD\u9898 \xB7 ${project.name}`, template: "wathet" } });
|
|
3796
|
+
}
|
|
3605
3797
|
function buildRmConfirmCard(name, origin) {
|
|
3606
3798
|
const note_ = (origin ?? "created") === "joined" ? "\u4EC5\u89E3\u7ED1\uFF08\u79FB\u9664\u6CE8\u518C\uFF09\uFF0C**\u4E0D\u5220\u4EE3\u7801\u76EE\u5F55**\u3002\u786E\u8BA4\u540E**\u6211\u4F1A\u9000\u51FA\u8BE5\u7FA4**\uFF08\u7FA4\u662F\u4F60\u4EEC\u7684\uFF0C\u4E0D\u4F1A\u89E3\u6563\uFF09\u3002" : "\u4EC5\u89E3\u7ED1\uFF08\u79FB\u9664\u6CE8\u518C + \u64A4\u9500\u7F6E\u9876\u6A2A\u5E45\uFF09\uFF0C**\u4E0D\u5220\u4EE3\u7801\u76EE\u5F55**\u3002\u7FA4\u4E3B\u4F1A\u8F6C\u7ED9\u4F60\uFF0C\u518D\u7531\u4F60\u81EA\u884C\u5728\u98DE\u4E66\u89E3\u6563\u7FA4\u3002";
|
|
3607
3799
|
return card(
|
|
@@ -3675,6 +3867,7 @@ function buildWatchdogCustomCard(cfg) {
|
|
|
3675
3867
|
function buildGroupSettingsCard(project) {
|
|
3676
3868
|
const kind = project.kind ?? "multi";
|
|
3677
3869
|
const noMention = project.noMention ?? defaultNoMention(project);
|
|
3870
|
+
const autoCompact = project.autoCompact ?? true;
|
|
3678
3871
|
const scopeNote = kind === "single" ? "\u5F00\u542F\u540E\uFF1A\u672C\u7FA4\u6240\u6709\u6D88\u606F(\u4E0D\u7528 @)\u90FD\u4EA4\u7ED9\u6211\u5904\u7406\u3002" : "\u5F00\u542F\u540E\uFF1A\u8BDD\u9898\u5185\u7684\u6D88\u606F(\u4E0D\u7528 @)\u90FD\u4EA4\u7ED9\u6211\u5904\u7406\uFF1B**\u5F00\u65B0\u8BDD\u9898\u4ECD\u9700 @\u6211**\u3002";
|
|
3679
3872
|
return card(
|
|
3680
3873
|
[
|
|
@@ -3685,7 +3878,12 @@ function buildGroupSettingsCard(project) {
|
|
|
3685
3878
|
{ label: "\u5173", value: "off" }
|
|
3686
3879
|
]),
|
|
3687
3880
|
note(scopeNote),
|
|
3688
|
-
note("\u26A0\uFE0F \u514D@ \u9700\u5E94\u7528\u5DF2\u5F00\u901A\u300C\u63A5\u6536\u7FA4\u5185\u6240\u6709\u6D88\u606F\u300D(im:message.group_msg)\u6743\u9650\uFF0C\u5426\u5219\u6536\u4E0D\u5230\u975E @ \u6D88\u606F\u3002")
|
|
3881
|
+
note("\u26A0\uFE0F \u514D@ \u9700\u5E94\u7528\u5DF2\u5F00\u901A\u300C\u63A5\u6536\u7FA4\u5185\u6240\u6709\u6D88\u606F\u300D(im:message.group_msg)\u6743\u9650\uFF0C\u5426\u5219\u6536\u4E0D\u5230\u975E @ \u6D88\u606F\u3002"),
|
|
3882
|
+
...optionRow("\u{1F5DC}\uFE0F \u81EA\u52A8\u538B\u7F29\u4E0A\u4E0B\u6587", GS.setAutoCompact, autoCompact ? "on" : "off", [
|
|
3883
|
+
{ label: "\u5F00", value: "on" },
|
|
3884
|
+
{ label: "\u5173", value: "off" }
|
|
3885
|
+
]),
|
|
3886
|
+
note("\u5F00\u542F\u540E\uFF1A\u4E0A\u4E0B\u6587\u63A5\u8FD1\u4E0A\u9650\u65F6 Codex \u81EA\u52A8\u603B\u7ED3\u65E9\u524D\u5BF9\u8BDD\u3001\u91CA\u653E\u7A7A\u95F4\uFF08\u9ED8\u8BA4\u5F00\uFF09\u3002\u6539\u52A8\u4E0B\u4E00\u8F6E\u4F1A\u8BDD\u751F\u6548\u3002")
|
|
3689
3887
|
],
|
|
3690
3888
|
{ header: { title: "\u2699\uFE0F \u7FA4\u8BBE\u7F6E", template: "blue" } }
|
|
3691
3889
|
);
|
|
@@ -3803,6 +4001,7 @@ function buildPermissionCard(p) {
|
|
|
3803
4001
|
function buildProjectSettingsCard(project) {
|
|
3804
4002
|
const kind = project.kind ?? "multi";
|
|
3805
4003
|
const noMention = project.noMention ?? defaultNoMention(project);
|
|
4004
|
+
const autoCompact = project.autoCompact ?? true;
|
|
3806
4005
|
return card(
|
|
3807
4006
|
[
|
|
3808
4007
|
md(`**\u9879\u76EE\u8BBE\u7F6E** \xB7 ${project.name}`),
|
|
@@ -3820,6 +4019,13 @@ function buildProjectSettingsCard(project) {
|
|
|
3820
4019
|
kind === "single" ? "\u5F00\u542F\u540E\uFF1A\u672C\u7FA4\u6240\u6709\u6D88\u606F(\u4E0D\u7528 @)\u90FD\u4EA4\u7ED9\u6211\u5904\u7406\u3002" : "\u5F00\u542F\u540E\uFF1A\u8BDD\u9898\u5185\u6D88\u606F(\u4E0D\u7528 @)\u90FD\u5904\u7406\uFF1B**\u5F00\u65B0\u8BDD\u9898\u4ECD\u9700 @\u6211**\u3002"
|
|
3821
4020
|
),
|
|
3822
4021
|
hr(),
|
|
4022
|
+
md("\u{1F5DC}\uFE0F \u81EA\u52A8\u538B\u7F29\u4E0A\u4E0B\u6587"),
|
|
4023
|
+
actions([
|
|
4024
|
+
button("\u5F00", { a: DM.setAutoCompactDm, v: "on", n: project.name }, autoCompact ? "primary" : "default"),
|
|
4025
|
+
button("\u5173", { a: DM.setAutoCompactDm, v: "off", n: project.name }, autoCompact ? "default" : "primary")
|
|
4026
|
+
]),
|
|
4027
|
+
note("\u5F00\u542F\u540E\uFF1A\u4E0A\u4E0B\u6587\u63A5\u8FD1\u4E0A\u9650\u65F6 Codex \u81EA\u52A8\u603B\u7ED3\u65E9\u524D\u5BF9\u8BDD\u3001\u91CA\u653E\u7A7A\u95F4\uFF08\u9ED8\u8BA4\u5F00\uFF09\u3002\u6539\u52A8\u4E0B\u4E00\u8F6E\u4F1A\u8BDD\u751F\u6548\u3002"),
|
|
4028
|
+
hr(),
|
|
3823
4029
|
actions([button("\u{1F6E1} \u54CD\u5E94\u767D\u540D\u5355", { a: DM.allowlist, n: project.name }, "primary")]),
|
|
3824
4030
|
note("\u8BBE\u7F6E\u8C01\u80FD\u8BA9\u6211\u5728\u672C\u7FA4\u54CD\u5E94 / \u8DD1 codex\uFF08\u7A7A = \u6240\u6709\u4EBA\uFF09\u3002"),
|
|
3825
4031
|
hr(),
|
|
@@ -5365,8 +5571,8 @@ async function patchSession(threadId, patch) {
|
|
|
5365
5571
|
const rec = sessions.find((s) => s.threadId === threadId);
|
|
5366
5572
|
if (!rec) return;
|
|
5367
5573
|
const target = rec;
|
|
5368
|
-
for (const [
|
|
5369
|
-
if (v !== void 0) target[
|
|
5574
|
+
for (const [k2, v] of Object.entries(patch)) {
|
|
5575
|
+
if (v !== void 0) target[k2] = v;
|
|
5370
5576
|
}
|
|
5371
5577
|
rec.updatedAt = Date.now();
|
|
5372
5578
|
await write2(sessions);
|
|
@@ -5484,7 +5690,7 @@ function walkForImageKeys(node, out) {
|
|
|
5484
5690
|
}
|
|
5485
5691
|
const obj = node;
|
|
5486
5692
|
if (obj.tag === "img" && typeof obj.image_key === "string") out.push(obj.image_key);
|
|
5487
|
-
for (const
|
|
5693
|
+
for (const k2 of Object.keys(obj)) walkForImageKeys(obj[k2], out);
|
|
5488
5694
|
}
|
|
5489
5695
|
async function downloadOne(channel, ref, index) {
|
|
5490
5696
|
try {
|
|
@@ -6125,6 +6331,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6125
6331
|
const runCards = /* @__PURE__ */ new Map();
|
|
6126
6332
|
const runStreams = /* @__PURE__ */ new Map();
|
|
6127
6333
|
const lastRunCard = /* @__PURE__ */ new Map();
|
|
6334
|
+
const lastUsage = /* @__PURE__ */ new Map();
|
|
6128
6335
|
let modelsCache = null;
|
|
6129
6336
|
async function listModels() {
|
|
6130
6337
|
if (!modelsCache) modelsCache = await backend.listModels();
|
|
@@ -6220,6 +6427,14 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6220
6427
|
await postModelCard(msg, ts.sessionKey);
|
|
6221
6428
|
return;
|
|
6222
6429
|
}
|
|
6430
|
+
if (cmd === "compact") {
|
|
6431
|
+
await runCompact(msg, ts.sessionKey, false, ts);
|
|
6432
|
+
return;
|
|
6433
|
+
}
|
|
6434
|
+
if (cmd === "context") {
|
|
6435
|
+
await postContextCard(msg, ts.sessionKey, false);
|
|
6436
|
+
return;
|
|
6437
|
+
}
|
|
6223
6438
|
handleTurn(msg, text, ts.sessionKey, true, project, ts);
|
|
6224
6439
|
return;
|
|
6225
6440
|
}
|
|
@@ -6233,6 +6448,14 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6233
6448
|
await postModelCard(msg, ts.sessionKey);
|
|
6234
6449
|
return;
|
|
6235
6450
|
}
|
|
6451
|
+
if (cmd === "compact") {
|
|
6452
|
+
await runCompact(msg, ts.sessionKey, true, ts);
|
|
6453
|
+
return;
|
|
6454
|
+
}
|
|
6455
|
+
if (cmd === "context") {
|
|
6456
|
+
await postContextCard(msg, ts.sessionKey, true);
|
|
6457
|
+
return;
|
|
6458
|
+
}
|
|
6236
6459
|
handleTurn(msg, text, ts.sessionKey, false, project, ts);
|
|
6237
6460
|
return;
|
|
6238
6461
|
}
|
|
@@ -6248,8 +6471,8 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6248
6471
|
await postGroupSettings(msg, project);
|
|
6249
6472
|
return;
|
|
6250
6473
|
}
|
|
6251
|
-
if (cmd === "model") {
|
|
6252
|
-
await channel.send(msg.chatId, { markdown:
|
|
6474
|
+
if (cmd === "model" || cmd === "compact" || cmd === "context") {
|
|
6475
|
+
await channel.send(msg.chatId, { markdown: `\`/${cmd}\` \u9700\u8981\u5728\u8BDD\u9898\u91CC\u4F7F\u7528\uFF08\u5148 @\u6211 \u5F00\u4E2A\u8BDD\u9898\uFF09\u3002` }, { replyTo: msg.messageId }).catch(() => void 0);
|
|
6253
6476
|
return;
|
|
6254
6477
|
}
|
|
6255
6478
|
startTopicDirectly(msg, text, project);
|
|
@@ -6257,7 +6480,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6257
6480
|
function parseCommand(text) {
|
|
6258
6481
|
const m = /^\/(\w+)/.exec(text);
|
|
6259
6482
|
const name = m?.[1]?.toLowerCase();
|
|
6260
|
-
return name === "resume" || name === "model" || name === "settings" || name === "help" ? name : null;
|
|
6483
|
+
return name === "resume" || name === "model" || name === "settings" || name === "help" || name === "compact" || name === "context" ? name : null;
|
|
6261
6484
|
}
|
|
6262
6485
|
function shouldRespondWithoutMention(project, msg) {
|
|
6263
6486
|
if (!(project.noMention ?? defaultNoMention(project))) return false;
|
|
@@ -6286,7 +6509,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6286
6509
|
function turnPerm(project, senderId) {
|
|
6287
6510
|
if (!project) return {};
|
|
6288
6511
|
const t = turnTier(project, isAdmin(cfg, senderId));
|
|
6289
|
-
return { mode: t.mode, network: project.network, roleSuffix: t.split ? t.role : void 0 };
|
|
6512
|
+
return { mode: t.mode, network: project.network, autoCompact: project.autoCompact, roleSuffix: t.split ? t.role : void 0 };
|
|
6290
6513
|
}
|
|
6291
6514
|
function turnSession(baseKey, project, senderId) {
|
|
6292
6515
|
const perm = turnPerm(project, senderId);
|
|
@@ -6351,7 +6574,8 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6351
6574
|
let firstText = preIngested ? text : await ingestContext(msg, text);
|
|
6352
6575
|
const { thread: resolved, recreated } = await resolveThread(sessionKey, msg.chatId, {
|
|
6353
6576
|
mode: perm.mode,
|
|
6354
|
-
network: perm.network
|
|
6577
|
+
network: perm.network,
|
|
6578
|
+
autoCompact: perm.autoCompact
|
|
6355
6579
|
});
|
|
6356
6580
|
let thread = resolved;
|
|
6357
6581
|
const neverSeen = !thread;
|
|
@@ -6359,7 +6583,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6359
6583
|
const prior = neverSeen ? void 0 : await getSession(sessionKey);
|
|
6360
6584
|
if (!thread) {
|
|
6361
6585
|
const cwd = project?.cwd ?? fallbackCwd;
|
|
6362
|
-
thread = await backend.startThread({ cwd, mode: perm.mode, network: perm.network });
|
|
6586
|
+
thread = await backend.startThread({ cwd, mode: perm.mode, network: perm.network, autoCompact: perm.autoCompact });
|
|
6363
6587
|
sessions.set(sessionKey, thread);
|
|
6364
6588
|
await upsertSession({
|
|
6365
6589
|
threadId: sessionKey,
|
|
@@ -6418,7 +6642,8 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6418
6642
|
model: rec.model,
|
|
6419
6643
|
effort: rec.effort,
|
|
6420
6644
|
mode: perm?.mode,
|
|
6421
|
-
network: perm?.network
|
|
6645
|
+
network: perm?.network,
|
|
6646
|
+
autoCompact: perm?.autoCompact
|
|
6422
6647
|
});
|
|
6423
6648
|
sessions.set(threadId, resumed);
|
|
6424
6649
|
return { thread: resumed, recreated: false };
|
|
@@ -6431,7 +6656,8 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6431
6656
|
model: rec.model,
|
|
6432
6657
|
effort: rec.effort,
|
|
6433
6658
|
mode: perm?.mode ?? project?.mode,
|
|
6434
|
-
network: perm?.network ?? project?.network
|
|
6659
|
+
network: perm?.network ?? project?.network,
|
|
6660
|
+
autoCompact: perm?.autoCompact ?? project?.autoCompact
|
|
6435
6661
|
});
|
|
6436
6662
|
sessions.set(threadId, fresh);
|
|
6437
6663
|
await patchSession(threadId, { codexThreadId: fresh.codexThreadId }).catch(() => void 0);
|
|
@@ -6459,7 +6685,7 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6459
6685
|
const { model, effort } = pickDefault(await listModels());
|
|
6460
6686
|
let thread;
|
|
6461
6687
|
try {
|
|
6462
|
-
thread = await backend.startThread({ cwd, model, effort, mode: perm.mode, network: perm.network });
|
|
6688
|
+
thread = await backend.startThread({ cwd, model, effort, mode: perm.mode, network: perm.network, autoCompact: perm.autoCompact });
|
|
6463
6689
|
} catch (err) {
|
|
6464
6690
|
reaction.done();
|
|
6465
6691
|
log.fail("card", err, { phase: "start-topic" });
|
|
@@ -6533,6 +6759,76 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6533
6759
|
log.info("card", "model", { threadId: sessionKey, model: state.model, effort: state.effort });
|
|
6534
6760
|
});
|
|
6535
6761
|
}
|
|
6762
|
+
async function postContextCard(msg, sessionKey, inThread) {
|
|
6763
|
+
const u = lastUsage.get(sessionKey);
|
|
6764
|
+
await sendManagedCard(channel, msg.chatId, buildContextCard(u?.used ?? 0, u?.window ?? null), msg.messageId, inThread).catch(
|
|
6765
|
+
(err) => log.fail("card", err, { phase: "context" })
|
|
6766
|
+
);
|
|
6767
|
+
}
|
|
6768
|
+
const COMPACT_ANIM_INTERVAL_MS = 800;
|
|
6769
|
+
async function runCompact(msg, sessionKey, inThread, perm) {
|
|
6770
|
+
const reply = (markdown) => channel.send(msg.chatId, { markdown }, { replyTo: msg.messageId, replyInThread: inThread }).then(() => void 0, () => void 0);
|
|
6771
|
+
if (active.get(sessionKey)) {
|
|
6772
|
+
await reply("\u23F3 \u8FD9\u4E00\u8F6E\u8FD8\u5728\u8DD1\uFF0C\u7ED3\u675F\u540E\u518D `/compact`\u3002");
|
|
6773
|
+
return;
|
|
6774
|
+
}
|
|
6775
|
+
const { thread } = await resolveThread(sessionKey, msg.chatId, {
|
|
6776
|
+
mode: perm.mode,
|
|
6777
|
+
network: perm.network,
|
|
6778
|
+
autoCompact: perm.autoCompact
|
|
6779
|
+
});
|
|
6780
|
+
if (!thread) {
|
|
6781
|
+
await reply("\u8FD9\u4E2A\u4F1A\u8BDD\u8FD8\u6CA1\u5F00\u59CB\uFF0C\u5148\u53D1\u6761\u6D88\u606F\u804A\u4E24\u53E5\u518D `/compact`\u3002");
|
|
6782
|
+
return;
|
|
6783
|
+
}
|
|
6784
|
+
let cardMsgId;
|
|
6785
|
+
try {
|
|
6786
|
+
const sent = await sendManagedCard(channel, msg.chatId, buildCompactingCard(0), msg.messageId, inThread);
|
|
6787
|
+
cardMsgId = sent.messageId;
|
|
6788
|
+
} catch (err) {
|
|
6789
|
+
log.fail("card", err, { phase: "compact-start-card" });
|
|
6790
|
+
}
|
|
6791
|
+
let stop = false;
|
|
6792
|
+
const wakers = [];
|
|
6793
|
+
const sleep = (ms) => new Promise((res) => {
|
|
6794
|
+
const t = setTimeout(res, ms);
|
|
6795
|
+
wakers.push(() => {
|
|
6796
|
+
clearTimeout(t);
|
|
6797
|
+
res();
|
|
6798
|
+
});
|
|
6799
|
+
});
|
|
6800
|
+
const anim = (async () => {
|
|
6801
|
+
let tick = 0;
|
|
6802
|
+
while (!stop && cardMsgId) {
|
|
6803
|
+
await sleep(COMPACT_ANIM_INTERVAL_MS);
|
|
6804
|
+
if (stop || !cardMsgId) break;
|
|
6805
|
+
tick++;
|
|
6806
|
+
await updateManagedCard(channel, cardMsgId, buildCompactingCard(tick)).catch(() => void 0);
|
|
6807
|
+
}
|
|
6808
|
+
})();
|
|
6809
|
+
const settle = async (result) => {
|
|
6810
|
+
stop = true;
|
|
6811
|
+
wakers.forEach((w) => w());
|
|
6812
|
+
await anim;
|
|
6813
|
+
if (cardMsgId && await updateManagedCard(channel, cardMsgId, result)) return;
|
|
6814
|
+
await sendManagedCard(channel, msg.chatId, result, msg.messageId, inThread).catch(
|
|
6815
|
+
(err) => log.fail("card", err, { phase: "compact-settle" })
|
|
6816
|
+
);
|
|
6817
|
+
};
|
|
6818
|
+
const before = lastUsage.get(sessionKey) ?? null;
|
|
6819
|
+
try {
|
|
6820
|
+
const { usage } = await thread.compact();
|
|
6821
|
+
if (usage) lastUsage.set(sessionKey, { used: usage.usedTokens, window: usage.contextWindow });
|
|
6822
|
+
else lastUsage.delete(sessionKey);
|
|
6823
|
+
log.info("intake", "compact", { sessionKey, used: usage?.usedTokens ?? null, before: before?.used ?? null });
|
|
6824
|
+
await settle(buildCompactedCard(usage, before));
|
|
6825
|
+
} catch (err) {
|
|
6826
|
+
const m = err instanceof Error ? err.message : String(err);
|
|
6827
|
+
const unsupported = /method not found|-32601|unknown (method|request)/i.test(m);
|
|
6828
|
+
log.fail("intake", err, { phase: "compact" });
|
|
6829
|
+
await settle(buildCompactFailedCard(unsupported ? "\u5F53\u524D codex \u7248\u672C\u4E0D\u652F\u6301 /compact\uFF0C\u8BF7\u5347\u7EA7\u540E\u518D\u8BD5\u3002" : m));
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
6536
6832
|
async function postHelpCard(msg, scope, inThread = false, project) {
|
|
6537
6833
|
const noMention = project ? project.noMention ?? defaultNoMention(project) : true;
|
|
6538
6834
|
await withTrace({ chatId: msg.chatId, msgId: msg.messageId }, async () => {
|
|
@@ -6548,24 +6844,26 @@ function createOrchestrator(channel, cfg, fallbackCwd) {
|
|
|
6548
6844
|
const settleUpdate = (msgId, c, fallbackChatId) => {
|
|
6549
6845
|
const armedAt = Date.now();
|
|
6550
6846
|
void (async () => {
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6847
|
+
try {
|
|
6848
|
+
await new Promise((r) => setTimeout(r, CARD_SETTLE_MS));
|
|
6849
|
+
const card2 = typeof c === "function" ? await c() : c;
|
|
6850
|
+
const ok = await updateManagedCard(channel, msgId, card2);
|
|
6851
|
+
log.info("console", "settle-update", { msgId, ok, waitedMs: Date.now() - armedAt, fallback: !ok && !!fallbackChatId });
|
|
6852
|
+
if (!ok && fallbackChatId) {
|
|
6853
|
+
await sendManagedCard(channel, fallbackChatId, card2);
|
|
6854
|
+
}
|
|
6855
|
+
} catch (err) {
|
|
6856
|
+
log.fail("console", err, { phase: "settle-update", msgId });
|
|
6559
6857
|
}
|
|
6560
6858
|
})();
|
|
6561
6859
|
};
|
|
6562
6860
|
function pruneResumePending() {
|
|
6563
6861
|
const now = Date.now();
|
|
6564
|
-
for (const [
|
|
6862
|
+
for (const [k2, s] of resumePending) if (now - s.createdAt > PENDING_TTL_MS) resumePending.delete(k2);
|
|
6565
6863
|
}
|
|
6566
6864
|
function pruneModelPending() {
|
|
6567
6865
|
const now = Date.now();
|
|
6568
|
-
for (const [
|
|
6866
|
+
for (const [k2, s] of modelPending) if (now - s.createdAt > PENDING_TTL_MS) modelPending.delete(k2);
|
|
6569
6867
|
}
|
|
6570
6868
|
function authPending(map, evt) {
|
|
6571
6869
|
const state = map.get(evt.messageId);
|
|
@@ -6929,6 +7227,19 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
6929
7227
|
}
|
|
6930
7228
|
return buildGroupSettingsCard({ name: "\u672C\u7FA4", kind: "multi", noMention: on });
|
|
6931
7229
|
});
|
|
7230
|
+
}).on(GS.setAutoCompact, ({ evt, value }) => {
|
|
7231
|
+
if (!isAdmin(cfg, evt.operator?.openId ?? "")) return;
|
|
7232
|
+
const on = value.v === "on";
|
|
7233
|
+
patch(evt, async () => {
|
|
7234
|
+
const project = await getProjectByChatId(evt.chatId);
|
|
7235
|
+
if (project) {
|
|
7236
|
+
await updateProject(project.name, { autoCompact: on });
|
|
7237
|
+
await evictLiveSessionsForChat(project.chatId);
|
|
7238
|
+
log.info("console", "group-autocompact", { project: project.name, on });
|
|
7239
|
+
return buildGroupSettingsCard({ ...project, autoCompact: on });
|
|
7240
|
+
}
|
|
7241
|
+
return buildGroupSettingsCard({ name: "\u672C\u7FA4", kind: "multi", autoCompact: on });
|
|
7242
|
+
});
|
|
6932
7243
|
}).on(DM.admins, ({ evt }) => {
|
|
6933
7244
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
6934
7245
|
patch(
|
|
@@ -7020,6 +7331,15 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
7020
7331
|
const p = await getProjectByName(name);
|
|
7021
7332
|
return p ? buildProjectSettingsCard(p) : buildDmMenuCard();
|
|
7022
7333
|
});
|
|
7334
|
+
}).on(DM.projectTopics, ({ evt, value }) => {
|
|
7335
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
7336
|
+
const name = typeof value.n === "string" ? value.n : "";
|
|
7337
|
+
patch(evt, async () => {
|
|
7338
|
+
const p = await getProjectByName(name);
|
|
7339
|
+
if (!p) return buildDmMenuCard();
|
|
7340
|
+
const sessions2 = (await listSessions()).filter((s) => s.chatId === p.chatId);
|
|
7341
|
+
return buildProjectTopicsCard(p, sessions2);
|
|
7342
|
+
});
|
|
7023
7343
|
}).on(DM.setNoMentionDm, ({ evt, value }) => {
|
|
7024
7344
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
7025
7345
|
const name = typeof value.n === "string" ? value.n : "";
|
|
@@ -7030,6 +7350,18 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
7030
7350
|
await updateProject(name, { noMention: on });
|
|
7031
7351
|
return buildProjectSettingsCard({ ...p, noMention: on });
|
|
7032
7352
|
});
|
|
7353
|
+
}).on(DM.setAutoCompactDm, ({ evt, value }) => {
|
|
7354
|
+
if (!dmAdmin(evt.operator?.openId)) return;
|
|
7355
|
+
const name = typeof value.n === "string" ? value.n : "";
|
|
7356
|
+
const on = value.v === "on";
|
|
7357
|
+
patch(evt, async () => {
|
|
7358
|
+
const p = await getProjectByName(name);
|
|
7359
|
+
if (!p) return buildDmMenuCard();
|
|
7360
|
+
await updateProject(name, { autoCompact: on });
|
|
7361
|
+
await evictLiveSessionsForChat(p.chatId);
|
|
7362
|
+
log.info("console", "project-autocompact", { project: name, on });
|
|
7363
|
+
return buildProjectSettingsCard({ ...p, autoCompact: on });
|
|
7364
|
+
});
|
|
7033
7365
|
}).on(DM.permission, ({ evt, value }) => {
|
|
7034
7366
|
if (!dmAdmin(evt.operator?.openId)) return;
|
|
7035
7367
|
const name = typeof value.n === "string" ? value.n : "";
|
|
@@ -7221,6 +7553,14 @@ ${tail}` }, { replyTo: evt.messageId }).catch(() => void 0);
|
|
|
7221
7553
|
}
|
|
7222
7554
|
lastEvAt = tEv;
|
|
7223
7555
|
evCount++;
|
|
7556
|
+
if (et === "context_usage" && topicThreadId) {
|
|
7557
|
+
const cu = ev;
|
|
7558
|
+
lastUsage.set(topicThreadId, { used: cu.usedTokens, window: cu.contextWindow });
|
|
7559
|
+
} else if (et === "context_compacted") {
|
|
7560
|
+
void sendManagedCard(channel, opts.chatId, buildAutoCompactCard(), cardMsgId, !opts.flat).catch(
|
|
7561
|
+
(err) => log.fail("card", err, { phase: "auto-compact-notice" })
|
|
7562
|
+
);
|
|
7563
|
+
}
|
|
7224
7564
|
render.apply(ev);
|
|
7225
7565
|
rc.rs = render.snapshot();
|
|
7226
7566
|
stream2.streamCoalesced(channel, buildRunCard(rc), ANSWER_EID);
|