deepseeker-code 1.0.62 → 1.0.64
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.mjs +1712 -348
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -624,7 +624,13 @@ var init_config = __esm({
|
|
|
624
624
|
* 之前生效——门禁/审批评估的是改写后参数)、PostToolUse 可改 resultForModel(仅模型视图,
|
|
625
625
|
* 用户视图 resultForUser 不变)。关闭时 PreToolUse 回退旧位(审批后、仅 deny),改写字段全忽略,
|
|
626
626
|
* 行为与改造前一致。env DEEP_SEEK_HOOK_REWRITE=0 回退。 */
|
|
627
|
-
hookRewrite: process.env.DEEP_SEEK_HOOK_REWRITE !== "0"
|
|
627
|
+
hookRewrite: process.env.DEEP_SEEK_HOOK_REWRITE !== "0",
|
|
628
|
+
/** ★ 外部记忆桥接开关(External Memory Bridge,2026-11-02):true 时 loader 在 bootstrap 把其他 agent 的
|
|
629
|
+
* 规则/指令文件(~/.claude/CLAUDE.md、~/.codex/AGENTS.md、~/.gemini/GEMINI.md、<cwd>/CLAUDE.md、AGENTS.md、
|
|
630
|
+
* GEMINI.md、.cursor/rules/*.md、.github/copilot-instructions.md,清单硬编码)以只读 ext- 前缀条目纳入记忆索引。
|
|
631
|
+
* 默认 false——关闭时记忆行为与未引入本功能逐字节一致。开启即意味着同意把这些文件内容随请求发给
|
|
632
|
+
* DeepSeek API(loader 首次接入时 console 提示一次)。外部条目只读:memory_save/delete 拒 ext- 前缀。 */
|
|
633
|
+
memoryExternal: false
|
|
628
634
|
};
|
|
629
635
|
MODEL_WINDOWS = {};
|
|
630
636
|
resolveModelWindow = (model) => {
|
|
@@ -657,7 +663,9 @@ var init_config = __esm({
|
|
|
657
663
|
undoBackupSensitive: (v) => v === "skip" || v === "deny" || v === "allow" ? v : void 0,
|
|
658
664
|
undoRetentionDays: (v) => typeof v === "number" && v > 0 && Number.isFinite(v) ? v : void 0,
|
|
659
665
|
traceRetentionDays: (v) => typeof v === "number" && v > 0 && Number.isFinite(v) ? v : void 0,
|
|
660
|
-
MAX_TOOL_RESULT_CHARS: (v) => typeof v === "number" && v > 0 && Number.isFinite(v) ? Math.floor(v) : void 0
|
|
666
|
+
MAX_TOOL_RESULT_CHARS: (v) => typeof v === "number" && v > 0 && Number.isFinite(v) ? Math.floor(v) : void 0,
|
|
667
|
+
memoryExternal: (v) => typeof v === "boolean" ? v : void 0
|
|
668
|
+
// 外部记忆桥接(只读接入其他 agent 规则文件;默认 false)
|
|
661
669
|
};
|
|
662
670
|
ENGINE_OVERRIDE_KEYS = Object.keys(ENGINE_OVERRIDE_VALIDATORS);
|
|
663
671
|
applyEngineOverrides = (cfg) => {
|
|
@@ -904,6 +912,7 @@ var init_ticket = __esm({
|
|
|
904
912
|
noAccept: "frontmatter \u7F3A accept\uFF08\u9A8C\u6536\u547D\u4EE4\uFF09\u3002\u6302\u673A\u4EFB\u52A1\u5FC5\u987B\u5E26\u53EF\u6267\u884C\u9A8C\u6536\uFF0C\u7F3A\u9A8C\u6536\u62D2\u7EDD\u5165\u961F\u3002",
|
|
905
913
|
badBudget: (v) => `budget-yuan \u975E\u6CD5\uFF08"${v}"\uFF09\uFF1A\u987B\u4E3A\u6B63\u6570\uFF08\u5143\uFF09\u3002`,
|
|
906
914
|
badTimeout: (v) => `timeout-min \u975E\u6CD5\uFF08"${v}"\uFF09\uFF1A\u987B\u4E3A\u6B63\u6574\u6570\uFF08\u5206\u949F\uFF09\u3002`,
|
|
915
|
+
badRetry: (v) => `retry \u975E\u6CD5\uFF08"${v}"\uFF09\uFF1A\u987B\u4E3A 0-3 \u7684\u6574\u6570\uFF08\u770B\u95E8\u72D7\u7EC8\u6B62\u540E\u5E26\u8BC1\u636E\u91CD\u8BD5\u6B21\u6570\uFF1B\u7F3A\u7701 0=\u4E0D\u91CD\u8BD5\uFF09\u3002`,
|
|
907
916
|
badTools: "tools \u4E3A\u7A7A\uFF1A\u8981\u4E48\u5220\u6389\u8BE5\u884C\uFF08\u7528\u7F3A\u7701\u7981\u7528\u6E05\u5355\uFF09\uFF0C\u8981\u4E48\u5217\u5DE5\u5177\u540D\uFF08\u9017\u53F7\u5206\u9694\uFF09\u3002",
|
|
908
917
|
emptyBody: "\u4EFB\u52A1\u6B63\u6587\u4E3A\u7A7A\uFF1Afrontmatter \u4E4B\u540E\u5E94\u5199\u4EFB\u52A1\u63CF\u8FF0\u3002",
|
|
909
918
|
untitled: "(\u672A\u547D\u540D\u4EFB\u52A1)",
|
|
@@ -914,6 +923,7 @@ var init_ticket = __esm({
|
|
|
914
923
|
noAccept: "Frontmatter is missing `accept` (acceptance command). Batch tasks must carry an executable acceptance check \u2014 rejected without one.",
|
|
915
924
|
badBudget: (v) => `Invalid budget-yuan ("${v}"): must be a positive number (yuan).`,
|
|
916
925
|
badTimeout: (v) => `Invalid timeout-min ("${v}"): must be a positive integer (minutes).`,
|
|
926
|
+
badRetry: (v) => `Invalid retry ("${v}"): must be an integer 0-3 (evidence-carrying retries after a watchdog kill; default 0 = no retry).`,
|
|
917
927
|
badTools: "Empty tools: either remove the line (use the default denylist) or list tool names (comma-separated).",
|
|
918
928
|
emptyBody: "Empty task body: write the task description after the frontmatter.",
|
|
919
929
|
untitled: "(untitled task)",
|
|
@@ -955,13 +965,19 @@ var init_ticket = __esm({
|
|
|
955
965
|
if (n === null) return { ok: false, error: ticketMsg(locale2, "badTimeout")(fm["timeout-min"]) };
|
|
956
966
|
timeoutMin = n;
|
|
957
967
|
}
|
|
968
|
+
let retry;
|
|
969
|
+
if (fm["retry"] !== void 0) {
|
|
970
|
+
const n = Number(fm["retry"].trim());
|
|
971
|
+
if (!Number.isInteger(n) || n < 0 || n > 3) return { ok: false, error: ticketMsg(locale2, "badRetry")(fm["retry"]) };
|
|
972
|
+
retry = n;
|
|
973
|
+
}
|
|
958
974
|
const tools = fm["tools"] !== void 0 ? parseToolsValue(fm["tools"]) : void 0;
|
|
959
975
|
if (tools && tools.length === 0) return { ok: false, error: ticketMsg(locale2, "badTools") };
|
|
960
976
|
const setup = (fm["setup"] ?? "").trim() || void 0;
|
|
961
977
|
const body = bodyRaw.trim();
|
|
962
978
|
if (!body) return { ok: false, error: ticketMsg(locale2, "emptyBody") };
|
|
963
979
|
const title = (fm["title"] ?? "").trim() || body.split(/\r?\n/).find((l) => l.trim())?.slice(0, 40) || ticketMsg(locale2, "untitled");
|
|
964
|
-
return { ok: true, ticket: { title, accept, setup, budgetYuan, timeoutMin, tools: tools && tools.length > 0 ? tools : void 0, body } };
|
|
980
|
+
return { ok: true, ticket: { title, accept, setup, budgetYuan, timeoutMin, retry, tools: tools && tools.length > 0 ? tools : void 0, body } };
|
|
965
981
|
};
|
|
966
982
|
readBatchTicket = async (filePath, locale2 = "zh") => {
|
|
967
983
|
let source;
|
|
@@ -1117,6 +1133,7 @@ var init_batchStrings = __esm({
|
|
|
1117
1133
|
setup: pnpm install # \u53EF\u9009\uFF1Aagent \u524D\u5728 worktree \u5185\u5907\u73AF\u5883\uFF08\u5E72\u51C0\u68C0\u51FA\u65E0 node_modules\uFF0C\u9A8C\u6536\u4F9D\u8D56\u73AF\u5883\u65F6\u5148\u88C5\uFF09
|
|
1118
1134
|
budget-yuan: 0.5 # \u53EF\u9009\uFF1A\u672C\u4EFB\u52A1\u82B1\u8D39\u4E0A\u9650\uFF08\u5143\uFF09
|
|
1119
1135
|
timeout-min: 30 # \u53EF\u9009\uFF1Aagent \u9636\u6BB5\u8D85\u65F6\uFF08\u5206\u949F\uFF0C\u7F3A\u7701 30\uFF09
|
|
1136
|
+
retry: 1 # \u53EF\u9009\uFF1A\u88AB\u770B\u95E8\u72D7\u7EC8\u6B62\u540E\u5E26\u8BC1\u636E\u91CD\u8BD5\u6B21\u6570\uFF080-3\uFF0C\u7F3A\u7701 0=\u6740\u5373\u7EC8\u6001\uFF09
|
|
1120
1137
|
tools: run_command # \u53EF\u9009\uFF1A\u663E\u5F0F\u89E3\u9501\u9AD8\u5371\u5DE5\u5177\uFF08\u7F3A\u7701\u7981 run_command/delete_path\uFF09
|
|
1121
1138
|
---
|
|
1122
1139
|
\u4EFB\u52A1\u63CF\u8FF0\u6B63\u6587\uFF08Markdown\uFF09\u2026
|
|
@@ -1143,6 +1160,10 @@ var init_batchStrings = __esm({
|
|
|
1143
1160
|
wtCreateFail: (e) => `\u521B\u5EFA worktree \u5931\u8D25\uFF08\u9700\u8981\u4E3B\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\uFF09\uFF1A${e}`,
|
|
1144
1161
|
agentErrorLine: (id, err) => `\u{1F4A5} [${id}] agent \u5F02\u5E38\uFF1A${err}`,
|
|
1145
1162
|
timeoutLine: (id, min) => `\u23F1\uFE0F [${id}] \u8D85\u65F6\uFF08${min}min\uFF09\uFF0C\u622A\u65AD\u6536\u5C3E\uFF08\u9A8C\u6536\u4E0D\u8DD1\uFF09`,
|
|
1163
|
+
wdNudgeNote: (id, level) => `\u{1F415} [${id}] watchdog nudge-${level} \u5DF2\u6CE8\u5165\u6536\u4EF6\u7BB1\uFF08\u7EA0\u504F\u63D0\u793A\uFF09`,
|
|
1164
|
+
wdKillNote: (id, why) => `\u{1F415} [${id}] watchdog \u7EC8\u6B62\uFF1A${why}`,
|
|
1165
|
+
wdRetryNote: (id, round) => `\u{1F501} [${id}] watchdog \u5E26\u8BC1\u636E\u91CD\u8BD5\uFF1A\u7B2C ${round} \u8F6E\uFF08\u540C worktree \u7EED\u8DD1\uFF0C\u5269\u4F59\u9884\u7B97\u7EED\u7528\uFF09`,
|
|
1166
|
+
wdBudgetStopNote: (id) => `\u{1F415} [${id}] \u91CD\u8BD5\u5DF2\u8DF3\u8FC7\uFF1A\u9884\u7B97\u8DE8\u8F6E\u7D2F\u8BA1\u5DF2\u8017\u5C3D`,
|
|
1146
1167
|
acceptLine: (passed, id, code) => `${passed ? "\u2705" : "\u274C"} [${id}] \u9A8C\u6536 exit ${code}`,
|
|
1147
1168
|
acceptKilledSuffix: "\uFF08\u8D85\u65F6\u88AB\u6740\uFF09",
|
|
1148
1169
|
timeoutAbort: (min) => `\u4EFB\u52A1\u8D85\u65F6\uFF08${min} \u5206\u949F\uFF09`,
|
|
@@ -1163,7 +1184,8 @@ var init_batchStrings = __esm({
|
|
|
1163
1184
|
timeout: "\u23F1\uFE0F \u8D85\u65F6\u622A\u65AD",
|
|
1164
1185
|
error: "\u{1F4A5} agent \u5F02\u5E38",
|
|
1165
1186
|
"setup-failed": "\u{1F6E0}\uFE0F \u73AF\u5883\u51C6\u5907\u5931\u8D25",
|
|
1166
|
-
rejected: "\u{1F6AB} \u5DE5\u5355\u88AB\u62D2"
|
|
1187
|
+
rejected: "\u{1F6AB} \u5DE5\u5355\u88AB\u62D2",
|
|
1188
|
+
"watchdog-killed": "\u{1F415} \u770B\u95E8\u72D7\u7EC8\u6B62"
|
|
1167
1189
|
})[s],
|
|
1168
1190
|
// —— 验收命令执行(输出嵌入报告) ——
|
|
1169
1191
|
spawnFail: (e) => `\u542F\u52A8\u5931\u8D25\uFF1A${e}`,
|
|
@@ -1202,7 +1224,18 @@ var init_batchStrings = __esm({
|
|
|
1202
1224
|
deniedNone: "\u65E0\u3002",
|
|
1203
1225
|
diffH: "## Diff \u6458\u8981\uFF08worktree \u76F8\u5BF9\u57FA\u5EA7\uFF0C\u672A\u63D0\u4EA4\uFF0C\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09",
|
|
1204
1226
|
diffNote: (branch, p) => `\u6539\u52A8\u5728\u72EC\u7ACB worktree\uFF08\u5206\u652F \`${branch}\`\uFF0C\u8DEF\u5F84 ${p}\uFF09\u3002\u5904\u7F6E\uFF1A\u68C0\u67E5\u6EE1\u610F \u2192 \u5728 worktree \u5185 commit \u5E76\u5408\u56DE\u4E3B\u4ED3\uFF1B\u4E22\u5F03 \u2192 \u5220\u9664\u8BE5 worktree \u76EE\u5F55 + \`git worktree prune\` + \`git branch -D <\u5206\u652F>\`\u3002`,
|
|
1205
|
-
diffNone: "\uFF08\u65E0\uFF09"
|
|
1227
|
+
diffNone: "\uFF08\u65E0\uFF09",
|
|
1228
|
+
// —— 看门狗(进展感知)报告段与进度行 ——
|
|
1229
|
+
wdH: "## \u770B\u95E8\u72D7\uFF08\u8FDB\u5C55\u611F\u77E5\uFF09",
|
|
1230
|
+
wdRecord: (s) => `- ${s}`,
|
|
1231
|
+
wdSuspicionRec: (s) => `\u5ACC\u7591\u8BB0\u5F55\uFF08\u672A\u8FBE\u52A8\u4F5C\u7EBF\uFF09\uFF1A${s}`,
|
|
1232
|
+
wdVerdictRec: (verdict, evidence) => `\u88C1\u51B3 ${verdict}\uFF1A${evidence || "\uFF08\u65E0\u8BC1\u636E\uFF09"}`,
|
|
1233
|
+
wdNudgeRec: (level, text) => `nudge-${level} \u5DF2\u6CE8\u5165\u6536\u4EF6\u7BB1\uFF1A${text}`,
|
|
1234
|
+
wdHealthRec: (s) => `\u5065\u5EB7\u4FE1\u53F7\u77ED\u8DEF\uFF08\u8DF3\u8FC7\u88C1\u51B3\uFF09\uFF1A${s}`,
|
|
1235
|
+
wdAuxFailRec: () => `\u4E8C\u5C42\u88C1\u51B3\u8C03\u7528\u5931\u8D25\uFF0C\u964D\u7EA7 normal\uFF08\u770B\u95E8\u72D7\u81EA\u8EAB\u6545\u969C\u4E0D\u8BEF\u6740\uFF09`,
|
|
1236
|
+
wdKillRec: (verdict, evidence) => `\u26D4 \u7EC8\u6B62\uFF1A${verdict} \u2014 ${evidence}`,
|
|
1237
|
+
wdAtKillRec: (round, costYuan) => `\u7EC8\u6B62\u65F6\u523B\uFF1A\u8F6E ${round} \xB7 \xA5${costYuan}`,
|
|
1238
|
+
wdRetryRec: (n) => `\u5E26\u8BC1\u636E\u91CD\u8BD5\uFF1A\u5171 ${n + 1} \u8F6E\uFF08\u540C worktree \u7EED\u8DD1\uFF0C\u8BC1\u636E\u89C1\u5404\u8F6E\u8BB0\u5F55\uFF09`
|
|
1206
1239
|
};
|
|
1207
1240
|
EN = {
|
|
1208
1241
|
usage: `deepseeker-code batch \u2014 unattended batch runs (cost trust chain)
|
|
@@ -1220,6 +1253,7 @@ Ticket format (Markdown; frontmatter \`accept\` is required \u2014 no acceptance
|
|
|
1220
1253
|
setup: pnpm install # optional: prep env inside the worktree before the agent (fresh checkout has no node_modules)
|
|
1221
1254
|
budget-yuan: 0.5 # optional: per-task cost cap (yuan)
|
|
1222
1255
|
timeout-min: 30 # optional: agent-phase timeout (minutes, default 30)
|
|
1256
|
+
retry: 1 # optional: evidence-carrying retries after a watchdog kill (0-3, default 0 = kill is final)
|
|
1223
1257
|
tools: run_command # optional: unlock risky tools (run_command/delete_path denied by default)
|
|
1224
1258
|
---
|
|
1225
1259
|
|
|
@@ -1246,6 +1280,10 @@ changes are harvested onto an isolated git worktree (temp branch), kept for manu
|
|
|
1246
1280
|
wtCreateFail: (e) => `Failed to create worktree (the main workspace must be a git repo): ${e}`,
|
|
1247
1281
|
agentErrorLine: (id, err) => `\u{1F4A5} [${id}] agent error: ${err}`,
|
|
1248
1282
|
timeoutLine: (id, min) => `\u23F1\uFE0F [${id}] Timed out (${min}min); wrapped up truncated (acceptance skipped)`,
|
|
1283
|
+
wdNudgeNote: (id, level) => `\u{1F415} [${id}] watchdog nudge-${level} injected into the inbox (course correction)`,
|
|
1284
|
+
wdKillNote: (id, why) => `\u{1F415} [${id}] watchdog killed: ${why}`,
|
|
1285
|
+
wdRetryNote: (id, round) => `\u{1F501} [${id}] watchdog evidence-carrying retry: round ${round} (same worktree, remaining budget carried over)`,
|
|
1286
|
+
wdBudgetStopNote: (id) => `\u{1F415} [${id}] retry skipped: cumulative budget exhausted across rounds`,
|
|
1249
1287
|
acceptLine: (passed, id, code) => `${passed ? "\u2705" : "\u274C"} [${id}] acceptance exit ${code}`,
|
|
1250
1288
|
acceptKilledSuffix: " (killed on timeout)",
|
|
1251
1289
|
timeoutAbort: (min) => `Task timed out (${min} minutes)`,
|
|
@@ -1265,7 +1303,8 @@ changes are harvested onto an isolated git worktree (temp branch), kept for manu
|
|
|
1265
1303
|
timeout: "\u23F1\uFE0F timed out",
|
|
1266
1304
|
error: "\u{1F4A5} agent error",
|
|
1267
1305
|
"setup-failed": "\u{1F6E0}\uFE0F setup failed",
|
|
1268
|
-
rejected: "\u{1F6AB} ticket rejected"
|
|
1306
|
+
rejected: "\u{1F6AB} ticket rejected",
|
|
1307
|
+
"watchdog-killed": "\u{1F415} watchdog killed"
|
|
1269
1308
|
})[s],
|
|
1270
1309
|
spawnFail: (e) => `Spawn failed: ${e}`,
|
|
1271
1310
|
execFail: (e) => `Execution failed: ${e}`,
|
|
@@ -1301,7 +1340,17 @@ changes are harvested onto an isolated git worktree (temp branch), kept for manu
|
|
|
1301
1340
|
deniedNone: "None.",
|
|
1302
1341
|
diffH: "## Diff summary (worktree vs base, uncommitted, awaiting manual handling)",
|
|
1303
1342
|
diffNote: (branch, p) => `Changes live in an isolated worktree (branch \`${branch}\`, path ${p}). To keep: commit inside the worktree and merge back. To discard: delete the worktree directory + \`git worktree prune\` + \`git branch -D <branch>\`.`,
|
|
1304
|
-
diffNone: "(none)"
|
|
1343
|
+
diffNone: "(none)",
|
|
1344
|
+
wdH: "## Watchdog (progress-aware)",
|
|
1345
|
+
wdRecord: (s) => `- ${s}`,
|
|
1346
|
+
wdSuspicionRec: (s) => `Suspicion logged (below action line): ${s}`,
|
|
1347
|
+
wdVerdictRec: (verdict, evidence) => `Verdict ${verdict}: ${evidence || "(no evidence)"}`,
|
|
1348
|
+
wdNudgeRec: (level, text) => `nudge-${level} injected into the inbox: ${text}`,
|
|
1349
|
+
wdHealthRec: (s) => `Health signal short-circuit (adjudication skipped): ${s}`,
|
|
1350
|
+
wdAuxFailRec: () => `Level-2 adjudication call failed; degraded to normal (a broken watchdog must not become a false-kill weapon)`,
|
|
1351
|
+
wdKillRec: (verdict, evidence) => `\u26D4 Killed: ${verdict} \u2014 ${evidence}`,
|
|
1352
|
+
wdAtKillRec: (round, costYuan) => `At kill time: round ${round} \xB7 \xA5${costYuan}`,
|
|
1353
|
+
wdRetryRec: (n) => `Evidence-carrying retries: ${n + 1} rounds total (same worktree; per-round evidence above)`
|
|
1305
1354
|
};
|
|
1306
1355
|
DICT = { zh: ZH, en: EN };
|
|
1307
1356
|
batchT = (locale2) => DICT[locale2] ?? ZH;
|
|
@@ -1482,27 +1531,29 @@ var init_client = __esm({
|
|
|
1482
1531
|
});
|
|
1483
1532
|
|
|
1484
1533
|
// ../core/src/llm/providers/deepseek/stream.ts
|
|
1485
|
-
var STREAM_IDLE_TIMEOUT_MS, stripHistoricalReasoning, streamChat;
|
|
1534
|
+
var STREAM_IDLE_TIMEOUT_MS, isEmptyAssistantMsg, stripHistoricalReasoning, streamChat;
|
|
1486
1535
|
var init_stream = __esm({
|
|
1487
1536
|
"../core/src/llm/providers/deepseek/stream.ts"() {
|
|
1488
1537
|
"use strict";
|
|
1489
1538
|
init_client();
|
|
1490
1539
|
STREAM_IDLE_TIMEOUT_MS = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
|
|
1540
|
+
isEmptyAssistantMsg = (m) => m?.role === "assistant" && !(typeof m.content === "string" && m.content.length > 0) && !(Array.isArray(m.tool_calls) && m.tool_calls.length > 0);
|
|
1491
1541
|
stripHistoricalReasoning = (messages) => {
|
|
1492
|
-
|
|
1542
|
+
const wire = messages.some(isEmptyAssistantMsg) ? messages.filter((m) => !isEmptyAssistantMsg(m)) : messages;
|
|
1543
|
+
if (process.env.DEEP_SEEK_REASONING_PASSTHROUGH === "1") return wire;
|
|
1493
1544
|
let lastNonSystem = null;
|
|
1494
|
-
for (let i =
|
|
1495
|
-
if (
|
|
1496
|
-
lastNonSystem =
|
|
1545
|
+
for (let i = wire.length - 1; i >= 0; i--) {
|
|
1546
|
+
if (wire[i]?.role !== "system") {
|
|
1547
|
+
lastNonSystem = wire[i];
|
|
1497
1548
|
break;
|
|
1498
1549
|
}
|
|
1499
1550
|
}
|
|
1500
|
-
const hasSplitToolTurn =
|
|
1501
|
-
if (lastNonSystem?.role === "assistant" || hasSplitToolTurn) return
|
|
1502
|
-
return
|
|
1503
|
-
const
|
|
1504
|
-
if (
|
|
1505
|
-
const copy = { ...
|
|
1551
|
+
const hasSplitToolTurn = wire.some((m, i) => i > 0 && m?.role === "assistant" && wire[i - 1]?.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0);
|
|
1552
|
+
if (lastNonSystem?.role === "assistant" || hasSplitToolTurn) return wire;
|
|
1553
|
+
return wire.map((m) => {
|
|
1554
|
+
const w = m;
|
|
1555
|
+
if (w?.role !== "assistant" || typeof w.reasoning_content !== "string") return m;
|
|
1556
|
+
const copy = { ...w };
|
|
1506
1557
|
delete copy.reasoning_content;
|
|
1507
1558
|
return copy;
|
|
1508
1559
|
});
|
|
@@ -1975,8 +2026,19 @@ var init_replay = __esm({
|
|
|
1975
2026
|
});
|
|
1976
2027
|
|
|
1977
2028
|
// ../core/src/llm/model.ts
|
|
2029
|
+
var model_exports = {};
|
|
2030
|
+
__export(model_exports, {
|
|
2031
|
+
activeProvider: () => activeProvider,
|
|
2032
|
+
chatWithModelWithSummary: () => chatWithModelWithSummary,
|
|
2033
|
+
classifyToolRisk: () => classifyToolRisk,
|
|
2034
|
+
isContextLengthError: () => isContextLengthError2,
|
|
2035
|
+
isImageUnsupportedError: () => isImageUnsupportedError2,
|
|
2036
|
+
isTransientApiError: () => isTransientApiError,
|
|
2037
|
+
resetActiveProvider: () => resetActiveProvider,
|
|
2038
|
+
setActiveProvider: () => setActiveProvider
|
|
2039
|
+
});
|
|
1978
2040
|
import { readFileSync as readFileSync4 } from "fs";
|
|
1979
|
-
var resolveInitialProvider, activeProvider, chatWithModelWithSummary, classifyToolRisk;
|
|
2041
|
+
var resolveInitialProvider, activeProvider, setActiveProvider, resetActiveProvider, chatWithModelWithSummary, classifyToolRisk, isContextLengthError2, isTransientApiError, isImageUnsupportedError2;
|
|
1980
2042
|
var init_model = __esm({
|
|
1981
2043
|
"../core/src/llm/model.ts"() {
|
|
1982
2044
|
"use strict";
|
|
@@ -1997,8 +2059,17 @@ var init_model = __esm({
|
|
|
1997
2059
|
return createReplayProvider(Array.isArray(raw) ? { turns: raw } : raw);
|
|
1998
2060
|
};
|
|
1999
2061
|
activeProvider = resolveInitialProvider();
|
|
2062
|
+
setActiveProvider = (p) => {
|
|
2063
|
+
activeProvider = p;
|
|
2064
|
+
};
|
|
2065
|
+
resetActiveProvider = () => {
|
|
2066
|
+
activeProvider = deepseekProvider;
|
|
2067
|
+
};
|
|
2000
2068
|
chatWithModelWithSummary = (messages, tools, opts) => activeProvider.summarize(messages, tools, opts);
|
|
2001
2069
|
classifyToolRisk = (toolName, args, detail, signal, onAuxUsage) => activeProvider.classifyRisk(toolName, args, detail, signal, onAuxUsage);
|
|
2070
|
+
isContextLengthError2 = (e) => activeProvider.isContextLengthError(e);
|
|
2071
|
+
isTransientApiError = (e) => activeProvider.isTransientError(e);
|
|
2072
|
+
isImageUnsupportedError2 = (e) => activeProvider.isImageUnsupportedError(e);
|
|
2002
2073
|
}
|
|
2003
2074
|
});
|
|
2004
2075
|
|
|
@@ -2532,13 +2603,17 @@ var init_transcript = __esm({
|
|
|
2532
2603
|
console.warn(`\u26A0\uFE0F [transcript] \u6D88\u606F\u542B ${fffdCount} \u4E2A\u4E71\u7801\u5B57\u7B26\uFF08U+FFFD\uFF09\uFF0C\u5DF2\u539F\u6837\u5199\u5165\u5386\u53F2\u3002\u6765\u6E90\u53EF\u80FD\u662F GBK \u5B50\u8FDB\u7A0B\u8F93\u51FA\u6216\u4E71\u7801\u7C98\u8D34\u3002`);
|
|
2533
2604
|
}
|
|
2534
2605
|
await appendLineWithRetry(p, payload);
|
|
2606
|
+
return true;
|
|
2535
2607
|
} catch (e) {
|
|
2536
2608
|
console.warn(`\u26A0\uFE0F \u6D88\u606F\u843D\u76D8\u5931\u8D25\uFF08\u5DF2\u91CD\u8BD5 3 \u6B21\uFF0C\u5185\u5B58\u4E0E\u78C1\u76D8\u8F6C\u5F55\u53EF\u80FD\u4E0D\u4E00\u81F4\uFF09:`, e);
|
|
2609
|
+
return false;
|
|
2537
2610
|
}
|
|
2538
2611
|
};
|
|
2539
2612
|
appendMessages = async (sessionId, entries) => {
|
|
2540
|
-
if (entries.length === 0) return;
|
|
2541
|
-
if (entries.length === 1)
|
|
2613
|
+
if (entries.length === 0) return { written: 0, failed: 0 };
|
|
2614
|
+
if (entries.length === 1) {
|
|
2615
|
+
return await appendMessage(entries[0]) ? { written: 1, failed: 0 } : { written: 0, failed: 1 };
|
|
2616
|
+
}
|
|
2542
2617
|
try {
|
|
2543
2618
|
await ensureSessionsDir(sessionId);
|
|
2544
2619
|
const p = getTranscriptPath(sessionId);
|
|
@@ -2551,28 +2626,35 @@ var init_transcript = __esm({
|
|
|
2551
2626
|
if (fffdTotal > 0) console.warn(`[transcript] \u6279\u91CF\u6D88\u606F\u542B ${fffdTotal} \u4E2A\u4E71\u7801\u5B57\u7B26\uFF08U+FFFD\uFF09\uFF0C\u5DF2\u539F\u6837\u5199\u5165\u5386\u53F2\u3002\u6765\u6E90\u53EF\u80FD\u662F GBK \u5B50\u8FDB\u7A0B\u8F93\u51FA\u6216\u4E71\u7801\u7C98\u8D34\u3002`);
|
|
2552
2627
|
let handle = null;
|
|
2553
2628
|
let i = 0;
|
|
2629
|
+
let failed = 0;
|
|
2554
2630
|
try {
|
|
2555
2631
|
handle = await fs9.open(p, "a");
|
|
2556
2632
|
for (; i < payloads.length; i++) await appendLineWithRetry(p, payloads[i], handle);
|
|
2557
2633
|
} catch (e) {
|
|
2558
2634
|
console.warn(`[transcript] \u6279\u91CF\u843D\u76D8\u7B2C ${i + 1}/${payloads.length} \u6761\u5931\u8D25\uFF0C\u8BE5\u6761\u8D77\u56DE\u843D\u9010\u6761\u65E7\u8DEF\u5F84:`, e instanceof Error ? e.message : e);
|
|
2559
|
-
for (; i < entries.length; i++)
|
|
2635
|
+
for (; i < entries.length; i++) {
|
|
2636
|
+
if (!await appendMessage(entries[i])) failed++;
|
|
2637
|
+
}
|
|
2560
2638
|
} finally {
|
|
2561
2639
|
if (handle) await handle.close().catch(() => {
|
|
2562
2640
|
});
|
|
2563
2641
|
}
|
|
2642
|
+
return { written: entries.length - failed, failed };
|
|
2564
2643
|
} catch (e) {
|
|
2565
2644
|
console.warn(`\u6279\u91CF\u6D88\u606F\u843D\u76D8\u5931\u8D25\uFF08\u5185\u5B58\u4E0E\u78C1\u76D8\u8F6C\u5F55\u53EF\u80FD\u4E0D\u4E00\u81F4\uFF09:`, e);
|
|
2645
|
+
return { written: 0, failed: entries.length };
|
|
2566
2646
|
}
|
|
2567
2647
|
};
|
|
2568
2648
|
appendEvent = async (sessionId, ev) => {
|
|
2569
|
-
if (!appConfig.transcriptEvents) return;
|
|
2649
|
+
if (!appConfig.transcriptEvents) return true;
|
|
2570
2650
|
try {
|
|
2571
2651
|
await ensureSessionsDir(sessionId);
|
|
2572
2652
|
const line = { id: createUUID(), ts: (/* @__PURE__ */ new Date()).toISOString(), ...ev };
|
|
2573
2653
|
await appendLineWithRetry(getTranscriptPath(sessionId), JSON.stringify(line) + "\n");
|
|
2654
|
+
return true;
|
|
2574
2655
|
} catch (e) {
|
|
2575
2656
|
console.warn(`\u26A0\uFE0F [transcript] \u4E8B\u4EF6\u884C\u843D\u76D8\u5931\u8D25\uFF08${ev.dscEvent}\uFF0C\u6062\u590D/\u5206\u53C9\u5C06\u56DE\u9000\u542F\u53D1\u5F0F\uFF09:`, e);
|
|
2657
|
+
return false;
|
|
2576
2658
|
}
|
|
2577
2659
|
};
|
|
2578
2660
|
}
|
|
@@ -2598,8 +2680,8 @@ var init_recovery = __esm({
|
|
|
2598
2680
|
if (!sawEvents || !lastStart) return { kind: "legacy" };
|
|
2599
2681
|
return closed.has(lastStart.runId) ? { kind: "clean" } : { kind: "crashed", runId: lastStart.runId };
|
|
2600
2682
|
};
|
|
2601
|
-
repairOrphanToolCalls = (msgs,
|
|
2602
|
-
const placeholder =
|
|
2683
|
+
repairOrphanToolCalls = (msgs, confirmedUnclosed) => {
|
|
2684
|
+
const placeholder = confirmedUnclosed ? "\uFF08\u4E0A\u6B21\u4F1A\u8BDD\u7684\u8FD9\u6B21\u5DE5\u5177\u8C03\u7528\u672A\u5B8C\u6210\uFF08run \u672A\u95ED\u5408\uFF09\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09" : "\uFF08\u8BE5\u5DE5\u5177\u8C03\u7528\u56E0\u4E0A\u6B21\u4F1A\u8BDD\u5F02\u5E38\u4E2D\u65AD\u672A\u7559\u4E0B\u7ED3\u679C\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09";
|
|
2603
2685
|
const out = [];
|
|
2604
2686
|
const repairedToolCallIds = [];
|
|
2605
2687
|
for (let i = 0; i < msgs.length; i++) {
|
|
@@ -2620,7 +2702,7 @@ var init_recovery = __esm({
|
|
|
2620
2702
|
}
|
|
2621
2703
|
}
|
|
2622
2704
|
}
|
|
2623
|
-
return { msgs: out, repairedToolCallIds,
|
|
2705
|
+
return { msgs: out, repairedToolCallIds, confirmedUnclosed };
|
|
2624
2706
|
};
|
|
2625
2707
|
reconcileCompaction = (state, lines) => {
|
|
2626
2708
|
let last;
|
|
@@ -2644,9 +2726,9 @@ var init_recovery = __esm({
|
|
|
2644
2726
|
await appendEvent(sessionId, {
|
|
2645
2727
|
dscEvent: "run.abandoned",
|
|
2646
2728
|
runId: interruption.runId,
|
|
2647
|
-
reason: "
|
|
2729
|
+
reason: "unclosed (cause unknown)"
|
|
2648
2730
|
});
|
|
2649
|
-
console.warn(`\u27E6recovery\u27E7 \u68C0\u6D4B\u5230\u672A\u95ED\u5408 run\uFF08${interruption.runId.slice(0, 8)}\u2026\uFF09\u2192 \u5DF2\u8865 run.abandoned \u95ED\u5893\u6807\u8BB0\uFF1B\u5B64\u513F\u4FEE\u590D\u6309\u300C\
|
|
2731
|
+
console.warn(`\u27E6recovery\u27E7 \u68C0\u6D4B\u5230\u672A\u95ED\u5408 run\uFF08${interruption.runId.slice(0, 8)}\u2026\uFF09\u2192 \u5DF2\u8865 run.abandoned \u95ED\u5893\u6807\u8BB0\uFF1B\u5B64\u513F\u4FEE\u590D\u6309\u300C\u672A\u95ED\u5408\u300D\u6863\u6267\u884C\u3002`);
|
|
2650
2732
|
}
|
|
2651
2733
|
const state = precomputed?.state ?? await getRollingState(sessionId);
|
|
2652
2734
|
const compaction = reconcileCompaction(
|
|
@@ -3476,19 +3558,63 @@ ${lines}
|
|
|
3476
3558
|
const hitRate = event.lastRealPromptTokens && event.lastCachedTokens != null && event.lastRealPromptTokens > 0 ? event.lastCachedTokens / event.lastRealPromptTokens : null;
|
|
3477
3559
|
const cacheFactor = hitRate == null ? 1 : Math.min(1.1, Math.max(0.9, 0.9 + hitRate * 0.2));
|
|
3478
3560
|
const effectiveRatio = Math.min(event.compactRatio * cacheFactor, 0.82);
|
|
3479
|
-
|
|
3561
|
+
const thresholdAtCheck = Math.round(event.modelWindow * effectiveRatio);
|
|
3562
|
+
const reportCheck = (decision, decayed2) => {
|
|
3563
|
+
event.onWindowCheck?.({
|
|
3564
|
+
estReal: Math.round(estReal(event.messageArr)),
|
|
3565
|
+
threshold: thresholdAtCheck,
|
|
3566
|
+
correctionRatio,
|
|
3567
|
+
toolsTokens,
|
|
3568
|
+
hitRate,
|
|
3569
|
+
cacheFactor,
|
|
3570
|
+
decision,
|
|
3571
|
+
...decayed2 === void 0 ? {} : { decayed: decayed2 }
|
|
3572
|
+
});
|
|
3573
|
+
};
|
|
3574
|
+
const estRealAtTrigger = Math.round(estReal(event.messageArr));
|
|
3575
|
+
const emitCheckEvent = (decision) => {
|
|
3576
|
+
event.events({
|
|
3577
|
+
sessionId: event.sessionId,
|
|
3578
|
+
eventType: "context.check",
|
|
3579
|
+
metadata: {
|
|
3580
|
+
depth: event.depth,
|
|
3581
|
+
decisionSource: "summary",
|
|
3582
|
+
ok: true,
|
|
3583
|
+
estReal: estRealAtTrigger,
|
|
3584
|
+
thresholdAtSend: thresholdAtCheck,
|
|
3585
|
+
correctionRatioUsed: correctionRatio,
|
|
3586
|
+
hitRateAtCheck: hitRate,
|
|
3587
|
+
cacheFactorAtCheck: cacheFactor,
|
|
3588
|
+
windowDecision: decision
|
|
3589
|
+
}
|
|
3590
|
+
});
|
|
3591
|
+
};
|
|
3592
|
+
if (!event.forceCompact && estReal(event.messageArr) <= event.modelWindow * effectiveRatio) {
|
|
3593
|
+
reportCheck("noop");
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3480
3596
|
const preDecaySize = estReal(event.messageArr);
|
|
3481
3597
|
const decayedArr = decayOldToolResults(event.messageArr);
|
|
3482
|
-
|
|
3598
|
+
const decayed = decayedArr !== event.messageArr;
|
|
3599
|
+
if (decayed) {
|
|
3483
3600
|
event.messageArr.length = 0;
|
|
3484
3601
|
event.messageArr.push(...decayedArr);
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3602
|
+
event.events({
|
|
3603
|
+
sessionId: event.sessionId,
|
|
3604
|
+
eventType: "context.decay",
|
|
3605
|
+
metadata: {
|
|
3606
|
+
depth: event.depth,
|
|
3607
|
+
decisionSource: "summary",
|
|
3608
|
+
ok: true,
|
|
3609
|
+
decayTokensBefore: Math.round(preDecaySize),
|
|
3610
|
+
decayTokensAfter: Math.round(estReal(event.messageArr))
|
|
3611
|
+
}
|
|
3612
|
+
});
|
|
3489
3613
|
}
|
|
3490
3614
|
const tokensThreshold = Math.round(event.modelWindow * effectiveRatio);
|
|
3615
|
+
let preCompactSent = false;
|
|
3491
3616
|
await dispatch("PreCompact", { sessionId: event.sessionId, depth: event.depth, tokensBefore: Math.round(estReal(event.messageArr)), tokensThreshold });
|
|
3617
|
+
preCompactSent = true;
|
|
3492
3618
|
const systemMsg = event.messageArr[0];
|
|
3493
3619
|
const summaryMsg = event.messageArr[1];
|
|
3494
3620
|
let keep = event.keepRecentUnits;
|
|
@@ -3496,102 +3622,113 @@ ${lines}
|
|
|
3496
3622
|
const preCompactSize = Math.round(lastSize);
|
|
3497
3623
|
const startTime = performance.now();
|
|
3498
3624
|
let round = 0;
|
|
3499
|
-
let
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3625
|
+
let compactDecision = "noop";
|
|
3626
|
+
let forcedPass = event.forceCompact === true || decayed;
|
|
3627
|
+
try {
|
|
3628
|
+
while (forcedPass || lastSize > event.modelWindow * effectiveRatio) {
|
|
3629
|
+
forcedPass = false;
|
|
3630
|
+
if (event.signal?.aborted) {
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
;
|
|
3634
|
+
const active = event.messageArr.slice(2);
|
|
3635
|
+
const { toCompact, keepRecent } = splitUntils(active, keep);
|
|
3636
|
+
try {
|
|
3637
|
+
if (toCompact.length > 0) {
|
|
3638
|
+
summaryMsg.content = mergeSummarySlot(summaryMsg?.content || "", "", extractArchiveEntities(toCompact));
|
|
3639
|
+
const batches = splitIntoBatches(toCompact);
|
|
3640
|
+
summaryMsg.content = batches.length === 1 ? await synthesizeSlotNarrativeFromBatch(summaryMsg.content, batches[0], event.signal, event.onAuxUsage) : await synthesizeSlotNarrative(summaryMsg.content, await compactBatches(batches, event.signal, event.depth === 0 ? event.onUIEvent : void 0, event.onAuxUsage), event.signal, event.onAuxUsage);
|
|
3641
|
+
const slotNotes = parseSummarySlot(typeof summaryMsg?.content === "string" ? summaryMsg.content : "").notes;
|
|
3642
|
+
if (estimateTokens([{ role: summaryMsg?.role ?? "system", content: slotNotes }]) > SUMMARY_SELF_COMPACT_THRESHOLD) {
|
|
3643
|
+
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
|
|
3644
|
+
}
|
|
3645
|
+
const endTime = performance.now();
|
|
3646
|
+
event.messageArr.length = 0;
|
|
3647
|
+
event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
|
|
3648
|
+
event.events({
|
|
3649
|
+
sessionId: event.sessionId,
|
|
3650
|
+
eventType: "session.summary",
|
|
3651
|
+
metadata: {
|
|
3652
|
+
depth: event.depth,
|
|
3653
|
+
decisionSource: "summary",
|
|
3654
|
+
ok: true,
|
|
3655
|
+
durationMs: endTime - startTime,
|
|
3656
|
+
round: round++
|
|
3657
|
+
},
|
|
3658
|
+
usage: {
|
|
3659
|
+
prompt_tokens: lastSize,
|
|
3660
|
+
compress_tokens: estReal(event.messageArr)
|
|
3661
|
+
}
|
|
3662
|
+
});
|
|
3663
|
+
const store = await getRollingState(event.sessionId);
|
|
3664
|
+
store.archivedMessageCount = (store.archivedMessageCount || 0) + toCompact.length;
|
|
3665
|
+
await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
|
|
3666
|
+
compactDecision = "compact";
|
|
3667
|
+
} else if (keep > 1) {
|
|
3668
|
+
keep--;
|
|
3669
|
+
continue;
|
|
3670
|
+
} else if (summaryMsg?.content) {
|
|
3514
3671
|
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
|
|
3672
|
+
compactDecision = "selfCompact";
|
|
3673
|
+
event.messageArr.length = 0;
|
|
3674
|
+
event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
|
|
3675
|
+
const store = await getRollingState(event.sessionId);
|
|
3676
|
+
await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
|
|
3677
|
+
} else {
|
|
3678
|
+
break;
|
|
3515
3679
|
}
|
|
3680
|
+
} catch (error) {
|
|
3681
|
+
if (event.signal?.aborted) return;
|
|
3682
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
3683
|
+
console.warn("\u26A0\uFE0F \u672C\u8F6E\u538B\u7F29\u5931\u8D25\uFF0C\u8DF3\u8FC7:", err.message);
|
|
3684
|
+
const store = await getRollingState(event.sessionId);
|
|
3685
|
+
const nextFailures = (store.consecutiveFailures || 0) + 1;
|
|
3686
|
+
await setRollingState(event.sessionId, {
|
|
3687
|
+
archivedMessageCount: store.archivedMessageCount || 0,
|
|
3688
|
+
rollingSummary: summaryMsg?.content || "",
|
|
3689
|
+
consecutiveFailures: nextFailures
|
|
3690
|
+
// 👈 同步落盘
|
|
3691
|
+
});
|
|
3516
3692
|
const endTime = performance.now();
|
|
3517
|
-
event.messageArr.length = 0;
|
|
3518
|
-
event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
|
|
3519
3693
|
event.events({
|
|
3520
3694
|
sessionId: event.sessionId,
|
|
3695
|
+
parentId: event.depth > 0 ? event.sessionId : "",
|
|
3521
3696
|
eventType: "session.summary",
|
|
3522
3697
|
metadata: {
|
|
3523
3698
|
depth: event.depth,
|
|
3699
|
+
messageId: summaryMsg?.id ?? "(rolling-summary-slot)",
|
|
3524
3700
|
decisionSource: "summary",
|
|
3525
|
-
ok:
|
|
3701
|
+
ok: false,
|
|
3526
3702
|
durationMs: endTime - startTime,
|
|
3527
|
-
|
|
3703
|
+
attempt: nextFailures
|
|
3528
3704
|
},
|
|
3529
3705
|
usage: {
|
|
3530
3706
|
prompt_tokens: lastSize,
|
|
3531
|
-
compress_tokens:
|
|
3707
|
+
compress_tokens: estimateTokens(event.messageArr)
|
|
3708
|
+
},
|
|
3709
|
+
payload: {
|
|
3710
|
+
output: err.message
|
|
3532
3711
|
}
|
|
3533
3712
|
});
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
continue;
|
|
3540
|
-
} else if (summaryMsg?.content) {
|
|
3541
|
-
summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
|
|
3542
|
-
event.messageArr.length = 0;
|
|
3543
|
-
event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
|
|
3544
|
-
const store = await getRollingState(event.sessionId);
|
|
3545
|
-
await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
|
|
3546
|
-
} else {
|
|
3713
|
+
if (nextFailures >= 3) {
|
|
3714
|
+
throw new Error(
|
|
3715
|
+
`\u274C [\u538B\u7F29\u7194\u65AD] \u4E0A\u4E0B\u6587\u538B\u7F29\u8FDE\u7EED ${nextFailures} \u6B21\u5931\u8D25\uFF0C\u4E3A\u9632\u5929\u4EF7\u8D26\u5355\u6B7B\u5FAA\u73AF\u5DF2\u6682\u505C\u81EA\u52A8\u538B\u7F29\u3002\u5E38\u89C1\u6839\u56E0\uFF1A\u6458\u8981\u8F85\u52A9\u6A21\u578B\u670D\u52A1\u5F02\u5E38\u6216\u7F51\u7EDC\u4E0D\u901A\uFF08\u53EF\u67E5 DEEP_SEEK_AUX_MODEL \u670D\u52A1\u53EF\u7528\u6027\uFF1B\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u5DF2\u7531\u7CFB\u7EDF\u81EA\u52A8\u9884\u622A\u65AD\uFF0C\u4E00\u822C\u4E0D\u518D\u662F\u4E3B\u56E0\uFF09\u3002\u81EA\u6108\uFF1A\u76F4\u63A5\u91CD\u53D1\u6D88\u606F\u5373\u53EF\u91CD\u8BD5\uFF08\u6210\u529F\u538B\u7F29\u4E00\u6B21\u8BA1\u6570\u5373\u6E05\u96F6\uFF09\uFF1B\u6216 /fork \u5206\u53C9\u7EED\u63A5\u3001/new \u5F00\u65B0\u4F1A\u8BDD\u3002`
|
|
3716
|
+
);
|
|
3717
|
+
}
|
|
3547
3718
|
break;
|
|
3548
3719
|
}
|
|
3549
|
-
|
|
3550
|
-
if (
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
await
|
|
3556
|
-
archivedMessageCount: store.archivedMessageCount || 0,
|
|
3557
|
-
rollingSummary: summaryMsg?.content || "",
|
|
3558
|
-
consecutiveFailures: nextFailures
|
|
3559
|
-
// 👈 同步落盘
|
|
3560
|
-
});
|
|
3561
|
-
const endTime = performance.now();
|
|
3562
|
-
event.events({
|
|
3563
|
-
sessionId: event.sessionId,
|
|
3564
|
-
parentId: event.depth > 0 ? event.sessionId : "",
|
|
3565
|
-
eventType: "session.summary",
|
|
3566
|
-
metadata: {
|
|
3567
|
-
depth: event.depth,
|
|
3568
|
-
messageId: summaryMsg?.id ?? "(rolling-summary-slot)",
|
|
3569
|
-
decisionSource: "summary",
|
|
3570
|
-
ok: false,
|
|
3571
|
-
durationMs: endTime - startTime,
|
|
3572
|
-
attempt: nextFailures
|
|
3573
|
-
},
|
|
3574
|
-
usage: {
|
|
3575
|
-
prompt_tokens: lastSize,
|
|
3576
|
-
compress_tokens: estimateTokens(event.messageArr)
|
|
3577
|
-
},
|
|
3578
|
-
payload: {
|
|
3579
|
-
output: err.message
|
|
3580
|
-
}
|
|
3720
|
+
const newSize = estReal(event.messageArr);
|
|
3721
|
+
if (newSize >= lastSize) break;
|
|
3722
|
+
lastSize = newSize;
|
|
3723
|
+
}
|
|
3724
|
+
} finally {
|
|
3725
|
+
if (preCompactSent) {
|
|
3726
|
+
await dispatch("PostCompact", { sessionId: event.sessionId, depth: event.depth, tokensBefore: Math.round(lastSize), tokensAfter: Math.round(estReal(event.messageArr)) }).catch(() => {
|
|
3581
3727
|
});
|
|
3582
|
-
if (nextFailures >= 3) {
|
|
3583
|
-
throw new Error(
|
|
3584
|
-
`\u274C [\u538B\u7F29\u7194\u65AD] \u4E0A\u4E0B\u6587\u538B\u7F29\u8FDE\u7EED ${nextFailures} \u6B21\u5931\u8D25\uFF0C\u4E3A\u9632\u5929\u4EF7\u8D26\u5355\u6B7B\u5FAA\u73AF\u5DF2\u6682\u505C\u81EA\u52A8\u538B\u7F29\u3002\u5E38\u89C1\u6839\u56E0\uFF1A\u6458\u8981\u8F85\u52A9\u6A21\u578B\u670D\u52A1\u5F02\u5E38\u6216\u7F51\u7EDC\u4E0D\u901A\uFF08\u53EF\u67E5 DEEP_SEEK_AUX_MODEL \u670D\u52A1\u53EF\u7528\u6027\uFF1B\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u5DF2\u7531\u7CFB\u7EDF\u81EA\u52A8\u9884\u622A\u65AD\uFF0C\u4E00\u822C\u4E0D\u518D\u662F\u4E3B\u56E0\uFF09\u3002\u81EA\u6108\uFF1A\u76F4\u63A5\u91CD\u53D1\u6D88\u606F\u5373\u53EF\u91CD\u8BD5\uFF08\u6210\u529F\u538B\u7F29\u4E00\u6B21\u8BA1\u6570\u5373\u6E05\u96F6\uFF09\uFF1B\u6216 /fork \u5206\u53C9\u7EED\u63A5\u3001/new \u5F00\u65B0\u4F1A\u8BDD\u3002`
|
|
3585
|
-
);
|
|
3586
|
-
}
|
|
3587
|
-
break;
|
|
3588
3728
|
}
|
|
3589
|
-
const newSize = estReal(event.messageArr);
|
|
3590
|
-
if (newSize >= lastSize) break;
|
|
3591
|
-
lastSize = newSize;
|
|
3592
3729
|
}
|
|
3593
|
-
|
|
3594
|
-
|
|
3730
|
+
reportCheck(compactDecision, decayed);
|
|
3731
|
+
if (compactDecision !== "noop") emitCheckEvent(compactDecision);
|
|
3595
3732
|
if (event.onUIEvent && event.depth === 0) {
|
|
3596
3733
|
const afterSize = Math.round(estReal(event.messageArr));
|
|
3597
3734
|
if (preCompactSize - afterSize > 0) {
|
|
@@ -4173,13 +4310,19 @@ var init_inbox = __esm({
|
|
|
4173
4310
|
};
|
|
4174
4311
|
flushLeftoverToTranscript = async (sessionId) => {
|
|
4175
4312
|
const texts = claimSessionInbox(sessionId);
|
|
4313
|
+
let failed = 0;
|
|
4176
4314
|
for (const t of texts) {
|
|
4177
|
-
await appendMessage({ sessionId, role: "user", content: t });
|
|
4315
|
+
const ok = await appendMessage({ sessionId, role: "user", content: t });
|
|
4316
|
+
if (!ok) failed++;
|
|
4178
4317
|
}
|
|
4179
4318
|
if (texts.length > 0) {
|
|
4180
|
-
|
|
4319
|
+
if (failed > 0) {
|
|
4320
|
+
console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u7ED3\u675F\u65F6 ${failed}/${texts.length} \u6761\u8865\u5145\u8F93\u5165\u843D\u76D8\u5931\u8D25\uFF08transcript \u4FA7\u6545\u969C\uFF0C\u4E0B\u8F6E\u65E0\u6CD5\u5E26\u5165\uFF09`);
|
|
4321
|
+
} else {
|
|
4322
|
+
console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u7ED3\u675F\u65F6 ${texts.length} \u6761\u8865\u5145\u8F93\u5165\u672A\u8BA4\u9886\uFF0C\u5DF2\u5199\u5165 transcript\uFF08\u4E0B\u8F6E\u81EA\u52A8\u5E26\u5165\uFF09`);
|
|
4323
|
+
}
|
|
4181
4324
|
}
|
|
4182
|
-
return texts.length;
|
|
4325
|
+
return { flushed: texts.length - failed, failed };
|
|
4183
4326
|
};
|
|
4184
4327
|
}
|
|
4185
4328
|
});
|
|
@@ -5548,6 +5691,153 @@ var init_httpExecutor = __esm({
|
|
|
5548
5691
|
}
|
|
5549
5692
|
});
|
|
5550
5693
|
|
|
5694
|
+
// ../core/src/agent/agentWake.ts
|
|
5695
|
+
var AGENT_DONE_SENTINEL, SUBAGENT_REPORT_MAX_CHARS, wakeDrivers, wakeQueued, busyRuns, inFlightByParent, quiesceWaiters, registerWakeDriver, registerWakeDriverIfAbsent, unregisterWakeDriver, markRunStart, markRunEnd, pumpWake, scheduleWake, clearQueuedWake, notifySubagentDone, buildSubagentDoneReport, trackSubagentInFlight, releaseSubagentInFlight, notifyQuiesceWaiters, isQuiescent, waitSessionQuiescent;
|
|
5696
|
+
var init_agentWake = __esm({
|
|
5697
|
+
"../core/src/agent/agentWake.ts"() {
|
|
5698
|
+
"use strict";
|
|
5699
|
+
init_inbox();
|
|
5700
|
+
init_transcript();
|
|
5701
|
+
AGENT_DONE_SENTINEL = "\u27E6DSC:AGENT-DONE\u27E7 \u5B50\u4EE3\u7406\u5B8C\u6210\u5524\u9192\uFF1A\u672C\u4F1A\u8BDD\u521A\u6CE8\u5165\u4E86\u5B50\u4EE3\u7406\u5B8C\u6210\u6C47\u62A5\uFF08\u89C1\u76F8\u90BB\u6D88\u606F\uFF09\uFF0C\u8BF7\u9010\u6761\u6D88\u5316\u6C47\u62A5\u5185\u5BB9\u5E76\u7EE7\u7EED\u63A8\u8FDB\u4E3B\u4EFB\u52A1\uFF1B\u82E5\u65E0\u5F85\u7EED\u4E8B\u9879\uFF0C\u7B80\u8981\u786E\u8BA4\u5373\u53EF\u3002";
|
|
5702
|
+
SUBAGENT_REPORT_MAX_CHARS = 4096;
|
|
5703
|
+
wakeDrivers = /* @__PURE__ */ new Map();
|
|
5704
|
+
wakeQueued = /* @__PURE__ */ new Set();
|
|
5705
|
+
busyRuns = /* @__PURE__ */ new Set();
|
|
5706
|
+
inFlightByParent = /* @__PURE__ */ new Map();
|
|
5707
|
+
quiesceWaiters = /* @__PURE__ */ new Map();
|
|
5708
|
+
registerWakeDriver = (sessionId, driver) => {
|
|
5709
|
+
if (!sessionId) return;
|
|
5710
|
+
wakeDrivers.set(sessionId, driver);
|
|
5711
|
+
};
|
|
5712
|
+
registerWakeDriverIfAbsent = (sessionId, driver) => {
|
|
5713
|
+
if (!sessionId || wakeDrivers.has(sessionId)) return;
|
|
5714
|
+
wakeDrivers.set(sessionId, driver);
|
|
5715
|
+
};
|
|
5716
|
+
unregisterWakeDriver = (sessionId) => {
|
|
5717
|
+
wakeDrivers.delete(sessionId);
|
|
5718
|
+
};
|
|
5719
|
+
markRunStart = (sessionId) => {
|
|
5720
|
+
if (!sessionId) return;
|
|
5721
|
+
busyRuns.add(sessionId);
|
|
5722
|
+
};
|
|
5723
|
+
markRunEnd = (sessionId) => {
|
|
5724
|
+
if (!sessionId) return;
|
|
5725
|
+
busyRuns.delete(sessionId);
|
|
5726
|
+
pumpWake(sessionId);
|
|
5727
|
+
notifyQuiesceWaiters(sessionId);
|
|
5728
|
+
};
|
|
5729
|
+
pumpWake = (sessionId) => {
|
|
5730
|
+
if (!wakeQueued.has(sessionId)) return;
|
|
5731
|
+
if (busyRuns.has(sessionId)) return;
|
|
5732
|
+
wakeQueued.delete(sessionId);
|
|
5733
|
+
const driver = wakeDrivers.get(sessionId);
|
|
5734
|
+
if (!driver) return;
|
|
5735
|
+
busyRuns.add(sessionId);
|
|
5736
|
+
void (async () => {
|
|
5737
|
+
try {
|
|
5738
|
+
await driver();
|
|
5739
|
+
} catch (e) {
|
|
5740
|
+
console.warn(`\u26A0\uFE0F [agentWake] \u5524\u9192 run \u5F02\u5E38\uFF08\u5DF2\u5FFD\u7565\uFF0C\u94FE\u8DEF\u7EE7\u7EED\u6536\u655B\uFF09: ${e?.message ?? e}`);
|
|
5741
|
+
} finally {
|
|
5742
|
+
busyRuns.delete(sessionId);
|
|
5743
|
+
notifyQuiesceWaiters(sessionId);
|
|
5744
|
+
pumpWake(sessionId);
|
|
5745
|
+
}
|
|
5746
|
+
})();
|
|
5747
|
+
};
|
|
5748
|
+
scheduleWake = (sessionId) => {
|
|
5749
|
+
if (!sessionId || wakeQueued.has(sessionId)) return;
|
|
5750
|
+
wakeQueued.add(sessionId);
|
|
5751
|
+
pumpWake(sessionId);
|
|
5752
|
+
notifyQuiesceWaiters(sessionId);
|
|
5753
|
+
};
|
|
5754
|
+
clearQueuedWake = (sessionId) => {
|
|
5755
|
+
wakeQueued.delete(sessionId);
|
|
5756
|
+
};
|
|
5757
|
+
notifySubagentDone = async (parentSessionId, report) => {
|
|
5758
|
+
if (!parentSessionId || !report) return;
|
|
5759
|
+
const delivered = pushSessionInbox(parentSessionId, report);
|
|
5760
|
+
if (!delivered) {
|
|
5761
|
+
try {
|
|
5762
|
+
await appendMessage({ sessionId: parentSessionId, role: "user", content: report });
|
|
5763
|
+
} catch {
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
scheduleWake(parentSessionId);
|
|
5767
|
+
};
|
|
5768
|
+
buildSubagentDoneReport = (agentId, taskBrief, result) => {
|
|
5769
|
+
const status = result.status ?? (result.output ? "final" : "error");
|
|
5770
|
+
const statusLabel = status === "final" ? "final\uFF08\u6B63\u5E38\u5B8C\u6210\uFF09" : status === "aborted" ? "aborted\uFF08\u88AB\u4E2D\u6B62\uFF09" : "error\uFF08\u5F02\u5E38\uFF09";
|
|
5771
|
+
const over = result.output.length > SUBAGENT_REPORT_MAX_CHARS;
|
|
5772
|
+
const body = over ? result.output.slice(0, SUBAGENT_REPORT_MAX_CHARS) : result.output;
|
|
5773
|
+
return [
|
|
5774
|
+
`\u{1F916} [\u5B50\u4EE3\u7406\u5B8C\u6210] \u{1F194} ${agentId}${taskBrief ? `\uFF08${taskBrief}\uFF09` : ""}`,
|
|
5775
|
+
`\u72B6\u6001\uFF1A${statusLabel}`,
|
|
5776
|
+
`\u6C47\u62A5\uFF1A`,
|
|
5777
|
+
body,
|
|
5778
|
+
over ? `
|
|
5779
|
+
\uFF08\u26A0\uFE0F \u6C47\u62A5\u8D85\u957F\u5DF2\u622A\u65AD\u81F3 ${SUBAGENT_REPORT_MAX_CHARS} \u5B57\u7B26\uFF1B\u5B8C\u6574\u5185\u5BB9\u53EF spawn_agent \u4F20 resume_session_id="${agentId}" \u8FFD\u95EE\u3002\uFF09` : ""
|
|
5780
|
+
].filter((s) => s !== "").join("\n");
|
|
5781
|
+
};
|
|
5782
|
+
trackSubagentInFlight = (parentSessionId, subSessionId) => {
|
|
5783
|
+
if (!parentSessionId) return;
|
|
5784
|
+
let set = inFlightByParent.get(parentSessionId);
|
|
5785
|
+
if (!set) {
|
|
5786
|
+
set = /* @__PURE__ */ new Set();
|
|
5787
|
+
inFlightByParent.set(parentSessionId, set);
|
|
5788
|
+
}
|
|
5789
|
+
set.add(subSessionId);
|
|
5790
|
+
};
|
|
5791
|
+
releaseSubagentInFlight = (parentSessionId, subSessionId) => {
|
|
5792
|
+
const set = inFlightByParent.get(parentSessionId);
|
|
5793
|
+
if (!set) return;
|
|
5794
|
+
set.delete(subSessionId);
|
|
5795
|
+
if (set.size === 0) inFlightByParent.delete(parentSessionId);
|
|
5796
|
+
notifyQuiesceWaiters(parentSessionId);
|
|
5797
|
+
};
|
|
5798
|
+
notifyQuiesceWaiters = (sessionId) => {
|
|
5799
|
+
const waiters = quiesceWaiters.get(sessionId);
|
|
5800
|
+
if (!waiters || waiters.size === 0) return;
|
|
5801
|
+
for (const w of [...waiters]) w();
|
|
5802
|
+
};
|
|
5803
|
+
isQuiescent = (sessionId) => !inFlightByParent.get(sessionId)?.size && !busyRuns.has(sessionId) && !wakeQueued.has(sessionId);
|
|
5804
|
+
waitSessionQuiescent = (sessionId, signal, timeoutMs2) => {
|
|
5805
|
+
if (isQuiescent(sessionId)) return Promise.resolve(true);
|
|
5806
|
+
return new Promise((resolve) => {
|
|
5807
|
+
let settled = false;
|
|
5808
|
+
const finish = (ok) => {
|
|
5809
|
+
if (settled) return;
|
|
5810
|
+
settled = true;
|
|
5811
|
+
if (timer) clearTimeout(timer);
|
|
5812
|
+
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
5813
|
+
const set = quiesceWaiters.get(sessionId);
|
|
5814
|
+
if (set) {
|
|
5815
|
+
set.delete(check);
|
|
5816
|
+
if (set.size === 0) quiesceWaiters.delete(sessionId);
|
|
5817
|
+
}
|
|
5818
|
+
resolve(ok);
|
|
5819
|
+
};
|
|
5820
|
+
const check = () => {
|
|
5821
|
+
if (isQuiescent(sessionId)) finish(true);
|
|
5822
|
+
};
|
|
5823
|
+
let timer;
|
|
5824
|
+
const onAbort = () => finish(false);
|
|
5825
|
+
if (!quiesceWaiters.has(sessionId)) quiesceWaiters.set(sessionId, /* @__PURE__ */ new Set());
|
|
5826
|
+
quiesceWaiters.get(sessionId).add(check);
|
|
5827
|
+
if (signal) {
|
|
5828
|
+
if (signal.aborted) {
|
|
5829
|
+
finish(false);
|
|
5830
|
+
return;
|
|
5831
|
+
}
|
|
5832
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5833
|
+
}
|
|
5834
|
+
if (timeoutMs2 != null && timeoutMs2 > 0) timer = setTimeout(() => finish(false), timeoutMs2);
|
|
5835
|
+
check();
|
|
5836
|
+
});
|
|
5837
|
+
};
|
|
5838
|
+
}
|
|
5839
|
+
});
|
|
5840
|
+
|
|
5551
5841
|
// ../core/src/agent/repeatBreaker.ts
|
|
5552
5842
|
var createRepeatBreaker;
|
|
5553
5843
|
var init_repeatBreaker = __esm({
|
|
@@ -5889,7 +6179,7 @@ ${style.body}`);
|
|
|
5889
6179
|
});
|
|
5890
6180
|
|
|
5891
6181
|
// ../core/src/memory/registry.ts
|
|
5892
|
-
var memories, registerMemory, listMemories, getMemory, unregisterMemory, clearMemories, memoryIndexLine, getMemoryIndex;
|
|
6182
|
+
var memories, registerMemory, listMemories, getMemory, unregisterMemory, clearMemories, memoryIndexLine, MEMORY_LIMITS, getMemoryIndex;
|
|
5893
6183
|
var init_registry6 = __esm({
|
|
5894
6184
|
"../core/src/memory/registry.ts"() {
|
|
5895
6185
|
"use strict";
|
|
@@ -5908,6 +6198,7 @@ var init_registry6 = __esm({
|
|
|
5908
6198
|
};
|
|
5909
6199
|
clearMemories = memories.clear;
|
|
5910
6200
|
memoryIndexLine = (m) => `- **${m.name}** (${m.type}) \u2014 ${m.description}`;
|
|
6201
|
+
MEMORY_LIMITS = { maxCount: 200, maxIndexBytes: 16 * 1024 };
|
|
5911
6202
|
getMemoryIndex = () => {
|
|
5912
6203
|
const all = memories.list();
|
|
5913
6204
|
if (all.length === 0) return null;
|
|
@@ -5917,7 +6208,7 @@ var init_registry6 = __esm({
|
|
|
5917
6208
|
});
|
|
5918
6209
|
|
|
5919
6210
|
// ../core/src/memory/inject.ts
|
|
5920
|
-
var MEMORY_FENCE, MEMORY_MARKER, injectMemory;
|
|
6211
|
+
var MEMORY_FENCE, MEMORY_MARKER, memorySnapshots, injectMemory, clearMemorySnapshot;
|
|
5921
6212
|
var init_inject5 = __esm({
|
|
5922
6213
|
"../core/src/memory/inject.ts"() {
|
|
5923
6214
|
"use strict";
|
|
@@ -5925,12 +6216,24 @@ var init_inject5 = __esm({
|
|
|
5925
6216
|
init_registry6();
|
|
5926
6217
|
MEMORY_FENCE = "\u27E6DSC:MEMORY\u27E7";
|
|
5927
6218
|
MEMORY_MARKER = "\u3010\u8BB0\u5FC6\u7D22\u5F15\u3011";
|
|
5928
|
-
|
|
5929
|
-
|
|
6219
|
+
memorySnapshots = /* @__PURE__ */ new Map();
|
|
6220
|
+
injectMemory = (message, sessionId) => {
|
|
6221
|
+
let index;
|
|
6222
|
+
if (sessionId === void 0) {
|
|
6223
|
+
index = getMemoryIndex();
|
|
6224
|
+
} else {
|
|
6225
|
+
if (!memorySnapshots.has(sessionId)) {
|
|
6226
|
+
memorySnapshots.set(sessionId, getMemoryIndex());
|
|
6227
|
+
}
|
|
6228
|
+
index = memorySnapshots.get(sessionId) ?? null;
|
|
6229
|
+
}
|
|
5930
6230
|
if (!index) return;
|
|
5931
6231
|
injectMarkedBlock(message, MEMORY_FENCE, `${MEMORY_MARKER}
|
|
5932
6232
|
${index}`);
|
|
5933
6233
|
};
|
|
6234
|
+
clearMemorySnapshot = (sessionId) => {
|
|
6235
|
+
memorySnapshots.delete(sessionId);
|
|
6236
|
+
};
|
|
5934
6237
|
}
|
|
5935
6238
|
});
|
|
5936
6239
|
|
|
@@ -6009,7 +6312,7 @@ var init_systemInjections = __esm({
|
|
|
6009
6312
|
injectSkillCatalog(message);
|
|
6010
6313
|
injectAgentCatalog(message);
|
|
6011
6314
|
injectProjectGuide(message);
|
|
6012
|
-
injectMemory(message);
|
|
6315
|
+
injectMemory(message, options.sessionId);
|
|
6013
6316
|
return { rawTools, cleanedToolSchemas, toolsTokens };
|
|
6014
6317
|
};
|
|
6015
6318
|
}
|
|
@@ -6102,7 +6405,12 @@ var init_agent = __esm({
|
|
|
6102
6405
|
type: "function",
|
|
6103
6406
|
function: {
|
|
6104
6407
|
name: "spawn_agent",
|
|
6105
|
-
description:
|
|
6408
|
+
description: [
|
|
6409
|
+
"\u5F02\u6B65\u6D3E\u751F\u62E5\u6709\u72EC\u7ACB\u6267\u884C\u73AF\u5883\u7684\u5B50 agent \u5904\u7406\u9694\u79BB\u5B50\u4EFB\u52A1\uFF1A\u8C03\u7528\u7ACB\u5373\u8FD4\u56DE agent_id\uFF0C\u4E0D\u7B49\u5F85\u5B8C\u6210\uFF1B\u5B50 agent \u5728\u540E\u53F0\u6267\u884C\uFF0C\u5B8C\u6210\u540E\u5176\u6C47\u62A5\u4F1A\u81EA\u52A8\u6CE8\u5165\u672C\u4F1A\u8BDD\u5E76\u5524\u9192\u4F60\u7EE7\u7EED\u63A8\u8FDB\uFF08\u65E0\u9700\u8F6E\u8BE2\u3001\u65E0\u9700\u7B49\u5F85\uFF09\u3002",
|
|
6410
|
+
"\u6D3E\u51FA\u540E\u4F60\u53EF\u7ACB\u5373\u7EE7\u7EED\u6D3E\u6D3B / \u5904\u7406\u5176\u4ED6\u4E8B\u52A1 / \u56DE\u590D\u7528\u6237\uFF1B\u5F53\u524D\u65E0\u5176\u4ED6\u4E8B\u52A1\u65F6\u53EF\u7B80\u77ED\u544A\u77E5\u5DF2\u59D4\u6D3E\u5E76\u7ED3\u675F\u672C\u8F6E\u2014\u2014\u6C47\u62A5\u9001\u8FBE\u65F6\u4F60\u4F1A\u88AB\u81EA\u52A8\u5524\u9192\u3002",
|
|
6411
|
+
"name \u53EF\u6307\u5B9A\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF0C\u5957\u7528\u5176\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF09\uFF1B\u7F3A\u7701\u8D70\u901A\u7528\u6A21\u677F\u3002",
|
|
6412
|
+
"\u7EED\u8DD1\uFF1A\u4F20 resume_session_id\uFF08\u6B64\u524D\u8FD4\u56DE\u7684 agent_id\uFF09\u590D\u7528\u8BE5\u5B50 Agent \u5B8C\u6574\u5386\u53F2\u8BB0\u5FC6\uFF0C\u9002\u5408\u8FFD\u95EE/\u7EE7\u7EED\u672A\u5B8C\u4EFB\u52A1\uFF08\u540C\u6837\u5F02\u6B65\uFF0C\u5B8C\u6210\u540E\u81EA\u52A8\u5524\u9192\uFF09\u3002"
|
|
6413
|
+
].join("\n"),
|
|
6106
6414
|
parameters: {
|
|
6107
6415
|
type: "object",
|
|
6108
6416
|
properties: {
|
|
@@ -6114,30 +6422,29 @@ var init_agent = __esm({
|
|
|
6114
6422
|
required: ["task"]
|
|
6115
6423
|
},
|
|
6116
6424
|
safetyLevel: "safe" /* SAFE */,
|
|
6117
|
-
isSync:
|
|
6118
|
-
async execute(args, ctx) {
|
|
6119
|
-
if (!ctx)
|
|
6120
|
-
|
|
6425
|
+
isSync: false,
|
|
6426
|
+
async *execute(args, ctx) {
|
|
6427
|
+
if (!ctx) {
|
|
6428
|
+
yield toolFailure("[\u6D3E\u751F\u5931\u8D25]\uFF1Aspawn_agent \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002");
|
|
6429
|
+
return;
|
|
6430
|
+
}
|
|
6431
|
+
const prep = await prepareSubagent(
|
|
6121
6432
|
{ task: args.task, name: args.name, role: args.role, resumeSessionId: args.resume_session_id },
|
|
6122
6433
|
ctx,
|
|
6123
6434
|
getGlobalTools
|
|
6124
6435
|
);
|
|
6125
|
-
if (!
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
`,
|
|
6132
|
-
|
|
6133
|
-
`,
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
`,
|
|
6138
|
-
`\u{1F6A8}\u3010\u91CD\u8981\u540C\u6B65\u901A\u77E5\u3011: \u8BE5\u5B50 Agent \u521A\u624D\u6781\u6709\u53EF\u80FD\u5DF2\u7ECF\u9AD8\u9891\u4FEE\u6539\u3001\u521B\u5EFA\u6216\u5220\u9664\u4E86\u4F60\u7BA1\u8F96\u533A\u5185\u7684\u672C\u5730\u6E90\u7801\u6587\u4EF6\u3002`,
|
|
6139
|
-
`\u5982\u679C\u4F60\uFF08\u7236\u7EA7Agent\uFF09\u63A5\u4E0B\u6765\u9700\u8981\u5BF9\u6587\u4EF6\u7CFB\u7EDF\u7EE7\u7EED\u5B9E\u65BD edit_file\u3001write_file \u6216\u9A8C\u8BC1\uFF0C\u3010\u4F60\u5FC5\u987B\u3011\u9996\u5148\u8C03\u7528 read_file \u6216 list_dir \u91CD\u65B0\u8BFB\u53D6\u6700\u65B0\u78C1\u76D8\u72B6\u6001\uFF0C\u7981\u6B62\u76F2\u76EE\u4F9D\u8D56\u4F60\u4E4B\u524D\u7684\u8BB0\u5FC6\u7F13\u5B58\uFF0C\u5426\u5219\u5FC5\u7136\u4F1A\u5F15\u53D1\u552F\u4E00\u6027\u51B2\u7A81\u5339\u914D\u5931\u8D25\uFF01`
|
|
6140
|
-
].join("");
|
|
6436
|
+
if (!prep.ok) {
|
|
6437
|
+
yield asToolFailure(prep.error);
|
|
6438
|
+
return;
|
|
6439
|
+
}
|
|
6440
|
+
const agentId = prep.prepared.subSessionId;
|
|
6441
|
+
yield [
|
|
6442
|
+
`\u2705 \u5B50\u4EE3\u7406\u5DF2\u5F02\u6B65\u542F\u52A8 \u{1F194} ${agentId}${prep.prepared.manifest ? `\uFF08\u58F0\u660E\u5F0F=${prep.prepared.manifest.name}\uFF09` : ""}`,
|
|
6443
|
+
`\u4EFB\u52A1\u6458\u8981\uFF1A${prep.prepared.taskBrief}${args.task.length > 60 ? "\u2026" : ""}`,
|
|
6444
|
+
`\u5B83\u5B8C\u6210\u540E\u6C47\u62A5\u4F1A\u81EA\u52A8\u9001\u8FBE\u672C\u4F1A\u8BDD\u5E76\u5524\u9192\u4F60\u7EE7\u7EED\uFF1B\u671F\u95F4\u65E0\u9700\u7B49\u5F85\u2014\u2014\u53EF\u7EE7\u7EED\u6D3E\u6D3B / \u5904\u7406\u5176\u4ED6\u4E8B\u52A1\uFF0C\u65E0\u5176\u4ED6\u4E8B\u52A1\u65F6\u7B80\u77ED\u544A\u77E5\u7528\u6237\u5373\u53EF\u6536\u5C3E\u672C\u8F6E\u3002`,
|
|
6445
|
+
`\uFF08\u8FFD\u95EE/\u7EED\u8DD1\uFF1Aspawn_agent \u4F20 resume_session_id="${agentId}"\uFF0C\u5176\u5386\u53F2\u8BB0\u5FC6\u5B8C\u6574\u4FDD\u7559\u3002\uFF09`
|
|
6446
|
+
].join("\n");
|
|
6447
|
+
await runPreparedSubagent(prep.prepared, ctx, { notifyDone: true });
|
|
6141
6448
|
}
|
|
6142
6449
|
}
|
|
6143
6450
|
}
|
|
@@ -6370,6 +6677,7 @@ var init_workflow = __esm({
|
|
|
6370
6677
|
init_config();
|
|
6371
6678
|
init_guard();
|
|
6372
6679
|
init_manager();
|
|
6680
|
+
init_agentWake();
|
|
6373
6681
|
init_workflowHelpers();
|
|
6374
6682
|
createWorkflowTools = (getGlobalTools) => [
|
|
6375
6683
|
{
|
|
@@ -6377,7 +6685,8 @@ var init_workflow = __esm({
|
|
|
6377
6685
|
function: {
|
|
6378
6686
|
name: "run_workflow",
|
|
6379
6687
|
description: [
|
|
6380
|
-
"\u591A\u5B50 agent \u7F16\u6392\uFF1A\u628A\u53EF\u5206\u89E3\u7684\u5927\u4EFB\u52A1\u6D3E\u7ED9\u591A\u4E2A\u72EC\u7ACB\u5B50 agent\u3002parallel\uFF08\u9ED8\u8BA4\uFF09=\u5404\u6B65\u9AA4\u5E76\u53D1\u6267\u884C\u3001\u7ED3\u679C\u6309\u5E8F\u805A\u5408\uFF0C\u9002\u5408\u4E92\u76F8\u72EC\u7ACB\u7684\u53EA\u8BFB/\u5206\u6790\u4EFB\u52A1\uFF1Bpipeline=\u6B65\u9AA4\u4E32\u884C\uFF0C\u524D\u9636\u6BB5\u4EA7\u51FA\u81EA\u52A8\u4F5C\u4E3A\u540E\u9636\u6BB5\u8F93\u5165\uFF0C\u9002\u5408\u5206\u9636\u6BB5\u52A0\u5DE5\uFF08\u62BD\u53D6\u2192\u5F52\u7C7B\u2192\u6C47\u603B\uFF09\u3002",
|
|
6688
|
+
"\u5F02\u6B65\u591A\u5B50 agent \u7F16\u6392\uFF1A\u628A\u53EF\u5206\u89E3\u7684\u5927\u4EFB\u52A1\u6D3E\u7ED9\u591A\u4E2A\u72EC\u7ACB\u5B50 agent\uFF0C\u8C03\u7528\u7ACB\u5373\u8FD4\u56DE\uFF08\u4E0D\u7B49\u5F85\u5B8C\u6210\uFF09\uFF0C\u5168\u90E8\u6B65\u9AA4\u7ED3\u675F\u540E\u4E00\u6761\u7EC8\u6001\u6C47\u603B\u81EA\u52A8\u6CE8\u5165\u672C\u4F1A\u8BDD\u5E76\u5524\u9192\u4F60\u7EE7\u7EED\u63A8\u8FDB\uFF08\u4E2D\u95F4\u6001\u4E0D\u56DE\u6D41\uFF0C\u9632\u4E8B\u4EF6\u6D2A\u5CF0\uFF09\u3002parallel\uFF08\u9ED8\u8BA4\uFF09=\u5404\u6B65\u9AA4\u5E76\u53D1\u6267\u884C\u3001\u7ED3\u679C\u6309\u5E8F\u805A\u5408\uFF0C\u9002\u5408\u4E92\u76F8\u72EC\u7ACB\u7684\u53EA\u8BFB/\u5206\u6790\u4EFB\u52A1\uFF1Bpipeline=\u6B65\u9AA4\u4E32\u884C\uFF0C\u524D\u9636\u6BB5\u4EA7\u51FA\u81EA\u52A8\u4F5C\u4E3A\u540E\u9636\u6BB5\u8F93\u5165\uFF0C\u9002\u5408\u5206\u9636\u6BB5\u52A0\u5DE5\uFF08\u62BD\u53D6\u2192\u5F52\u7C7B\u2192\u6C47\u603B\uFF09\u3002",
|
|
6689
|
+
"\u6D3E\u51FA\u540E\u4F60\u53EF\u7ACB\u5373\u7EE7\u7EED\u6D3E\u6D3B / \u5904\u7406\u5176\u4ED6\u4E8B\u52A1 / \u56DE\u590D\u7528\u6237\uFF1B\u5F53\u524D\u65E0\u5176\u4ED6\u4E8B\u52A1\u65F6\u53EF\u7B80\u77ED\u544A\u77E5\u5DF2\u7F16\u6392\u5E76\u7ED3\u675F\u672C\u8F6E\u2014\u2014\u6C47\u603B\u9001\u8FBE\u65F6\u4F60\u4F1A\u88AB\u81EA\u52A8\u5524\u9192\u3002",
|
|
6381
6690
|
'\u6B65\u9AA4\u53EF\u7528 name \u5957\u7528\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF09\u3001key \u4F5C\u7ED3\u679C\u6807\u7B7E\u3002parallel \u9ED8\u8BA4\u5171\u4EAB\u5DE5\u4F5C\u533A\uFF0C\u4EC5\u9002\u5408\u5E76\u884C\u8BFB/\u5206\u6790\uFF1B\u5E76\u884C\u5199/\u8FC1\u79FB\u7528 isolation="worktree"\uFF08\u6BCF\u6B65\u72EC\u7ACB git worktree\uFF0C\u7ED3\u675F\u540E\u6536\u5272\u5404\u6B65 diff\uFF09\u3002',
|
|
6382
6691
|
"\u4E0E spawn_agent \u7684\u5206\u5DE5\uFF1A\u5355\u4EFB\u52A1\u4E32\u884C\u59D4\u6D3E\u7528 spawn_agent\uFF1B\u4EFB\u52A1\u53EF\u5206\u89E3\u3001\u591A\u5206\u652F\u6216\u6D41\u6C34\u7EBF\u5F0F\u65F6\u7528\u672C\u5DE5\u5177\u3002"
|
|
6383
6692
|
].join("\n"),
|
|
@@ -6417,18 +6726,29 @@ var init_workflow = __esm({
|
|
|
6417
6726
|
required: ["steps"]
|
|
6418
6727
|
},
|
|
6419
6728
|
safetyLevel: "safe" /* SAFE */,
|
|
6420
|
-
isSync:
|
|
6729
|
+
isSync: false,
|
|
6421
6730
|
// 聚合输出整体兜底截断(各子 agent 结果已按 workflowPerStepChars 单独截断)。
|
|
6731
|
+
// ★ 异步化后工具结果 = 首个 yield 的启动回执(天然短);此上限改用于终态汇总的 inbox 报文截断。
|
|
6422
6732
|
maxOutputCharacters: 18e3,
|
|
6423
|
-
async
|
|
6424
|
-
|
|
6733
|
+
// ★ 子代理异步化(方案 §4.4):async* + 首个 yield 立即回执;全部步骤在 generator 挂起期间后台执行,
|
|
6734
|
+
// 完成后一条终态汇总经 agentWake.notifySubagentDone 回流(中间态不回流,防事件洪峰)。
|
|
6735
|
+
// 宿主中止时执行层 gen.return() 拦在 await 前 → 不产生中止后的唤醒(与 spawn_agent 同语义)。
|
|
6736
|
+
async *execute(args, ctx) {
|
|
6737
|
+
if (!ctx) {
|
|
6738
|
+
yield toolFailure("[\u5DE5\u4F5C\u6D41]\uFF1Arun_workflow \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002");
|
|
6739
|
+
return;
|
|
6740
|
+
}
|
|
6425
6741
|
const maxSteps = appConfig.workflowMaxSteps;
|
|
6426
6742
|
const validationError = validateWorkflowArgs(args, maxSteps);
|
|
6427
|
-
if (validationError)
|
|
6743
|
+
if (validationError) {
|
|
6744
|
+
yield validationError;
|
|
6745
|
+
return;
|
|
6746
|
+
}
|
|
6428
6747
|
const mode = args.mode === "pipeline" ? "pipeline" : "parallel";
|
|
6429
6748
|
const isolation = args.isolation === "worktree" ? "worktree" : "none";
|
|
6430
6749
|
if (isolation === "worktree" && mode === "pipeline") {
|
|
6431
|
-
|
|
6750
|
+
yield toolFailure('[\u5DE5\u4F5C\u6D41]\uFF1Aisolation="worktree" \u6682\u4EC5\u652F\u6301 parallel \u6A21\u5F0F\uFF08pipeline \u4E32\u884C\u65E0\u9700\u9694\u79BB\uFF09\u3002\u8BF7\u6539 mode=parallel \u6216 isolation=none\u3002');
|
|
6751
|
+
return;
|
|
6432
6752
|
}
|
|
6433
6753
|
const steps = args.steps.map((s) => ({
|
|
6434
6754
|
task: String(s.task),
|
|
@@ -6437,8 +6757,13 @@ var init_workflow = __esm({
|
|
|
6437
6757
|
key: s.key ? String(s.key) : void 0
|
|
6438
6758
|
}));
|
|
6439
6759
|
if (ctx.depth >= MAX_AGENT_DEPTH) {
|
|
6440
|
-
|
|
6760
|
+
yield toolFailure(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u5DF2\u8FBE\u5230\u6700\u5927 Agent \u5D4C\u5957\u6DF1\u5EA6\uFF08${MAX_AGENT_DEPTH}\u5C42\uFF09\uFF0C\u65E0\u6CD5\u6D3E\u751F\u5DE5\u4F5C\u6D41\u5B50 agent\u3002\u8BF7\u5728\u672C\u5C42\u7EA7\u81EA\u884C\u6D88\u5316\u4EFB\u52A1\u3002`);
|
|
6761
|
+
return;
|
|
6441
6762
|
}
|
|
6763
|
+
yield [
|
|
6764
|
+
`\u2705 \u5DE5\u4F5C\u6D41\u5DF2\u5F02\u6B65\u542F\u52A8\uFF08mode=${mode}\uFF0C\u5171 ${steps.length} \u6B65${isolation === "worktree" ? "\uFF0C\u6BCF\u6B65\u72EC\u7ACB git worktree \u9694\u79BB" : ""}\uFF09\u3002`,
|
|
6765
|
+
`\u5168\u90E8\u6B65\u9AA4\u540E\u53F0\u6267\u884C\u5B8C\u6BD5\u540E\uFF0C\u4E00\u6761\u7EC8\u6001\u6C47\u603B\u4F1A\u81EA\u52A8\u9001\u8FBE\u672C\u4F1A\u8BDD\u5E76\u5524\u9192\u4F60\u7EE7\u7EED\uFF08\u4E2D\u95F4\u6001\u4E0D\u56DE\u6D41\uFF09\uFF1B\u671F\u95F4\u65E0\u9700\u7B49\u5F85\u2014\u2014\u53EF\u7EE7\u7EED\u6D3E\u6D3B / \u5904\u7406\u5176\u4ED6\u4E8B\u52A1\uFF0C\u65E0\u5176\u4ED6\u4E8B\u52A1\u65F6\u7B80\u77ED\u544A\u77E5\u7528\u6237\u5373\u53EF\u6536\u5C3E\u672C\u8F6E\u3002`
|
|
6766
|
+
].join("\n");
|
|
6442
6767
|
const approvalMutex = createSemaphore(1);
|
|
6443
6768
|
const serializedApproval = ctx.requestApproval ? async (...raArgs) => {
|
|
6444
6769
|
await approvalMutex.acquire();
|
|
@@ -6521,9 +6846,25 @@ var init_workflow = __esm({
|
|
|
6521
6846
|
outcomes = await Promise.all(running);
|
|
6522
6847
|
}
|
|
6523
6848
|
} catch (err) {
|
|
6524
|
-
|
|
6849
|
+
await notifySubagentDone(ctx.sessionId, [
|
|
6850
|
+
`\u{1F916} [\u5DE5\u4F5C\u6D41\u5B8C\u6210]\uFF08mode=${mode}\uFF0C\u5171 ${steps.length} \u6B65\uFF09`,
|
|
6851
|
+
`\u72B6\u6001\uFF1Aerror\uFF08\u7F16\u6392\u5F02\u5E38\uFF09`,
|
|
6852
|
+
`\u6C47\u603B\uFF1A`,
|
|
6853
|
+
`\u26A0\uFE0F [\u5DE5\u4F5C\u6D41\u7F16\u6392\u5F02\u5E38]\uFF1A${err?.message ?? err}`
|
|
6854
|
+
].join("\n"));
|
|
6855
|
+
return;
|
|
6525
6856
|
}
|
|
6526
|
-
|
|
6857
|
+
const allOk = outcomes.every((o) => o.ok);
|
|
6858
|
+
const abortedCount = outcomes.filter((o) => !o.ok && o.output.includes("\u5DF2\u4E2D\u6B62")).length;
|
|
6859
|
+
const status = abortedCount === outcomes.length ? "aborted" : allOk ? "final" : "error";
|
|
6860
|
+
const statusLabel = status === "final" ? "final\uFF08\u5168\u90E8\u6B65\u9AA4\u6B63\u5E38\u5B8C\u6210\uFF09" : status === "aborted" ? "aborted\uFF08\u5168\u90E8\u6B65\u9AA4\u88AB\u4E2D\u6B62\uFF09" : "error\uFF08\u90E8\u5206\u6B65\u9AA4\u5931\u8D25/\u4E2D\u6B62\uFF09";
|
|
6861
|
+
await notifySubagentDone(ctx.sessionId, [
|
|
6862
|
+
`\u{1F916} [\u5DE5\u4F5C\u6D41\u5B8C\u6210]\uFF08mode=${mode}\uFF0C\u5171 ${steps.length} \u6B65\uFF09`,
|
|
6863
|
+
`\u72B6\u6001\uFF1A${statusLabel}`,
|
|
6864
|
+
`\u6C47\u603B\uFF1A`,
|
|
6865
|
+
// 截断口径与原同步返回一致(maxOutputCharacters 18000)
|
|
6866
|
+
truncateToolResult(formatWorkflowResult(mode, outcomes), 18e3)
|
|
6867
|
+
].join("\n"));
|
|
6527
6868
|
}
|
|
6528
6869
|
}
|
|
6529
6870
|
}
|
|
@@ -10797,8 +11138,9 @@ var init_frontmatter = __esm({
|
|
|
10797
11138
|
|
|
10798
11139
|
// ../core/src/memory/loader.ts
|
|
10799
11140
|
import fs28 from "fs/promises";
|
|
11141
|
+
import os5 from "os";
|
|
10800
11142
|
import path30 from "path";
|
|
10801
|
-
var GLOBAL_MEMORY_DIR, SOURCES, VALID_TYPES, parseMemoryAt, loadMemories, initMemories;
|
|
11143
|
+
var GLOBAL_MEMORY_DIR, SOURCES, VALID_TYPES, NAME_RE, parseMemoryAt, MAX_EXTERNAL_ITEMS, EXTERNAL_DESC_MAX_CHARS, EXTERNAL_BODY_MAX_BYTES, EXTERNAL_RULES, sanitizeExternalStem, extractExternalTitle, buildExternalDescription, truncateUtf8ToBytes, scanExternalRule, externalNoticeShown, loadExternalMemories, loadMemories, initMemories;
|
|
10802
11144
|
var init_loader3 = __esm({
|
|
10803
11145
|
"../core/src/memory/loader.ts"() {
|
|
10804
11146
|
"use strict";
|
|
@@ -10812,6 +11154,7 @@ var init_loader3 = __esm({
|
|
|
10812
11154
|
{ dir: path30.join(process.cwd(), ".deepseeker-code", "memory"), source: "project" }
|
|
10813
11155
|
];
|
|
10814
11156
|
VALID_TYPES = ["user", "feedback", "project", "reference"];
|
|
11157
|
+
NAME_RE = /^[a-z0-9-]+$/;
|
|
10815
11158
|
parseMemoryAt = async (file, _dir, source) => {
|
|
10816
11159
|
let raw;
|
|
10817
11160
|
try {
|
|
@@ -10830,7 +11173,7 @@ var init_loader3 = __esm({
|
|
|
10830
11173
|
console.warn(`\u26A0\uFE0F [memory] \u7F3A\u5C11 name \u6216 description\uFF08${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
10831
11174
|
return null;
|
|
10832
11175
|
}
|
|
10833
|
-
if (
|
|
11176
|
+
if (!NAME_RE.test(name)) {
|
|
10834
11177
|
console.warn(`\u26A0\uFE0F [memory] name "${name}" \u4E0D\u5408\u6CD5\uFF08\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26\uFF0C${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
10835
11178
|
return null;
|
|
10836
11179
|
}
|
|
@@ -10845,6 +11188,128 @@ var init_loader3 = __esm({
|
|
|
10845
11188
|
}
|
|
10846
11189
|
return { name, description, type, body: parsed.body.trim(), source, file };
|
|
10847
11190
|
};
|
|
11191
|
+
MAX_EXTERNAL_ITEMS = 12;
|
|
11192
|
+
EXTERNAL_DESC_MAX_CHARS = 120;
|
|
11193
|
+
EXTERNAL_BODY_MAX_BYTES = 64 * 1024;
|
|
11194
|
+
EXTERNAL_RULES = [
|
|
11195
|
+
// 全局层(~ 下,存在才扫,缺省静默跳过)
|
|
11196
|
+
{ dir: path30.join(os5.homedir(), ".claude"), file: "CLAUDE.md", name: "ext-claude", projectLayer: false },
|
|
11197
|
+
{ dir: path30.join(os5.homedir(), ".codex"), file: "AGENTS.md", name: "ext-codex", projectLayer: false },
|
|
11198
|
+
{ dir: path30.join(os5.homedir(), ".gemini"), file: "GEMINI.md", name: "ext-gemini", projectLayer: false },
|
|
11199
|
+
// 项目层(<cwd> 下)
|
|
11200
|
+
{ dir: process.cwd(), file: "CLAUDE.md", name: "ext-claude-project", projectLayer: true },
|
|
11201
|
+
{ dir: process.cwd(), file: "AGENTS.md", name: "ext-agents", projectLayer: true },
|
|
11202
|
+
{ dir: process.cwd(), file: "GEMINI.md", name: "ext-gemini-project", projectLayer: true },
|
|
11203
|
+
{ dir: path30.join(process.cwd(), ".cursor", "rules"), name: (stem) => `ext-cursor-${stem}`, projectLayer: true },
|
|
11204
|
+
{ dir: path30.join(process.cwd(), ".github"), file: "copilot-instructions.md", name: "ext-copilot", projectLayer: true }
|
|
11205
|
+
];
|
|
11206
|
+
sanitizeExternalStem = (stem) => stem.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11207
|
+
extractExternalTitle = (raw, fallbackStem) => {
|
|
11208
|
+
const lines = raw.split(/\r?\n/);
|
|
11209
|
+
for (const line of lines) {
|
|
11210
|
+
const m = /^#\s+(.+)$/.exec(line);
|
|
11211
|
+
if (m) return m[1].trim();
|
|
11212
|
+
}
|
|
11213
|
+
for (const line of lines) {
|
|
11214
|
+
const t = line.trim();
|
|
11215
|
+
if (t) return t.replace(/^#+\s*/, "") || fallbackStem;
|
|
11216
|
+
}
|
|
11217
|
+
return fallbackStem;
|
|
11218
|
+
};
|
|
11219
|
+
buildExternalDescription = (label, title) => {
|
|
11220
|
+
const full = `[\u5916\u90E8:${label}] ${title.replace(/\s+/g, " ").trim()}`;
|
|
11221
|
+
return full.length > EXTERNAL_DESC_MAX_CHARS ? full.slice(0, EXTERNAL_DESC_MAX_CHARS) : full;
|
|
11222
|
+
};
|
|
11223
|
+
truncateUtf8ToBytes = (text, maxBytes) => {
|
|
11224
|
+
const buf = Buffer.from(text, "utf-8");
|
|
11225
|
+
if (buf.length <= maxBytes) return { text, truncated: false };
|
|
11226
|
+
return { text: buf.subarray(0, maxBytes).toString("utf-8"), truncated: true };
|
|
11227
|
+
};
|
|
11228
|
+
scanExternalRule = async (rule) => {
|
|
11229
|
+
let entries;
|
|
11230
|
+
try {
|
|
11231
|
+
entries = await fs28.readdir(rule.dir, { withFileTypes: true });
|
|
11232
|
+
} catch {
|
|
11233
|
+
return [];
|
|
11234
|
+
}
|
|
11235
|
+
const out = [];
|
|
11236
|
+
for (const entry of entries) {
|
|
11237
|
+
if (!entry.isFile()) continue;
|
|
11238
|
+
if (rule.file ? entry.name !== rule.file : !entry.name.endsWith(".md")) continue;
|
|
11239
|
+
const stem = entry.name.replace(/\.md$/, "");
|
|
11240
|
+
const file = path30.join(rule.dir, entry.name);
|
|
11241
|
+
const name = typeof rule.name === "function" ? sanitizeExternalStem(stem) ? `ext-cursor-${sanitizeExternalStem(stem)}` : "" : rule.name;
|
|
11242
|
+
if (!name || !NAME_RE.test(name)) {
|
|
11243
|
+
console.warn(`\u26A0\uFE0F [memory] \u5916\u90E8\u6587\u4EF6\u540D\u4E3B\u5E72\u65E0\u6CD5\u751F\u6210\u5408\u6CD5\u8BB0\u5FC6\u540D\uFF08${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
|
|
11244
|
+
continue;
|
|
11245
|
+
}
|
|
11246
|
+
let raw;
|
|
11247
|
+
try {
|
|
11248
|
+
raw = await fs28.readFile(file, "utf-8");
|
|
11249
|
+
} catch {
|
|
11250
|
+
continue;
|
|
11251
|
+
}
|
|
11252
|
+
const body = truncateUtf8ToBytes(raw.trim(), EXTERNAL_BODY_MAX_BYTES);
|
|
11253
|
+
if (!body.text) continue;
|
|
11254
|
+
if (body.truncated) console.warn(`\u26A0\uFE0F [memory] \u5916\u90E8\u6587\u4EF6\u8D85 ${EXTERNAL_BODY_MAX_BYTES} \u5B57\u8282\u5DF2\u622A\u65AD\uFF08${file}\uFF09`);
|
|
11255
|
+
const stat = await fs28.stat(file).catch(() => null);
|
|
11256
|
+
out.push({
|
|
11257
|
+
item: {
|
|
11258
|
+
name,
|
|
11259
|
+
description: buildExternalDescription(rule.file ?? entry.name, extractExternalTitle(raw, stem)),
|
|
11260
|
+
type: "reference",
|
|
11261
|
+
// 外部二手资料的语义即参考(§4),不冒充 user/feedback 画像
|
|
11262
|
+
body: body.text,
|
|
11263
|
+
source: "external",
|
|
11264
|
+
file
|
|
11265
|
+
},
|
|
11266
|
+
mtimeMs: stat?.mtimeMs ?? 0
|
|
11267
|
+
});
|
|
11268
|
+
}
|
|
11269
|
+
return out;
|
|
11270
|
+
};
|
|
11271
|
+
externalNoticeShown = false;
|
|
11272
|
+
loadExternalMemories = async (includeProject) => {
|
|
11273
|
+
if (!appConfig.memoryExternal) return 0;
|
|
11274
|
+
const candidates = [];
|
|
11275
|
+
for (const rule of EXTERNAL_RULES) {
|
|
11276
|
+
if (rule.projectLayer && !includeProject) continue;
|
|
11277
|
+
candidates.push(...await scanExternalRule(rule));
|
|
11278
|
+
}
|
|
11279
|
+
if (candidates.length === 0) return 0;
|
|
11280
|
+
const dropped = [];
|
|
11281
|
+
if (candidates.length > MAX_EXTERNAL_ITEMS) {
|
|
11282
|
+
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
11283
|
+
for (const c of candidates.slice(MAX_EXTERNAL_ITEMS)) dropped.push(c.item.name);
|
|
11284
|
+
candidates.length = MAX_EXTERNAL_ITEMS;
|
|
11285
|
+
}
|
|
11286
|
+
const skipped = [];
|
|
11287
|
+
for (const { item } of candidates) {
|
|
11288
|
+
const all = listMemories();
|
|
11289
|
+
if (all.length >= MEMORY_LIMITS.maxCount) {
|
|
11290
|
+
skipped.push(item.name);
|
|
11291
|
+
continue;
|
|
11292
|
+
}
|
|
11293
|
+
const curBytes = all.length === 0 ? 0 : Buffer.byteLength(all.map(memoryIndexLine).join("\n"), "utf-8");
|
|
11294
|
+
if (curBytes + Buffer.byteLength(memoryIndexLine(item), "utf-8") > MEMORY_LIMITS.maxIndexBytes) {
|
|
11295
|
+
skipped.push(item.name);
|
|
11296
|
+
continue;
|
|
11297
|
+
}
|
|
11298
|
+
registerMemory(item);
|
|
11299
|
+
}
|
|
11300
|
+
if (dropped.length > 0) {
|
|
11301
|
+
console.warn(`\u26A0\uFE0F [memory] \u5916\u90E8\u6761\u76EE\u8D85\u8F6F\u4E0A\u9650 ${MAX_EXTERNAL_ITEMS}\uFF0C\u6309 mtime \u4E22\u5F03\u6700\u65E7 ${dropped.length} \u6761\uFF1A${dropped.join("\u3001")}`);
|
|
11302
|
+
}
|
|
11303
|
+
if (skipped.length > 0) {
|
|
11304
|
+
console.warn(`\u26A0\uFE0F [memory] \u5916\u90E8\u6761\u76EE\u8D85\u5171\u4EAB\u9884\u7B97\uFF08MEMORY_LIMITS\uFF1A${MEMORY_LIMITS.maxCount} \u6761/${MEMORY_LIMITS.maxIndexBytes} \u5B57\u8282\uFF09\uFF0C\u62D2\u8F7D\uFF1A${skipped.join("\u3001")}`);
|
|
11305
|
+
}
|
|
11306
|
+
const n = candidates.length - skipped.length;
|
|
11307
|
+
if (n > 0 && !externalNoticeShown) {
|
|
11308
|
+
externalNoticeShown = true;
|
|
11309
|
+
console.log("\u{1F310} [memory] \u5916\u90E8\u8BB0\u5FC6\u5DF2\u63A5\u5165\uFF08\u53EA\u8BFB\uFF09\uFF1A\u6765\u6E90\u6587\u4EF6\u5185\u5BB9\u5C06\u968F\u8BF7\u6C42\u53D1\u9001\u7ED9 DeepSeek API\u3002\u5173\u95ED\uFF1Asettings.json engine.memoryExternal=false");
|
|
11310
|
+
}
|
|
11311
|
+
return n;
|
|
11312
|
+
};
|
|
10848
11313
|
loadMemories = async (includeProject) => {
|
|
10849
11314
|
clearMemories();
|
|
10850
11315
|
const picked = await scanSources(
|
|
@@ -10853,6 +11318,7 @@ var init_loader3 = __esm({
|
|
|
10853
11318
|
parseMemoryAt
|
|
10854
11319
|
);
|
|
10855
11320
|
for (const { item } of picked) registerMemory(item);
|
|
11321
|
+
await loadExternalMemories(includeProject);
|
|
10856
11322
|
return listMemories().length;
|
|
10857
11323
|
};
|
|
10858
11324
|
initMemories = async (includeProject) => {
|
|
@@ -10900,7 +11366,7 @@ var init_usage = __esm({
|
|
|
10900
11366
|
// ../core/src/tool/registry/memory.ts
|
|
10901
11367
|
import fs30 from "fs/promises";
|
|
10902
11368
|
import path32 from "path";
|
|
10903
|
-
var MAX_BODY_BYTES, MAX_DESCRIPTION_CHARS,
|
|
11369
|
+
var MAX_BODY_BYTES, MAX_DESCRIPTION_CHARS, NAME_RE2, VALID_TYPES2, memoryFilePath, sanitizeLine, buildMemoryMd, writeAndRegister, indexLineBytes, memoryTools;
|
|
10904
11370
|
var init_memory = __esm({
|
|
10905
11371
|
"../core/src/tool/registry/memory.ts"() {
|
|
10906
11372
|
"use strict";
|
|
@@ -10911,8 +11377,7 @@ var init_memory = __esm({
|
|
|
10911
11377
|
init_usage();
|
|
10912
11378
|
MAX_BODY_BYTES = 64 * 1024;
|
|
10913
11379
|
MAX_DESCRIPTION_CHARS = 200;
|
|
10914
|
-
|
|
10915
|
-
NAME_RE = /^[a-z0-9-]+$/;
|
|
11380
|
+
NAME_RE2 = /^[a-z0-9-]+$/;
|
|
10916
11381
|
VALID_TYPES2 = ["user", "feedback", "project", "reference"];
|
|
10917
11382
|
memoryFilePath = (name, project) => project ? path32.join(process.cwd(), ".deepseeker-code", "memory", `${name}.md`) : path32.join(GLOBAL_MEMORY_DIR, `${name}.md`);
|
|
10918
11383
|
sanitizeLine = (s) => String(s ?? "").replace(/[\r\n]+/g, " ").trim();
|
|
@@ -10975,7 +11440,8 @@ ${body.trim()}
|
|
|
10975
11440
|
const body = typeof args?.body === "string" ? args.body : "";
|
|
10976
11441
|
const type = sanitizeLine(args?.type) || "reference";
|
|
10977
11442
|
const project = args?.project === true;
|
|
10978
|
-
if (!
|
|
11443
|
+
if (!NAME_RE2.test(name)) return toolFailure(`[memory_save] name \u975E\u6CD5\uFF1A\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26\uFF0C\u6536\u5230 "${name}"\u3002`);
|
|
11444
|
+
if (name.startsWith("ext-")) return toolFailure(`[memory_save] "ext-" \u540D\u79F0\u524D\u7F00\u4E3A\u5916\u90E8\u8BB0\u5FC6\u4FDD\u7559\u5B57\uFF08\u8BE5\u524D\u7F00\u6761\u76EE\u7531 loader \u4ECE\u5176\u4ED6 agent \u7684\u89C4\u5219/\u6307\u4EE4\u6587\u4EF6\u53EA\u8BFB\u6865\u63A5\uFF0C\u540D\u79F0\u56FA\u5B9A\u5206\u914D\uFF09\u3002\u8BF7\u6539\u7528\u5176\u4ED6\u540D\u79F0\u3002`);
|
|
10979
11445
|
if (!description) return toolFailure("[memory_save] description \u4E0D\u80FD\u4E3A\u7A7A\u3002");
|
|
10980
11446
|
if (description.length > MAX_DESCRIPTION_CHARS) return toolFailure(`[memory_save] description \u8D85\u8FC7 ${MAX_DESCRIPTION_CHARS} \u5B57\u7B26\u4E0A\u9650\uFF08\u5F53\u524D ${description.length}\uFF09\u3002\u8BF7\u538B\u7F29\u4E3A\u4E00\u53E5\u8BDD\u3002`);
|
|
10981
11447
|
if (!body.trim()) return toolFailure("[memory_save] body \u4E0D\u80FD\u4E3A\u7A7A\u3002");
|
|
@@ -11002,7 +11468,7 @@ ${body.trim()}
|
|
|
11002
11468
|
type: "function",
|
|
11003
11469
|
function: {
|
|
11004
11470
|
name: "memory_read",
|
|
11005
|
-
description: "\u8BFB\u53D6\u6307\u5B9A\u8BB0\u5FC6\u7684\u5B8C\u6574\u6B63\u6587\u3002\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4EC5\u6CE8\u5165\u8BB0\u5FC6\u7D22\u5F15\uFF08\u4E00\u884C/\u6761\uFF0C\u7701 token\uFF09\uFF1B\u5F53\u7D22\u5F15\u4E2D\u67D0\u6761\u4E0E\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u3001\u9700\u8981\u5168\u6587\u7EC6\u8282\u65F6\uFF0C\u8C03\u7528\u672C\u5DE5\u5177\u6309\u9700\u53EC\u56DE\u3002",
|
|
11471
|
+
description: "\u8BFB\u53D6\u6307\u5B9A\u8BB0\u5FC6\u7684\u5B8C\u6574\u6B63\u6587\u3002\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4EC5\u6CE8\u5165\u8BB0\u5FC6\u7D22\u5F15\uFF08\u4E00\u884C/\u6761\uFF0C\u7701 token\uFF09\uFF1B\u5F53\u7D22\u5F15\u4E2D\u67D0\u6761\u4E0E\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u3001\u9700\u8981\u5168\u6587\u7EC6\u8282\u65F6\uFF0C\u8C03\u7528\u672C\u5DE5\u5177\u6309\u9700\u53EC\u56DE\u3002\u7D22\u5F15\u4E2D\u6807 [\u5916\u90E8:*] \u7684\u6761\u76EE\u662F\u5176\u4ED6 agent \u89C4\u5219\u6587\u4EF6\u7684\u4E8C\u624B\u6458\u5F55\uFF08type=reference\uFF09\uFF1A\u4E0E\u73B0\u573A\u5B9E\u9645\u4E0D\u7B26\u65F6\u4EE5\u73B0\u573A\u4E3A\u51C6\uFF0C\u5E76\u53EF memory_save \u4E00\u6761\u81EA\u6709\u8BB0\u5FC6\u7EA0\u504F\u3002",
|
|
11006
11472
|
parameters: {
|
|
11007
11473
|
type: "object",
|
|
11008
11474
|
properties: {
|
|
@@ -11061,6 +11527,7 @@ ${lines.join("\n")}`;
|
|
|
11061
11527
|
requireApproval: (args) => `\u5220\u9664\u6301\u4E45\u8BB0\u5FC6 **${sanitizeLine(args?.name)}**\uFF08\u8DE8\u4F1A\u8BDD\u751F\u6548\uFF0C\u5220\u9664\u540E\u4E0D\u53EF\u6062\u590D\uFF09`,
|
|
11062
11528
|
async execute(args) {
|
|
11063
11529
|
const name = sanitizeLine(args?.name);
|
|
11530
|
+
if (name.startsWith("ext-")) return toolFailure(`[memory_delete] \u5916\u90E8\u6761\u76EE\u53EA\u8BFB\uFF0C\u8BF7\u5230\u6765\u6E90\u6587\u4EF6\u5904\u4FEE\u6539\uFF08ext- \u524D\u7F00\u6761\u76EE\u7531 loader \u4ECE\u5176\u4ED6 agent \u7684\u89C4\u5219/\u6307\u4EE4\u6587\u4EF6\u53EA\u8BFB\u6865\u63A5\uFF0C\u5220\u9664/\u4FEE\u6539\u8BF7\u76F4\u63A5\u7F16\u8F91\u6765\u6E90\u6587\u4EF6\u540E\u91CD\u542F\u8FDB\u7A0B\uFF09\u3002`);
|
|
11064
11531
|
const m = getMemory(name);
|
|
11065
11532
|
if (!m) return toolFailure(`\u672A\u627E\u5230\u8BB0\u5FC6\uFF1A${name}\uFF08\u53EF\u80FD\u5DF2\u88AB\u5220\u9664\uFF09\u3002`);
|
|
11066
11533
|
try {
|
|
@@ -12189,7 +12656,7 @@ var init_toolExecution = __esm({
|
|
|
12189
12656
|
const isForegroundCommand = isCommandTool && matchedTool.function.isSync !== false;
|
|
12190
12657
|
const cmdIsScriptRunner = isCommandTool && isScriptRunnerCommand(typeof state.calledArgs?.command === "string" ? state.calledArgs.command : "");
|
|
12191
12658
|
const argCwd = typeof state.calledArgs?.cwd === "string" ? state.calledArgs.cwd.trim() : "";
|
|
12192
|
-
const
|
|
12659
|
+
const samePath2 = (a, b) => {
|
|
12193
12660
|
try {
|
|
12194
12661
|
const ra = path36.resolve(a), rb = path36.resolve(b);
|
|
12195
12662
|
return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
|
|
@@ -12197,7 +12664,7 @@ var init_toolExecution = __esm({
|
|
|
12197
12664
|
return false;
|
|
12198
12665
|
}
|
|
12199
12666
|
};
|
|
12200
|
-
const scriptRunnerCwdDrift = cmdIsScriptRunner && argCwd !== "" && !
|
|
12667
|
+
const scriptRunnerCwdDrift = cmdIsScriptRunner && argCwd !== "" && !samePath2(argCwd, toolCtx.cwd ?? process.cwd());
|
|
12201
12668
|
const scriptRunnerBlocked = cmdIsScriptRunner && (perm === null || scriptRunnerCwdDrift);
|
|
12202
12669
|
if (scriptRunnerCwdDrift) needApproval = true;
|
|
12203
12670
|
if (perm === null && needApproval && !state.denied && !scriptRunnerBlocked && isForegroundCommand && isReadOnlyCommand(typeof state.calledArgs?.command === "string" ? state.calledArgs.command : "")) {
|
|
@@ -12485,13 +12952,21 @@ var init_toolScheduling = __esm({
|
|
|
12485
12952
|
scheduleToolCalls = async function* (assistantMessage, message, ctx) {
|
|
12486
12953
|
const { sessionId, signal, rawTools } = ctx;
|
|
12487
12954
|
let abortedDuringTools = false;
|
|
12955
|
+
const degraded = [];
|
|
12956
|
+
const markDegraded = (name) => {
|
|
12957
|
+
if (!degraded.includes(name)) degraded.push(name);
|
|
12958
|
+
};
|
|
12959
|
+
const noteWrite = (ok, name) => {
|
|
12960
|
+
if (!ok) markDegraded(name);
|
|
12961
|
+
};
|
|
12962
|
+
const degradedField = () => degraded.length > 0 ? { degraded: [...degraded] } : {};
|
|
12488
12963
|
const pendingImages = [];
|
|
12489
12964
|
const fillRestPlaceholders = async (fromIdx) => {
|
|
12490
12965
|
const tcs2 = assistantMessage.tool_calls;
|
|
12491
12966
|
for (let j = fromIdx + 1; j < tcs2.length; j++) {
|
|
12492
12967
|
const ph = "\uFF08\u5DF2\u8DF3\u8FC7\uFF1A\u7EC8\u7ED3\u7C7B\u5DE5\u5177\u4E4B\u540E\u7684\u5E76\u884C\u8C03\u7528\u672A\u6267\u884C\uFF09";
|
|
12493
12968
|
message.push({ role: "tool", tool_call_id: tcs2[j].id, content: ph });
|
|
12494
|
-
await appendMessage({ sessionId, role: "tool", tool_call_id: tcs2[j].id, content: ph });
|
|
12969
|
+
noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: tcs2[j].id, content: ph }), "appendMessage(placeholder)");
|
|
12495
12970
|
}
|
|
12496
12971
|
};
|
|
12497
12972
|
const parallelSafeToolsEnabled = appConfig.parallelSafeTools;
|
|
@@ -12530,7 +13005,7 @@ var init_toolScheduling = __esm({
|
|
|
12530
13005
|
for (let j = idx; j < tcs.length; j++) {
|
|
12531
13006
|
const ph = "\uFF08\u5DF2\u4E2D\u6B62\uFF0C\u672A\u6267\u884C\uFF09";
|
|
12532
13007
|
message.push({ role: "tool", tool_call_id: tcs[j].id, content: ph });
|
|
12533
|
-
await appendMessage({ sessionId, role: "tool", tool_call_id: tcs[j].id, content: ph });
|
|
13008
|
+
noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: tcs[j].id, content: ph }), "appendMessage(placeholder)");
|
|
12534
13009
|
}
|
|
12535
13010
|
break;
|
|
12536
13011
|
}
|
|
@@ -12576,7 +13051,8 @@ var init_toolScheduling = __esm({
|
|
|
12576
13051
|
if (oc.imageAttachments?.length) pendingImages.push(...oc.imageAttachments);
|
|
12577
13052
|
if (oc.aborted) abortedDuringTools = true;
|
|
12578
13053
|
}
|
|
12579
|
-
await appendMessages(sessionId, batchEntries);
|
|
13054
|
+
const batchWrite = await appendMessages(sessionId, batchEntries);
|
|
13055
|
+
if (batchWrite.failed > 0) markDegraded("appendMessages(tool-results)");
|
|
12580
13056
|
if (budget) {
|
|
12581
13057
|
try {
|
|
12582
13058
|
const tokensAfter = estimateTokens(message);
|
|
@@ -12610,22 +13086,22 @@ var init_toolScheduling = __esm({
|
|
|
12610
13086
|
const oc = await processToolCall(tc, ctx);
|
|
12611
13087
|
if (oc.terminal) {
|
|
12612
13088
|
message.push({ role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
|
|
12613
|
-
await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
|
|
13089
|
+
noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel }), "appendMessage(tool-result)");
|
|
12614
13090
|
await fillRestPlaceholders(idx);
|
|
12615
13091
|
yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
|
|
12616
13092
|
if (oc.terminal.kind === "exit_plan_mode") {
|
|
12617
13093
|
yield { type: "plan.proposed", plan: oc.terminal.plan || "" };
|
|
12618
|
-
return { kind: "terminal", terminalText: oc.terminal.plan || oc.resultForUser };
|
|
13094
|
+
return { kind: "terminal", terminalText: oc.terminal.plan || oc.resultForUser, ...degradedField() };
|
|
12619
13095
|
} else if (oc.terminal.kind === "enter_plan_mode") {
|
|
12620
13096
|
yield { type: "plan.enterRequested", reason: oc.terminal.reason || "" };
|
|
12621
|
-
return { kind: "terminal", terminalText: oc.terminal.reason ? `\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF1A${oc.terminal.reason}` : oc.resultForUser };
|
|
13097
|
+
return { kind: "terminal", terminalText: oc.terminal.reason ? `\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF1A${oc.terminal.reason}` : oc.resultForUser, ...degradedField() };
|
|
12622
13098
|
} else {
|
|
12623
|
-
return { kind: "terminal", terminalText: oc.resultForUser };
|
|
13099
|
+
return { kind: "terminal", terminalText: oc.resultForUser, ...degradedField() };
|
|
12624
13100
|
}
|
|
12625
13101
|
}
|
|
12626
13102
|
yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
|
|
12627
13103
|
message.push({ role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
|
|
12628
|
-
await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
|
|
13104
|
+
noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel }), "appendMessage(tool-result)");
|
|
12629
13105
|
if (oc.imageAttachments?.length) pendingImages.push(...oc.imageAttachments);
|
|
12630
13106
|
if (oc.aborted) abortedDuringTools = true;
|
|
12631
13107
|
idx++;
|
|
@@ -12634,17 +13110,17 @@ var init_toolScheduling = __esm({
|
|
|
12634
13110
|
if (!abortedDuringTools && pendingImages.length > 0) {
|
|
12635
13111
|
const parts = buildImageFollowUpParts(pendingImages);
|
|
12636
13112
|
message.push({ role: "user", content: parts });
|
|
12637
|
-
await appendMessage({ sessionId, role: "user", content: parts });
|
|
13113
|
+
noteWrite(await appendMessage({ sessionId, role: "user", content: parts }), "appendMessage(image-followup)");
|
|
12638
13114
|
}
|
|
12639
|
-
if (abortedDuringTools) return { kind: "aborted" };
|
|
12640
|
-
return { kind: "completed" };
|
|
13115
|
+
if (abortedDuringTools) return { kind: "aborted", ...degradedField() };
|
|
13116
|
+
return { kind: "completed", ...degradedField() };
|
|
12641
13117
|
};
|
|
12642
13118
|
}
|
|
12643
13119
|
});
|
|
12644
13120
|
|
|
12645
13121
|
// ../core/src/agent/streamInference.ts
|
|
12646
13122
|
import { createHash as createHash2 } from "node:crypto";
|
|
12647
|
-
var sha1short, toolsFingerprintCache, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
|
|
13123
|
+
var sha1short, toolsFingerprintCache, buildPrefixFingerprint, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
|
|
12648
13124
|
var init_streamInference = __esm({
|
|
12649
13125
|
"../core/src/agent/streamInference.ts"() {
|
|
12650
13126
|
"use strict";
|
|
@@ -12657,6 +13133,19 @@ var init_streamInference = __esm({
|
|
|
12657
13133
|
init_createModel();
|
|
12658
13134
|
sha1short = (s) => createHash2("sha1").update(s).digest("hex").slice(0, 10);
|
|
12659
13135
|
toolsFingerprintCache = /* @__PURE__ */ new WeakMap();
|
|
13136
|
+
buildPrefixFingerprint = (message, cleanedToolSchemas) => ({
|
|
13137
|
+
toolsHash: (() => {
|
|
13138
|
+
let h = toolsFingerprintCache.get(cleanedToolSchemas);
|
|
13139
|
+
if (h === void 0) {
|
|
13140
|
+
h = sha1short(JSON.stringify(cleanedToolSchemas));
|
|
13141
|
+
toolsFingerprintCache.set(cleanedToolSchemas, h);
|
|
13142
|
+
}
|
|
13143
|
+
return h;
|
|
13144
|
+
})(),
|
|
13145
|
+
sysHash: sha1short(String(message[0]?.content ?? "")),
|
|
13146
|
+
sumHash: sha1short(String(message[1]?.content ?? "")),
|
|
13147
|
+
msgCount: message.length
|
|
13148
|
+
});
|
|
12660
13149
|
RETRY_BACKOFF_CAP_MS = 3e4;
|
|
12661
13150
|
extractRetryAfterMs = (e) => {
|
|
12662
13151
|
try {
|
|
@@ -12700,28 +13189,32 @@ var init_streamInference = __esm({
|
|
|
12700
13189
|
keepRecentUnits,
|
|
12701
13190
|
compactRatio,
|
|
12702
13191
|
modelWindow,
|
|
12703
|
-
toolsTokens = 0
|
|
13192
|
+
toolsTokens = 0,
|
|
13193
|
+
windowCheck
|
|
12704
13194
|
} = ctx;
|
|
12705
13195
|
let inferenceMessages = withNudgeTail(message, nudgeMsg);
|
|
12706
13196
|
let assistantMessage = { role: "assistant", content: null };
|
|
12707
13197
|
try {
|
|
12708
|
-
const prefixFingerprint =
|
|
12709
|
-
|
|
12710
|
-
let h = toolsFingerprintCache.get(cleanedToolSchemas);
|
|
12711
|
-
if (h === void 0) {
|
|
12712
|
-
h = sha1short(JSON.stringify(cleanedToolSchemas));
|
|
12713
|
-
toolsFingerprintCache.set(cleanedToolSchemas, h);
|
|
12714
|
-
}
|
|
12715
|
-
return h;
|
|
12716
|
-
})(),
|
|
12717
|
-
sysHash: sha1short(String(message[0]?.content ?? "")),
|
|
12718
|
-
sumHash: sha1short(String(message[1]?.content ?? "")),
|
|
12719
|
-
msgCount: message.length
|
|
12720
|
-
};
|
|
13198
|
+
const prefixFingerprint = buildPrefixFingerprint(message, cleanedToolSchemas);
|
|
13199
|
+
const wc = windowCheck;
|
|
12721
13200
|
events({
|
|
12722
13201
|
sessionId,
|
|
12723
13202
|
eventType: "llm.request",
|
|
12724
|
-
metadata: {
|
|
13203
|
+
metadata: {
|
|
13204
|
+
depth,
|
|
13205
|
+
decisionSource: userDecisionSource,
|
|
13206
|
+
ok: true,
|
|
13207
|
+
durationMs: performance.now() - startTime,
|
|
13208
|
+
round,
|
|
13209
|
+
model,
|
|
13210
|
+
...prefixFingerprint,
|
|
13211
|
+
...wc ? {
|
|
13212
|
+
estReal: wc.estReal,
|
|
13213
|
+
thresholdAtSend: wc.threshold,
|
|
13214
|
+
correctionRatioUsed: wc.correctionRatio,
|
|
13215
|
+
windowDecision: wc.decision
|
|
13216
|
+
} : {}
|
|
13217
|
+
},
|
|
12725
13218
|
// ★ P2 口径:估算加上工具 schema 常数项(与 API 真实 prompt_tokens 同口径,trace 里 est/real 才可比)
|
|
12726
13219
|
usage: { prompt_tokens: estimateTokens(inferenceMessages) + toolsTokens },
|
|
12727
13220
|
payload: { input: msgText(message[message.length - 1].content) }
|
|
@@ -12769,6 +13262,16 @@ var init_streamInference = __esm({
|
|
|
12769
13262
|
break;
|
|
12770
13263
|
}
|
|
12771
13264
|
}
|
|
13265
|
+
if (!signal?.aborted && !contentBuf && toolCallsBuf.size === 0 && !lastUsage && idleRetries < MAX_STREAM_RETRIES) {
|
|
13266
|
+
idleRetries++;
|
|
13267
|
+
if (reasoningBuf) yield { type: "text.reset" };
|
|
13268
|
+
console.warn(`\u26A0\uFE0F \u6D41\u7A7A\u7ED3\u675F\uFF080 \u5185\u5BB9 0 usage\uFF0C\u7591\u4F3C\u670D\u52A1\u7AEF\u65AD\u6D41\uFF09\uFF0C\u7B2C ${idleRetries}/${MAX_STREAM_RETRIES} \u6B21\u91CD\u8BD5...`);
|
|
13269
|
+
contentBuf = "";
|
|
13270
|
+
reasoningBuf = "";
|
|
13271
|
+
toolCallsBuf.clear();
|
|
13272
|
+
lastUsage = void 0;
|
|
13273
|
+
continue;
|
|
13274
|
+
}
|
|
12772
13275
|
break;
|
|
12773
13276
|
} catch (streamErr) {
|
|
12774
13277
|
if (signal?.aborted) throw streamErr;
|
|
@@ -12838,6 +13341,19 @@ var init_streamInference = __esm({
|
|
|
12838
13341
|
onUIEvent: ctx.onUIEvent,
|
|
12839
13342
|
onAuxUsage: ctx.onAuxUsage
|
|
12840
13343
|
});
|
|
13344
|
+
events({
|
|
13345
|
+
sessionId,
|
|
13346
|
+
eventType: "llm.contextDowngraded",
|
|
13347
|
+
metadata: {
|
|
13348
|
+
depth,
|
|
13349
|
+
decisionSource: llmDecisionSource,
|
|
13350
|
+
ok: true,
|
|
13351
|
+
round,
|
|
13352
|
+
model,
|
|
13353
|
+
estReal: estimateTokens(withNudgeTail(message, nudgeMsg)) + (toolsTokens ?? 0)
|
|
13354
|
+
},
|
|
13355
|
+
payload: { output: streamErr instanceof Error ? streamErr.message : String(streamErr) }
|
|
13356
|
+
});
|
|
12841
13357
|
inferenceMessages = withNudgeTail(message, nudgeMsg);
|
|
12842
13358
|
compactedThisRound = true;
|
|
12843
13359
|
contentBuf = "";
|
|
@@ -12885,7 +13401,17 @@ var init_streamInference = __esm({
|
|
|
12885
13401
|
events({
|
|
12886
13402
|
sessionId,
|
|
12887
13403
|
eventType: "llm.response",
|
|
12888
|
-
|
|
13404
|
+
// ★ emptyStream 标记:0 内容 0 usage 的退化完成(重试耗尽仍空)——修复前该形态记 ok:true
|
|
13405
|
+
// 无任何异常痕迹(0d245bd0 取证时 trace 里完全看不出 round 13 是空流),此处如实回报。
|
|
13406
|
+
metadata: {
|
|
13407
|
+
depth,
|
|
13408
|
+
decisionSource: llmDecisionSource,
|
|
13409
|
+
ok: true,
|
|
13410
|
+
durationMs: performance.now() - startTime,
|
|
13411
|
+
round,
|
|
13412
|
+
model,
|
|
13413
|
+
...!contentBuf && toolCallsBuf.size === 0 && !lastUsage ? { emptyStream: true } : {}
|
|
13414
|
+
},
|
|
12889
13415
|
usage: {
|
|
12890
13416
|
prompt_tokens: lastUsage?.prompt_tokens,
|
|
12891
13417
|
completion_tokens: lastUsage?.completion_tokens,
|
|
@@ -13186,7 +13712,7 @@ var init_prices = __esm({
|
|
|
13186
13712
|
peak: { input: 9, output: 27, cacheHit: 0.3 }
|
|
13187
13713
|
}
|
|
13188
13714
|
};
|
|
13189
|
-
PRICES_VERIFIED_AT = "2026-
|
|
13715
|
+
PRICES_VERIFIED_AT = "2026-11-03";
|
|
13190
13716
|
PRICES_STALE_DAYS = 30;
|
|
13191
13717
|
pricesAgeDays = () => Math.max(0, Math.floor((Date.now() - Date.parse(`${PRICES_VERIFIED_AT}T00:00:00Z`)) / 864e5));
|
|
13192
13718
|
isBeijingPeak = (ts2 = /* @__PURE__ */ new Date()) => {
|
|
@@ -13364,7 +13890,7 @@ var init_usageLog = __esm({
|
|
|
13364
13890
|
ensuredDir = null;
|
|
13365
13891
|
getUsageLogPath = (month) => path37.join(usageDir(), `usage-${month}.jsonl`);
|
|
13366
13892
|
recordUsageRun = async (rec) => {
|
|
13367
|
-
if (USAGE_DISABLED) return;
|
|
13893
|
+
if (USAGE_DISABLED) return true;
|
|
13368
13894
|
try {
|
|
13369
13895
|
const full = { workspace: appConfig.userWorkspaceDir, ...rec };
|
|
13370
13896
|
const month = (full.ts || (/* @__PURE__ */ new Date()).toISOString()).slice(0, 7);
|
|
@@ -13374,8 +13900,10 @@ var init_usageLog = __esm({
|
|
|
13374
13900
|
ensuredDir = dir;
|
|
13375
13901
|
}
|
|
13376
13902
|
await fs34.appendFile(getUsageLogPath(month), JSON.stringify(full) + "\n", "utf-8");
|
|
13903
|
+
return true;
|
|
13377
13904
|
} catch (e) {
|
|
13378
13905
|
console.warn("\u26A0\uFE0F [usageLog] \u4F7F\u7528\u65E5\u5FD7\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", e instanceof Error ? e.message : e);
|
|
13906
|
+
return false;
|
|
13379
13907
|
}
|
|
13380
13908
|
};
|
|
13381
13909
|
USAGE_FILE_RE = /^usage-\d{4}-\d{2}\.jsonl$/;
|
|
@@ -13653,7 +14181,7 @@ async function evictOldestForSize2(targetBytes) {
|
|
|
13653
14181
|
}
|
|
13654
14182
|
return deleted;
|
|
13655
14183
|
}
|
|
13656
|
-
var CLEANUP_INTERVAL_MS2, ACTIVE_GRACE_MS2, MAX_TRACE_FOLDER_BYTES, SIZE_RECLAIM_TARGET_RATIO2, SIZE_CLEANUP_COOLDOWN_MS2, workspaceTraceBytes, diskLastCleanupAt2, clockLoaded2, lastSizeCleanupAt2, cleanupInProgress2, TRACE_BATCH_LINES, TRACE_BATCH_MS, traceBuf, traceTimers, cleanupTriggerCount, needImmediateFlush, scheduleTraceTimer, flushTraceBuffer, ensureClockLoaded2, tracePayloadMaxChars, truncateTracePayload;
|
|
14184
|
+
var CLEANUP_INTERVAL_MS2, ACTIVE_GRACE_MS2, MAX_TRACE_FOLDER_BYTES, SIZE_RECLAIM_TARGET_RATIO2, SIZE_CLEANUP_COOLDOWN_MS2, workspaceTraceBytes, diskLastCleanupAt2, clockLoaded2, lastSizeCleanupAt2, cleanupInProgress2, TRACE_BATCH_LINES, TRACE_BATCH_MS, traceBuf, droppedBatches, traceTimers, cleanupTriggerCount, needImmediateFlush, scheduleTraceTimer, flushChains, runSerialized, flushTraceBuffer, ensureClockLoaded2, tracePayloadMaxChars, truncateTracePayload;
|
|
13657
14185
|
var init_trace = __esm({
|
|
13658
14186
|
"../core/src/observability/trace.ts"() {
|
|
13659
14187
|
"use strict";
|
|
@@ -13673,6 +14201,7 @@ var init_trace = __esm({
|
|
|
13673
14201
|
TRACE_BATCH_LINES = 50;
|
|
13674
14202
|
TRACE_BATCH_MS = 200;
|
|
13675
14203
|
traceBuf = /* @__PURE__ */ new Map();
|
|
14204
|
+
droppedBatches = /* @__PURE__ */ new Map();
|
|
13676
14205
|
traceTimers = /* @__PURE__ */ new Map();
|
|
13677
14206
|
cleanupTriggerCount = 0;
|
|
13678
14207
|
needImmediateFlush = (ev) => ev.metadata?.ok === false || ev.eventType === "user.aborted" || ev.eventType === "tool.repeat_break" || ev.eventType === "session.end";
|
|
@@ -13685,34 +14214,55 @@ var init_trace = __esm({
|
|
|
13685
14214
|
t.unref?.();
|
|
13686
14215
|
traceTimers.set(sessionId, t);
|
|
13687
14216
|
};
|
|
14217
|
+
flushChains = /* @__PURE__ */ new Map();
|
|
14218
|
+
runSerialized = (id, fn) => {
|
|
14219
|
+
const prev = flushChains.get(id) ?? Promise.resolve();
|
|
14220
|
+
const next = prev.then(fn, fn);
|
|
14221
|
+
flushChains.set(id, next.catch(() => void 0));
|
|
14222
|
+
return next;
|
|
14223
|
+
};
|
|
13688
14224
|
flushTraceBuffer = async (sessionId) => {
|
|
13689
|
-
const
|
|
14225
|
+
const reportSticky = sessionId === void 0;
|
|
14226
|
+
const ids = sessionId !== void 0 ? [sessionId] : [.../* @__PURE__ */ new Set([...traceBuf.keys(), ...droppedBatches.keys()])];
|
|
14227
|
+
let failedSessions = 0;
|
|
13690
14228
|
for (const id of ids) {
|
|
13691
|
-
const
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
traceBuf.set(id, []);
|
|
13697
|
-
const timer = traceTimers.get(id);
|
|
13698
|
-
if (timer) {
|
|
13699
|
-
clearTimeout(timer);
|
|
13700
|
-
traceTimers.delete(id);
|
|
13701
|
-
}
|
|
13702
|
-
try {
|
|
13703
|
-
const joined = arr.join("");
|
|
13704
|
-
const p = await getTraceStorePath(id);
|
|
13705
|
-
await fs36.appendFile(p, joined, "utf-8");
|
|
13706
|
-
if (workspaceTraceBytes < 0) {
|
|
13707
|
-
workspaceTraceBytes = await getDirBytes(getTracePath());
|
|
14229
|
+
const degraded = await runSerialized(id, async () => {
|
|
14230
|
+
let bad = false;
|
|
14231
|
+
if (reportSticky && droppedBatches.has(id)) {
|
|
14232
|
+
droppedBatches.delete(id);
|
|
14233
|
+
bad = true;
|
|
13708
14234
|
}
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
14235
|
+
const arr = traceBuf.get(id);
|
|
14236
|
+
if (!arr || arr.length === 0) {
|
|
14237
|
+
traceBuf.delete(id);
|
|
14238
|
+
return bad;
|
|
14239
|
+
}
|
|
14240
|
+
traceBuf.set(id, []);
|
|
14241
|
+
const timer = traceTimers.get(id);
|
|
14242
|
+
if (timer) {
|
|
14243
|
+
clearTimeout(timer);
|
|
14244
|
+
traceTimers.delete(id);
|
|
14245
|
+
}
|
|
14246
|
+
try {
|
|
14247
|
+
const joined = arr.join("");
|
|
14248
|
+
const p = await getTraceStorePath(id);
|
|
14249
|
+
await fs36.appendFile(p, joined, "utf-8");
|
|
14250
|
+
if (workspaceTraceBytes < 0) {
|
|
14251
|
+
workspaceTraceBytes = await getDirBytes(getTracePath());
|
|
14252
|
+
}
|
|
14253
|
+
workspaceTraceBytes += Buffer.byteLength(joined, "utf-8");
|
|
14254
|
+
cleanupTriggerCount++;
|
|
14255
|
+
void maybeCleanupAfterAppend(id);
|
|
14256
|
+
} catch (error) {
|
|
14257
|
+
bad = true;
|
|
14258
|
+
if (!reportSticky) droppedBatches.set(id, (droppedBatches.get(id) ?? 0) + 1);
|
|
14259
|
+
console.warn("\u26A0\uFE0F [Trace] \u6279\u91CF\u843D\u76D8\u5931\u8D25\uFF0C\u4E22\u5F03\u8BE5\u6279\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", error);
|
|
14260
|
+
}
|
|
14261
|
+
return bad;
|
|
14262
|
+
});
|
|
14263
|
+
if (degraded) failedSessions++;
|
|
13715
14264
|
}
|
|
14265
|
+
return failedSessions;
|
|
13716
14266
|
};
|
|
13717
14267
|
ensureClockLoaded2 = async () => {
|
|
13718
14268
|
if (clockLoaded2) return;
|
|
@@ -13799,7 +14349,16 @@ async function* runAgent(message, options) {
|
|
|
13799
14349
|
const inheritedBudgetCheck = options.budgetCheck ?? (depth === 0 ? ledger.overHardLimit : void 0);
|
|
13800
14350
|
let planWriteRej = 0;
|
|
13801
14351
|
const toolCounts = {};
|
|
13802
|
-
|
|
14352
|
+
const degraded = [];
|
|
14353
|
+
const markDegraded = (name) => {
|
|
14354
|
+
if (!degraded.includes(name)) degraded.push(name);
|
|
14355
|
+
};
|
|
14356
|
+
try {
|
|
14357
|
+
const ok = await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
|
|
14358
|
+
if (!ok) markDegraded("appendEvent(run.start)");
|
|
14359
|
+
} catch {
|
|
14360
|
+
markDegraded("appendEvent(run.start)");
|
|
14361
|
+
}
|
|
13803
14362
|
try {
|
|
13804
14363
|
while (true) {
|
|
13805
14364
|
round++;
|
|
@@ -13843,13 +14402,20 @@ async function* runAgent(message, options) {
|
|
|
13843
14402
|
}
|
|
13844
14403
|
const inboxTexts = claimSessionInbox(sessionId);
|
|
13845
14404
|
if (inboxTexts.length > 0) {
|
|
14405
|
+
clearQueuedWake(sessionId);
|
|
13846
14406
|
for (const t of inboxTexts) {
|
|
13847
14407
|
message.push({ role: "user", content: t });
|
|
13848
|
-
|
|
14408
|
+
try {
|
|
14409
|
+
const ok = await appendMessage({ sessionId, role: "user", content: t });
|
|
14410
|
+
if (!ok) markDegraded("appendMessage(user)");
|
|
14411
|
+
} catch {
|
|
14412
|
+
markDegraded("appendMessage(user)");
|
|
14413
|
+
}
|
|
13849
14414
|
}
|
|
13850
14415
|
yield { type: "inbox.claimed", texts: inboxTexts };
|
|
13851
14416
|
}
|
|
13852
14417
|
yield { type: "round.start", round };
|
|
14418
|
+
let lastWindowCheck;
|
|
13853
14419
|
try {
|
|
13854
14420
|
await ensureFitsWindow(
|
|
13855
14421
|
{
|
|
@@ -13871,6 +14437,10 @@ async function* runAgent(message, options) {
|
|
|
13871
14437
|
onUIEvent: (evt) => {
|
|
13872
14438
|
if (evt.type === "compact.done") runCompactCount++;
|
|
13873
14439
|
options.onUIEvent?.(evt);
|
|
14440
|
+
},
|
|
14441
|
+
// ★ M1:判定快照出口(含 noop;抛异常路径不回调——那轮推理本就不发生)
|
|
14442
|
+
onWindowCheck: (check) => {
|
|
14443
|
+
lastWindowCheck = check;
|
|
13874
14444
|
}
|
|
13875
14445
|
}
|
|
13876
14446
|
);
|
|
@@ -13906,7 +14476,8 @@ async function* runAgent(message, options) {
|
|
|
13906
14476
|
modelWindow,
|
|
13907
14477
|
toolsTokens,
|
|
13908
14478
|
onUIEvent: options.onUIEvent,
|
|
13909
|
-
onAuxUsage: reportAuxUsage
|
|
14479
|
+
onAuxUsage: reportAuxUsage,
|
|
14480
|
+
windowCheck: lastWindowCheck
|
|
13910
14481
|
});
|
|
13911
14482
|
if (infResult.kind === "aborted") {
|
|
13912
14483
|
yield { type: "final", text: infResult.partialText || lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
|
|
@@ -13928,13 +14499,28 @@ async function* runAgent(message, options) {
|
|
|
13928
14499
|
}
|
|
13929
14500
|
ledger.addUsage(infResult.usage);
|
|
13930
14501
|
const assistantMessage = infResult.assistantMessage;
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
14502
|
+
const emptyAssistant = !(typeof assistantMessage.content === "string" && assistantMessage.content.length > 0) && !(Array.isArray(assistantMessage.tool_calls) && assistantMessage.tool_calls.length > 0);
|
|
14503
|
+
if (emptyAssistant) {
|
|
14504
|
+
markDegraded("emptyAssistantSkip");
|
|
14505
|
+
} else {
|
|
14506
|
+
message.push(assistantMessage);
|
|
14507
|
+
try {
|
|
14508
|
+
const ok = await appendMessage({ ...assistantMessage, sessionId });
|
|
14509
|
+
if (!ok) markDegraded("appendMessage(assistant)");
|
|
14510
|
+
} catch {
|
|
14511
|
+
markDegraded("appendMessage(assistant)");
|
|
14512
|
+
}
|
|
14513
|
+
if (typeof assistantMessage.content === "string" && assistantMessage.content) {
|
|
14514
|
+
lastContent = assistantMessage.content;
|
|
14515
|
+
}
|
|
13935
14516
|
}
|
|
13936
14517
|
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
13937
|
-
|
|
14518
|
+
try {
|
|
14519
|
+
const ok = await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
14520
|
+
if (!ok) markDegraded("appendEvent(round.end)");
|
|
14521
|
+
} catch {
|
|
14522
|
+
markDegraded("appendEvent(round.end)");
|
|
14523
|
+
}
|
|
13938
14524
|
emitContextUsage();
|
|
13939
14525
|
ledger.flushRelay();
|
|
13940
14526
|
const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
|
|
@@ -13955,7 +14541,12 @@ async function* runAgent(message, options) {
|
|
|
13955
14541
|
const ph = "\uFF08\u91CD\u590D\u8C03\u7528\u7194\u65AD\uFF0C\u672A\u6267\u884C\uFF09";
|
|
13956
14542
|
message.push({ role: "tool", tool_call_id: tc.id, content: ph });
|
|
13957
14543
|
}
|
|
13958
|
-
|
|
14544
|
+
try {
|
|
14545
|
+
const phWrite = await appendMessages(sessionId, assistantMessage.tool_calls.map((tc) => ({ sessionId, role: "tool", tool_call_id: tc.id, content: "\uFF08\u91CD\u590D\u8C03\u7528\u7194\u65AD\uFF0C\u672A\u6267\u884C\uFF09" })));
|
|
14546
|
+
if (phWrite.failed > 0) markDegraded("appendMessages(repeat-placeholder)");
|
|
14547
|
+
} catch {
|
|
14548
|
+
markDegraded("appendMessages(repeat-placeholder)");
|
|
14549
|
+
}
|
|
13959
14550
|
yield { type: "final", text: repeatVerdict.text };
|
|
13960
14551
|
return;
|
|
13961
14552
|
}
|
|
@@ -14006,6 +14597,7 @@ async function* runAgent(message, options) {
|
|
|
14006
14597
|
batchBudgetChars: void 0
|
|
14007
14598
|
};
|
|
14008
14599
|
const scheduleResult = yield* scheduleToolCalls(assistantMessage, message, toolCallCtx);
|
|
14600
|
+
if (scheduleResult.degraded) for (const name of scheduleResult.degraded) markDegraded(name);
|
|
14009
14601
|
if (scheduleResult.kind === "aborted") {
|
|
14010
14602
|
yield { type: "final", text: lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
|
|
14011
14603
|
return;
|
|
@@ -14014,7 +14606,12 @@ async function* runAgent(message, options) {
|
|
|
14014
14606
|
yield { type: "final", text: scheduleResult.terminalText };
|
|
14015
14607
|
return;
|
|
14016
14608
|
}
|
|
14017
|
-
|
|
14609
|
+
try {
|
|
14610
|
+
const ok = await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
|
|
14611
|
+
if (!ok) markDegraded("appendEvent(round.end)");
|
|
14612
|
+
} catch {
|
|
14613
|
+
markDegraded("appendEvent(round.end)");
|
|
14614
|
+
}
|
|
14018
14615
|
emitContextUsage();
|
|
14019
14616
|
ledger.flushRelay();
|
|
14020
14617
|
if (options.planMode && planWriteRej >= PLAN_REJECT_FORCE_AT) {
|
|
@@ -14036,20 +14633,26 @@ async function* runAgent(message, options) {
|
|
|
14036
14633
|
return;
|
|
14037
14634
|
} finally {
|
|
14038
14635
|
try {
|
|
14039
|
-
await flushLeftoverToTranscript(sessionId);
|
|
14636
|
+
const flushResult = await flushLeftoverToTranscript(sessionId);
|
|
14637
|
+
if (flushResult.failed > 0) markDegraded("flushLeftoverToTranscript");
|
|
14040
14638
|
} catch {
|
|
14639
|
+
markDegraded("flushLeftoverToTranscript");
|
|
14041
14640
|
}
|
|
14042
14641
|
try {
|
|
14043
|
-
await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: ledger.usageSummary() });
|
|
14642
|
+
const ok = await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: ledger.usageSummary() });
|
|
14643
|
+
if (!ok) markDegraded("appendEvent(run.end)");
|
|
14044
14644
|
} catch {
|
|
14645
|
+
markDegraded("appendEvent(run.end)");
|
|
14045
14646
|
}
|
|
14046
14647
|
try {
|
|
14047
|
-
await flushTraceBuffer();
|
|
14648
|
+
const failedSessions = await flushTraceBuffer();
|
|
14649
|
+
if (failedSessions > 0) markDegraded("flushTraceBuffer");
|
|
14048
14650
|
} catch {
|
|
14651
|
+
markDegraded("flushTraceBuffer");
|
|
14049
14652
|
}
|
|
14050
14653
|
try {
|
|
14051
14654
|
const us = ledger.usageSummary();
|
|
14052
|
-
|
|
14655
|
+
const ok = await recordUsageRun({
|
|
14053
14656
|
v: 1,
|
|
14054
14657
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14055
14658
|
sessionId,
|
|
@@ -14067,11 +14670,20 @@ async function* runAgent(message, options) {
|
|
|
14067
14670
|
// costYuan/toolCostYuan/auxCostYuan(口径与舍入在 runLedger)
|
|
14068
14671
|
tools: toolCounts
|
|
14069
14672
|
});
|
|
14673
|
+
if (!ok) markDegraded("recordUsageRun");
|
|
14070
14674
|
} catch {
|
|
14675
|
+
markDegraded("recordUsageRun");
|
|
14071
14676
|
}
|
|
14072
14677
|
try {
|
|
14073
14678
|
ledger.flushRelay();
|
|
14074
14679
|
} catch {
|
|
14680
|
+
markDegraded("ledger.flushRelay");
|
|
14681
|
+
}
|
|
14682
|
+
try {
|
|
14683
|
+
await updateCalibration(sessionId, effectiveModel, { calibRatio, lastRealPromptTokens, lastCachedTokens });
|
|
14684
|
+
} catch (e) {
|
|
14685
|
+
markDegraded("updateCalibration");
|
|
14686
|
+
console.warn("\u26A0\uFE0F \u538B\u7F29\u6821\u51C6\u72B6\u6001\u843D\u76D8\u5931\u8D25\uFF08\u4E0B run \u6821\u51C6\u91CD\u96F6\uFF0C\u7F13\u5B58\u611F\u77E5\u538B\u7F29\u9000\u5316\u56DE\u7F3A\u7701 1.4\uFF09:", e instanceof Error ? e.message : e);
|
|
14075
14687
|
}
|
|
14076
14688
|
try {
|
|
14077
14689
|
const us = ledger.usageSummary();
|
|
@@ -14087,22 +14699,24 @@ async function* runAgent(message, options) {
|
|
|
14087
14699
|
...ledger.receiptFields(),
|
|
14088
14700
|
// costYuan(null=无价)/toolCostYuan/auxCostYuan/subCostYuan(口径与舍入在 runLedger)
|
|
14089
14701
|
durationMs: Math.round(performance.now() - startTime),
|
|
14090
|
-
budgetTruncated: budgetTruncated || void 0
|
|
14702
|
+
budgetTruncated: budgetTruncated || void 0,
|
|
14091
14703
|
// 预算截断标注(E2;undefined 自动省略)
|
|
14704
|
+
degraded: degraded.length > 0 ? [...degraded] : void 0
|
|
14705
|
+
// D5:结算单发出前已降级的旁路快照(边界=updateCalibration 之后、Stop hook 之前;undefined 自动省略)
|
|
14092
14706
|
});
|
|
14093
14707
|
}
|
|
14094
14708
|
} catch {
|
|
14709
|
+
markDegraded("run.receipt");
|
|
14095
14710
|
}
|
|
14096
14711
|
try {
|
|
14097
14712
|
const reason = signal?.aborted ? "aborted" : stopReason;
|
|
14098
14713
|
await dispatch("Stop", { sessionId, cwd, lastText: lastContent || "", reason });
|
|
14099
14714
|
} catch (e) {
|
|
14715
|
+
markDegraded("Stop-hook");
|
|
14100
14716
|
console.warn("\u26A0\uFE0F Stop hook \u5916\u5C42\u515C\u5E95\uFF08dispatch \u5185\u90E8\u5BB9\u9519\u5931\u6548\uFF0C\u5C5E\u5F02\u5E38\u4FE1\u53F7\uFF09:", e instanceof Error ? e.message : e);
|
|
14101
14717
|
}
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
} catch (e) {
|
|
14105
|
-
console.warn("\u26A0\uFE0F \u538B\u7F29\u6821\u51C6\u72B6\u6001\u843D\u76D8\u5931\u8D25\uFF08\u4E0B run \u6821\u51C6\u91CD\u96F6\uFF0C\u7F13\u5B58\u611F\u77E5\u538B\u7F29\u9000\u5316\u56DE\u7F3A\u7701 1.4\uFF09:", e instanceof Error ? e.message : e);
|
|
14718
|
+
if (degraded.length > 0) {
|
|
14719
|
+
console.warn(`\u26A0\uFE0F [run \u964D\u7EA7] ${degraded.length} \u9879\uFF08\u4E0D\u5F71\u54CD\u4E3B\u6D41\u7A0B\uFF1B\u5177\u4F53\u540E\u679C\u89C1\u7EC4\u4EF6\u540D\uFF09: ${degraded.join(", ")}`);
|
|
14106
14720
|
}
|
|
14107
14721
|
}
|
|
14108
14722
|
}
|
|
@@ -14112,6 +14726,7 @@ var init_runAgent = __esm({
|
|
|
14112
14726
|
"use strict";
|
|
14113
14727
|
init_transcript();
|
|
14114
14728
|
init_inbox();
|
|
14729
|
+
init_agentWake();
|
|
14115
14730
|
init_common();
|
|
14116
14731
|
init_contextCore();
|
|
14117
14732
|
init_contentParts();
|
|
@@ -14198,7 +14813,7 @@ var init_lineage = __esm({
|
|
|
14198
14813
|
});
|
|
14199
14814
|
|
|
14200
14815
|
// ../core/src/agent/subagent.ts
|
|
14201
|
-
var SUBAGENT_DENYLIST, filterToolsForSubagent, activeSubagents, failText, RESUME_NOTE, createProgressThrottler, runSubagent;
|
|
14816
|
+
var SUBAGENT_DENYLIST, filterToolsForSubagent, activeSubagents, failText, RESUME_NOTE, createProgressThrottler, prepareSubagent, runPreparedSubagent, runSubagent;
|
|
14202
14817
|
var init_subagent = __esm({
|
|
14203
14818
|
"../core/src/agent/subagent.ts"() {
|
|
14204
14819
|
"use strict";
|
|
@@ -14211,6 +14826,7 @@ var init_subagent = __esm({
|
|
|
14211
14826
|
init_registry4();
|
|
14212
14827
|
init_hooks();
|
|
14213
14828
|
init_lineage();
|
|
14829
|
+
init_agentWake();
|
|
14214
14830
|
SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path", "run_in_background"]);
|
|
14215
14831
|
filterToolsForSubagent = (allTools, manifest) => {
|
|
14216
14832
|
if (manifest && manifest.tools.length > 0) {
|
|
@@ -14245,57 +14861,33 @@ var init_subagent = __esm({
|
|
|
14245
14861
|
}
|
|
14246
14862
|
};
|
|
14247
14863
|
};
|
|
14248
|
-
|
|
14864
|
+
prepareSubagent = async (spec, ctx, getGlobalTools) => {
|
|
14249
14865
|
const { task, name, role, resumeSessionId } = spec;
|
|
14250
14866
|
const resuming = typeof resumeSessionId === "string" && resumeSessionId.length > 0;
|
|
14251
14867
|
if (ctx.depth >= MAX_AGENT_DEPTH) {
|
|
14252
|
-
return {
|
|
14253
|
-
ok: false,
|
|
14254
|
-
output: failText(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u5DF2\u8FBE\u5230\u6700\u5927 Agent \u5D4C\u5957\u6DF1\u5EA6\uFF08${MAX_AGENT_DEPTH}\u5C42\uFF09\u3002\u7981\u6B62\u7EE7\u7EED\u65E0\u9650\u5411\u4E0B\u5D4C\u5957\uFF0C\u8BF7\u7236\u5C42\u7EA7 Agent \u81EA\u884C\u6D88\u5316\u5F53\u524D\u4EFB\u52A1\u3002`),
|
|
14255
|
-
sessionId: ""
|
|
14256
|
-
};
|
|
14868
|
+
return { ok: false, error: failText(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u5DF2\u8FBE\u5230\u6700\u5927 Agent \u5D4C\u5957\u6DF1\u5EA6\uFF08${MAX_AGENT_DEPTH}\u5C42\uFF09\u3002\u7981\u6B62\u7EE7\u7EED\u65E0\u9650\u5411\u4E0B\u5D4C\u5957\uFF0C\u8BF7\u7236\u5C42\u7EA7 Agent \u81EA\u884C\u6D88\u5316\u5F53\u524D\u4EFB\u52A1\u3002`) };
|
|
14257
14869
|
}
|
|
14258
14870
|
if (resuming) {
|
|
14259
14871
|
const target = resumeSessionId;
|
|
14260
14872
|
if (!target.includes("__sub__")) {
|
|
14261
|
-
return {
|
|
14262
|
-
ok: false,
|
|
14263
|
-
output: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A${target} \u4E0D\u662F\u5B50 Agent \u4F1A\u8BDD ID\uFF08\u5E94\u5F62\u5982 <\u4F1A\u8BDD>__sub__<uuid>\uFF0C\u6765\u81EA\u6B64\u524D spawn_agent \u8FD4\u56DE\u7684 agent_id\uFF09\u3002`),
|
|
14264
|
-
sessionId: ""
|
|
14265
|
-
};
|
|
14873
|
+
return { ok: false, error: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A${target} \u4E0D\u662F\u5B50 Agent \u4F1A\u8BDD ID\uFF08\u5E94\u5F62\u5982 <\u4F1A\u8BDD>__sub__<uuid>\uFF0C\u6765\u81EA\u6B64\u524D spawn_agent \u8FD4\u56DE\u7684 agent_id\uFF09\u3002`) };
|
|
14266
14874
|
}
|
|
14267
14875
|
if (!ownerCanApprove(target, ctx.sessionId)) {
|
|
14268
|
-
return {
|
|
14269
|
-
ok: false,
|
|
14270
|
-
output: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50\u4F1A\u8BDD ${target} \u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF08\u53CA\u5176 fork \u8840\u7F18\uFF09\uFF0C\u7981\u6B62\u8DE8\u4F1A\u8BDD\u590D\u6D3B\u3002\u8BF7\u6838\u5BF9 agent_id \u6216\u6539\u7528\u65B0\u5EFA\u6D3E\u751F\u3002`),
|
|
14271
|
-
sessionId: ""
|
|
14272
|
-
};
|
|
14876
|
+
return { ok: false, error: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50\u4F1A\u8BDD ${target} \u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF08\u53CA\u5176 fork \u8840\u7F18\uFF09\uFF0C\u7981\u6B62\u8DE8\u4F1A\u8BDD\u590D\u6D3B\u3002\u8BF7\u6838\u5BF9 agent_id \u6216\u6539\u7528\u65B0\u5EFA\u6D3E\u751F\u3002`) };
|
|
14273
14877
|
}
|
|
14274
14878
|
if (activeSubagents.has(target)) {
|
|
14275
|
-
return {
|
|
14276
|
-
ok: false,
|
|
14277
|
-
output: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50 Agent ${target} \u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u7981\u6B62\u5E76\u53D1\u7EED\u8DD1\u540C\u4E00\u5B50\u4F1A\u8BDD\uFF08\u4F1A\u4EA4\u53C9\u5199\u574F\u8F6C\u5F55\uFF09\u3002\u8BF7\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u8BD5\u3002`),
|
|
14278
|
-
sessionId: ""
|
|
14279
|
-
};
|
|
14879
|
+
return { ok: false, error: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50 Agent ${target} \u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u7981\u6B62\u5E76\u53D1\u7EED\u8DD1\u540C\u4E00\u5B50\u4F1A\u8BDD\uFF08\u4F1A\u4EA4\u53C9\u5199\u574F\u8F6C\u5F55\uFF09\u3002\u8BF7\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u8BD5\u3002`) };
|
|
14280
14880
|
}
|
|
14281
14881
|
const existing = await readTranscriptLines(target);
|
|
14282
14882
|
if (existing.length === 0) {
|
|
14283
|
-
return {
|
|
14284
|
-
ok: false,
|
|
14285
|
-
output: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50\u4F1A\u8BDD ${target} \u7684\u8F6C\u5F55\u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u7EED\u8DD1\u3002\u8BF7\u6539\u7528\u65B0\u5EFA\u6D3E\u751F\u3002`),
|
|
14286
|
-
sessionId: ""
|
|
14287
|
-
};
|
|
14883
|
+
return { ok: false, error: failText(`[\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50\u4F1A\u8BDD ${target} \u7684\u8F6C\u5F55\u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u7EED\u8DD1\u3002\u8BF7\u6539\u7528\u65B0\u5EFA\u6D3E\u751F\u3002`) };
|
|
14288
14884
|
}
|
|
14289
14885
|
}
|
|
14290
14886
|
const subSessionId = resuming ? resumeSessionId : `${ctx.sessionId}__sub__${createUUID()}`;
|
|
14291
14887
|
const parentSystemPrompt = ctx.parentSystemPrompt || "";
|
|
14292
14888
|
const manifest = name ? getAgent(name) : void 0;
|
|
14293
14889
|
if (name && !manifest) {
|
|
14294
|
-
return {
|
|
14295
|
-
ok: false,
|
|
14296
|
-
output: failText(`[\u6D3E\u751F\u5931\u8D25]\uFF1A\u672A\u627E\u5230\u58F0\u660E\u5F0F\u5B50 Agent "${name}"\u3002\u8BF7\u6838\u5BF9\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\u7684\u540D\u79F0\u62FC\u5199\u3002`),
|
|
14297
|
-
sessionId: ""
|
|
14298
|
-
};
|
|
14890
|
+
return { ok: false, error: failText(`[\u6D3E\u751F\u5931\u8D25]\uFF1A\u672A\u627E\u5230\u58F0\u660E\u5F0F\u5B50 Agent "${name}"\u3002\u8BF7\u6838\u5BF9\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\u7684\u540D\u79F0\u62FC\u5199\u3002`) };
|
|
14299
14891
|
}
|
|
14300
14892
|
const SUBAGENT_REPORT_REQ = `\u3010\u6C47\u62A5\u8981\u6C42\u3011\u6700\u7EC8\u6C47\u62A5\u63A7\u5236\u5728\u4E00\u5C4F\u5185\uFF1A\u5DF2\u5B8C\u6210/\u6539\u52A8\u70B9\uFF08\u6587\u4EF6\u8DEF\u5F84\uFF09/\u9A8C\u8BC1\u7ED3\u679C/\u672A\u5B8C\u6210\u9879\u3002\u4E0D\u8981\u590D\u8FF0\u8FC7\u7A0B\u4E0E\u5DE5\u5177\u8F93\u51FA\u2014\u2014\u7236\u7EA7\u53EF\u76F4\u63A5\u7ECF resume \u8FFD\u95EE\u7EC6\u8282\u3002`;
|
|
14301
14893
|
const subSystem = manifest ? [
|
|
@@ -14327,8 +14919,15 @@ var init_subagent = __esm({
|
|
|
14327
14919
|
const allTools = getGlobalTools();
|
|
14328
14920
|
const toolSchemas = filterToolsForSubagent(allTools, manifest);
|
|
14329
14921
|
console.log(`${resuming ? "\u{1F501} \u7EED\u8DD1" : "\u{1F423} \u6D3E\u751F"}\u5B50 Agent [\u6DF1\u5EA6: ${ctx.depth + 1}/${MAX_AGENT_DEPTH}][${subSessionId}]${manifest ? ` \u58F0\u660E\u5F0F=${manifest.name}` : ""} \u4EFB\u52A1: "${task.slice(0, 50)}..."`);
|
|
14922
|
+
const startCtx = { sessionId: subSessionId, parentSessionId: ctx.sessionId, task, depth: ctx.depth + 1, name: manifest?.name, cwd: ctx.cwd };
|
|
14923
|
+
return { ok: true, prepared: { subSessionId, resuming, manifest, subSystem, subTaskPrompt, toolSchemas, startCtx, taskBrief: task.slice(0, 60) } };
|
|
14924
|
+
};
|
|
14925
|
+
runPreparedSubagent = async (prepared, ctx, opts) => {
|
|
14926
|
+
const { subSessionId, manifest, subTaskPrompt, subSystem, toolSchemas, startCtx } = prepared;
|
|
14927
|
+
const manifestName = manifest?.name;
|
|
14330
14928
|
activeSubagents.add(subSessionId);
|
|
14331
|
-
ctx.
|
|
14929
|
+
trackSubagentInFlight(ctx.sessionId, subSessionId);
|
|
14930
|
+
ctx.onUIEvent?.({ type: "subagent.count", running: activeSubagents.size, phase: "start", name: manifest?.name, depth: startCtx.depth });
|
|
14332
14931
|
try {
|
|
14333
14932
|
const subMessages = await buildContextMessages(subSessionId, { role: "user", content: subTaskPrompt }, subSystem, manifest?.model);
|
|
14334
14933
|
await appendMessage({ sessionId: subSessionId, role: "user", content: subTaskPrompt });
|
|
@@ -14346,7 +14945,7 @@ var init_subagent = __esm({
|
|
|
14346
14945
|
depth: ctx.depth + 1,
|
|
14347
14946
|
keepRecentUnits: ctx.keepRecentUnits,
|
|
14348
14947
|
compactRatio: ctx.compactRatio,
|
|
14349
|
-
parentSystemPrompt,
|
|
14948
|
+
parentSystemPrompt: ctx.parentSystemPrompt,
|
|
14350
14949
|
events: ctx.events,
|
|
14351
14950
|
onUIEvent: ctx.onUIEvent,
|
|
14352
14951
|
// ★ 必须透传:否则子 agent 调用需审批工具时前端收不到弹窗,waitForUserApproval 永久挂起(死锁)
|
|
@@ -14365,14 +14964,13 @@ var init_subagent = __esm({
|
|
|
14365
14964
|
budgetCheck: ctx.budgetCheck
|
|
14366
14965
|
// ★ P1-5 预算熔断链式透传:子 runAgent 轮边界探测父预算,超支即收尾(不等烧完)
|
|
14367
14966
|
};
|
|
14368
|
-
const startCtx = { sessionId: subSessionId, parentSessionId: ctx.sessionId, task, depth: ctx.depth + 1, name: manifest?.name, cwd: ctx.cwd };
|
|
14369
14967
|
await dispatch("SubagentStart", startCtx);
|
|
14370
14968
|
let result;
|
|
14371
14969
|
const progress = createProgressThrottler((line) => ctx.onUIEvent?.({ type: "subagent.progress", text: line, agentKey: subSessionId, name: manifest?.name }));
|
|
14372
14970
|
try {
|
|
14373
14971
|
for await (const e of runAgent(subMessages, subOptions)) {
|
|
14374
14972
|
if (ctx.abortSignal?.aborted) {
|
|
14375
|
-
result = { ok: false, output: failText(`[\u5B50Agent\u4E2D\u65AD]\uFF1A\u6267\u884C\u5DF2\u88AB\u7528\u6237\u4E3B\u52A8\u53D1\u8D77\u7684 AbortSignal \u5F3A\u884C\u7194\u65AD\u3002`), sessionId: subSessionId, manifestName: manifest?.name };
|
|
14973
|
+
result = { ok: false, output: failText(`[\u5B50Agent\u4E2D\u65AD]\uFF1A\u6267\u884C\u5DF2\u88AB\u7528\u6237\u4E3B\u52A8\u53D1\u8D77\u7684 AbortSignal \u5F3A\u884C\u7194\u65AD\u3002`), sessionId: subSessionId, manifestName: manifest?.name, status: "aborted" };
|
|
14376
14974
|
break;
|
|
14377
14975
|
}
|
|
14378
14976
|
if (e.type === "final") {
|
|
@@ -14386,10 +14984,10 @@ var init_subagent = __esm({
|
|
|
14386
14984
|
if (!hasFinalResult || !subResult.trim()) {
|
|
14387
14985
|
subResult = `\uFF08\u672A\u80FD\u83B7\u53D6\u5230\u7ED3\u6784\u5316 final \u56DE\u62A5\u3002\u8BF7\u68C0\u67E5\u8BE5\u5B50 Session [${subSessionId}] \u7684\u6267\u884C\u5386\u53F2\uFF09`;
|
|
14388
14986
|
}
|
|
14389
|
-
result = { ok: true, output: subResult, sessionId: subSessionId, manifestName: manifest?.name };
|
|
14987
|
+
result = { ok: true, output: subResult, sessionId: subSessionId, manifestName: manifest?.name, status: "final" };
|
|
14390
14988
|
}
|
|
14391
14989
|
} catch (streamError) {
|
|
14392
|
-
result = { ok: false, output: failText(`[\u5B50Agent\u5D29\u6E83]\uFF1A\u5B50 Agent \u5728\u8FED\u4EE3\u63A8\u7406\u4E3B\u5FAA\u73AF\u65F6\u906D\u9047\u5E95\u5C42\u5F02\u5E38: ${streamError.message}`), sessionId: subSessionId, manifestName: manifest?.name };
|
|
14990
|
+
result = { ok: false, output: failText(`[\u5B50Agent\u5D29\u6E83]\uFF1A\u5B50 Agent \u5728\u8FED\u4EE3\u63A8\u7406\u4E3B\u5FAA\u73AF\u65F6\u906D\u9047\u5E95\u5C42\u5F02\u5E38: ${streamError.message}`), sessionId: subSessionId, manifestName: manifest?.name, status: "error" };
|
|
14393
14991
|
} finally {
|
|
14394
14992
|
try {
|
|
14395
14993
|
ctx.onUIEvent?.({ type: "subagent.progress", text: "", done: true, agentKey: subSessionId });
|
|
@@ -14399,13 +14997,27 @@ var init_subagent = __esm({
|
|
|
14399
14997
|
await dispatch("SubagentStop", { ...startCtx, ok: result.ok, output: result.output.slice(0, 2e3) }).catch((e) => console.warn(`\u26A0\uFE0F SubagentStop hook \u5F02\u5E38\uFF08\u5DF2\u5FFD\u7565\uFF09: ${e?.message ?? e}`));
|
|
14400
14998
|
}
|
|
14401
14999
|
}
|
|
14402
|
-
|
|
15000
|
+
if (opts?.notifyDone && result && result.status !== "aborted") {
|
|
15001
|
+
try {
|
|
15002
|
+
await notifySubagentDone(ctx.sessionId, buildSubagentDoneReport(subSessionId, prepared.taskBrief, { status: result.status, output: result.output }));
|
|
15003
|
+
} catch {
|
|
15004
|
+
}
|
|
15005
|
+
}
|
|
15006
|
+
return result ?? { ok: false, output: failText(`[\u6D3E\u751F\u6267\u884C\u5931\u8D25]: \u672A\u77E5\u9519\u8BEF`), sessionId: subSessionId, manifestName: manifest?.name, status: "error" };
|
|
14403
15007
|
} catch (error) {
|
|
14404
|
-
return { ok: false, output: failText(`[\u6D3E\u751F\u6267\u884C\u5931\u8D25]: ${error.message}`), sessionId: subSessionId, manifestName: manifest?.name };
|
|
15008
|
+
return { ok: false, output: failText(`[\u6D3E\u751F\u6267\u884C\u5931\u8D25]: ${error.message}`), sessionId: subSessionId, manifestName: manifest?.name, status: "error" };
|
|
14405
15009
|
} finally {
|
|
14406
15010
|
activeSubagents.delete(subSessionId);
|
|
14407
|
-
ctx.
|
|
15011
|
+
releaseSubagentInFlight(ctx.sessionId, subSessionId);
|
|
15012
|
+
ctx.onUIEvent?.({ type: "subagent.count", running: activeSubagents.size, phase: "stop", name: manifestName, depth: startCtx.depth });
|
|
15013
|
+
}
|
|
15014
|
+
};
|
|
15015
|
+
runSubagent = async (spec, ctx, getGlobalTools) => {
|
|
15016
|
+
const prep = await prepareSubagent(spec, ctx, getGlobalTools);
|
|
15017
|
+
if (!prep.ok) {
|
|
15018
|
+
return { ok: false, output: prep.error, sessionId: "", status: "error" };
|
|
14408
15019
|
}
|
|
15020
|
+
return runPreparedSubagent(prep.prepared, ctx);
|
|
14409
15021
|
};
|
|
14410
15022
|
}
|
|
14411
15023
|
});
|
|
@@ -15461,7 +16073,7 @@ var init_expand = __esm({
|
|
|
15461
16073
|
// ../core/src/serve/chatProcessing.ts
|
|
15462
16074
|
import path48 from "path";
|
|
15463
16075
|
import fs46 from "fs";
|
|
15464
|
-
var handleUnifiedChat;
|
|
16076
|
+
var handleUnifiedChat, handleUnifiedChatRun;
|
|
15465
16077
|
var init_chatProcessing = __esm({
|
|
15466
16078
|
"../core/src/serve/chatProcessing.ts"() {
|
|
15467
16079
|
"use strict";
|
|
@@ -15481,8 +16093,34 @@ var init_chatProcessing = __esm({
|
|
|
15481
16093
|
init_guard();
|
|
15482
16094
|
init_systemPrompt();
|
|
15483
16095
|
init_model();
|
|
16096
|
+
init_agentWake();
|
|
16097
|
+
init_inject5();
|
|
15484
16098
|
handleUnifiedChat = async (inbound, sendOutbound, sseWrite, abortSignal, opts) => {
|
|
16099
|
+
let busyKey = typeof inbound.sessionId === "string" && inbound.sessionId ? inbound.sessionId : "";
|
|
16100
|
+
markRunStart(busyKey);
|
|
16101
|
+
try {
|
|
16102
|
+
await handleUnifiedChatRun(inbound, sendOutbound, sseWrite, abortSignal, opts, (resolved) => {
|
|
16103
|
+
if (busyKey === resolved) return;
|
|
16104
|
+
markRunStart(resolved);
|
|
16105
|
+
if (busyKey) markRunEnd(busyKey);
|
|
16106
|
+
busyKey = resolved;
|
|
16107
|
+
});
|
|
16108
|
+
} finally {
|
|
16109
|
+
markRunEnd(busyKey);
|
|
16110
|
+
}
|
|
16111
|
+
};
|
|
16112
|
+
handleUnifiedChatRun = async (inbound, sendOutbound, sseWrite, abortSignal, opts, transferBusy) => {
|
|
15485
16113
|
const sessionId = await getOrCreateSessionId(inbound.sessionId);
|
|
16114
|
+
transferBusy?.(sessionId);
|
|
16115
|
+
registerWakeDriverIfAbsent(sessionId, async () => {
|
|
16116
|
+
await handleUnifiedChat(
|
|
16117
|
+
{ ...inbound, sessionId, content: AGENT_DONE_SENTINEL, attachments: void 0 },
|
|
16118
|
+
sendOutbound,
|
|
16119
|
+
sseWrite,
|
|
16120
|
+
abortSignal,
|
|
16121
|
+
opts
|
|
16122
|
+
);
|
|
16123
|
+
});
|
|
15486
16124
|
try {
|
|
15487
16125
|
inbound.content = expandSlashCommand(inbound.content);
|
|
15488
16126
|
} catch {
|
|
@@ -15624,6 +16262,7 @@ ${envLines.join("\n")}`;
|
|
|
15624
16262
|
}
|
|
15625
16263
|
await dispatch("SessionEnd", { sessionId, cwd: process.cwd() }).catch(() => {
|
|
15626
16264
|
});
|
|
16265
|
+
clearMemorySnapshot(sessionId);
|
|
15627
16266
|
await emitTrace({
|
|
15628
16267
|
sessionId,
|
|
15629
16268
|
eventType: "session.end",
|
|
@@ -16162,6 +16801,7 @@ var init_useChatState = __esm({
|
|
|
16162
16801
|
init_transcript();
|
|
16163
16802
|
init_fork();
|
|
16164
16803
|
init_inbox();
|
|
16804
|
+
init_agentWake();
|
|
16165
16805
|
init_contextCore();
|
|
16166
16806
|
init_bashDirect();
|
|
16167
16807
|
init_guard();
|
|
@@ -16632,6 +17272,17 @@ var init_useChatState = __esm({
|
|
|
16632
17272
|
budgetYuan: budgetYuanRef.current
|
|
16633
17273
|
// ★ 预算制运行(成本信任链第二环 E1):/budget 设置,超限 soft nudge + 120% 硬截断
|
|
16634
17274
|
};
|
|
17275
|
+
registerWakeDriver(sid, async () => {
|
|
17276
|
+
if (sessionIdRef.current !== sid) return;
|
|
17277
|
+
const myGen = ++turnGenRef.current;
|
|
17278
|
+
setBusy(true);
|
|
17279
|
+
pushInfo("\u{1F916} \u5B50\u4EE3\u7406\u5B8C\u6210\uFF0C\u81EA\u52A8\u5524\u9192\u4E3B\u4F1A\u8BDD\u7EE7\u7EED\u2026");
|
|
17280
|
+
try {
|
|
17281
|
+
await runOnce(sid, AGENT_DONE_SENTINEL, false);
|
|
17282
|
+
} finally {
|
|
17283
|
+
if (turnGenRef.current === myGen) setBusy(false);
|
|
17284
|
+
}
|
|
17285
|
+
});
|
|
16635
17286
|
try {
|
|
16636
17287
|
await handleUnifiedChat(
|
|
16637
17288
|
{ sessionId: sid, content: body },
|
|
@@ -19164,15 +19815,595 @@ ${listText}`);
|
|
|
19164
19815
|
}
|
|
19165
19816
|
});
|
|
19166
19817
|
|
|
19818
|
+
// ../core/src/agent/stallDetector.ts
|
|
19819
|
+
import { execFile as execFile2 } from "child_process";
|
|
19820
|
+
var CHECK_INTERVAL_SEC, GRACE_MIN, R1_WINDOW_MIN, R1_MIN_HITS, R2_WINDOW_MIN, R2_MIN_CALLS, R2_MAX_DISTINCT_SIG, R3_SILENCE_MIN, R4_WINDOW_MIN, R4_COST_DIVISOR, R6_STALL_BAND, HEALTH_GROWTH_LINES, SERIES_CAP, LOCKFILE_RE, EDIT_TOOLS, READONLY_TOOLS, normPath2, samePath, sortKeysDeep, argsKeyOf, similarSigOf, editTargetOf, extractTargetPaths, parseNumstatNet, MIN, StallDetector;
|
|
19821
|
+
var init_stallDetector = __esm({
|
|
19822
|
+
"../core/src/agent/stallDetector.ts"() {
|
|
19823
|
+
"use strict";
|
|
19824
|
+
CHECK_INTERVAL_SEC = 60;
|
|
19825
|
+
GRACE_MIN = 5;
|
|
19826
|
+
R1_WINDOW_MIN = 10;
|
|
19827
|
+
R1_MIN_HITS = 3;
|
|
19828
|
+
R2_WINDOW_MIN = 15;
|
|
19829
|
+
R2_MIN_CALLS = 3;
|
|
19830
|
+
R2_MAX_DISTINCT_SIG = 2;
|
|
19831
|
+
R3_SILENCE_MIN = 5;
|
|
19832
|
+
R4_WINDOW_MIN = 5;
|
|
19833
|
+
R4_COST_DIVISOR = 4;
|
|
19834
|
+
R6_STALL_BAND = 3;
|
|
19835
|
+
HEALTH_GROWTH_LINES = 20;
|
|
19836
|
+
SERIES_CAP = 64;
|
|
19837
|
+
LOCKFILE_RE = /(^|\/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb|bun\.lock|composer\.lock|poetry\.lock|Cargo\.lock|go\.sum)$/i;
|
|
19838
|
+
EDIT_TOOLS = /* @__PURE__ */ new Set(["edit_file", "create_file", "write_file", "move_file", "notebook_edit"]);
|
|
19839
|
+
READONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
19840
|
+
"read_file",
|
|
19841
|
+
"list_dir",
|
|
19842
|
+
"glob",
|
|
19843
|
+
"search_grep",
|
|
19844
|
+
"read_image",
|
|
19845
|
+
"read_docx",
|
|
19846
|
+
"read_xlsx",
|
|
19847
|
+
"read_project_guide",
|
|
19848
|
+
"recall",
|
|
19849
|
+
"get_git_diff",
|
|
19850
|
+
"git_status",
|
|
19851
|
+
"git_log",
|
|
19852
|
+
"find_references",
|
|
19853
|
+
"get_diagnostics",
|
|
19854
|
+
"goto_definition",
|
|
19855
|
+
"view_symbol_outline",
|
|
19856
|
+
"web_search",
|
|
19857
|
+
"web_fetch"
|
|
19858
|
+
]);
|
|
19859
|
+
normPath2 = (p) => p.trim().replace(/\\/g, "/").replace(/^[A-Za-z]:/, "").replace(/^\.\/+/, "").replace(/\/+$/, "").toLowerCase();
|
|
19860
|
+
samePath = (a, b) => a === b || a.endsWith("/" + b) || b.endsWith("/" + a);
|
|
19861
|
+
sortKeysDeep = (v) => {
|
|
19862
|
+
if (Array.isArray(v)) return v.map(sortKeysDeep);
|
|
19863
|
+
if (v && typeof v === "object") {
|
|
19864
|
+
const out = {};
|
|
19865
|
+
for (const k of Object.keys(v).sort()) out[k] = sortKeysDeep(v[k]);
|
|
19866
|
+
return out;
|
|
19867
|
+
}
|
|
19868
|
+
return v;
|
|
19869
|
+
};
|
|
19870
|
+
argsKeyOf = (args) => {
|
|
19871
|
+
try {
|
|
19872
|
+
return JSON.stringify(sortKeysDeep(args ?? {})).slice(0, 200);
|
|
19873
|
+
} catch {
|
|
19874
|
+
return String(args);
|
|
19875
|
+
}
|
|
19876
|
+
};
|
|
19877
|
+
similarSigOf = (args) => {
|
|
19878
|
+
let s;
|
|
19879
|
+
try {
|
|
19880
|
+
s = JSON.stringify(sortKeysDeep(args ?? {}));
|
|
19881
|
+
} catch {
|
|
19882
|
+
return String(args);
|
|
19883
|
+
}
|
|
19884
|
+
return s.replace(/\d+/g, "").replace(/\/[^/"]+(?=")/, "");
|
|
19885
|
+
};
|
|
19886
|
+
editTargetOf = (args) => normPath2(String(args?.path ?? args?.source ?? args?.destination ?? args?.file_path ?? ""));
|
|
19887
|
+
extractTargetPaths = (text) => {
|
|
19888
|
+
const out = /* @__PURE__ */ new Set();
|
|
19889
|
+
for (const m of text.match(/(?:[A-Za-z]:)?[\w.\-一-鿿]+(?:[/\\][\w.\-一-鿿]+)+\.[A-Za-z0-9]{1,6}/g) ?? []) {
|
|
19890
|
+
out.add(normPath2(m));
|
|
19891
|
+
}
|
|
19892
|
+
return out;
|
|
19893
|
+
};
|
|
19894
|
+
parseNumstatNet = (stdout) => {
|
|
19895
|
+
let net = 0;
|
|
19896
|
+
for (const line of stdout.split("\n")) {
|
|
19897
|
+
const m = /^(\d+|-)\t(\d+|-)\t(.+)$/.exec(line.trim());
|
|
19898
|
+
if (!m) continue;
|
|
19899
|
+
if (LOCKFILE_RE.test(m[3])) continue;
|
|
19900
|
+
net += (m[1] === "-" ? 0 : parseInt(m[1], 10)) + (m[2] === "-" ? 0 : parseInt(m[2], 10));
|
|
19901
|
+
}
|
|
19902
|
+
return net;
|
|
19903
|
+
};
|
|
19904
|
+
MIN = 6e4;
|
|
19905
|
+
StallDetector = class {
|
|
19906
|
+
constructor(opts) {
|
|
19907
|
+
this.stopFn = null;
|
|
19908
|
+
this.started = false;
|
|
19909
|
+
this.stopped = false;
|
|
19910
|
+
this.paused = false;
|
|
19911
|
+
this.ticking = false;
|
|
19912
|
+
/** 宽限期基准(豁免解除时重置——豁免期不该吃掉 agent 的起步宽限)。字段名与同名 getter 刻意区分
|
|
19913
|
+
* (tsconfig 为 set 语义,私有字段与只读 getter 同名会在字段初始化时炸 "only a getter")。 */
|
|
19914
|
+
this.activeAt = 0;
|
|
19915
|
+
this.lastEventAt = 0;
|
|
19916
|
+
/** 抑制窗截止(政策层经 setSuppressUntil 设置:batch=nudge 后宽限;影子=强命中去重)。 */
|
|
19917
|
+
this.suppressUntil = 0;
|
|
19918
|
+
this.roundNo = 0;
|
|
19919
|
+
this.calls = [];
|
|
19920
|
+
this.finalSeen = false;
|
|
19921
|
+
/** R6 净变更行数时间序列(batch 基线 = setup 完成后首读;交互 run 首 tick 采样即基线)。 */
|
|
19922
|
+
this.series = [];
|
|
19923
|
+
/** R4 成本采样序列。 */
|
|
19924
|
+
this.costSeries = [];
|
|
19925
|
+
this.opts = opts;
|
|
19926
|
+
this.now = opts.now ?? Date.now;
|
|
19927
|
+
this.intervalMs = opts.intervalMs ?? CHECK_INTERVAL_SEC * 1e3;
|
|
19928
|
+
this.targetPaths = extractTargetPaths(opts.targetText ?? "");
|
|
19929
|
+
this.lastEventAt = this.now();
|
|
19930
|
+
}
|
|
19931
|
+
/** 启动旁观者心跳。判定在解除暂停前不会真正开始(batch setup 执行期挂 PAUSED)。 */
|
|
19932
|
+
start() {
|
|
19933
|
+
if (this.started || this.stopped) return;
|
|
19934
|
+
this.started = true;
|
|
19935
|
+
this.activeAt = this.now();
|
|
19936
|
+
const schedule = this.opts.schedule ?? ((fn, ms) => {
|
|
19937
|
+
const h = setInterval(fn, ms);
|
|
19938
|
+
h.unref?.();
|
|
19939
|
+
return () => clearInterval(h);
|
|
19940
|
+
});
|
|
19941
|
+
this.stopFn = schedule(() => void this.tick(), this.intervalMs);
|
|
19942
|
+
}
|
|
19943
|
+
stop() {
|
|
19944
|
+
this.stopped = true;
|
|
19945
|
+
this.stopFn?.();
|
|
19946
|
+
this.stopFn = null;
|
|
19947
|
+
}
|
|
19948
|
+
/** 暂停判定(batch setup/accept 执行期;交互审批/提问挂起期);解除时对齐事件时钟并重置起步宽限。 */
|
|
19949
|
+
setPaused(p) {
|
|
19950
|
+
if (p === this.paused || this.stopped) return;
|
|
19951
|
+
this.paused = p;
|
|
19952
|
+
if (!p) {
|
|
19953
|
+
const t = this.now();
|
|
19954
|
+
this.lastEventAt = Math.max(this.lastEventAt, t);
|
|
19955
|
+
this.activeAt = t;
|
|
19956
|
+
}
|
|
19957
|
+
}
|
|
19958
|
+
/** 抑制窗(政策层动作语义映射:batch nudge 后宽限 / 影子强命中 5 分钟去重)。 */
|
|
19959
|
+
setSuppressUntil(t) {
|
|
19960
|
+
this.suppressUntil = t;
|
|
19961
|
+
}
|
|
19962
|
+
/** R6 基线:batch 在 setup 完成后取首个读数(装依赖动 lockfile,故基线后置 + 排除 lockfile)。 */
|
|
19963
|
+
async markBaseline() {
|
|
19964
|
+
const s = await this.sampleDiffSafe();
|
|
19965
|
+
this.series = s ? [{ ...s, t: this.now() }] : [];
|
|
19966
|
+
}
|
|
19967
|
+
/**
|
|
19968
|
+
* 事件喂入口:tool.start/tool.end/round.start/text.delta/thinking.delta/final 全喂。
|
|
19969
|
+
* 任何事件都刷新 lastEventAt(R3 断流的对面);豁免期不喂不计时(resume 时对齐)。
|
|
19970
|
+
* 白名单外的事件类型静默忽略(VSCode sink 混流 UIEvent,挂起交互事件天然无害)。
|
|
19971
|
+
*/
|
|
19972
|
+
feed(evt) {
|
|
19973
|
+
if (this.stopped || this.paused || !evt?.type) return;
|
|
19974
|
+
const t = this.now();
|
|
19975
|
+
this.lastEventAt = t;
|
|
19976
|
+
if (evt.type === "round.start") {
|
|
19977
|
+
this.roundNo = Number(evt.round) || this.roundNo;
|
|
19978
|
+
return;
|
|
19979
|
+
}
|
|
19980
|
+
if (evt.type === "final") {
|
|
19981
|
+
this.finalSeen = true;
|
|
19982
|
+
return;
|
|
19983
|
+
}
|
|
19984
|
+
if (evt.type === "tool.start") {
|
|
19985
|
+
const toolName = String(evt.toolName ?? "");
|
|
19986
|
+
const args = evt.args ?? {};
|
|
19987
|
+
let digest = "";
|
|
19988
|
+
try {
|
|
19989
|
+
digest = JSON.stringify(args);
|
|
19990
|
+
} catch {
|
|
19991
|
+
digest = String(args);
|
|
19992
|
+
}
|
|
19993
|
+
this.calls.push({
|
|
19994
|
+
toolCallId: String(evt.toolCallId ?? ""),
|
|
19995
|
+
toolName,
|
|
19996
|
+
argsKey: argsKeyOf(args),
|
|
19997
|
+
sig: similarSigOf(args),
|
|
19998
|
+
isEdit: EDIT_TOOLS.has(toolName),
|
|
19999
|
+
isReadonly: READONLY_TOOLS.has(toolName),
|
|
20000
|
+
target: editTargetOf(args),
|
|
20001
|
+
argsDigest: digest.slice(0, 120),
|
|
20002
|
+
t,
|
|
20003
|
+
round: this.roundNo
|
|
20004
|
+
});
|
|
20005
|
+
if (this.calls.length > 512) this.calls.splice(0, this.calls.length - 512);
|
|
20006
|
+
}
|
|
20007
|
+
}
|
|
20008
|
+
/** 判定一步(测试可直接驱动;生产由 scheduler 每 tick 调)。自身异常绝不外泄。 */
|
|
20009
|
+
async tick() {
|
|
20010
|
+
if (!this.started || this.stopped || this.paused || this.ticking) return;
|
|
20011
|
+
this.ticking = true;
|
|
20012
|
+
try {
|
|
20013
|
+
await this.evaluate();
|
|
20014
|
+
} catch {
|
|
20015
|
+
} finally {
|
|
20016
|
+
this.ticking = false;
|
|
20017
|
+
}
|
|
20018
|
+
}
|
|
20019
|
+
// —— 政策层访问器(拼 AUX 任务书 / 日志行用;不含动作语义) ——
|
|
20020
|
+
get round() {
|
|
20021
|
+
return this.roundNo;
|
|
20022
|
+
}
|
|
20023
|
+
get activeAtMs() {
|
|
20024
|
+
return this.activeAt;
|
|
20025
|
+
}
|
|
20026
|
+
/** 最近一次 diff 采样(影子日志 netLines 字段;无采样返回 null)。 */
|
|
20027
|
+
get lastNetSample() {
|
|
20028
|
+
return this.series.length > 0 ? this.series[this.series.length - 1] : null;
|
|
20029
|
+
}
|
|
20030
|
+
/** 成本采样(不进序列——evaluate 每 tick 的采样才进,batch costLine 的即时读数与原实现同形)。 */
|
|
20031
|
+
sampleCost() {
|
|
20032
|
+
return this.sampleCostSafe();
|
|
20033
|
+
}
|
|
20034
|
+
/** 时间窗内调用记录(batch 轨迹拼装:只读合并计数、写入逐条)。 */
|
|
20035
|
+
callsWithin(t, windowMs) {
|
|
20036
|
+
return this.calls.filter((c) => t - c.t <= windowMs);
|
|
20037
|
+
}
|
|
20038
|
+
// —— 评估主体:产出 StallEvaluation 交政策层 ——
|
|
20039
|
+
async evaluate() {
|
|
20040
|
+
const t = this.now();
|
|
20041
|
+
if (t - this.activeAt < GRACE_MIN * MIN) {
|
|
20042
|
+
await this.opts.onEvaluation({ t, skipped: "grace-start", silenceMin: null, health: null, strong: [], weak: [] });
|
|
20043
|
+
return;
|
|
20044
|
+
}
|
|
20045
|
+
if (t < this.suppressUntil) {
|
|
20046
|
+
await this.opts.onEvaluation({ t, skipped: "suppressed", silenceMin: null, health: null, strong: [], weak: [] });
|
|
20047
|
+
return;
|
|
20048
|
+
}
|
|
20049
|
+
const sample = await this.sampleDiffSafe();
|
|
20050
|
+
if (sample) {
|
|
20051
|
+
this.series.push({ ...sample, t });
|
|
20052
|
+
if (this.series.length > SERIES_CAP) this.series.splice(0, this.series.length - SERIES_CAP);
|
|
20053
|
+
}
|
|
20054
|
+
const cost = await this.sampleCostSafe();
|
|
20055
|
+
if (cost) {
|
|
20056
|
+
this.costSeries.push({ costYuan: cost.costYuan, t });
|
|
20057
|
+
if (this.costSeries.length > SERIES_CAP) this.costSeries.splice(0, this.costSeries.length - SERIES_CAP);
|
|
20058
|
+
}
|
|
20059
|
+
const silenceMs = t - this.lastEventAt;
|
|
20060
|
+
if (silenceMs >= R3_SILENCE_MIN * MIN) {
|
|
20061
|
+
await this.opts.onEvaluation({ t, skipped: null, silenceMin: silenceMs / MIN, health: null, strong: [], weak: [] });
|
|
20062
|
+
return;
|
|
20063
|
+
}
|
|
20064
|
+
const health = this.healthCheck();
|
|
20065
|
+
if (health) {
|
|
20066
|
+
await this.opts.onEvaluation({ t, skipped: null, silenceMin: null, health, strong: [], weak: [] });
|
|
20067
|
+
return;
|
|
20068
|
+
}
|
|
20069
|
+
const strong = [this.r1Check(t), this.r6Check()].filter((r) => r !== null);
|
|
20070
|
+
const weak = [this.r2Check(t), this.r4Check(t), this.r5Check(t)].filter((r) => r !== null);
|
|
20071
|
+
await this.opts.onEvaluation({ t, skipped: null, silenceMin: null, health: null, strong, weak });
|
|
20072
|
+
}
|
|
20073
|
+
// —— 规则实现 ——
|
|
20074
|
+
/** R1 重复模式:同 (tool, 规范化 args) 窗口内 ≥3 次且跨轮(轮内重复 repeatBreaker 已管)。 */
|
|
20075
|
+
r1Check(t) {
|
|
20076
|
+
const groups = /* @__PURE__ */ new Map();
|
|
20077
|
+
for (const c of this.calls) {
|
|
20078
|
+
if (t - c.t > R1_WINDOW_MIN * MIN) continue;
|
|
20079
|
+
const key = `${c.toolName} ${c.argsKey}`;
|
|
20080
|
+
const g = groups.get(key) ?? { tool: c.toolName, n: 0, rounds: /* @__PURE__ */ new Set() };
|
|
20081
|
+
g.n += 1;
|
|
20082
|
+
g.rounds.add(c.round);
|
|
20083
|
+
groups.set(key, g);
|
|
20084
|
+
}
|
|
20085
|
+
for (const g of groups.values()) {
|
|
20086
|
+
if (g.n >= R1_MIN_HITS && g.rounds.size >= 2) {
|
|
20087
|
+
return { rule: "R1", evidence: `\u8FD1 ${R1_WINDOW_MIN} \u5206\u949F ${g.tool} \u540C\u53C2\u6570\u8C03\u7528 ${g.n} \u6B21\uFF08\u8DE8 ${g.rounds.size} \u8F6E\uFF09` };
|
|
20088
|
+
}
|
|
20089
|
+
}
|
|
20090
|
+
return null;
|
|
20091
|
+
}
|
|
20092
|
+
/** R2 只读空转(弱):窗口内调用全只读且朴素规范化后变体 ≤2 种(args 持续变化=调研,防误伤)。 */
|
|
20093
|
+
r2Check(t) {
|
|
20094
|
+
const recent = this.calls.filter((c) => t - c.t <= R2_WINDOW_MIN * MIN);
|
|
20095
|
+
if (recent.length < R2_MIN_CALLS || recent.some((c) => !c.isReadonly)) return null;
|
|
20096
|
+
const sigs = new Set(recent.map((c) => c.sig));
|
|
20097
|
+
if (sigs.size > R2_MAX_DISTINCT_SIG) return null;
|
|
20098
|
+
return { rule: "R2", evidence: `\u8FD1 ${R2_WINDOW_MIN} \u5206\u949F ${recent.length} \u6B21\u8C03\u7528\u5168\u4E3A\u53EA\u8BFB\u4E14\u540C\u5F62\uFF08${sigs.size} \u79CD\u53D8\u4F53\uFF09` };
|
|
20099
|
+
}
|
|
20100
|
+
/** R4 烧钱无产出(弱):窗口成本增量 > 预算 1/4 且期间零 edit、零 final。 */
|
|
20101
|
+
r4Check(t) {
|
|
20102
|
+
if (this.opts.budgetYuan === void 0) return null;
|
|
20103
|
+
const win = this.costSeries.filter((s) => t - s.t <= R4_WINDOW_MIN * MIN);
|
|
20104
|
+
if (win.length < 2) return null;
|
|
20105
|
+
const first = win[0];
|
|
20106
|
+
const last = win[win.length - 1];
|
|
20107
|
+
if (first.costYuan == null || last.costYuan == null) return null;
|
|
20108
|
+
const delta = last.costYuan - first.costYuan;
|
|
20109
|
+
if (delta <= this.opts.budgetYuan / R4_COST_DIVISOR) return null;
|
|
20110
|
+
if (this.calls.some((c) => c.isEdit && t - c.t <= R4_WINDOW_MIN * MIN) || this.finalSeen) return null;
|
|
20111
|
+
return { rule: "R4", evidence: `\u8FD1 ${R4_WINDOW_MIN} \u5206\u949F\u82B1\u8D39 \xA5${delta.toFixed(4)}\uFF08> \u9884\u7B97 1/4\uFF09\uFF0C\u671F\u95F4\u96F6 edit \u96F6 final` };
|
|
20112
|
+
}
|
|
20113
|
+
/** R5 写入方向偏差(弱):窗口内 edit 目标与任务目标集合全无交集;提取不到目标集合 → 整条关闭。 */
|
|
20114
|
+
r5Check(t) {
|
|
20115
|
+
if (this.targetPaths.size === 0) return null;
|
|
20116
|
+
const edits = this.calls.filter((c) => c.isEdit && t - c.t <= R2_WINDOW_MIN * MIN);
|
|
20117
|
+
if (edits.length === 0) return null;
|
|
20118
|
+
const hit = edits.some((c) => c.target && [...this.targetPaths].some((p) => samePath(c.target, p)));
|
|
20119
|
+
if (hit) return null;
|
|
20120
|
+
return { rule: "R5", evidence: `\u8FD1 ${R2_WINDOW_MIN} \u5206\u949F ${edits.length} \u6B21 edit \u5168\u90E8\u843D\u5728\u4EFB\u52A1\u76EE\u6807\u8DEF\u5F84\u4E4B\u5916\uFF08\u5982 ${edits[0].target || "?"}\uFF09` };
|
|
20121
|
+
}
|
|
20122
|
+
/** R6 净进展停滞(强):连续 2 窗口净变更 ≈0(±3 行)且同期 edit 调用 >0——写了又改、没沉淀。 */
|
|
20123
|
+
r6Check() {
|
|
20124
|
+
const s = this.series;
|
|
20125
|
+
if (s.length < 3) return null;
|
|
20126
|
+
const a = s[s.length - 3];
|
|
20127
|
+
const b = s[s.length - 2];
|
|
20128
|
+
const c = s[s.length - 1];
|
|
20129
|
+
if (Math.abs(b.net - a.net) > R6_STALL_BAND || Math.abs(c.net - b.net) > R6_STALL_BAND) return null;
|
|
20130
|
+
const edits = this.calls.filter((e) => e.isEdit && e.t >= a.t).length;
|
|
20131
|
+
if (edits === 0) return null;
|
|
20132
|
+
return { rule: "R6", evidence: `\u51C0\u53D8\u66F4 ${a.net}\u2192${b.net}\u2192${c.net} \u884C\uFF08\u8FDE\u7EED 2 \u7A97\u53E3 \xB1${R6_STALL_BAND} \u5185\uFF09\u671F\u95F4 ${edits} \u6B21 edit` };
|
|
20133
|
+
}
|
|
20134
|
+
/** 健康短路:净变更显著增长或新文件出现 → 直接 normal(R2/R4 弱信号的反证)。 */
|
|
20135
|
+
healthCheck() {
|
|
20136
|
+
const s = this.series;
|
|
20137
|
+
if (s.length < 2) return null;
|
|
20138
|
+
const prev = s[s.length - 2];
|
|
20139
|
+
const last = s[s.length - 1];
|
|
20140
|
+
if (last.newFiles.length > 0) return `\u65B0\u6587\u4EF6\u51FA\u73B0\uFF1A${last.newFiles.slice(0, 3).join("\u3001")}`;
|
|
20141
|
+
const delta = last.net - prev.net;
|
|
20142
|
+
if (delta > HEALTH_GROWTH_LINES) return `\u51C0\u53D8\u66F4\u589E\u957F +${delta} \u884C\uFF08> ${HEALTH_GROWTH_LINES}\uFF09`;
|
|
20143
|
+
return null;
|
|
20144
|
+
}
|
|
20145
|
+
// —— 采样 ——
|
|
20146
|
+
async sampleDiffSafe() {
|
|
20147
|
+
if (this.opts.sampleDiff) return this.opts.sampleDiff();
|
|
20148
|
+
try {
|
|
20149
|
+
const run = (args) => new Promise((resolve, reject) => {
|
|
20150
|
+
execFile2("git", ["-C", this.opts.worktreePath, ...args], { maxBuffer: 1024 * 1024 }, (err, stdout) => err ? reject(err) : resolve(String(stdout)));
|
|
20151
|
+
});
|
|
20152
|
+
const [numstat, others] = await Promise.all([
|
|
20153
|
+
run(["diff", "--numstat", "HEAD"]),
|
|
20154
|
+
run(["ls-files", "--others", "--exclude-standard"])
|
|
20155
|
+
]);
|
|
20156
|
+
const newFiles = others.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
20157
|
+
return { net: parseNumstatNet(numstat), newFiles };
|
|
20158
|
+
} catch {
|
|
20159
|
+
return null;
|
|
20160
|
+
}
|
|
20161
|
+
}
|
|
20162
|
+
async sampleCostSafe() {
|
|
20163
|
+
if (!this.opts.getCost) return null;
|
|
20164
|
+
try {
|
|
20165
|
+
return await this.opts.getCost();
|
|
20166
|
+
} catch {
|
|
20167
|
+
return null;
|
|
20168
|
+
}
|
|
20169
|
+
}
|
|
20170
|
+
};
|
|
20171
|
+
}
|
|
20172
|
+
});
|
|
20173
|
+
|
|
20174
|
+
// src/batchWatchdog.ts
|
|
20175
|
+
var MIN2, NUDGE_GRACE_MIN, FREEZE_MIN, KILL_STRIKES, AUX_TIMEOUT_MS, RETRY_MAX, NUDGE_1, NUDGE_2, RETRY_PREAMBLE, ADJUDICATE_PROMPT, parseVerdictJson, defaultAdjudicate, sharedBudgetYuan, BatchWatchdog, buildRetryTaskContent;
|
|
20176
|
+
var init_batchWatchdog = __esm({
|
|
20177
|
+
"src/batchWatchdog.ts"() {
|
|
20178
|
+
"use strict";
|
|
20179
|
+
init_stallDetector();
|
|
20180
|
+
MIN2 = 6e4;
|
|
20181
|
+
NUDGE_GRACE_MIN = 5;
|
|
20182
|
+
FREEZE_MIN = 5;
|
|
20183
|
+
KILL_STRIKES = 3;
|
|
20184
|
+
AUX_TIMEOUT_MS = 6e4;
|
|
20185
|
+
RETRY_MAX = 3;
|
|
20186
|
+
NUDGE_1 = (evidence) => `\u26A0\uFE0F [watchdog] ${evidence}\u3002\u82E5\u5728\u539F\u5730\u6253\u8F6C\u8BF7\u6362\u601D\u8DEF\u6216\u6536\u5C3E\uFF1B\u82E5\u5224\u65AD\u6709\u8BEF\u8BF7\u7EE7\u7EED\u5E76\u8BF4\u660E\u7406\u7531\u3002`;
|
|
20187
|
+
NUDGE_2 = (clause) => `\u26D4 [watchdog] \u518D\u6B21\u5224\u5B9A\uFF1A${clause}\u3002\u8BF7\u7ACB\u5373\u505C\u6B62\u63A2\u7D22\uFF0C\u5728 2-3 \u8F6E\u5185\u6536\u5C3E\uFF1A\u603B\u7ED3\u5DF2\u5B8C\u6210 / \u672A\u5B8C\u6210 / \u5361\u70B9\u4E0E\u5EFA\u8BAE\uFF0C\u4F5C\u4E3A\u6700\u7EC8\u7B54\u590D\u3002`;
|
|
20188
|
+
RETRY_PREAMBLE = (verdict, evidence, finalDigest) => `\u3010\u7CFB\u7EDF\u63D0\u793A\u3011\u4E0A\u6B21\u5C1D\u8BD5\u88AB watchdog \u7EC8\u6B62\uFF08\u5224\u5B9A ${verdict}\uFF1A${evidence}\uFF09\u3002\u4E0A\u8F6E\u6536\u5C3E\u524D\u4EA7\u51FA\u6458\u8981\uFF1A${finalDigest || "\uFF08\u65E0\uFF09"}\u3002\u8BF7\u6362\u601D\u8DEF\uFF0C\u52FF\u91CD\u590D\u4E0A\u6B21\u8DEF\u5F84\u3002
|
|
20189
|
+
|
|
20190
|
+
---
|
|
20191
|
+
|
|
20192
|
+
`;
|
|
20193
|
+
ADJUDICATE_PROMPT = (args) => `\u4F60\u662F\u6279\u5904\u7406\u7F16\u7801\u4EFB\u52A1\u7684\u770B\u95E8\u72D7\u3002\u5224\u65AD\u4EFB\u52A1\u662F\u5426\u5728\u7A7A\u8F6C\uFF08idle\uFF1A\u53CD\u590D\u8BFB\u540C\u4E00\u6279\u6587\u4EF6/\u6362\u59FF\u52BF\u641C\u540C\u4E00\u4E1C\u897F\uFF09\u6216\u65B9\u5411\u504F\u79BB\uFF08offtrack\uFF1A\u5199\u5165\u843D\u70B9\u4E0E\u4EFB\u52A1\u76EE\u6807\u65E0\u5173\uFF09\uFF1B\u6B63\u5E38\u63A8\u8FDB\uFF08\u542B\u6B63\u5F53\u7684\u957F\u65F6\u95F4\u53EA\u8BFB\u8C03\u7814\uFF09\u5224 normal\u3002\u53EA\u8F93\u51FA\u4E25\u683C JSON\uFF1A{"verdict":"normal|idle|offtrack","evidence":"\u4E00\u53E5\u8BDD\u8BC1\u636E\uFF08\u5F15\u7528\u8F68\u8FF9\u4E2D\u7684\u5177\u4F53\u4E8B\u4EF6\uFF09"}
|
|
20194
|
+
|
|
20195
|
+
[\u4EFB\u52A1\u76EE\u6807] ${args.title}
|
|
20196
|
+
\u9A8C\u6536\u547D\u4EE4\uFF1A${args.accept}
|
|
20197
|
+
[\u6D88\u8017] ${args.costLine}
|
|
20198
|
+
[\u89C4\u5219\u4FE1\u53F7]
|
|
20199
|
+
${args.rules}
|
|
20200
|
+
[\u6700\u8FD1\u8F68\u8FF9\uFF0810 \u5206\u949F\uFF09]
|
|
20201
|
+
${args.trajectory}`;
|
|
20202
|
+
parseVerdictJson = (raw) => {
|
|
20203
|
+
const m = /\{[\s\S]*\}/.exec(raw ?? "");
|
|
20204
|
+
if (!m) return null;
|
|
20205
|
+
try {
|
|
20206
|
+
const j = JSON.parse(m[0]);
|
|
20207
|
+
const v = String(j?.verdict ?? "").trim().toLowerCase();
|
|
20208
|
+
if (v !== "normal" && v !== "idle" && v !== "offtrack") return null;
|
|
20209
|
+
return { verdict: v, evidence: String(j?.evidence ?? "").trim() };
|
|
20210
|
+
} catch {
|
|
20211
|
+
return null;
|
|
20212
|
+
}
|
|
20213
|
+
};
|
|
20214
|
+
defaultAdjudicate = async (prompt, signal) => {
|
|
20215
|
+
const { chatWithModelWithSummary: chatWithModelWithSummary2 } = await Promise.resolve().then(() => (init_model(), model_exports));
|
|
20216
|
+
const resp = await chatWithModelWithSummary2([{ role: "user", content: prompt }], [], { signal });
|
|
20217
|
+
return resp?.choices?.[0]?.message?.content ?? "";
|
|
20218
|
+
};
|
|
20219
|
+
sharedBudgetYuan = (budgetYuan, spentYuan) => {
|
|
20220
|
+
if (budgetYuan === void 0) return void 0;
|
|
20221
|
+
return Math.max(0, Math.round((budgetYuan - (spentYuan ?? 0)) * 1e4) / 1e4);
|
|
20222
|
+
};
|
|
20223
|
+
BatchWatchdog = class {
|
|
20224
|
+
constructor(opts) {
|
|
20225
|
+
/** 连续非 normal 计数(normal 裁决/健康短路清零)。 */
|
|
20226
|
+
this.streak = 0;
|
|
20227
|
+
/** 收尾冻结窗口截止(nudge-2 后 FREEZE_MIN 分钟内动作冻结——采样/R3 照常,二层不调)。 */
|
|
20228
|
+
this.freezeUntil = 0;
|
|
20229
|
+
this.records = [];
|
|
20230
|
+
this.killInfo = null;
|
|
20231
|
+
this.opts = opts;
|
|
20232
|
+
this.now = opts.now ?? Date.now;
|
|
20233
|
+
this.detector = new StallDetector({
|
|
20234
|
+
worktreePath: opts.worktreePath,
|
|
20235
|
+
targetText: opts.targetText,
|
|
20236
|
+
budgetYuan: opts.budgetYuan,
|
|
20237
|
+
getCost: opts.getCost,
|
|
20238
|
+
sampleDiff: opts.sampleDiff,
|
|
20239
|
+
now: opts.now,
|
|
20240
|
+
schedule: opts.schedule,
|
|
20241
|
+
intervalMs: opts.intervalMs,
|
|
20242
|
+
onEvaluation: (e) => this.consume(e)
|
|
20243
|
+
// promise 会被 detector tick await(AUX 裁决 tick 内完成)
|
|
20244
|
+
});
|
|
20245
|
+
}
|
|
20246
|
+
/** 启动旁观者心跳。判定在解除暂停前不会真正开始(setup 执行期挂 PAUSED,batch 方案 §4)。 */
|
|
20247
|
+
start() {
|
|
20248
|
+
this.detector.start();
|
|
20249
|
+
}
|
|
20250
|
+
stop() {
|
|
20251
|
+
this.detector.stop();
|
|
20252
|
+
}
|
|
20253
|
+
/** 暂停判定(setup/accept 命令执行期豁免);解除时对齐事件时钟并重置起步宽限。 */
|
|
20254
|
+
setPaused(p) {
|
|
20255
|
+
this.detector.setPaused(p);
|
|
20256
|
+
}
|
|
20257
|
+
/** R6 基线:setup 完成后取首个读数(装依赖动 lockfile,故基线后置 + 排除 lockfile)。 */
|
|
20258
|
+
markBaseline() {
|
|
20259
|
+
return this.detector.markBaseline();
|
|
20260
|
+
}
|
|
20261
|
+
feed(evt) {
|
|
20262
|
+
this.detector.feed(evt);
|
|
20263
|
+
}
|
|
20264
|
+
/** 判定一步(测试可直接驱动;生产由 scheduler 每 tick 调)。自身异常绝不外泄。 */
|
|
20265
|
+
async tick() {
|
|
20266
|
+
return this.detector.tick();
|
|
20267
|
+
}
|
|
20268
|
+
/** 终止证据快照(runner 据此置 watchdog-killed 与重试前置锚)。 */
|
|
20269
|
+
getKill() {
|
|
20270
|
+
return this.killInfo;
|
|
20271
|
+
}
|
|
20272
|
+
/** 报告证据链快照(跨重试轮由 runner 聚合)。 */
|
|
20273
|
+
snapshot() {
|
|
20274
|
+
return { kill: this.killInfo, records: [...this.records], roundsAtKill: this.detector.round };
|
|
20275
|
+
}
|
|
20276
|
+
// —— 求值消费:StallEvaluation → batch 动作(与影子政策是同一结果的两种消费,方案 §3) ——
|
|
20277
|
+
async consume(e) {
|
|
20278
|
+
if (e.skipped) return;
|
|
20279
|
+
if (e.silenceMin !== null) {
|
|
20280
|
+
this.kill({ verdict: "stall", rule: "R3", evidence: `\u65AD\u6D41\uFF1A${R3_SILENCE_MIN} \u5206\u949F\u65E0\u4EFB\u4F55\u4E8B\u4EF6\uFF08agent \u53EF\u80FD\u5DF2\u5361\u6B7B\uFF09` });
|
|
20281
|
+
return;
|
|
20282
|
+
}
|
|
20283
|
+
if (e.health) {
|
|
20284
|
+
this.streak = 0;
|
|
20285
|
+
this.records.push({ kind: "health", at: e.t, text: e.health });
|
|
20286
|
+
return;
|
|
20287
|
+
}
|
|
20288
|
+
if (e.strong.length === 0) {
|
|
20289
|
+
if (e.weak.length > 0) this.records.push({ kind: "suspicion", at: e.t, text: e.weak.map((r) => `${r.rule} ${r.evidence}`).join("\uFF1B") });
|
|
20290
|
+
return;
|
|
20291
|
+
}
|
|
20292
|
+
if (e.t < this.freezeUntil) {
|
|
20293
|
+
this.records.push({ kind: "suspicion", at: e.t, text: `\u6536\u5C3E\u51BB\u7ED3\u7A97\u53E3\u5185\uFF08nudge-2 \u540E ${FREEZE_MIN} \u5206\u949F\uFF09\uFF0C\u4EC5\u8BB0\u5F55\uFF1A${e.strong.map((r) => r.rule).join("+")}` });
|
|
20294
|
+
return;
|
|
20295
|
+
}
|
|
20296
|
+
const verdict = await this.adjudicate(e);
|
|
20297
|
+
if (verdict === null) {
|
|
20298
|
+
this.records.push({ kind: "aux-fail", at: e.t });
|
|
20299
|
+
return;
|
|
20300
|
+
}
|
|
20301
|
+
if (verdict.verdict === "normal") {
|
|
20302
|
+
this.streak = 0;
|
|
20303
|
+
this.records.push({ kind: "verdict", at: e.t, verdict: "normal", evidence: verdict.evidence });
|
|
20304
|
+
return;
|
|
20305
|
+
}
|
|
20306
|
+
this.streak += 1;
|
|
20307
|
+
this.records.push({ kind: "verdict", at: e.t, verdict: verdict.verdict, evidence: verdict.evidence });
|
|
20308
|
+
if (this.streak >= KILL_STRIKES) {
|
|
20309
|
+
this.kill({
|
|
20310
|
+
verdict: verdict.verdict,
|
|
20311
|
+
rule: e.strong.map((r) => r.rule).join("+"),
|
|
20312
|
+
evidence: `${verdict.evidence}\uFF08\u8FDE\u7EED ${this.streak} \u6B21\u975E normal\uFF09`
|
|
20313
|
+
});
|
|
20314
|
+
return;
|
|
20315
|
+
}
|
|
20316
|
+
this.nudge(this.streak, verdict.verdict, verdict.evidence, e.t);
|
|
20317
|
+
}
|
|
20318
|
+
/** 二层裁决。返回 null = 调用失败/非法输出(降级 normal,绝不误杀)。 */
|
|
20319
|
+
async adjudicate(e) {
|
|
20320
|
+
const costLine = await this.costLine(e.t);
|
|
20321
|
+
const prompt = ADJUDICATE_PROMPT({
|
|
20322
|
+
title: this.opts.ticketTitle,
|
|
20323
|
+
accept: this.opts.acceptCmd,
|
|
20324
|
+
costLine,
|
|
20325
|
+
rules: [
|
|
20326
|
+
...e.strong.map((r) => `${r.rule}\uFF08\u5F3A\uFF09\uFF1A${r.evidence}`),
|
|
20327
|
+
...e.weak.map((r) => `${r.rule}\uFF08\u5F31\uFF0C\u4EC5\u53C2\u8003\uFF09\uFF1A${r.evidence}`)
|
|
20328
|
+
].join("\n") || "\uFF08\u65E0\uFF09",
|
|
20329
|
+
trajectory: this.trajectory(e.t)
|
|
20330
|
+
});
|
|
20331
|
+
try {
|
|
20332
|
+
const raw = await (this.opts.auxAdjudicate ?? defaultAdjudicate)(prompt, this.auxSignal());
|
|
20333
|
+
return parseVerdictJson(raw);
|
|
20334
|
+
} catch {
|
|
20335
|
+
return null;
|
|
20336
|
+
}
|
|
20337
|
+
}
|
|
20338
|
+
/** AUX 调用信号:任务 abort 或 60s 超时,先到先停(对齐 classifyRisk 合并信号形态)。 */
|
|
20339
|
+
auxSignal() {
|
|
20340
|
+
const ac = new AbortController();
|
|
20341
|
+
const timer = setTimeout(() => ac.abort(), AUX_TIMEOUT_MS);
|
|
20342
|
+
timer.unref?.();
|
|
20343
|
+
this.opts.ac.signal.addEventListener("abort", () => {
|
|
20344
|
+
clearTimeout(timer);
|
|
20345
|
+
ac.abort();
|
|
20346
|
+
}, { once: true });
|
|
20347
|
+
return ac.signal;
|
|
20348
|
+
}
|
|
20349
|
+
async costLine(t) {
|
|
20350
|
+
const cost = await this.detector.sampleCost();
|
|
20351
|
+
const mins = Math.round((t - this.detector.activeAtMs) / MIN2);
|
|
20352
|
+
const rounds = cost?.rounds ?? this.detector.round;
|
|
20353
|
+
const yuan = cost?.costYuan;
|
|
20354
|
+
return `\u5DF2\u7528\u65F6 ${mins} \u5206\u949F \xB7 \u8F6E\u6570 ${rounds}` + (yuan != null ? ` \xB7 \u82B1\u8D39 \xA5${yuan.toFixed(4)}` : "");
|
|
20355
|
+
}
|
|
20356
|
+
/** 最近 10 分钟轨迹(二层任务书):只读类按工具合并计数,写入类逐条带 args 摘要。 */
|
|
20357
|
+
trajectory(t) {
|
|
20358
|
+
const recent = this.detector.callsWithin(t, R1_WINDOW_MIN * MIN2);
|
|
20359
|
+
if (recent.length === 0) return "\uFF08\u65E0\u5DE5\u5177\u8C03\u7528\uFF09";
|
|
20360
|
+
const readonlyCounts = /* @__PURE__ */ new Map();
|
|
20361
|
+
const lines = [];
|
|
20362
|
+
for (const c of recent) {
|
|
20363
|
+
if (!c.isReadonly) {
|
|
20364
|
+
lines.push(`- ${c.toolName}${c.target ? ` \u2192 ${c.target}` : ""}\uFF08${c.argsDigest}\uFF09`);
|
|
20365
|
+
continue;
|
|
20366
|
+
}
|
|
20367
|
+
readonlyCounts.set(c.toolName, (readonlyCounts.get(c.toolName) ?? 0) + 1);
|
|
20368
|
+
}
|
|
20369
|
+
for (const [tool, n] of readonlyCounts) lines.push(`- ${tool} \xD7${n}\uFF08\u53EA\u8BFB\uFF09`);
|
|
20370
|
+
return lines.join("\n").slice(0, 2400);
|
|
20371
|
+
}
|
|
20372
|
+
nudge(streak, verdict, evidence, t) {
|
|
20373
|
+
const text = streak === 1 ? NUDGE_1(evidence) : NUDGE_2(`${verdict}\uFF1A${evidence}`);
|
|
20374
|
+
this.records.push({ kind: "nudge", at: t, level: streak, text });
|
|
20375
|
+
this.detector.setSuppressUntil(t + NUDGE_GRACE_MIN * MIN2);
|
|
20376
|
+
if (streak === 2) this.freezeUntil = t + FREEZE_MIN * MIN2;
|
|
20377
|
+
try {
|
|
20378
|
+
this.opts.onNudge(text);
|
|
20379
|
+
} catch {
|
|
20380
|
+
}
|
|
20381
|
+
}
|
|
20382
|
+
kill(info) {
|
|
20383
|
+
if (this.killInfo) return;
|
|
20384
|
+
this.killInfo = info;
|
|
20385
|
+
this.records.push({ kind: "kill", at: this.now(), verdict: info.verdict, evidence: `${info.rule} ${info.evidence}` });
|
|
20386
|
+
this.detector.stop();
|
|
20387
|
+
try {
|
|
20388
|
+
this.opts.onKill(new Error(`watchdog: ${info.verdict} \u2014 ${info.evidence}`), info);
|
|
20389
|
+
} catch {
|
|
20390
|
+
}
|
|
20391
|
+
}
|
|
20392
|
+
};
|
|
20393
|
+
buildRetryTaskContent = (body, kill, finalText) => RETRY_PREAMBLE(kill.verdict, kill.evidence, finalText.trim().replace(/\s+/g, " ").slice(0, 400)) + body;
|
|
20394
|
+
}
|
|
20395
|
+
});
|
|
20396
|
+
|
|
19167
20397
|
// src/batchRunner.ts
|
|
19168
20398
|
var batchRunner_exports = {};
|
|
19169
20399
|
__export(batchRunner_exports, {
|
|
20400
|
+
buildReport: () => buildReport,
|
|
19170
20401
|
parseWaitWindow: () => parseWaitWindow,
|
|
19171
20402
|
runBatchQueue: () => runBatchQueue
|
|
19172
20403
|
});
|
|
19173
20404
|
import fs50 from "fs";
|
|
19174
20405
|
import { spawn as spawn6 } from "child_process";
|
|
19175
|
-
var BATCH_EDIT_TOOLS, BATCH_DEFAULT_DENYLIST, ACCEPT_TIMEOUT_MIN, ACCEPT_TAIL_LINES, emit2, ts, runAcceptCommand, collectSessionCost, buildReport, runOneTask, parseWaitWindow, nowMinutes, sleep, waitUntilWindow, runBatchQueue;
|
|
20406
|
+
var BATCH_EDIT_TOOLS, BATCH_DEFAULT_DENYLIST, ACCEPT_TIMEOUT_MIN, ACCEPT_TAIL_LINES, emit2, ts, runAcceptCommand, collectSessionCost, buildWatchdogLines, buildReport, runOneTask, parseWaitWindow, nowMinutes, sleep, waitUntilWindow, runBatchQueue;
|
|
19176
20407
|
var init_batchRunner = __esm({
|
|
19177
20408
|
"src/batchRunner.ts"() {
|
|
19178
20409
|
"use strict";
|
|
@@ -19189,6 +20420,9 @@ var init_batchRunner = __esm({
|
|
|
19189
20420
|
init_queue();
|
|
19190
20421
|
init_batchStrings();
|
|
19191
20422
|
init_strings();
|
|
20423
|
+
init_batchWatchdog();
|
|
20424
|
+
init_inbox();
|
|
20425
|
+
init_agentWake();
|
|
19192
20426
|
BATCH_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit_file", "create_file", "write_file", "move_file", "notebook_edit"]);
|
|
19193
20427
|
BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path", "run_in_background"]);
|
|
19194
20428
|
ACCEPT_TIMEOUT_MIN = 10;
|
|
@@ -19258,6 +20492,33 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
|
|
|
19258
20492
|
if (c.costYuan === null) c.partialPrice = false;
|
|
19259
20493
|
return c;
|
|
19260
20494
|
};
|
|
20495
|
+
buildWatchdogLines = (snaps, costYuan) => {
|
|
20496
|
+
if (snaps.length === 0) return null;
|
|
20497
|
+
const T = batchT(getLocale());
|
|
20498
|
+
const render2 = (r) => {
|
|
20499
|
+
switch (r.kind) {
|
|
20500
|
+
case "suspicion":
|
|
20501
|
+
return T.wdSuspicionRec(r.text ?? "");
|
|
20502
|
+
case "verdict":
|
|
20503
|
+
return T.wdVerdictRec(r.verdict ?? "", r.evidence ?? "");
|
|
20504
|
+
case "nudge":
|
|
20505
|
+
return T.wdNudgeRec(r.level ?? 0, r.text ?? "");
|
|
20506
|
+
case "health":
|
|
20507
|
+
return T.wdHealthRec(r.text ?? "");
|
|
20508
|
+
case "aux-fail":
|
|
20509
|
+
return T.wdAuxFailRec();
|
|
20510
|
+
case "kill":
|
|
20511
|
+
return T.wdKillRec(r.verdict ?? "", r.evidence ?? "");
|
|
20512
|
+
}
|
|
20513
|
+
};
|
|
20514
|
+
const lines = [];
|
|
20515
|
+
for (const snap of snaps) {
|
|
20516
|
+
lines.push(...snap.records.map(render2));
|
|
20517
|
+
if (snap.kill) lines.push(T.wdAtKillRec(snap.roundsAtKill, costYuan !== null ? costYuan.toFixed(4) : "-"));
|
|
20518
|
+
}
|
|
20519
|
+
if (snaps.length > 1) lines.push(T.wdRetryRec(snaps.length - 1));
|
|
20520
|
+
return lines;
|
|
20521
|
+
};
|
|
19261
20522
|
buildReport = (args) => {
|
|
19262
20523
|
const { id, ticket, status, budgetTruncated, cost, receipt, finalText, accept, deniedTools, diff, wt } = args;
|
|
19263
20524
|
const T = batchT(getLocale());
|
|
@@ -19280,7 +20541,11 @@ ${T.statusEntry(`${T.statusLabel(status)}${budgetNote}`)}
|
|
|
19280
20541
|
${T.costEntry(T.costSummary(cost))}
|
|
19281
20542
|
${T.receiptEntry(receipt ? T.receiptLine(receipt) : T.noReceipt)}
|
|
19282
20543
|
${args.errorMessage ? T.errorEntry(args.errorMessage) : ""}
|
|
19283
|
-
${T.
|
|
20544
|
+
${args.watchdogLines?.length ? `${T.wdH}
|
|
20545
|
+
|
|
20546
|
+
${args.watchdogLines.map(T.wdRecord).join("\n")}
|
|
20547
|
+
|
|
20548
|
+
` : ""}${T.taskH}
|
|
19284
20549
|
|
|
19285
20550
|
${ticket.body}
|
|
19286
20551
|
|
|
@@ -19384,89 +20649,187 @@ ${parsed.error}`);
|
|
|
19384
20649
|
emit2(`\u{1F4A5} [${task.id}] ${msg}`);
|
|
19385
20650
|
return { status: "error", cost: cost2, budgetTruncated: false };
|
|
19386
20651
|
}
|
|
19387
|
-
|
|
19388
|
-
|
|
19389
|
-
|
|
19390
|
-
|
|
19391
|
-
|
|
19392
|
-
|
|
20652
|
+
const watchdogEnabled = process.env.DEEP_SEEK_BATCH_WATCHDOG !== "0";
|
|
20653
|
+
const wdSnapshots = [];
|
|
20654
|
+
const maxRounds = 1 + Math.min(Math.max(0, Math.floor(t.retry ?? 0)), RETRY_MAX);
|
|
20655
|
+
let content = t.body;
|
|
20656
|
+
let roundBudget = t.budgetYuan;
|
|
20657
|
+
let receipt;
|
|
20658
|
+
let finalText = "";
|
|
20659
|
+
let agentError;
|
|
20660
|
+
let budgetTruncated = false;
|
|
20661
|
+
let wdKill = null;
|
|
20662
|
+
let status;
|
|
20663
|
+
let accept = null;
|
|
20664
|
+
for (let round = 1; ; round++) {
|
|
20665
|
+
wdKill = null;
|
|
20666
|
+
finalText = "";
|
|
20667
|
+
receipt = void 0;
|
|
20668
|
+
agentError = void 0;
|
|
20669
|
+
let nudgeCount = 0;
|
|
20670
|
+
const roundAc = new AbortController();
|
|
20671
|
+
const wd = watchdogEnabled ? new BatchWatchdog({
|
|
20672
|
+
sessionId,
|
|
20673
|
+
ticketTitle: t.title,
|
|
20674
|
+
acceptCmd: t.accept,
|
|
20675
|
+
targetText: `${t.title}
|
|
20676
|
+
${t.body}
|
|
20677
|
+
${t.accept}${t.setup ? `
|
|
20678
|
+
${t.setup}` : ""}`,
|
|
20679
|
+
ac: roundAc,
|
|
20680
|
+
worktreePath: wt.path,
|
|
20681
|
+
budgetYuan: roundBudget,
|
|
20682
|
+
getCost: () => collectSessionCost(sessionId),
|
|
20683
|
+
onNudge: (text) => {
|
|
20684
|
+
nudgeCount++;
|
|
20685
|
+
pushSessionInbox(sessionId, text);
|
|
20686
|
+
emit2(T.wdNudgeNote(task.id, nudgeCount));
|
|
20687
|
+
},
|
|
20688
|
+
onKill: (err, info) => {
|
|
20689
|
+
wdKill = info;
|
|
20690
|
+
emit2(T.wdKillNote(task.id, `${info.verdict} \u2014 ${info.evidence}`));
|
|
20691
|
+
roundAc.abort(err);
|
|
20692
|
+
}
|
|
20693
|
+
}) : null;
|
|
20694
|
+
wd?.start();
|
|
20695
|
+
wd?.setPaused(true);
|
|
20696
|
+
if (round === 1 && t.setup) {
|
|
20697
|
+
emit2(T.setupLine(t.setup));
|
|
20698
|
+
const s = await runAcceptCommand(t.setup, wt.path);
|
|
20699
|
+
if (s.code !== 0 || s.timedOut) {
|
|
20700
|
+
wd?.stop();
|
|
20701
|
+
const msg = `${T.setupFail(t.setup, s.code)}${s.timedOut ? T.acceptTimeoutSuffix : ""}
|
|
19393
20702
|
|
|
19394
20703
|
\`\`\`
|
|
19395
20704
|
${s.output}
|
|
19396
20705
|
\`\`\``;
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
|
|
19403
|
-
|
|
19404
|
-
|
|
19405
|
-
|
|
19406
|
-
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
20706
|
+
await killBackgroundTasksUnder(wt.path).catch(() => {
|
|
20707
|
+
});
|
|
20708
|
+
const cost2 = await collectSessionCost(sessionId);
|
|
20709
|
+
await finishTicket(task.id, buildReport({
|
|
20710
|
+
id: task.id,
|
|
20711
|
+
ticket: t,
|
|
20712
|
+
status: "setup-failed",
|
|
20713
|
+
budgetTruncated: false,
|
|
20714
|
+
cost: cost2,
|
|
20715
|
+
receipt: void 0,
|
|
20716
|
+
finalText: "",
|
|
20717
|
+
accept: null,
|
|
20718
|
+
deniedTools: [],
|
|
20719
|
+
diff: T.diffNone,
|
|
20720
|
+
wt,
|
|
20721
|
+
errorMessage: msg
|
|
20722
|
+
}));
|
|
20723
|
+
emit2(`\u{1F6E0}\uFE0F [${task.id}] ${T.setupFail(t.setup, s.code)}`);
|
|
20724
|
+
return { status: "setup-failed", cost: cost2, budgetTruncated: false };
|
|
20725
|
+
}
|
|
19416
20726
|
}
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
|
|
19420
|
-
|
|
19421
|
-
|
|
19422
|
-
|
|
19423
|
-
|
|
19424
|
-
|
|
19425
|
-
|
|
19426
|
-
|
|
19427
|
-
|
|
19428
|
-
|
|
19429
|
-
|
|
19430
|
-
|
|
19431
|
-
|
|
19432
|
-
|
|
19433
|
-
|
|
19434
|
-
|
|
19435
|
-
|
|
19436
|
-
|
|
19437
|
-
|
|
19438
|
-
|
|
19439
|
-
|
|
20727
|
+
wd?.setPaused(false);
|
|
20728
|
+
await wd?.markBaseline();
|
|
20729
|
+
const timer = setTimeout(() => roundAc.abort(new Error(T.timeoutAbort(timeoutMin))), timeoutMin * 6e4);
|
|
20730
|
+
setSessionWorktree(sessionId, wt);
|
|
20731
|
+
registerWakeDriver(sessionId, async () => {
|
|
20732
|
+
emit2(`\u{1F916} [${task.id}] \u5B50\u4EE3\u7406\u5B8C\u6210\uFF0C\u5524\u9192 run \u7EED\u8DD1\u2026`);
|
|
20733
|
+
setSessionWorktree(sessionId, wt);
|
|
20734
|
+
const wakeAc = new AbortController();
|
|
20735
|
+
const wakeTimer = setTimeout(() => wakeAc.abort(new Error(T.timeoutAbort(timeoutMin))), timeoutMin * 6e4);
|
|
20736
|
+
try {
|
|
20737
|
+
await handleUnifiedChat(
|
|
20738
|
+
{ sessionId, content: AGENT_DONE_SENTINEL },
|
|
20739
|
+
async () => {
|
|
20740
|
+
},
|
|
20741
|
+
(evt) => {
|
|
20742
|
+
if (evt?.type === "text.delta") finalText += evt.text;
|
|
20743
|
+
else if (evt?.type === "text.reset") finalText = "";
|
|
20744
|
+
else if (evt?.type === "final") finalText = evt.text || finalText;
|
|
20745
|
+
},
|
|
20746
|
+
wakeAc.signal,
|
|
20747
|
+
{
|
|
20748
|
+
requestApproval: async (detail, meta) => {
|
|
20749
|
+
if (allowApproval.has(meta.toolName)) return "allow-once";
|
|
20750
|
+
deniedTools.push({ tool: meta.toolName, detail: detail.replace(/\n+/g, " ").slice(0, 140) });
|
|
20751
|
+
return "deny";
|
|
20752
|
+
},
|
|
20753
|
+
onUIEvent: (evt) => {
|
|
20754
|
+
if (evt.type === "run.receipt") receipt = evt;
|
|
20755
|
+
},
|
|
20756
|
+
budgetYuan: roundBudget,
|
|
20757
|
+
toolSchemas: gatedSchemas
|
|
20758
|
+
}
|
|
20759
|
+
);
|
|
20760
|
+
} finally {
|
|
20761
|
+
clearTimeout(wakeTimer);
|
|
20762
|
+
clearSessionWorktree(sessionId);
|
|
20763
|
+
}
|
|
20764
|
+
});
|
|
20765
|
+
try {
|
|
20766
|
+
await handleUnifiedChat(
|
|
20767
|
+
{ sessionId, content },
|
|
20768
|
+
async () => {
|
|
19440
20769
|
},
|
|
19441
|
-
|
|
19442
|
-
|
|
20770
|
+
(evt) => {
|
|
20771
|
+
wd?.feed(evt);
|
|
20772
|
+
if (evt?.type === "text.delta") finalText += evt.text;
|
|
20773
|
+
else if (evt?.type === "text.reset") finalText = "";
|
|
20774
|
+
else if (evt?.type === "final") finalText = evt.text || finalText;
|
|
20775
|
+
else if (evt?.type === "round.start") emit2(T.roundLine(ts(), evt.round));
|
|
19443
20776
|
},
|
|
19444
|
-
|
|
19445
|
-
|
|
20777
|
+
roundAc.signal,
|
|
20778
|
+
{
|
|
20779
|
+
requestApproval: async (detail, meta) => {
|
|
20780
|
+
if (allowApproval.has(meta.toolName)) return "allow-once";
|
|
20781
|
+
deniedTools.push({ tool: meta.toolName, detail: detail.replace(/\n+/g, " ").slice(0, 140) });
|
|
20782
|
+
return "deny";
|
|
20783
|
+
},
|
|
20784
|
+
onUIEvent: (evt) => {
|
|
20785
|
+
if (evt.type === "run.receipt") receipt = evt;
|
|
20786
|
+
},
|
|
20787
|
+
budgetYuan: roundBudget,
|
|
20788
|
+
toolSchemas: gatedSchemas
|
|
20789
|
+
}
|
|
20790
|
+
);
|
|
20791
|
+
} catch (e) {
|
|
20792
|
+
agentError = e instanceof Error ? e.message : String(e);
|
|
20793
|
+
} finally {
|
|
20794
|
+
clearTimeout(timer);
|
|
20795
|
+
wd?.stop();
|
|
20796
|
+
clearSessionWorktree(sessionId);
|
|
20797
|
+
}
|
|
20798
|
+
if (wd) wdSnapshots.push(wd.snapshot());
|
|
20799
|
+
budgetTruncated = receipt?.budgetTruncated === true;
|
|
20800
|
+
if (agentError) {
|
|
20801
|
+
status = "error";
|
|
20802
|
+
emit2(T.agentErrorLine(task.id, agentError));
|
|
20803
|
+
break;
|
|
20804
|
+
}
|
|
20805
|
+
if (wdKill) {
|
|
20806
|
+
const spent = await collectSessionCost(sessionId);
|
|
20807
|
+
const nextBudget = sharedBudgetYuan(t.budgetYuan, spent.costYuan);
|
|
20808
|
+
if (round < maxRounds && (nextBudget === void 0 || nextBudget > 0)) {
|
|
20809
|
+
emit2(T.wdRetryNote(task.id, round + 1));
|
|
20810
|
+
content = buildRetryTaskContent(t.body, wdKill, finalText);
|
|
20811
|
+
roundBudget = nextBudget;
|
|
20812
|
+
continue;
|
|
19446
20813
|
}
|
|
19447
|
-
|
|
19448
|
-
|
|
19449
|
-
|
|
19450
|
-
|
|
19451
|
-
|
|
19452
|
-
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
|
|
19458
|
-
|
|
19459
|
-
status = "error";
|
|
19460
|
-
emit2(T.agentErrorLine(task.id, agentError));
|
|
19461
|
-
} else if (timedOut) {
|
|
19462
|
-
status = "timeout";
|
|
19463
|
-
emit2(T.timeoutLine(task.id, timeoutMin));
|
|
19464
|
-
} else {
|
|
20814
|
+
if (round < maxRounds) emit2(T.wdBudgetStopNote(task.id));
|
|
20815
|
+
status = "watchdog-killed";
|
|
20816
|
+
break;
|
|
20817
|
+
}
|
|
20818
|
+
if (roundAc.signal.aborted) {
|
|
20819
|
+
status = "timeout";
|
|
20820
|
+
emit2(T.timeoutLine(task.id, timeoutMin));
|
|
20821
|
+
break;
|
|
20822
|
+
}
|
|
20823
|
+
const quiet = await waitSessionQuiescent(sessionId, void 0, timeoutMin * 6e4);
|
|
20824
|
+
if (!quiet) emit2(`\u23F3 [${task.id}] \u5524\u9192\u94FE\u672A\u5728\u65F6\u9650\u5185\u6536\u655B\uFF0C\u6309\u73B0\u72B6\u9A8C\u6536`);
|
|
20825
|
+
wd?.setPaused(true);
|
|
19465
20826
|
const r = await runAcceptCommand(t.accept, wt.path);
|
|
19466
20827
|
accept = { cmd: t.accept, code: r.code, output: r.output, timedOut: r.timedOut };
|
|
19467
20828
|
status = r.code === 0 && !r.timedOut ? "passed" : "accept-failed";
|
|
19468
20829
|
emit2(T.acceptLine(status === "passed", task.id, r.code) + (r.timedOut ? T.acceptKilledSuffix : ""));
|
|
20830
|
+
break;
|
|
19469
20831
|
}
|
|
20832
|
+
unregisterWakeDriver(sessionId);
|
|
19470
20833
|
await killBackgroundTasksUnder(wt.path).catch(() => {
|
|
19471
20834
|
});
|
|
19472
20835
|
const diff = await harvestDiff(wt);
|
|
@@ -19483,7 +20846,8 @@ ${s.output}
|
|
|
19483
20846
|
deniedTools,
|
|
19484
20847
|
diff,
|
|
19485
20848
|
wt,
|
|
19486
|
-
errorMessage: agentError
|
|
20849
|
+
errorMessage: agentError,
|
|
20850
|
+
watchdogLines: buildWatchdogLines(wdSnapshots, cost.costYuan) ?? void 0
|
|
19487
20851
|
}));
|
|
19488
20852
|
emit2(` ${T.statusLabel(status)} \xB7 ${T.costSummary(cost)}`);
|
|
19489
20853
|
emit2(T.artifactLine(wt.path, wt.branch));
|
|
@@ -19621,7 +20985,7 @@ init_prefs();
|
|
|
19621
20985
|
init_updateCheck();
|
|
19622
20986
|
init_trust();
|
|
19623
20987
|
import { render } from "ink";
|
|
19624
|
-
import
|
|
20988
|
+
import os6 from "os";
|
|
19625
20989
|
import path50 from "path";
|
|
19626
20990
|
import { emitKeypressEvents } from "node:readline";
|
|
19627
20991
|
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
@@ -19771,7 +21135,7 @@ var main = async () => {
|
|
|
19771
21135
|
process.stdout.write("\x1B[2J\x1B[3J\x1B[1;1H");
|
|
19772
21136
|
const cwdIsHome = () => {
|
|
19773
21137
|
try {
|
|
19774
|
-
return path50.resolve(cwd).toLowerCase() === path50.resolve(
|
|
21138
|
+
return path50.resolve(cwd).toLowerCase() === path50.resolve(os6.homedir()).toLowerCase();
|
|
19775
21139
|
} catch {
|
|
19776
21140
|
return false;
|
|
19777
21141
|
}
|