deepseeker-code 1.0.52 → 1.0.53

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +1571 -736
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -181,6 +181,9 @@ ${dim} \u8FD0\u884C npm i -g deepseeker-code@latest \u66F4\u65B0${reset}
181
181
  sessionsPrompt: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u8F7D\u5165 \xB7 Esc \u53D6\u6D88",
182
182
  noHistory: "\uFF08\u6682\u65E0\u5386\u53F2\u4F1A\u8BDD\uFF09",
183
183
  sessionLoaded: (id) => `\u5DF2\u8F7D\u5165\u4F1A\u8BDD ${id}\uFF08\u7EE7\u7EED\u5BF9\u8BDD\u5C06\u7EED\u63A5\u6B64\u4F1A\u8BDD\uFF09`,
184
+ cmdNew: "\u5F00\u542F\u65B0\u4F1A\u8BDD\uFF08\u6E05\u5C4F\uFF1B\u65E7\u4F1A\u8BDD\u4FDD\u7559\uFF0C/sessions \u53EF\u627E\u56DE\uFF09",
185
+ sessionNew: (id) => `\u{1F195} \u5DF2\u5F00\u542F\u65B0\u4F1A\u8BDD ${id}\uFF08\u65E7\u4F1A\u8BDD\u5B8C\u6574\u4FDD\u7559\uFF0C/sessions \u53EF\u627E\u56DE\uFF09`,
186
+ sessionNewBusy: "\u751F\u6210\u4E2D\u2014\u2014\u5148 Esc/Ctrl+G \u4E2D\u6B62\u5F53\u524D\u56DE\u590D\uFF0C\u518D\u5F00\u65B0\u4F1A\u8BDD",
184
187
  cmdFork: "\u4ECE\u5F53\u524D\u4F1A\u8BDD\u7684\u67D0\u8F6E\u56DE\u590D\u5904\u5206\u53C9\u51FA\u65B0\u4F1A\u8BDD",
185
188
  forkTitle: "\u{1F33F} \u5206\u53C9\u5F53\u524D\u4F1A\u8BDD\uFF08\u2191\u2193 \u9009\u68C0\u67E5\u70B9 \xB7 Enter \u5206\u53C9\uFF09",
186
189
  forkPrompt: "\u2191\u2193 \u9009\u62E9 \xB7 Enter \u5206\u53C9 \xB7 Esc \u53D6\u6D88",
@@ -229,6 +232,7 @@ DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u54
229
232
  `/thinking [off|high|max] \u2014 \u5207\u6362\u601D\u8003\u7B49\u7EA7\uFF08\u5F53\u524D ${thinking}\uFF09`,
230
233
  `/lang [zh|en] \u2014 \u5207\u6362\u754C\u9762\u8BED\u8A00\uFF08\u5F53\u524D ${locale2}\uFF09`,
231
234
  "/sessions \u2014 \u9009\u62E9\u5E76\u8F7D\u5165\u5386\u53F2\u4F1A\u8BDD\uFF08\u7EED\u63A5\u5BF9\u8BDD\uFF09",
235
+ "/new \u2014 \u5F00\u542F\u65B0\u4F1A\u8BDD\uFF08\u65E7\u4F1A\u8BDD\u4FDD\u7559\uFF0C/sessions \u53EF\u627E\u56DE\uFF09",
232
236
  "/fork \u2014 \u4ECE\u5F53\u524D\u4F1A\u8BDD\u7684\u67D0\u8F6E\u56DE\u590D\u5904\u5206\u53C9\u51FA\u65B0\u4F1A\u8BDD",
233
237
  "/trust \u2014 \u7BA1\u7406\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF08\u9879\u76EE\u7EA7 hooks/skills \u7B49\u4EC5\u5728\u4FE1\u4EFB\u76EE\u5F55\u52A0\u8F7D\uFF1BCI \u7528 --trust \u663E\u5F0F\u4FE1\u4EFB\uFF09",
234
238
  "Ctrl+C \u9000\u51FA \xB7 Esc \u4E2D\u6B62/\u6E05\u8F93\u5165 \xB7 Ctrl+G \u4E2D\u6B62 \xB7 Ctrl+T \u5C55\u5F00/\u6536\u8D77\u601D\u8003"
@@ -330,6 +334,9 @@ ${dim} Run npm i -g deepseeker-code@latest to update${reset}
330
334
  sessionsPrompt: "\u2191\u2193 pick \xB7 Enter resume \xB7 Esc cancel",
331
335
  noHistory: "(no past sessions)",
332
336
  sessionLoaded: (id) => `Resumed session ${id} (new messages continue it)`,
337
+ cmdNew: "Start a new session (clears screen; old ones remain via /sessions)",
338
+ sessionNew: (id) => `\u{1F195} New session ${id} started (old sessions kept, resume via /sessions)`,
339
+ sessionNewBusy: "Busy \u2014 abort the current reply first (Esc/Ctrl+G), then start a new session",
333
340
  cmdFork: "Fork the current session from a past round",
334
341
  forkTitle: "\u{1F33F} Fork current session (\u2191\u2193 to pick \xB7 Enter to fork)",
335
342
  forkPrompt: "\u2191\u2193 pick \xB7 Enter fork \xB7 Esc cancel",
@@ -378,6 +385,7 @@ DeepSeeker-Code will read, edit, and execute files here.`,
378
385
  `/thinking [off|high|max] \u2014 Switch thinking level (current ${thinking})`,
379
386
  `/lang [zh|en] \u2014 Switch interface language (current ${locale2})`,
380
387
  "/sessions \u2014 Pick a past session to resume",
388
+ "/new \u2014 Start a new session (old ones remain via /sessions)",
381
389
  "/fork \u2014 Fork the current session from a past round",
382
390
  "/trust \u2014 Manage trusted dirs (project hooks/skills load only when trusted; CI uses --trust)",
383
391
  "Ctrl+C exit \xB7 Esc abort/clear input \xB7 Ctrl+G abort \xB7 Ctrl+T toggle thinking"
@@ -399,7 +407,7 @@ DeepSeeker-Code will read, edit, and execute files here.`,
399
407
  S = new Proxy({}, {
400
408
  get: (_t, k) => typeof k === "string" ? STRINGS[locale][k] : void 0
401
409
  });
402
- LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "switch", "thinking", "lang", "output-style", "sessions", "fork", "usage", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
410
+ LOCAL_COMMAND_NAMES = ["help", "status", "plan", "auto", "model", "switch", "thinking", "lang", "output-style", "new", "sessions", "fork", "usage", "context", "permissions", "mcp", "hooks", "trust", "debug", "clear", "exit"];
403
411
  }
404
412
  });
405
413
 
@@ -498,7 +506,7 @@ var init_config = __esm({
498
506
  * 最终聚合再受工具 maxOutputCharacters 兜底。 */
499
507
  workflowPerStepChars: 6e3,
500
508
  /** ★ P0-A 计划模式强制位置:'runtime'(默认)= 工具表全会话恒定(两态统一 appendPlanControlTools),
501
- * 计划期写工具由 processToolCall 执行层按 PLAN_ALLOWED_TOOLS 拒绝——保 DeepSeek 前缀缓存
509
+ * 计划期写工具由 processToolCall 执行层按工具声明 planAllowed 拒绝——保 DeepSeek 前缀缓存
502
510
  * (tools 参数序列化在请求头部,计划模式裁表翻转 = 全会话历史 re-prefill 两次);
503
511
  * 'schema' = 旧路径(计划期裁成只读白名单子表)。env DEEP_SEEK_PLAN_ENFORCEMENT=schema 回退。 */
504
512
  planEnforcement: process.env.DEEP_SEEK_PLAN_ENFORCEMENT === "schema" ? "schema" : "runtime",
@@ -783,7 +791,7 @@ var init_trust = __esm({
783
791
  });
784
792
 
785
793
  // ../core/src/tool/type.ts
786
- var MAX_AGENT_DEPTH, ToolSafetyLevel, ToolExecutionResultStatus;
794
+ var MAX_AGENT_DEPTH, ToolSafetyLevel, ToolExecutionResultStatus, asToolFailure, toolFailure;
787
795
  var init_type = __esm({
788
796
  "../core/src/tool/type.ts"() {
789
797
  "use strict";
@@ -797,10 +805,45 @@ var init_type = __esm({
797
805
  ToolExecutionResultStatus = /* @__PURE__ */ ((ToolExecutionResultStatus2) => {
798
806
  ToolExecutionResultStatus2["SUCCESS"] = "success";
799
807
  ToolExecutionResultStatus2["FAILED"] = "failed";
800
- ToolExecutionResultStatus2["TIMEOUT"] = "timeout";
801
- ToolExecutionResultStatus2["ABORTED"] = "aborted";
802
808
  return ToolExecutionResultStatus2;
803
809
  })(ToolExecutionResultStatus || {});
810
+ asToolFailure = (content, errorCategory = "runtime") => ({ content, status: "failed", errorCategory });
811
+ toolFailure = (msg, errorCategory = "runtime") => asToolFailure(`\u274C ${msg}`, errorCategory);
812
+ }
813
+ });
814
+
815
+ // ../core/src/tool/argsValidator.ts
816
+ import { Ajv } from "ajv";
817
+ var ajv, compiled, validateToolArgs;
818
+ var init_argsValidator = __esm({
819
+ "../core/src/tool/argsValidator.ts"() {
820
+ "use strict";
821
+ ajv = new Ajv({
822
+ coerceTypes: true,
823
+ // 数字/布尔字符串化容错(就地修正 args)
824
+ strict: false,
825
+ // 手写 schema 可能含非严格关键字(注释性字段等),宽松编译防误伤
826
+ allErrors: false
827
+ // 首错即返:早退文案只报第一处,引导逐个修
828
+ });
829
+ compiled = /* @__PURE__ */ new WeakMap();
830
+ validateToolArgs = (schema, args) => {
831
+ let validate = compiled.get(schema);
832
+ if (!validate) {
833
+ try {
834
+ validate = ajv.compile(schema);
835
+ compiled.set(schema, validate);
836
+ } catch (e) {
837
+ console.warn(`\u26A0\uFE0F [argsValidator] schema \u7F16\u8BD1\u5931\u8D25\uFF08fail-open \u653E\u884C\uFF09: ${e?.message ?? e}`);
838
+ return { ok: true };
839
+ }
840
+ }
841
+ if (validate(args)) return { ok: true };
842
+ const err = validate.errors?.[0];
843
+ const where = err?.instancePath || "(\u9876\u5C42)";
844
+ const what = err?.message ?? "\u4E0D\u7B26\u5408\u5DE5\u5177 parameters schema";
845
+ return { ok: false, message: `${where} ${err?.keyword ? `[${err.keyword}] ` : ""}${what}` };
846
+ };
804
847
  }
805
848
  });
806
849
 
@@ -1095,7 +1138,7 @@ var init_visionCapability = __esm({
1095
1138
  });
1096
1139
 
1097
1140
  // ../core/src/session/contentParts.ts
1098
- var MAX_IMAGE_BYTES, msgText, imageUrlsOf, hasImagePart, VISION_DEGRADE_NOTE, isVisionEnabled, estimateImageTokens, toWireUserContent, toIngestContents, replaceImageParts, collapseToText, degradeImagesForAux;
1141
+ var MAX_IMAGE_BYTES, msgText, imageUrlsOf, hasImagePart, VISION_DEGRADE_NOTE, isVisionEnabled, estimateImageTokens, toWireUserContent, toIngestContents, replaceImageParts, collapseToText, degradeImagesForAux, buildImageFollowUpParts;
1099
1142
  var init_contentParts = __esm({
1100
1143
  "../core/src/session/contentParts.ts"() {
1101
1144
  "use strict";
@@ -1197,6 +1240,20 @@ ${paths.join("\n")}
1197
1240
  return { ...m, content: text };
1198
1241
  };
1199
1242
  degradeImagesForAux = (m) => collapseToText(m, "[\u56FE\u7247\u5DF2\u5F52\u6863]");
1243
+ buildImageFollowUpParts = (images) => {
1244
+ const labels = images.map((img, i) => ` ${i + 1}. ${img.name || "image"}\uFF08${img.mime}\uFF09`).join("\n");
1245
+ return [
1246
+ {
1247
+ type: "text",
1248
+ text: `\uFF08\u7CFB\u7EDF\u6CE8\u5165\uFF1A\u4E0A\u4E00\u6761\u5DE5\u5177\u7ED3\u679C\u8BFB\u53D6\u4E86 ${images.length} \u5F20\u56FE\u7247\uFF0C\u9644\u4E8E\u672C\u6D88\u606F\u4F9B\u89C6\u89C9\u5206\u6790\u3002\u82E5\u770B\u4E0D\u6E05\u6216\u65E0\u6CD5\u8BFB\u53D6\uFF0C\u8BF7\u544A\u77E5\u7528\u6237\u5F53\u524D\u6A21\u578B\u53EF\u80FD\u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\u3002\uFF09
1249
+ ${labels}`
1250
+ },
1251
+ ...images.map((img) => ({
1252
+ type: "image_url",
1253
+ image_url: { url: `data:${img.mime};base64,${img.base64}` }
1254
+ }))
1255
+ ];
1256
+ };
1200
1257
  }
1201
1258
  });
1202
1259
 
@@ -1321,7 +1378,7 @@ var init_model = __esm({
1321
1378
  });
1322
1379
 
1323
1380
  // ../core/src/session/contextCore.ts
1324
- var isCjkCodePoint, estimateTextTokens, estimateTokens, cleanMsg, groupUnits, splitUntils;
1381
+ var isCjkCodePoint, estimateTextTokens, computeMessageTokens, msgTokenCache, messageTokens, estimateTokens, cleanMsg, groupUnits, splitUntils;
1325
1382
  var init_contextCore = __esm({
1326
1383
  "../core/src/session/contextCore.ts"() {
1327
1384
  "use strict";
@@ -1345,35 +1402,48 @@ var init_contextCore = __esm({
1345
1402
  }
1346
1403
  return cjk + Math.ceil(rest / divisor);
1347
1404
  };
1348
- estimateTokens = (messagesArr) => {
1349
- return messagesArr.reduce((total, m) => {
1350
- let pureText = "";
1351
- let imageCount = 0;
1352
- if (m.content) {
1353
- if (typeof m.content === "string") {
1354
- pureText += m.content;
1355
- } else if (Array.isArray(m.content)) {
1356
- pureText += msgText(m.content);
1357
- imageCount += imageUrlsOf(m.content).length;
1405
+ computeMessageTokens = (m) => {
1406
+ let pureText = "";
1407
+ let imageCount = 0;
1408
+ if (m.content) {
1409
+ if (typeof m.content === "string") {
1410
+ pureText += m.content;
1411
+ } else if (Array.isArray(m.content)) {
1412
+ pureText += msgText(m.content);
1413
+ imageCount += imageUrlsOf(m.content).length;
1414
+ }
1415
+ }
1416
+ if (m.tool_calls && Array.isArray(m.tool_calls)) {
1417
+ for (const call of m.tool_calls) {
1418
+ if (call.function) {
1419
+ pureText += ` ${call.function.name} ${call.function.arguments || ""}`;
1358
1420
  }
1359
1421
  }
1360
- if (m.tool_calls && Array.isArray(m.tool_calls)) {
1361
- for (const call of m.tool_calls) {
1362
- if (call.function) {
1363
- pureText += ` ${call.function.name} ${call.function.arguments || ""}`;
1364
- }
1365
- }
1366
- }
1367
- const knownKeys = ["content", "tool_calls"];
1368
- for (const key of Object.keys(m)) {
1369
- if (!knownKeys.includes(key) && typeof m[key] === "string") {
1370
- pureText += ` ${m[key]}`;
1371
- }
1422
+ }
1423
+ const knownKeys = ["content", "tool_calls"];
1424
+ for (const key of Object.keys(m)) {
1425
+ if (!knownKeys.includes(key) && typeof m[key] === "string") {
1426
+ pureText += ` ${m[key]}`;
1372
1427
  }
1373
- const isStructured = m.role === "tool" || Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
1374
- const tokens = estimateTextTokens(pureText, isStructured ? 4 : 4.8) + imageCount * estimateImageTokens() + 4;
1375
- return total + (isNaN(tokens) ? 0 : tokens);
1376
- }, 0);
1428
+ }
1429
+ const isStructured = m.role === "tool" || Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
1430
+ const imagePrice = imageCount > 0 ? estimateImageTokens() : 0;
1431
+ const tokens = estimateTextTokens(pureText, isStructured ? 4 : 4.8) + imageCount * imagePrice + 4;
1432
+ return { tokens: isNaN(tokens) ? 0 : tokens, imageCount, imagePrice };
1433
+ };
1434
+ msgTokenCache = /* @__PURE__ */ new WeakMap();
1435
+ messageTokens = (m) => {
1436
+ const toolCalls = m.tool_calls;
1437
+ const cached2 = msgTokenCache.get(m);
1438
+ if (cached2 && cached2.role === m.role && cached2.toolCalls === toolCalls && cached2.content === m.content && (cached2.imageCount === 0 || cached2.imagePrice === estimateImageTokens())) {
1439
+ return cached2.tokens;
1440
+ }
1441
+ const { tokens, imageCount, imagePrice } = computeMessageTokens(m);
1442
+ msgTokenCache.set(m, { content: m.content, toolCalls, role: m.role, imageCount, imagePrice, tokens });
1443
+ return tokens;
1444
+ };
1445
+ estimateTokens = (messagesArr) => {
1446
+ return messagesArr.reduce((total, m) => total + messageTokens(m), 0);
1377
1447
  };
1378
1448
  cleanMsg = (m) => {
1379
1449
  const { id, sessionId, ts, ...rest } = m;
@@ -1473,7 +1543,7 @@ async function setTodos(sessionId, todos) {
1473
1543
  await writeStore(sessionId, store);
1474
1544
  });
1475
1545
  }
1476
- var getSessionsDirPath, getWorkspaceSessionsDir, getStatePath, getTranscriptPath, ensureSessionsDir, migrateLegacyFiles, writeStore, storeLocks, withStoreLock, getOrCreateSessionId, readStore, updateCalibration, listSessions, getMostRecentSessionId, renameSession, deleteSession;
1546
+ var getSessionsDirPath, getWorkspaceSessionsDir, getStatePath, getTranscriptPath, ensuredSessionDirs, ensureSessionsDir, migrateLegacyFiles, writeStore, storeLocks, withStoreLock, getOrCreateSessionId, readStore, updateCalibration, listSessions, getMostRecentSessionId, renameSession, deleteSession;
1477
1547
  var init_store = __esm({
1478
1548
  "../core/src/session/store.ts"() {
1479
1549
  "use strict";
@@ -1494,10 +1564,14 @@ var init_store = __esm({
1494
1564
  assertSafeSessionId(sessionId);
1495
1565
  return path7.join(getSessionsDirPath(sessionId), `${sessionId}.jsonl`);
1496
1566
  };
1567
+ ensuredSessionDirs = /* @__PURE__ */ new Set();
1497
1568
  ensureSessionsDir = async (sessionId) => {
1498
- await fs5.mkdir(getSessionsDirPath(sessionId), { recursive: true });
1569
+ const dir = getSessionsDirPath(sessionId);
1570
+ if (ensuredSessionDirs.has(dir)) return;
1571
+ await fs5.mkdir(dir, { recursive: true });
1499
1572
  await migrateLegacyFiles(sessionId).catch(() => {
1500
1573
  });
1574
+ ensuredSessionDirs.add(dir);
1501
1575
  };
1502
1576
  migrateLegacyFiles = async (sessionId) => {
1503
1577
  assertSafeSessionId(sessionId);
@@ -1676,8 +1750,34 @@ var init_store = __esm({
1676
1750
  return summaries;
1677
1751
  };
1678
1752
  getMostRecentSessionId = async () => {
1679
- const list = await listSessions();
1680
- return list.length > 0 ? list[0].sessionId : null;
1753
+ const root = getWorkspaceSessionsDir();
1754
+ let entries;
1755
+ try {
1756
+ entries = await fs5.readdir(root, { withFileTypes: true });
1757
+ } catch {
1758
+ return null;
1759
+ }
1760
+ let best;
1761
+ let noTimestampFirst;
1762
+ for (const entry of entries) {
1763
+ if (!entry.isDirectory()) continue;
1764
+ const sessionId = entry.name;
1765
+ if (!isSafeSessionId(sessionId)) continue;
1766
+ const state = await readJSONFile(getStatePath(sessionId));
1767
+ let updatedAt = state?.updatedAt;
1768
+ if (!updatedAt) {
1769
+ try {
1770
+ updatedAt = (await fs5.stat(path7.join(root, sessionId))).mtime.toISOString();
1771
+ } catch {
1772
+ }
1773
+ }
1774
+ if (!updatedAt) {
1775
+ noTimestampFirst ??= sessionId;
1776
+ continue;
1777
+ }
1778
+ if (!best || updatedAt > best.updatedAt) best = { sessionId, updatedAt };
1779
+ }
1780
+ return best?.sessionId ?? noTimestampFirst ?? null;
1681
1781
  };
1682
1782
  renameSession = async (sessionId, title) => {
1683
1783
  assertSafeSessionId(sessionId);
@@ -1693,13 +1793,14 @@ var init_store = __esm({
1693
1793
  assertSafeSessionId(sessionId);
1694
1794
  const dir = getSessionsDirPath(sessionId);
1695
1795
  await fs5.rm(dir, { recursive: true, force: true });
1796
+ ensuredSessionDirs.delete(dir);
1696
1797
  };
1697
1798
  }
1698
1799
  });
1699
1800
 
1700
1801
  // ../core/src/session/transcript.ts
1701
1802
  import fs6 from "fs/promises";
1702
- var isEventLine, readTranscriptLines, appendLineWithRetry, appendMessage, appendEvent;
1803
+ var isEventLine, readTranscriptLines, appendSizeCache, appendLineWithRetry, appendMessage, appendEvent;
1703
1804
  var init_transcript = __esm({
1704
1805
  "../core/src/session/transcript.ts"() {
1705
1806
  "use strict";
@@ -1728,22 +1829,29 @@ var init_transcript = __esm({
1728
1829
  throw err;
1729
1830
  }
1730
1831
  };
1832
+ appendSizeCache = /* @__PURE__ */ new Map();
1731
1833
  appendLineWithRetry = async (p, payload) => {
1732
1834
  const MAX_ATTEMPTS = 3;
1835
+ const payloadBytes = Buffer.byteLength(payload, "utf-8");
1733
1836
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
1734
- let beforeSize = 0;
1735
- try {
1736
- beforeSize = (await fs6.stat(p)).size;
1737
- } catch {
1837
+ let beforeSize = appendSizeCache.get(p);
1838
+ if (beforeSize === void 0) {
1839
+ beforeSize = 0;
1840
+ try {
1841
+ beforeSize = (await fs6.stat(p)).size;
1842
+ } catch {
1843
+ }
1738
1844
  }
1739
1845
  try {
1740
1846
  await fs6.appendFile(p, payload, "utf-8");
1847
+ appendSizeCache.set(p, beforeSize + payloadBytes);
1741
1848
  return;
1742
1849
  } catch (e) {
1743
1850
  try {
1744
1851
  await fs6.truncate(p, beforeSize);
1745
1852
  } catch {
1746
1853
  }
1854
+ appendSizeCache.set(p, beforeSize);
1747
1855
  if (attempt === MAX_ATTEMPTS) throw e;
1748
1856
  await new Promise((r) => setTimeout(r, 50 * attempt));
1749
1857
  }
@@ -1778,6 +1886,161 @@ var init_transcript = __esm({
1778
1886
  }
1779
1887
  });
1780
1888
 
1889
+ // ../core/src/session/recovery.ts
1890
+ var detectInterruption, repairOrphanToolCalls, reconcileCompaction, recoverSession;
1891
+ var init_recovery = __esm({
1892
+ "../core/src/session/recovery.ts"() {
1893
+ "use strict";
1894
+ init_transcript();
1895
+ init_store();
1896
+ detectInterruption = (lines) => {
1897
+ let sawEvents = false;
1898
+ let lastStart;
1899
+ const closed = /* @__PURE__ */ new Set();
1900
+ for (const l of lines) {
1901
+ if (!isEventLine(l)) continue;
1902
+ sawEvents = true;
1903
+ if (l.dscEvent === "run.start") lastStart = l;
1904
+ else if (l.dscEvent === "run.end" || l.dscEvent === "run.abandoned") closed.add(l.runId);
1905
+ }
1906
+ if (!sawEvents || !lastStart) return { kind: "legacy" };
1907
+ return closed.has(lastStart.runId) ? { kind: "clean" } : { kind: "crashed", runId: lastStart.runId };
1908
+ };
1909
+ repairOrphanToolCalls = (msgs, confirmedCrash) => {
1910
+ const placeholder = confirmedCrash ? "\uFF08\u4E0A\u6B21\u4F1A\u8BDD\u5728\u5DE5\u5177\u6267\u884C\u4E2D\u5D29\u6E83\uFF08run \u672A\u95ED\u5408\uFF09\uFF0C\u8BE5\u8C03\u7528\u672A\u6267\u884C\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";
1911
+ const out = [];
1912
+ const repairedToolCallIds = [];
1913
+ for (let i = 0; i < msgs.length; i++) {
1914
+ const m = msgs[i];
1915
+ out.push(m);
1916
+ if (m.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0) {
1917
+ const answered = /* @__PURE__ */ new Set();
1918
+ let j = i + 1;
1919
+ while (j < msgs.length && msgs[j].role === "tool") {
1920
+ answered.add(msgs[j].tool_call_id);
1921
+ j++;
1922
+ }
1923
+ for (const tc of m.tool_calls) {
1924
+ if (tc.id && !answered.has(tc.id)) {
1925
+ out.push({ role: "tool", tool_call_id: tc.id, content: placeholder });
1926
+ repairedToolCallIds.push(tc.id);
1927
+ }
1928
+ }
1929
+ }
1930
+ }
1931
+ return { msgs: out, repairedToolCallIds, confirmedCrash };
1932
+ };
1933
+ reconcileCompaction = (state, lines) => {
1934
+ let last;
1935
+ for (const l of lines) {
1936
+ if (isEventLine(l) && l.dscEvent === "compaction") last = l;
1937
+ }
1938
+ if (!last) return { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary, desync: "legacy" };
1939
+ let desync = "none";
1940
+ if (last.archivedMessageCount !== state.archivedMessageCount) {
1941
+ desync = state.archivedMessageCount > last.archivedMessageCount ? "state-ahead" : "event-ahead";
1942
+ console.warn(
1943
+ `\u26A0\uFE0F [recovery] \u538B\u7F29\u72B6\u6001 desync\uFF08${desync}\uFF09\uFF1Astate.json archivedMessageCount=${state.archivedMessageCount} vs \u672B\u4E2A compaction \u4E8B\u4EF6=${last.archivedMessageCount}\u3002\u4EE5 state \u4E3A\u51C6\uFF08slice \u6267\u884C\u95F8\u95E8\uFF09\uFF0C\u4E8B\u4EF6\u4FA7\u4EC5\u955C\u50CF\u3002`
1944
+ );
1945
+ }
1946
+ return { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary, desync };
1947
+ };
1948
+ recoverSession = async (sessionId, precomputed) => {
1949
+ const lines = precomputed?.lines ?? await readTranscriptLines(sessionId);
1950
+ const interruption = detectInterruption(lines);
1951
+ if (interruption.kind === "crashed") {
1952
+ await appendEvent(sessionId, {
1953
+ dscEvent: "run.abandoned",
1954
+ runId: interruption.runId,
1955
+ reason: "run.end missing: interrupted by process exit/kill"
1956
+ });
1957
+ 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\u5D29\u6E83\u786E\u8BA4\u300D\u6863\u6267\u884C\u3002`);
1958
+ }
1959
+ const state = precomputed?.state ?? await getRollingState(sessionId);
1960
+ const compaction = reconcileCompaction(
1961
+ { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary },
1962
+ lines
1963
+ );
1964
+ return { interruption, compaction };
1965
+ };
1966
+ }
1967
+ });
1968
+
1969
+ // ../core/src/session/content.ts
1970
+ var decayOldToolResults, enforceVisionGate, buildContextMessages;
1971
+ var init_content = __esm({
1972
+ "../core/src/session/content.ts"() {
1973
+ "use strict";
1974
+ init_contextCore();
1975
+ init_contentParts();
1976
+ init_transcript();
1977
+ init_store();
1978
+ init_config();
1979
+ init_visionCapability();
1980
+ init_recovery();
1981
+ decayOldToolResults = (msgs) => {
1982
+ const units = groupUnits(msgs);
1983
+ const cutoffUnitIdx = Math.max(0, units.length - appConfig.KEEP_RECENT_UNITS);
1984
+ if (cutoffUnitIdx === 0) return msgs;
1985
+ const decayIds = /* @__PURE__ */ new Set();
1986
+ let hasOldImage = false;
1987
+ for (let u = 0; u < cutoffUnitIdx; u++) {
1988
+ for (const m of units[u]) {
1989
+ const mm = m;
1990
+ if (mm.role === "tool" && typeof mm.tool_call_id === "string") decayIds.add(mm.tool_call_id);
1991
+ if (hasImagePart(m.content)) hasOldImage = true;
1992
+ }
1993
+ }
1994
+ if (decayIds.size === 0 && !hasOldImage) return msgs;
1995
+ const keep = appConfig.BOUNDARY_TOOL_KEEP_CHARS;
1996
+ const oldRefs = /* @__PURE__ */ new Set();
1997
+ for (let u = 0; u < cutoffUnitIdx; u++) for (const m of units[u]) oldRefs.add(m);
1998
+ return msgs.map((m) => {
1999
+ if (!oldRefs.has(m)) return m;
2000
+ const mm = m;
2001
+ if (mm.role === "tool" && decayIds.has(mm.tool_call_id) && typeof mm.content === "string" && mm.content.length > keep) {
2002
+ const head = mm.content.slice(0, keep);
2003
+ return { ...mm, content: `${head}
2004
+
2005
+ [\u2026 \u5386\u53F2\u5DE5\u5177\u8F93\u51FA\u5DF2\u6298\u53E0\uFF08\u5171 ${mm.content.length} \u5B57\u7B26\uFF09\uFF1A\u7EC6\u8282\u5148\u7528 recall \u5DE5\u5177\u6309\u5173\u952E\u8BCD\u68C0\u7D22\u672C\u4F1A\u8BDD\u5386\u53F2\uFF08\u539F\u6587\u82E5\u66FE\u622A\u65AD\u5B58\u6863\uFF0Crecall \u4F20 with_full="${mm.tool_call_id}" \u53EF\u53D6\u56DE\u5168\u6587\uFF09\uFF0C\u65E0\u679C\u518D\u91CD\u65B0\u8C03\u7528\u5DE5\u5177 \u2026]` };
2006
+ }
2007
+ return replaceImageParts(mm, "[\u5386\u53F2\u56FE\u7247\u5DF2\u6298\u53E0\uFF1A\u539F\u56FE\u4ECD\u5728\u4F1A\u8BDD\u5F52\u6863\u4E2D\uFF0C\u5982\u9700\u518D\u6B21\u67E5\u770B\u8BF7\u91CD\u65B0\u63D0\u4F9B\u8BE5\u56FE\u7247]");
2008
+ });
2009
+ };
2010
+ enforceVisionGate = (msgs, modelId) => isVisionEnabled(modelId) ? msgs : msgs.map((m) => collapseToText(m, VISION_DEGRADE_NOTE));
2011
+ buildContextMessages = async (sessionId, currentUserMsg, systemPrompt, modelId) => {
2012
+ await ensureVisionCacheLoaded();
2013
+ const lines = await readTranscriptLines(sessionId);
2014
+ const store = await getRollingState(sessionId);
2015
+ const eventAware = appConfig.transcriptEvents || lines.some(isEventLine);
2016
+ const recovery = eventAware ? await recoverSession(sessionId, {
2017
+ lines,
2018
+ state: { archivedMessageCount: store.archivedMessageCount, rollingSummary: store.rollingSummary }
2019
+ }) : {
2020
+ interruption: { kind: "legacy" },
2021
+ compaction: { archivedMessageCount: store.archivedMessageCount, rollingSummary: store.rollingSummary, desync: "legacy" }
2022
+ };
2023
+ const all = lines.filter((l) => !isEventLine(l)).map(cleanMsg);
2024
+ const result = [
2025
+ { role: "system", content: systemPrompt },
2026
+ // 存储系统提示词
2027
+ { role: "system", content: recovery.compaction.rollingSummary }
2028
+ // 后续存储摘要使用
2029
+ ];
2030
+ const archivedMessageCount = recovery.compaction.archivedMessageCount;
2031
+ const messageAll = all.slice(archivedMessageCount);
2032
+ if (messageAll.length === 0) {
2033
+ result.push(currentUserMsg);
2034
+ return enforceVisionGate(result, modelId);
2035
+ }
2036
+ const repair = repairOrphanToolCalls(messageAll, recovery.interruption.kind === "crashed");
2037
+ result.push(...decayOldToolResults(repair.msgs));
2038
+ result.push(currentUserMsg);
2039
+ return enforceVisionGate(result, modelId);
2040
+ };
2041
+ }
2042
+ });
2043
+
1781
2044
  // ../core/src/hooks/types.ts
1782
2045
  var INTERCEPTABLE_EVENTS, TOOL_EVENTS, ALL_EVENTS, DEFAULT_TIMEOUT_BY_EVENT;
1783
2046
  var init_types = __esm({
@@ -1964,7 +2227,7 @@ var init_registry = __esm({
1964
2227
  if (m instanceof RegExp) return `${m.toString()}`;
1965
2228
  return "[fn]";
1966
2229
  };
1967
- return rules.map((r) => ({ event: r.event, matcher: strMatcher(r.matcher), source: r.source, onError: r.onError }));
2230
+ return rules.map((r) => ({ event: r.event, matcher: strMatcher(r.matcher), source: r.source, onError: r.onError, firstRunApproval: r.firstRunApproval === true ? true : void 0 }));
1968
2231
  };
1969
2232
  isPlainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
1970
2233
  dispatch = async (event, ctx) => {
@@ -2069,6 +2332,7 @@ var init_hooks = __esm({
2069
2332
  // ../core/src/agent/truncate.ts
2070
2333
  import path9 from "path";
2071
2334
  function stripAnsi(text) {
2335
+ if (!text || !text.includes(ANSI_ESC_CHAR)) return text;
2072
2336
  return text.replace(ANSI_ESCAPE, "");
2073
2337
  }
2074
2338
  function microcompactTextContent(raw) {
@@ -2077,10 +2341,7 @@ function microcompactTextContent(raw) {
2077
2341
  s = s.replace(/\n{3,}/g, "\n\n").trim();
2078
2342
  return s;
2079
2343
  }
2080
- function isAsyncGenerator(x) {
2081
- return x != null && typeof x[Symbol.asyncIterator] === "function";
2082
- }
2083
- var ANSI_ESCAPE, getFileAccessRoots, relativizeWorkspacePathsInText, truncateToolResult, truncateApprovalDetail, batchSummaryPrompt, compactBatch, ARCHIVE_INDEX_MAX_ENTRIES, ARCHIVE_INDEX_BATCH_CAP, extractArchiveEntities, parseSummarySlot, mergeSummarySlot, CHECKPOINT_MAX_CHARS, SLOT_PLACEHOLDER, CHECKPOINT_FORMAT, checkpointRules, checkpointPrompt, checkpointDirectPrompt, assembleSlot, parseSlotNarrativeState, synthesizeSlotNarrative, synthesizeSlotNarrativeFromBatch, compactSlotNarrative, splitIntoBatches, compactBatches, ensureSummarySlot, SUMMARY_SELF_COMPACT_THRESHOLD, persistCompaction, ensureFitsWindow, collectToolResult;
2344
+ var ANSI_ESCAPE, getFileAccessRoots, rootPathRegexCache, relativizeWorkspacePathsInText, ANSI_ESC_CHAR, truncateToolResult, truncateApprovalDetail, batchSummaryPrompt, compactBatch, ARCHIVE_INDEX_MAX_ENTRIES, ARCHIVE_INDEX_BATCH_CAP, entityMinLen, extractArchiveEntities, parseSummarySlot, mergeSummarySlot, CHECKPOINT_MAX_CHARS, SLOT_PLACEHOLDER, CHECKPOINT_FORMAT, checkpointRules, checkpointPrompt, checkpointDirectPrompt, assembleSlot, parseSlotNarrativeState, synthesizeSlotNarrative, synthesizeSlotNarrativeFromBatch, compactSlotNarrative, MAX_BATCH_TOKENS, PRETRUNCATE_FLOOR_TOKENS, foldableTokensOf, foldTextToTarget, foldArguments, foldMessageTo, pretruncateOversizedUnit, splitIntoBatches, compactBatches, ensureSummarySlot, SUMMARY_SELF_COMPACT_THRESHOLD, persistCompaction, ensureFitsWindow;
2084
2345
  var init_truncate = __esm({
2085
2346
  "../core/src/agent/truncate.ts"() {
2086
2347
  "use strict";
@@ -2088,6 +2349,7 @@ var init_truncate = __esm({
2088
2349
  init_model();
2089
2350
  init_contextCore();
2090
2351
  init_contentParts();
2352
+ init_content();
2091
2353
  init_store();
2092
2354
  init_transcript();
2093
2355
  init_hooks();
@@ -2102,6 +2364,7 @@ var init_truncate = __esm({
2102
2364
  return norm;
2103
2365
  });
2104
2366
  };
2367
+ rootPathRegexCache = /* @__PURE__ */ new Map();
2105
2368
  relativizeWorkspacePathsInText = (text) => {
2106
2369
  if (!text) return text;
2107
2370
  const roots = /* @__PURE__ */ new Set();
@@ -2114,20 +2377,25 @@ var init_truncate = __esm({
2114
2377
  }
2115
2378
  }
2116
2379
  } catch {
2117
- return text.replace(ANSI_ESCAPE, "");
2380
+ return stripAnsi(text);
2118
2381
  }
2119
2382
  let out = text;
2120
2383
  for (const root of roots) {
2121
2384
  const norm = root.replace(/\\/g, "/");
2122
2385
  if (norm.length < 3) continue;
2123
- const re = new RegExp(
2124
- norm.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(\\/|\\\\|$)",
2125
- "gi"
2126
- );
2386
+ let re = rootPathRegexCache.get(norm);
2387
+ if (!re) {
2388
+ re = new RegExp(
2389
+ norm.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(\\/|\\\\|$)",
2390
+ "gi"
2391
+ );
2392
+ rootPathRegexCache.set(norm, re);
2393
+ }
2127
2394
  out = out.replace(re, "./$1");
2128
2395
  }
2129
2396
  return out;
2130
2397
  };
2398
+ ANSI_ESC_CHAR = String.fromCharCode(27);
2131
2399
  truncateToolResult = (result, maxChars, sidecarNote) => {
2132
2400
  const finalMaxChars = maxChars ?? appConfig.MAX_TOOL_RESULT_CHARS;
2133
2401
  const newResult = microcompactTextContent(result);
@@ -2164,25 +2432,36 @@ ${tail}`;
2164
2432
  };
2165
2433
  ARCHIVE_INDEX_MAX_ENTRIES = 120;
2166
2434
  ARCHIVE_INDEX_BATCH_CAP = 60;
2435
+ entityMinLen = (s) => /[一-鿿]/.test(s) ? 2 : 3;
2167
2436
  extractArchiveEntities = (batch) => {
2168
- let text = "";
2437
+ let contentText = "";
2438
+ let argsText = "";
2169
2439
  for (const m of batch) {
2170
- if (m.content) text += ` ${msgText(m.content)}`;
2440
+ if (m.content) contentText += ` ${msgText(m.content)}`;
2171
2441
  const calls = m.tool_calls;
2172
- if (Array.isArray(calls)) for (const c of calls) text += ` ${c?.function?.name ?? ""} ${c?.function?.arguments ?? ""}`;
2442
+ if (Array.isArray(calls)) for (const c of calls) argsText += ` ${c?.function?.name ?? ""} ${c?.function?.arguments ?? ""}`;
2173
2443
  }
2174
2444
  const found = [];
2175
2445
  const seen = /* @__PURE__ */ new Set();
2176
2446
  const push = (raw) => {
2177
2447
  const s = raw.trim();
2178
- if (!s || s.length < 3 || s.length > 60 || seen.has(s)) return;
2448
+ if (!s || s.length < entityMinLen(s) || s.length > 60 || seen.has(s.toLowerCase()) || s.includes("|")) return;
2179
2449
  const lastSeg = s.split(/[\\/]/).pop() ?? "";
2180
2450
  if (/^[\d.]+$/.test(lastSeg)) return;
2181
- seen.add(s);
2451
+ seen.add(s.toLowerCase());
2182
2452
  found.push(s);
2183
2453
  };
2184
- for (const m of text.match(/(?:[A-Za-z]:)?[\w.\-]+(?:[/\\][\w.\-]+)+\.[A-Za-z0-9]{1,6}/g) ?? []) push(m);
2185
- for (const m of text.match(/`([^`\n]{2,48})`/g) ?? []) push(m.slice(1, -1));
2454
+ const scanSurfaces = [contentText, argsText];
2455
+ for (const text2 of scanSurfaces) {
2456
+ for (const m of text2.match(/(?:[A-Za-z]:)?[\w.\-一-鿿]+(?:[/\\][\w.\-一-鿿]+)+\.[A-Za-z0-9]{1,6}/g) ?? []) push(m);
2457
+ for (const m of text2.match(/`([^`\n]{2,48})`/g) ?? []) push(m.slice(1, -1));
2458
+ for (const m of text2.match(/https?:\/\/[^\s"'<>()【】,。]+/g) ?? []) push(m);
2459
+ }
2460
+ const text = contentText;
2461
+ for (const m of text.match(/"([^"\n]{2,48})"/g) ?? []) push(m.slice(1, -1));
2462
+ for (const m of text.match(/[「“]([^」”\n]{2,48})[」”]/g) ?? []) push(m.slice(1, -1));
2463
+ for (const m of text.match(/'([^'\n]{2,48})'/g) ?? []) if (!m.slice(1, -1).includes(" ")) push(m.slice(1, -1));
2464
+ for (const m of text.match(/[一-鿿][一-鿿0-9A-Za-z_]+/g) ?? []) if (m.length <= 14) push(m);
2186
2465
  return found.slice(0, ARCHIVE_INDEX_BATCH_CAP);
2187
2466
  };
2188
2467
  parseSummarySlot = (content) => {
@@ -2201,8 +2480,8 @@ ${tail}`;
2201
2480
  const merged = [];
2202
2481
  const seen = /* @__PURE__ */ new Set();
2203
2482
  for (const e of [...newEntities, ...index]) {
2204
- if (!e || seen.has(e)) continue;
2205
- seen.add(e);
2483
+ if (!e || seen.has(e.toLowerCase())) continue;
2484
+ seen.add(e.toLowerCase());
2206
2485
  merged.push(e);
2207
2486
  if (merged.length >= ARCHIVE_INDEX_MAX_ENTRIES) break;
2208
2487
  }
@@ -2265,14 +2544,80 @@ ${lines}
2265
2544
  return assembleSlot(index, newNotes);
2266
2545
  };
2267
2546
  compactSlotNarrative = async (slotContent, _modelWindow, signal) => synthesizeSlotNarrative(slotContent, "", signal);
2547
+ MAX_BATCH_TOKENS = 16e3;
2548
+ PRETRUNCATE_FLOOR_TOKENS = 512;
2549
+ foldableTokensOf = (m) => Math.max(0, estimateTokens([m]) - 4);
2550
+ foldTextToTarget = (text, curTokens, targetTokens) => {
2551
+ const density = text.length / Math.max(1, curTokens);
2552
+ const keepChars = Math.max(64, Math.floor(targetTokens * density));
2553
+ const half = Math.floor(keepChars / 2);
2554
+ const totalLines = text.split("\n").length;
2555
+ const omitted = Math.max(0, totalLines - text.slice(0, half).split("\n").length - text.slice(-half).split("\n").length);
2556
+ return [text.slice(0, half), "", `\u2026[\u538B\u7F29\u9884\u622A\u65AD\uFF1A\u8D85\u51FA\u5355\u6279\u538B\u7F29\u9884\u7B97\uFF0C\u5DF2\u53BB\u4E2D\u6BB5\u7EA6 ${omitted} \u884C\uFF08\u5171 ${totalLines} \u884C\uFF09\uFF1B\u5B8C\u6574\u5185\u5BB9\u53EF\u7ECF recall \u5DE5\u5177\u68C0\u7D22]\u2026`, "", text.slice(-half)].join("\n");
2557
+ };
2558
+ foldArguments = (args, maxChars) => {
2559
+ if (args.length <= maxChars) return args;
2560
+ try {
2561
+ const walk = (v) => {
2562
+ if (typeof v === "string") return v.length > maxChars ? `\u2026[\u5DF2\u6298\u53E0 ${v.length} \u5B57\u7B26]` : v;
2563
+ if (Array.isArray(v)) return v.map(walk);
2564
+ if (v && typeof v === "object") {
2565
+ for (const k of Object.keys(v)) v[k] = walk(v[k]);
2566
+ }
2567
+ return v;
2568
+ };
2569
+ return JSON.stringify(walk(JSON.parse(args)));
2570
+ } catch {
2571
+ return JSON.stringify(`[\u5DE5\u5177\u53C2\u6570\u8FC7\u957F\u5DF2\u6298\u53E0\uFF0C\u539F ${args.length} \u5B57\u7B26]`);
2572
+ }
2573
+ };
2574
+ foldMessageTo = (m, targetTokens) => {
2575
+ const mm = m;
2576
+ const keepChars = Math.max(256, targetTokens * 4);
2577
+ if (Array.isArray(mm.tool_calls) && mm.tool_calls.length > 0) {
2578
+ let content = mm.content;
2579
+ if (typeof content === "string" && content.length > keepChars) {
2580
+ content = foldTextToTarget(content, Math.max(1, foldableTokensOf(m)), targetTokens);
2581
+ }
2582
+ return {
2583
+ ...mm,
2584
+ content,
2585
+ tool_calls: mm.tool_calls.map((c) => c?.function?.arguments ? { ...c, function: { ...c.function, arguments: foldArguments(String(c.function.arguments), keepChars) } } : c)
2586
+ };
2587
+ }
2588
+ if (typeof mm.content === "string" && mm.content.length > 0) {
2589
+ return { ...mm, content: foldTextToTarget(mm.content, Math.max(1, foldableTokensOf(m)), targetTokens) };
2590
+ }
2591
+ return m;
2592
+ };
2593
+ pretruncateOversizedUnit = (unit, budgetTokens = MAX_BATCH_TOKENS) => {
2594
+ let out = unit.map((m) => Array.isArray(m.content) ? collapseToText(m) : m);
2595
+ let guard = unit.length * 16 + 16;
2596
+ while (estimateTokens(out) > budgetTokens && guard-- > 0) {
2597
+ let idx = -1, maxT = 0;
2598
+ for (let i = 0; i < out.length; i++) {
2599
+ const t = foldableTokensOf(out[i]);
2600
+ if (t > maxT) {
2601
+ maxT = t;
2602
+ idx = i;
2603
+ }
2604
+ }
2605
+ if (idx < 0 || maxT <= PRETRUNCATE_FLOOR_TOKENS) break;
2606
+ out[idx] = foldMessageTo(out[idx], Math.floor(maxT / 2));
2607
+ }
2608
+ return out;
2609
+ };
2268
2610
  splitIntoBatches = (toCompact) => {
2269
- const MAX_BATCH_TOKENS = 16e3;
2270
2611
  const units = groupUnits(toCompact);
2271
2612
  const batches = [];
2272
2613
  let batch = [];
2273
2614
  let batchTokens = 0;
2274
- for (const unit of units) {
2275
- const size = estimateTokens(unit);
2615
+ for (let unit of units) {
2616
+ let size = estimateTokens(unit);
2617
+ if (size > MAX_BATCH_TOKENS) {
2618
+ unit = pretruncateOversizedUnit(unit);
2619
+ size = estimateTokens(unit);
2620
+ }
2276
2621
  if (batchTokens + size > MAX_BATCH_TOKENS && batch.length > 0) {
2277
2622
  batches.push(batch);
2278
2623
  batch = [];
@@ -2318,6 +2663,16 @@ ${lines}
2318
2663
  const cacheFactor = hitRate == null ? 1 : Math.min(1.1, Math.max(0.9, 0.9 + hitRate * 0.2));
2319
2664
  const effectiveRatio = Math.min(event.compactRatio * cacheFactor, 0.82);
2320
2665
  if (estReal(event.messageArr) <= event.modelWindow * effectiveRatio) return;
2666
+ const preDecaySize = estReal(event.messageArr);
2667
+ const decayedArr = decayOldToolResults(event.messageArr);
2668
+ if (decayedArr !== event.messageArr) {
2669
+ event.messageArr.length = 0;
2670
+ event.messageArr.push(...decayedArr);
2671
+ }
2672
+ if (estReal(event.messageArr) <= event.modelWindow * effectiveRatio) {
2673
+ console.log(`\u{1F4C9} [\u7A97\u53E3\u6CBB\u7406] \u514D\u8D39\u8870\u51CF\uFF08\u65E7\u5DE5\u5177\u7ED3\u679C\u6298\u53E0\uFF09\u540E\u56DE\u5230\u9608\u503C\u5185\uFF08${Math.round(preDecaySize)} \u2192 ${Math.round(estReal(event.messageArr))} token\uFF09\uFF0C\u8DF3\u8FC7 LLM \u538B\u7F29`);
2674
+ return;
2675
+ }
2321
2676
  const tokensThreshold = Math.round(event.modelWindow * effectiveRatio);
2322
2677
  await dispatch("PreCompact", { sessionId: event.sessionId, depth: event.depth, tokensBefore: Math.round(estReal(event.messageArr)), tokensThreshold });
2323
2678
  const systemMsg = event.messageArr[0];
@@ -2408,7 +2763,9 @@ ${lines}
2408
2763
  }
2409
2764
  });
2410
2765
  if (nextFailures >= 3) {
2411
- throw new Error(`\u274C [\u7269\u7406\u7194\u65AD] \u4E0A\u4E0B\u6587\u538B\u7F29\u5DF2\u8FDE\u7EED\u906D\u9047 ${nextFailures} \u6B21\u5931\u8D25\u3002\u4E3A\u9632\u6B62\u5929\u4EF7\u8D26\u5355\u6B7B\u5FAA\u73AF\uFF0C\u7CFB\u7EDF\u5DF2\u5F3A\u884C\u62E6\u622A\u3002\u8BF7\u6392\u67E5\u7F51\u7EDC\u6216\u5927\u6A21\u578B\u63D0\u4F9B\u5546\u662F\u5426\u5D29\u6E83\u3002`);
2766
+ throw new Error(
2767
+ `\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`
2768
+ );
2412
2769
  }
2413
2770
  break;
2414
2771
  }
@@ -2424,83 +2781,18 @@ ${lines}
2424
2781
  throw new Error(`\u4E0A\u4E0B\u6587\u8D85\u51FA\u6A21\u578B\u7A97\u53E3\u4E0A\u9650\uFF08\u6821\u51C6\u7EA6 ${Math.round(finalTokens)} / ${event.modelWindow} token\uFF0C\u515C\u5E95 ${hardLimitRatio}\xD7window\uFF09\uFF0C\u5373\u4F7F\u5168\u91CF\u538B\u7F29\u4ECD\u65E0\u6CD5\u5BB9\u7EB3\u3002\u4EFB\u52A1\u8FC7\u5927\uFF0C\u8BF7\u62C6\u5206\u4EFB\u52A1\u3001\u51CF\u5C0F\u5355\u6B21\u8BFB\u53D6\u91CF\uFF0C\u6216\u589E\u5927 modelWindow\u3002`);
2425
2782
  }
2426
2783
  };
2427
- collectToolResult = async (ret, onChunk, signal) => {
2428
- if (isAsyncGenerator(ret)) {
2429
- let full = "";
2430
- const idleMs = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
2431
- while (true) {
2432
- let timer2;
2433
- const outcome = await Promise.race([
2434
- ret.next().then((step) => ({ timedOut: false, step })),
2435
- new Promise((resolve) => {
2436
- timer2 = setTimeout(() => resolve({ timedOut: true }), idleMs);
2437
- })
2438
- ]);
2439
- if (timer2) clearTimeout(timer2);
2440
- if (outcome.timedOut) {
2441
- full += `
2442
-
2443
- [\u23F3 \u5DE5\u5177\u6D41\u5F0F\u8F93\u51FA idle \u8D85\u65F6\uFF08${Math.round(idleMs / 1e3)}s \u65E0\u65B0\u5757\uFF09\uFF0C\u5DF2\u7194\u65AD\u8FD4\u56DE\u5DF2\u6536\u96C6\u5185\u5BB9]`;
2444
- try {
2445
- await ret.return(void 0);
2446
- } catch {
2447
- }
2448
- break;
2449
- }
2450
- if (outcome.step.done) break;
2451
- full += outcome.step.value;
2452
- onChunk?.(outcome.step.value);
2453
- }
2454
- return full;
2455
- }
2456
- const timeoutMs2 = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
2457
- let timer;
2458
- const raced = await Promise.race([
2459
- Promise.resolve(ret).then((v) => ({ kind: "ok", value: typeof v === "string" ? v : JSON.stringify(v) })),
2460
- new Promise((resolve) => {
2461
- timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs2);
2462
- }),
2463
- ...signal ? [new Promise((resolve) => {
2464
- signal.addEventListener("abort", () => resolve({ kind: "abort" }), { once: true });
2465
- })] : []
2466
- ]);
2467
- if (timer) clearTimeout(timer);
2468
- if (raced.kind === "timeout") {
2469
- return `[\u23F3 \u5DE5\u5177\u6267\u884C\u8D85\u65F6\uFF08${Math.round(timeoutMs2 / 1e3)}s \u672A\u8FD4\u56DE\uFF09\uFF0C\u5DF2\u7194\u65AD\u8DF3\u8FC7\u3002\u8BE5\u5DE5\u5177\u53EF\u80FD hang \u6216\u4E0D\u54CD\u5E94\u4E2D\u6B62\u4FE1\u53F7\u3002]`;
2470
- }
2471
- if (raced.kind === "abort") {
2472
- throw new Error("aborted");
2473
- }
2474
- return raced.value;
2475
- };
2476
2784
  }
2477
2785
  });
2478
2786
 
2479
2787
  // ../core/src/tool/permissions.ts
2480
2788
  import fs7 from "fs/promises";
2481
2789
  import path10 from "path";
2482
- var PRIMARY_ARG, rules2, trustedProject, globCache, globToRegex, DANGER_COMMAND_TOOLS, compileRule, readPermissionConfig, toolNameMatches, ruleMatches, initPermissions, checkPermission, listPermissionRules, buildScopedAllowRule, resolveSettingsPath, addPermissionRule;
2790
+ var rules2, trustedProject, globCache, globToRegex, DANGER_COMMAND_TOOLS, compileRule, readPermissionConfig, toolNameMatches, ruleMatches, initPermissions, checkPermission, listPermissionRules, buildScopedAllowRule, resolveSettingsPath, addPermissionRule;
2483
2791
  var init_permissions = __esm({
2484
2792
  "../core/src/tool/permissions.ts"() {
2485
2793
  "use strict";
2486
2794
  init_config();
2487
2795
  init_common();
2488
- PRIMARY_ARG = {
2489
- run_command: "command",
2490
- run_in_background: "command",
2491
- web_fetch: "url",
2492
- web_search: "query",
2493
- read_file: "path",
2494
- list_dir: "path",
2495
- edit_file: "path",
2496
- create_file: "path",
2497
- write_file: "path",
2498
- delete_path: "path",
2499
- notebook_edit: "path",
2500
- move_file: "src",
2501
- search_grep: "query",
2502
- glob: "pattern"
2503
- };
2504
2796
  rules2 = { allow: [], deny: [], ask: [] };
2505
2797
  trustedProject = false;
2506
2798
  globCache = /* @__PURE__ */ new Map();
@@ -2566,8 +2858,8 @@ var init_permissions = __esm({
2566
2858
  if (!Array.isArray(list)) continue;
2567
2859
  for (const item of list) {
2568
2860
  if (typeof item !== "string") continue;
2569
- const compiled = compileRule(item, configPath, key);
2570
- if (compiled) merged[key].push(compiled);
2861
+ const compiled2 = compileRule(item, configPath, key);
2862
+ if (compiled2) merged[key].push(compiled2);
2571
2863
  }
2572
2864
  }
2573
2865
  }
@@ -2578,12 +2870,11 @@ var init_permissions = __esm({
2578
2870
  if (pattern.endsWith("*") && name.startsWith(pattern.slice(0, -1))) return true;
2579
2871
  return false;
2580
2872
  };
2581
- ruleMatches = (rule, toolName, args) => {
2873
+ ruleMatches = (rule, toolName, args, primaryArg) => {
2582
2874
  if (!toolNameMatches(rule.toolName, toolName)) return false;
2583
2875
  if (rule.argRegex === null) return true;
2584
- const argKey = PRIMARY_ARG[toolName];
2585
- if (!argKey) return true;
2586
- const val = args?.[argKey];
2876
+ if (!primaryArg) return true;
2877
+ const val = args?.[primaryArg];
2587
2878
  if (typeof val !== "string") return false;
2588
2879
  return rule.argRegex.test(val);
2589
2880
  };
@@ -2600,11 +2891,11 @@ var init_permissions = __esm({
2600
2891
  rules2 = { allow: [], deny: [], ask: [] };
2601
2892
  }
2602
2893
  };
2603
- checkPermission = (toolName, args) => {
2894
+ checkPermission = (toolName, args, primaryArg) => {
2604
2895
  try {
2605
- for (const r of rules2.deny) if (ruleMatches(r, toolName, args)) return "deny";
2606
- for (const r of rules2.ask) if (ruleMatches(r, toolName, args)) return "ask";
2607
- for (const r of rules2.allow) if (ruleMatches(r, toolName, args)) return "allow";
2896
+ for (const r of rules2.deny) if (ruleMatches(r, toolName, args, primaryArg)) return "deny";
2897
+ for (const r of rules2.ask) if (ruleMatches(r, toolName, args, primaryArg)) return "ask";
2898
+ for (const r of rules2.allow) if (ruleMatches(r, toolName, args, primaryArg)) return "allow";
2608
2899
  return null;
2609
2900
  } catch {
2610
2901
  return null;
@@ -2614,8 +2905,8 @@ var init_permissions = __esm({
2614
2905
  const proj = (rs) => rs.map((r) => ({ toolName: r.toolName, raw: r.raw }));
2615
2906
  return { allow: proj(rules2.allow), deny: proj(rules2.deny), ask: proj(rules2.ask) };
2616
2907
  };
2617
- buildScopedAllowRule = (toolName, args) => {
2618
- const argKey = PRIMARY_ARG[toolName];
2908
+ buildScopedAllowRule = (toolName, args, primaryArg) => {
2909
+ const argKey = primaryArg;
2619
2910
  const val = argKey ? args?.[argKey] : void 0;
2620
2911
  if (!argKey || typeof val !== "string" || !val) return null;
2621
2912
  if (argKey === "command") {
@@ -2636,8 +2927,8 @@ var init_permissions = __esm({
2636
2927
  resolveSettingsPath = (scope) => scope === "global" ? path10.join(appConfig.dataDir, "settings.json") : path10.join(process.cwd(), ".deepseeker-code", "settings.json");
2637
2928
  addPermissionRule = async (scope, kind, ruleStr) => {
2638
2929
  if (scope === "project" && !trustedProject) return false;
2639
- const compiled = compileRule(ruleStr, `runtime:addPermissionRule(${scope})`, kind);
2640
- if (!compiled) return false;
2930
+ const compiled2 = compileRule(ruleStr, `runtime:addPermissionRule(${scope})`, kind);
2931
+ if (!compiled2) return false;
2641
2932
  try {
2642
2933
  const file = resolveSettingsPath(scope);
2643
2934
  const cfg = await readJSONFile(file, {}) ?? {};
@@ -2648,7 +2939,7 @@ var init_permissions = __esm({
2648
2939
  await fs7.mkdir(path10.dirname(file), { recursive: true });
2649
2940
  await atomicWriteJSON(file, cfg);
2650
2941
  }
2651
- if (!rules2[kind].some((r) => r.raw === compiled.raw)) rules2[kind].push(compiled);
2942
+ if (!rules2[kind].some((r) => r.raw === compiled2.raw)) rules2[kind].push(compiled2);
2652
2943
  return true;
2653
2944
  } catch (e) {
2654
2945
  console.warn(`\u26A0\uFE0F [permissions] addPermissionRule \u5199\u5165\u5931\u8D25\uFF08${scope}/${kind} ${ruleStr}\uFF09: ${e?.message ?? e}`);
@@ -2688,7 +2979,7 @@ import fsSync from "fs";
2688
2979
  import path11 from "path";
2689
2980
  import ignore from "ignore";
2690
2981
  import { AsyncLocalStorage } from "node:async_hooks";
2691
- var WORKSPACE_ROOT, workspaceAls, getActiveWorkspaceRoot, runWithWorkspaceRoot, runWithSessionContext, BUILTIN_IGNORE_RULES, ignoreCache, ignoreBuilding, allowedRootsReal, getAllowedWorkspaceRoots, getContainingRoot, allowedBoundaryRoots, resolveSafePath, resolveReadablePath, assertWithinWorkspace, PROTECTED_WRITE_DIRS, COMMAND_ENV_DENYLIST, SENSITIVE_ENV_PATTERN, scrubCommandEnv, isProtectedWrite, scanIgnoreFilesRecursive, buildIgnoreEngine, getIgnoreForBase, initializeWorkspaceIgnore, checkIsPathIgnored, requestApproval;
2982
+ var WORKSPACE_ROOT, workspaceAls, getActiveWorkspaceRoot, runWithWorkspaceRoot, runWithSessionContext, BUILTIN_IGNORE_RULES, ignoreCache, ignoreBuilding, allowedRootsReal, getAllowedWorkspaceRoots, realpathNativeSync, getContainingRoot, allowedBoundaryRoots, resolveSafePath, resolveReadablePath, assertWithinWorkspace, PROTECTED_WRITE_DIRS, COMMAND_ENV_DENYLIST, SENSITIVE_ENV_PATTERN, scrubCommandEnv, isProtectedWrite, SCAN_PRUNE_DIRS, scanIgnoreFilesRecursive, buildIgnoreEngine, getIgnoreForBase, initializeWorkspaceIgnore, checkIsPathIgnored, requestApproval;
2692
2983
  var init_guard = __esm({
2693
2984
  "../core/src/tool/guard.ts"() {
2694
2985
  "use strict";
@@ -2738,15 +3029,24 @@ var init_guard = __esm({
2738
3029
  ignoreBuilding = /* @__PURE__ */ new Map();
2739
3030
  allowedRootsReal = [];
2740
3031
  getAllowedWorkspaceRoots = () => [...allowedRootsReal];
3032
+ realpathNativeSync = (p) => {
3033
+ try {
3034
+ return fsSync.realpathSync.native(p);
3035
+ } catch {
3036
+ return p;
3037
+ }
3038
+ };
2741
3039
  getContainingRoot = (absPath) => {
3040
+ const target = realpathNativeSync(absPath);
2742
3041
  let best;
2743
3042
  for (const r of allowedBoundaryRoots()) {
2744
- const rel = path11.relative(r, absPath);
3043
+ const rReal = realpathNativeSync(r);
3044
+ const rel = path11.relative(rReal, target);
2745
3045
  if (!rel.startsWith("..") && !path11.isAbsolute(rel)) {
2746
- if (!best || r.length > best.length) best = r;
3046
+ if (!best || rReal.length > best.length) best = rReal;
2747
3047
  }
2748
3048
  }
2749
- return best ?? getActiveWorkspaceRoot();
3049
+ return best ?? realpathNativeSync(getActiveWorkspaceRoot());
2750
3050
  };
2751
3051
  allowedBoundaryRoots = () => {
2752
3052
  if (allowedRootsReal.length > 0) return allowedRootsReal;
@@ -2864,6 +3164,26 @@ var init_guard = __esm({
2864
3164
  return false;
2865
3165
  }
2866
3166
  };
3167
+ SCAN_PRUNE_DIRS = /* @__PURE__ */ new Set([
3168
+ "node_modules",
3169
+ ".git",
3170
+ "vendor",
3171
+ "__pycache__",
3172
+ ".venv",
3173
+ ".pytest_cache",
3174
+ ".gradle",
3175
+ "build",
3176
+ "target",
3177
+ ".settings",
3178
+ ".vs",
3179
+ "Debug",
3180
+ "Release",
3181
+ "out",
3182
+ ".DerivedData",
3183
+ "Pods",
3184
+ ".idea",
3185
+ ".vscode"
3186
+ ]);
2867
3187
  scanIgnoreFilesRecursive = async (dirPath, base, engine) => {
2868
3188
  try {
2869
3189
  const entries = await fs8.readdir(dirPath, { withFileTypes: true });
@@ -2881,7 +3201,7 @@ var init_guard = __esm({
2881
3201
  }
2882
3202
  }
2883
3203
  for (const entry of entries) {
2884
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
3204
+ if (entry.isDirectory() && !SCAN_PRUNE_DIRS.has(entry.name)) {
2885
3205
  await scanIgnoreFilesRecursive(path11.join(dirPath, entry.name), base, engine);
2886
3206
  }
2887
3207
  }
@@ -2923,7 +3243,7 @@ var init_guard = __esm({
2923
3243
  return false;
2924
3244
  }
2925
3245
  };
2926
- requestApproval = async (toolName, toolCallId, detail, ctx, safetyLevel, args) => {
3246
+ requestApproval = async (toolName, toolCallId, detail, ctx, safetyLevel, args, primaryArg) => {
2927
3247
  const safeDetail = truncateApprovalDetail(detail);
2928
3248
  if (!ctx.requestApproval) {
2929
3249
  ctx.onUIEvent?.({ type: "tool.denied", toolsId: toolCallId, toolName });
@@ -2933,11 +3253,11 @@ var init_guard = __esm({
2933
3253
  await dispatch("PermissionRequest", { sessionId: ctx.sessionId, cwd: ctx.cwd, toolName, toolCallId, detail: safeDetail, safetyLevel, args }).catch(() => {
2934
3254
  });
2935
3255
  const decision = await ctx.requestApproval(safeDetail, { toolName, toolCallId, sessionId: ctx.sessionId });
2936
- const approved = decision !== "deny";
2937
- if (!approved) {
3256
+ const approved2 = decision !== "deny";
3257
+ if (!approved2) {
2938
3258
  ctx.onUIEvent?.({ type: "tool.denied", toolsId: toolCallId, toolName });
2939
3259
  } else if (decision === "allow-always") {
2940
- const ruleStr = buildScopedAllowRule(toolName, args);
3260
+ const ruleStr = buildScopedAllowRule(toolName, args, primaryArg);
2941
3261
  if (ruleStr) {
2942
3262
  const persisted = await addPermissionRule("project", "allow", ruleStr).catch(() => false);
2943
3263
  console.log(persisted ? `\u{1F4CC} [\u5BA1\u6279\u8BB0\u5FC6] \u5DF2\u5199\u6301\u4E45 allow \u89C4\u5219\uFF1A${ruleStr}\uFF08\u9879\u76EE .deepseeker-code/settings.json\uFF09\uFF0C\u540E\u7EED\u547D\u4E2D\u8BE5\u4F5C\u7528\u57DF\u514D\u5BA1` : `\u267B\uFE0F [\u5BA1\u6279] ${toolName} \u672C\u6B21\u653E\u884C${safetyLevel ? ` (${safetyLevel})` : ""}\uFF08\u672A\u6301\u4E45\u5316\uFF1A\u672A\u4FE1\u4EFB\u76EE\u5F55\u6216\u5199\u5165\u5931\u8D25\uFF09`);
@@ -2945,7 +3265,7 @@ var init_guard = __esm({
2945
3265
  console.log(`\u267B\uFE0F [\u5BA1\u6279] ${toolName} \u672C\u6B21\u653E\u884C${safetyLevel ? ` (${safetyLevel})` : ""}\uFF08\u672A\u6301\u4E45\u5316\uFF1A\u65E0\u6CD5\u5B89\u5168\u751F\u6210\u4F5C\u7528\u57DF\uFF0C\u907F\u514D\u88F8\u5DE5\u5177\u540D\u9759\u9ED8\u653E\u884C\u5168\u90E8\u8C03\u7528\uFF09`);
2946
3266
  }
2947
3267
  }
2948
- return approved;
3268
+ return approved2;
2949
3269
  };
2950
3270
  }
2951
3271
  });
@@ -3138,6 +3458,8 @@ var init_background = __esm({
3138
3458
  type: "function",
3139
3459
  function: {
3140
3460
  name: "run_in_background",
3461
+ primaryArg: "command",
3462
+ autoApproval: "command",
3141
3463
  description: "\u5728\u540E\u53F0\u542F\u52A8\u4E00\u4E2A\u957F\u65F6 shell \u547D\u4EE4\u5E76\u7ACB\u5373\u8FD4\u56DE task_id\uFF0C\u4E0D\u963B\u585E\u540E\u7EED\u63A8\u7406\u3002\u9002\u7528\u4E24\u7C7B\u573A\u666F\uFF1A\u2460 \u957F\u8FDE\u63A5/\u5E38\u9A7B\u8FDB\u7A0B\uFF08dev server\u3001watch\u3001tail -f\uFF09\u2014\u2014\u5148\u8D77\u670D\u52A1\u518D\u7EE7\u7EED\u5E72\u522B\u7684\u6D3B\uFF1B\u2461 \u9884\u8BA1\u8FD0\u884C\u6570\u5206\u949F\u4EE5\u4E0A\u3001\u65E0\u9700\u5B9E\u65F6\u76EF\u770B\u7684\u6709\u9650\u957F\u4EFB\u52A1\uFF08\u5168\u91CF\u6D4B\u8BD5 / \u5927\u6784\u5EFA / \u5927\u4F53\u79EF\u5B89\u88C5\u2014\u2014\u8DD1\u5B8C\u7528 get_background_output \u5E26 wait_seconds \u7B49\u7ED3\u679C\uFF0C\u4E0D\u8981\u7528\u524D\u53F0 run_command \u5E72\u7B49\uFF09\u3002\u7528 get_background_output \u67E5\u65E5\u5FD7\u4E0E\u72B6\u6001\uFF0Cstop_background_task \u7EC8\u6B62\u3002",
3142
3464
  parameters: {
3143
3465
  type: "object",
@@ -3185,7 +3507,7 @@ var init_background = __esm({
3185
3507
  proc = spawn2(args.command, { shell: isWin ? resolveWinShell() ?? true : true, cwd, detached: !isWin, env: scrubCommandEnv() });
3186
3508
  proc.unref?.();
3187
3509
  } catch (e) {
3188
- yield `\u274C [\u540E\u53F0\u542F\u52A8\u5931\u8D25]\uFF1A${e.message}`;
3510
+ yield toolFailure(`[\u540E\u53F0\u542F\u52A8\u5931\u8D25]\uFF1A${e.message}`);
3189
3511
  return;
3190
3512
  }
3191
3513
  const codec = {
@@ -3229,7 +3551,7 @@ task_id: ${taskId}
3229
3551
  async execute(args, ctx) {
3230
3552
  const task = registry.get(args.task_id);
3231
3553
  if (!task || task.sessionId !== ctx?.sessionId) {
3232
- return `\u274C [\u67E5\u8BE2\u5931\u8D25]\uFF1A\u672A\u627E\u5230 task_id=${args.task_id}\uFF08\u53EF\u80FD\u5DF2\u968F\u670D\u52A1\u91CD\u542F\u4E22\u5931\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF09\u3002`;
3554
+ return toolFailure(`[\u67E5\u8BE2\u5931\u8D25]\uFF1A\u672A\u627E\u5230 task_id=${args.task_id}\uFF08\u53EF\u80FD\u5DF2\u968F\u670D\u52A1\u91CD\u542F\u4E22\u5931\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF09\u3002`);
3233
3555
  }
3234
3556
  const waitMs = Math.min(Math.max(0, args.wait_seconds ?? 0), 120) * 1e3;
3235
3557
  if (waitMs > 0 && task.status === "running") {
@@ -3269,7 +3591,7 @@ task_id: ${taskId}
3269
3591
  async execute(args, ctx) {
3270
3592
  const task = registry.get(args.task_id);
3271
3593
  if (!task || task.sessionId !== ctx?.sessionId) {
3272
- return `\u274C [\u7EC8\u6B62\u5931\u8D25]\uFF1A\u672A\u627E\u5230 task_id=${args.task_id}\uFF08\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF09\u3002`;
3594
+ return toolFailure(`[\u7EC8\u6B62\u5931\u8D25]\uFF1A\u672A\u627E\u5230 task_id=${args.task_id}\uFF08\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF09\u3002`);
3273
3595
  }
3274
3596
  if (task.status !== "running") {
3275
3597
  return `\u2139\uFE0F [stop]\uFF1A\u4EFB\u52A1 ${args.task_id} \u5F53\u524D\u72B6\u6001\u4E3A ${task.status}\uFF08\u5DF2\u4E0D\u5728\u8FD0\u884C\uFF09\uFF0C\u65E0\u9700\u7EC8\u6B62\u3002`;
@@ -3307,6 +3629,7 @@ var init_client2 = __esm({
3307
3629
  "use strict";
3308
3630
  init_background();
3309
3631
  init_config();
3632
+ init_type();
3310
3633
  REQUEST_TIMEOUT_MS = 3e4;
3311
3634
  PROTOCOL_VERSION_STDIO = "2024-11-05";
3312
3635
  PROTOCOL_VERSION_HTTP = "2025-03-26";
@@ -3320,8 +3643,8 @@ var init_client2 = __esm({
3320
3643
  const text = content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
3321
3644
  const capped = capResultText(text);
3322
3645
  if (result?.isError) {
3323
- return `\u274C [MCP \u5DE5\u5177\u62A5\u9519]
3324
- ${capped || JSON.stringify(result)}`;
3646
+ return toolFailure(`[MCP \u5DE5\u5177\u62A5\u9519]
3647
+ ${capped || JSON.stringify(result)}`).content;
3325
3648
  }
3326
3649
  return capped || JSON.stringify(result);
3327
3650
  };
@@ -3972,6 +4295,7 @@ var init_loader = __esm({
3972
4295
  "../core/src/tool/mcp/loader.ts"() {
3973
4296
  "use strict";
3974
4297
  init_type();
4298
+ init_argsValidator();
3975
4299
  init_config();
3976
4300
  init_client2();
3977
4301
  clients = [];
@@ -4050,10 +4374,10 @@ ${out.map((r) => `- [${r.server}] ${r.uri}\uFF08${r.name}${r.description ? `\uFF
4050
4374
  \uFF08\u5C06\u628A MCP server \u63D0\u4F9B\u7684\u5185\u5BB9\u62C9\u5165\u6A21\u578B\u4E0A\u4E0B\u6587\uFF1B\u8BF7\u786E\u8BA4\u6765\u6E90\u53EF\u4FE1\u3001\u4E0D\u542B\u654F\u611F\u6570\u636E\uFF09`,
4051
4375
  async execute(args) {
4052
4376
  const { server, uri } = args ?? {};
4053
- if (!server || !uri) return "\u274C [read_mcp_resource] \u7F3A\u5C11 server \u6216 uri\u3002";
4377
+ if (!server || !uri) return toolFailure("[read_mcp_resource] \u7F3A\u5C11 server \u6216 uri\u3002");
4054
4378
  const c = findClient(server);
4055
- if (!c) return `\u274C \u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}`;
4056
- if (!c.supportsResources) return `\u274C server "${server}" \u4E0D\u652F\u6301 resources\u3002`;
4379
+ if (!c) return toolFailure(`\u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}`);
4380
+ if (!c.supportsResources) return toolFailure(`server "${server}" \u4E0D\u652F\u6301 resources\u3002`);
4057
4381
  const text = await c.readResource(uri);
4058
4382
  return `[MCP resource ${server}${uri}]
4059
4383
  ${text}`;
@@ -4120,10 +4444,10 @@ ${out.map((p) => `- [${p.server}] ${p.name}${p.description ? `\uFF1A${p.descript
4120
4444
  \uFF08\u5C06\u628A MCP server \u6E32\u67D3\u7684 prompt \u6587\u672C\u62C9\u5165\u6A21\u578B\u4E0A\u4E0B\u6587\uFF1B\u8BF7\u786E\u8BA4\u6765\u6E90\u53EF\u4FE1\uFF09`,
4121
4445
  async execute(args) {
4122
4446
  const { server, name, arguments: pargs } = args ?? {};
4123
- if (!server || !name) return "\u274C [get_mcp_prompt] \u7F3A\u5C11 server \u6216 name\u3002";
4447
+ if (!server || !name) return toolFailure("[get_mcp_prompt] \u7F3A\u5C11 server \u6216 name\u3002");
4124
4448
  const c = findClient(server);
4125
- if (!c) return `\u274C \u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}`;
4126
- if (!c.supportsPrompts) return `\u274C server "${server}" \u4E0D\u652F\u6301 prompts\u3002`;
4449
+ if (!c) return toolFailure(`\u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}`);
4450
+ if (!c.supportsPrompts) return toolFailure(`server "${server}" \u4E0D\u652F\u6301 prompts\u3002`);
4127
4451
  const text = await c.getPrompt(name, pargs);
4128
4452
  return `[MCP prompt ${server}/${name}]
4129
4453
  ${text}`;
@@ -4178,34 +4502,63 @@ ${lines.join("\n")}`;
4178
4502
  type: "function",
4179
4503
  function: {
4180
4504
  name: "mcp_call",
4181
- description: '\u8C03\u7528\u6307\u5B9A MCP server \u7684\u5916\u90E8\u5DE5\u5177\u3002server \u4E0E\u5DE5\u5177\u540D\u5FC5\u586B\u2014\u2014\u5747\u6765\u81EA mcp_list_tools \u7684\u76EE\u5F55\u8F93\u51FA\uFF08\u7528\u539F\u540D\uFF0C\u4E0D\u8981\u6539\u5199\uFF09\u3002args \u4E3A\u8BE5\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF08\u53C2\u6570\u540D\u89C1\u76EE\u5F55\uFF1B\u65E0\u53C2\u6570\u4F20 "{}"\uFF09\u3002\u6267\u884C\u5916\u90E8\u903B\u8F91\uFF0C\u6BCF\u6B21\u8C03\u7528\u9700\u5BA1\u6279\u3002',
4505
+ description: "\u8C03\u7528\u6307\u5B9A MCP server \u7684\u5916\u90E8\u5DE5\u5177\u3002server \u4E0E\u5DE5\u5177\u540D\u5FC5\u586B\u2014\u2014\u5747\u6765\u81EA mcp_list_tools \u7684\u76EE\u5F55\u8F93\u51FA\uFF08\u7528\u539F\u540D\uFF0C\u4E0D\u8981\u6539\u5199\uFF09\u3002args \u4E3A\u8BE5\u5DE5\u5177\u53C2\u6570\u7684\u5BF9\u8C61\uFF08\u53C2\u6570\u540D\u89C1\u76EE\u5F55\uFF1B\u65E0\u53C2\u6570\u4F20 {}\uFF09\uFF1B\u5386\u53F2\u517C\u5BB9\u4E5F\u63A5\u53D7 JSON \u5B57\u7B26\u4E32\u3002\u6267\u884C\u5916\u90E8\u903B\u8F91\uFF0C\u6BCF\u6B21\u8C03\u7528\u9700\u5BA1\u6279\u3002",
4182
4506
  parameters: {
4183
4507
  type: "object",
4184
4508
  properties: {
4185
4509
  server: { type: "string", description: "MCP server \u540D\uFF08\u89C1 mcp_list_tools \u8F93\u51FA\uFF09" },
4186
4510
  tool: { type: "string", description: "\u5DE5\u5177\u540D\uFF08\u89C1 mcp_list_tools \u8F93\u51FA\uFF0C\u7528\u539F\u540D\uFF09" },
4187
- args: { type: "string", description: '\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF0C\u5982 {"query":"..."}\uFF1B\u65E0\u53C2\u6570\u4F20 "{}"' }
4511
+ args: {
4512
+ // #8c:收回结构化 object(历史 JSON 字符串兼容保留——防旧 prompt 缓存/模型惯性)。
4513
+ // anyOf 双形态而非仅 object:否则旧形态调用会被执行层 #8c 参数校验早退,兼容形同虚设。
4514
+ anyOf: [
4515
+ { type: "object", additionalProperties: true, description: '\u5DE5\u5177\u53C2\u6570\u5BF9\u8C61\uFF0C\u5982 {"query":"..."}\uFF1B\u65E0\u53C2\u6570\u4F20 {}' },
4516
+ { type: "string", description: '\uFF08\u5386\u53F2\u517C\u5BB9\uFF09\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF0C\u5982 "{\\"query\\":\\"...\\"}"\uFF1B\u65E0\u53C2\u6570\u4F20 "{}"' }
4517
+ ]
4518
+ }
4188
4519
  },
4189
4520
  required: ["server", "tool"]
4190
4521
  },
4191
4522
  safetyLevel: "danger" /* DANGER */,
4192
4523
  isSync: true,
4193
- requireApproval: (args) => `\u26A0\uFE0F\u3010MCP \u5DE5\u5177\u5BA1\u6279\u3011
4524
+ requireApproval: (args) => {
4525
+ const a = args?.args;
4526
+ const rendered = a === void 0 || a === null ? "{}" : typeof a === "string" ? a : JSON.stringify(a);
4527
+ return `\u26A0\uFE0F\u3010MCP \u5DE5\u5177\u5BA1\u6279\u3011
4194
4528
  \u670D\u52A1: ${args?.server} / \u5DE5\u5177: ${args?.tool}
4195
- \u53C2\u6570: ${args?.args}`,
4529
+ \u53C2\u6570: ${rendered}`;
4530
+ },
4196
4531
  async execute(args) {
4197
4532
  const { server, tool } = args ?? {};
4198
- if (!server || !tool) return "\u274C [mcp_call] \u7F3A\u5C11 server \u6216 tool\uFF08\u5148\u7528 mcp_list_tools \u67E5\u76EE\u5F55\uFF09\u3002";
4533
+ if (!server || !tool) return toolFailure("[mcp_call] \u7F3A\u5C11 server \u6216 tool\uFF08\u5148\u7528 mcp_list_tools \u67E5\u76EE\u5F55\uFF09\u3002");
4199
4534
  let parsed = {};
4200
- if (typeof args.args === "string" && args.args.trim() && args.args.trim() !== "{}") {
4201
- try {
4202
- parsed = JSON.parse(args.args);
4203
- } catch {
4204
- return `\u274C [mcp_call] args \u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${String(args.args).slice(0, 200)}\u3002\u8BF7\u4F20\u8BE5\u5DE5\u5177\u53C2\u6570\u7684 JSON \u5B57\u7B26\u4E32\uFF08\u65E0\u53C2\u6570\u4F20 "{}"\uFF09\u3002`;
4535
+ if (args.args !== void 0 && args.args !== null) {
4536
+ if (typeof args.args === "string") {
4537
+ if (args.args.trim() && args.args.trim() !== "{}") {
4538
+ try {
4539
+ parsed = JSON.parse(args.args);
4540
+ } catch {
4541
+ return toolFailure(`[mcp_call] args \u4E0D\u662F\u5408\u6CD5 JSON\uFF1A${String(args.args).slice(0, 200)}\u3002\u8BF7\u4F20\u8BE5\u5DE5\u5177\u53C2\u6570\u7684\u5BF9\u8C61\uFF08\u65E0\u53C2\u6570\u4F20 {}\uFF09\u3002`, "syntax");
4542
+ }
4543
+ }
4544
+ } else if (typeof args.args === "object" && !Array.isArray(args.args)) {
4545
+ parsed = args.args;
4546
+ } else {
4547
+ return toolFailure(`[mcp_call] args \u9700\u4E3A\u53C2\u6570\u5BF9\u8C61\u6216 JSON \u5B57\u7B26\u4E32\uFF0C\u6536\u5230 ${typeof args.args}\u3002\u8BF7\u4FEE\u6B63\u540E\u91CD\u8BD5\u3002`, "syntax");
4205
4548
  }
4206
4549
  }
4207
4550
  const c = findClient(server);
4208
- if (!c) return `\u274C \u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}\uFF08\u7528 mcp_list_tools \u67E5\u770B\u5DF2\u8FDE\u63A5\u5217\u8868\uFF09`;
4551
+ if (!c) return toolFailure(`\u672A\u8FDE\u63A5\u7684 MCP server\uFF1A${server}\uFF08\u7528 mcp_list_tools \u67E5\u770B\u5DF2\u8FDE\u63A5\u5217\u8868\uFF09`);
4552
+ try {
4553
+ const target = (await c.listTools()).find((t) => t.name === tool);
4554
+ if (target?.inputSchema && typeof target.inputSchema === "object") {
4555
+ const verdict = validateToolArgs(target.inputSchema, parsed);
4556
+ if (!verdict.ok) {
4557
+ return toolFailure(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A${server}/${tool} ${verdict.message}\uFF1B\u8BF7\u4FEE\u6B63 args \u540E\u91CD\u8BD5\uFF08schema \u89C1 mcp_list_tools \u76EE\u5F55\uFF09\u3002`, "syntax");
4558
+ }
4559
+ }
4560
+ } catch {
4561
+ }
4209
4562
  const result = await c.callTool(tool, parsed);
4210
4563
  return `[MCP ${server}/${tool}]
4211
4564
  ${result}`;
@@ -4388,7 +4741,7 @@ var init_repeatBreaker = __esm({
4388
4741
 
4389
4742
  // ../core/src/agent/planMode.ts
4390
4743
  function filterToolsForPlanMode(tools) {
4391
- const allowed = tools.filter((t) => PLAN_ALLOWED_TOOLS.has(t.function.name));
4744
+ const allowed = tools.filter((t) => t.function.planAllowed === true);
4392
4745
  return [...allowed, exitPlanModeToolSchema];
4393
4746
  }
4394
4747
  function appendPlanControlTools(tools) {
@@ -4398,30 +4751,10 @@ function appendPlanControlTools(tools) {
4398
4751
  exitPlanModeToolSchema
4399
4752
  ];
4400
4753
  }
4401
- var PLAN_ALLOWED_TOOLS, exitPlanModeToolSchema, enterPlanModeToolSchema, PLAN_MODE_AUTO_ENTER_HINT;
4754
+ var exitPlanModeToolSchema, enterPlanModeToolSchema, PLAN_MODE_AUTO_ENTER_HINT;
4402
4755
  var init_planMode = __esm({
4403
4756
  "../core/src/agent/planMode.ts"() {
4404
4757
  "use strict";
4405
- PLAN_ALLOWED_TOOLS = /* @__PURE__ */ new Set([
4406
- "read_file",
4407
- "list_dir",
4408
- "view_symbol_outline",
4409
- "read_project_guide",
4410
- "search_grep",
4411
- "glob",
4412
- "get_git_diff",
4413
- "git_status",
4414
- "git_log",
4415
- "inspect_dependencies",
4416
- "get_diagnostics",
4417
- "goto_definition",
4418
- // ★ P0-A 补漏:LSP 导航/诊断恰是调研阶段最需要的(原白名单漂移漏登记,schema 档与 runtime 档共用此表)
4419
- "recall",
4420
- // 只读检索本会话历史(长调研中早前轮次可能已被压缩归档,调研阶段恰是 recall 主场景)
4421
- "web_fetch",
4422
- "web_search"
4423
- // 只读研究类(虽为 DANGER 但不写本地状态;仍走各自审批)
4424
- ]);
4425
4758
  exitPlanModeToolSchema = {
4426
4759
  type: "function",
4427
4760
  function: {
@@ -4702,7 +5035,7 @@ ${style.body}`);
4702
5035
  });
4703
5036
 
4704
5037
  // ../core/src/memory/registry.ts
4705
- var memories, registerMemory, listMemories, getMemory, unregisterMemory, clearMemories, getMemoryIndex;
5038
+ var memories, registerMemory, listMemories, getMemory, unregisterMemory, clearMemories, memoryIndexLine, getMemoryIndex;
4706
5039
  var init_registry6 = __esm({
4707
5040
  "../core/src/memory/registry.ts"() {
4708
5041
  "use strict";
@@ -4720,10 +5053,11 @@ var init_registry6 = __esm({
4720
5053
  return true;
4721
5054
  };
4722
5055
  clearMemories = memories.clear;
5056
+ memoryIndexLine = (m) => `- **${m.name}** (${m.type}) \u2014 ${m.description}`;
4723
5057
  getMemoryIndex = () => {
4724
5058
  const all = memories.list();
4725
5059
  if (all.length === 0) return null;
4726
- return all.slice().sort((a, b) => a.name.localeCompare(b.name)).map((m) => `- **${m.name}** (${m.type}) \u2014 ${m.description}`).join("\n");
5060
+ return all.slice().sort((a, b) => a.name.localeCompare(b.name)).map(memoryIndexLine).join("\n");
4727
5061
  };
4728
5062
  }
4729
5063
  });
@@ -4747,7 +5081,7 @@ ${index}`);
4747
5081
  });
4748
5082
 
4749
5083
  // ../core/src/agent/systemInjections.ts
4750
- var prepareToolsAndInjections;
5084
+ var cleanedSchemaCache, cleanToolSchema, prepareToolsAndInjections;
4751
5085
  var init_systemInjections = __esm({
4752
5086
  "../core/src/agent/systemInjections.ts"() {
4753
5087
  "use strict";
@@ -4761,6 +5095,19 @@ var init_systemInjections = __esm({
4761
5095
  init_inject4();
4762
5096
  init_inject5();
4763
5097
  init_common();
5098
+ cleanedSchemaCache = /* @__PURE__ */ new WeakMap();
5099
+ cleanToolSchema = (t) => {
5100
+ let entry = cleanedSchemaCache.get(t);
5101
+ if (!entry) {
5102
+ const cleaned = {
5103
+ type: t.type,
5104
+ function: { name: t.function.name, description: t.function.description, parameters: t.function.parameters }
5105
+ };
5106
+ entry = { cleaned, fragment: JSON.stringify(cleaned) };
5107
+ cleanedSchemaCache.set(t, entry);
5108
+ }
5109
+ return entry;
5110
+ };
4764
5111
  prepareToolsAndInjections = async (message, options, events) => {
4765
5112
  const { toolSchemas, planMode, locale: locale2, outputStyle } = options;
4766
5113
  const rawToolsPreEnv = planMode && appConfig.planEnforcement === "schema" ? filterToolsForPlanMode(toolSchemas ?? []) : appendPlanControlTools(toolSchemas ?? []);
@@ -4779,11 +5126,10 @@ var init_systemInjections = __esm({
4779
5126
  requestQuestion: options.requestQuestion
4780
5127
  };
4781
5128
  const rawTools = await filterByEnvironment(rawToolsPreEnv, validationCtx);
4782
- const cleanedToolSchemas = rawTools.map((t) => ({
4783
- type: t.type,
4784
- function: { name: t.function.name, description: t.function.description, parameters: t.function.parameters }
4785
- }));
4786
- const toolsTokens = Math.round(JSON.stringify(cleanedToolSchemas).length / 4);
5129
+ const cleanedEntries = rawTools.map((t) => cleanToolSchema(t));
5130
+ const cleanedToolSchemas = cleanedEntries.map((e) => e.cleaned);
5131
+ const schemaJsonLen = cleanedEntries.length === 0 ? 2 : cleanedEntries.reduce((sum, e) => sum + e.fragment.length, 0) + cleanedEntries.length + 1;
5132
+ const toolsTokens = Math.round(schemaJsonLen / 4);
4787
5133
  ensureSummarySlot(message);
4788
5134
  const LOCALE_HINTS = {
4789
5135
  zh: "\u59CB\u7EC8\u7528\u4E2D\u6587\u8F93\u51FA\u6240\u6709\u9762\u5411\u7528\u6237\u7684\u5185\u5BB9\uFF1A\u4F60\u7684\u56DE\u590D\u3001\u4E2D\u95F4\u53D9\u8FF0\u3001todo \u4EFB\u52A1\u9879\u6807\u9898\u3001\u901A\u8FC7 exit_plan_mode \u63D0\u4EA4\u7684\u65B9\u6848\u6587\u672C\u3001\u4EE3\u7801\u6CE8\u91CA\u4E0E\u63D0\u4EA4\u4FE1\u606F\u3002\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u539F\u6837\uFF1B\u5DE5\u5177\u7ED3\u679C\u4E0E\u7CFB\u7EDF\u6307\u4EE4\u53EF\u80FD\u5939\u6742\u5176\u4ED6\u8BED\u8A00\u2014\u2014\u5FC5\u8981\u65F6\u7167\u5F15\u539F\u6587\uFF0C\u4F46\u4F60\u81EA\u5DF1\u7684\u53D9\u8FF0\u4E0E\u89E3\u91CA\u4E00\u5F8B\u7528\u4E2D\u6587\u3002",
@@ -4813,6 +5159,73 @@ var init_systemInjections = __esm({
4813
5159
  }
4814
5160
  });
4815
5161
 
5162
+ // ../core/src/agent/toolResultCollect.ts
5163
+ function isAsyncGenerator(x) {
5164
+ return x != null && typeof x[Symbol.asyncIterator] === "function";
5165
+ }
5166
+ var collectToolResult;
5167
+ var init_toolResultCollect = __esm({
5168
+ "../core/src/agent/toolResultCollect.ts"() {
5169
+ "use strict";
5170
+ collectToolResult = async (ret, onChunk, signal) => {
5171
+ if (isAsyncGenerator(ret)) {
5172
+ let full = "";
5173
+ const idleMs = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
5174
+ while (true) {
5175
+ let timer2;
5176
+ const outcome = await Promise.race([
5177
+ ret.next().then((step) => ({ timedOut: false, step })),
5178
+ new Promise((resolve) => {
5179
+ timer2 = setTimeout(() => resolve({ timedOut: true }), idleMs);
5180
+ })
5181
+ ]);
5182
+ if (timer2) clearTimeout(timer2);
5183
+ if (outcome.timedOut) {
5184
+ full += `
5185
+
5186
+ [\u23F3 \u5DE5\u5177\u6D41\u5F0F\u8F93\u51FA idle \u8D85\u65F6\uFF08${Math.round(idleMs / 1e3)}s \u65E0\u65B0\u5757\uFF09\uFF0C\u5DF2\u7194\u65AD\u8FD4\u56DE\u5DF2\u6536\u96C6\u5185\u5BB9]`;
5187
+ try {
5188
+ await ret.return(void 0);
5189
+ } catch {
5190
+ }
5191
+ return { content: full, status: "failed", errorCategory: "runtime" };
5192
+ }
5193
+ if (outcome.step.done) break;
5194
+ const chunk = typeof outcome.step.value === "string" ? outcome.step.value : String(outcome.step.value?.content ?? outcome.step.value ?? "");
5195
+ full += chunk;
5196
+ onChunk?.(chunk);
5197
+ }
5198
+ return { content: full, status: "success" };
5199
+ }
5200
+ const timeoutMs2 = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
5201
+ let timer;
5202
+ let onAbort;
5203
+ const raced = await Promise.race([
5204
+ Promise.resolve(ret).then((v2) => ({ kind: "ok", value: v2 })),
5205
+ new Promise((resolve) => {
5206
+ timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs2);
5207
+ }),
5208
+ ...signal ? [new Promise((resolve) => {
5209
+ onAbort = () => resolve({ kind: "abort" });
5210
+ signal.addEventListener("abort", onAbort, { once: true });
5211
+ })] : []
5212
+ ]);
5213
+ if (timer) clearTimeout(timer);
5214
+ if (onAbort && signal) signal.removeEventListener("abort", onAbort);
5215
+ if (raced.kind === "timeout") {
5216
+ return { content: `[\u23F3 \u5DE5\u5177\u6267\u884C\u8D85\u65F6\uFF08${Math.round(timeoutMs2 / 1e3)}s \u672A\u8FD4\u56DE\uFF09\uFF0C\u5DF2\u7194\u65AD\u8DF3\u8FC7\u3002\u8BE5\u5DE5\u5177\u53EF\u80FD hang \u6216\u4E0D\u54CD\u5E94\u4E2D\u6B62\u4FE1\u53F7\u3002]`, status: "failed", errorCategory: "runtime" };
5217
+ }
5218
+ if (raced.kind === "abort") {
5219
+ throw new Error("aborted");
5220
+ }
5221
+ const v = raced.value;
5222
+ if (typeof v === "string") return { content: v, status: "success" };
5223
+ if (v && typeof v === "object" && typeof v.content === "string" && (v.status === "success" || v.status === "failed")) return v;
5224
+ return { content: JSON.stringify(v), status: "success" };
5225
+ };
5226
+ }
5227
+ });
5228
+
4816
5229
  // ../core/src/tool/registry/agent.ts
4817
5230
  var createAgentTools;
4818
5231
  var init_agent = __esm({
@@ -4839,13 +5252,13 @@ var init_agent = __esm({
4839
5252
  safetyLevel: "safe" /* SAFE */,
4840
5253
  isSync: true,
4841
5254
  async execute(args, ctx) {
4842
- if (!ctx) return "\u274C [\u6D3E\u751F\u5931\u8D25]\uFF1Aspawn_agent \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002";
5255
+ if (!ctx) return toolFailure("[\u6D3E\u751F\u5931\u8D25]\uFF1Aspawn_agent \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002");
4843
5256
  const res = await runSubagent(
4844
5257
  { task: args.task, name: args.name, role: args.role, resumeSessionId: args.resume_session_id },
4845
5258
  ctx,
4846
5259
  getGlobalTools
4847
5260
  );
4848
- if (!res.ok) return res.output;
5261
+ if (!res.ok) return asToolFailure(res.output);
4849
5262
  return [
4850
5263
  `\u{1F389} [\u5B50 Agent \u6267\u884C\u5B8C\u6BD5${args.resume_session_id ? "\xB7\u7EED\u8DD1" : ""}] \u6D3E\u751F\u5B50\u4EFB\u52A1\u6C47\u62A5\u5982\u4E0B\uFF1A
4851
5264
  `,
@@ -5015,18 +5428,19 @@ var validateWorkflowArgs, stepLabel, buildPipelineTask, formatWorkflowResult;
5015
5428
  var init_workflowHelpers = __esm({
5016
5429
  "../core/src/tool/registry/workflowHelpers.ts"() {
5017
5430
  "use strict";
5431
+ init_type();
5018
5432
  validateWorkflowArgs = (args, maxSteps) => {
5019
5433
  const steps = args?.steps;
5020
5434
  if (!Array.isArray(steps) || steps.length === 0) {
5021
- return "\u274C [\u5DE5\u4F5C\u6D41] steps \u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4\uFF08\u81F3\u5C11 1 \u4E2A\u6B65\u9AA4\uFF09\u3002";
5435
+ return toolFailure("[\u5DE5\u4F5C\u6D41] steps \u5FC5\u987B\u662F\u975E\u7A7A\u6570\u7EC4\uFF08\u81F3\u5C11 1 \u4E2A\u6B65\u9AA4\uFF09\u3002");
5022
5436
  }
5023
5437
  if (steps.length > maxSteps) {
5024
- return `\u274C [\u5DE5\u4F5C\u6D41] \u6B65\u9AA4\u6570 ${steps.length} \u8D85\u8FC7\u4E0A\u9650 ${maxSteps}\uFF08\u9632\u5931\u63A7\u6D3E\u751F\u70E7 token\uFF09\u3002\u8BF7\u62C6\u5206\u4E3A\u591A\u6B21 run_workflow \u8C03\u7528\uFF0C\u6216\u7CBE\u7B80\u6B65\u9AA4\u3002`;
5438
+ return toolFailure(`[\u5DE5\u4F5C\u6D41] \u6B65\u9AA4\u6570 ${steps.length} \u8D85\u8FC7\u4E0A\u9650 ${maxSteps}\uFF08\u9632\u5931\u63A7\u6D3E\u751F\u70E7 token\uFF09\u3002\u8BF7\u62C6\u5206\u4E3A\u591A\u6B21 run_workflow \u8C03\u7528\uFF0C\u6216\u7CBE\u7B80\u6B65\u9AA4\u3002`);
5025
5439
  }
5026
5440
  for (let i = 0; i < steps.length; i++) {
5027
5441
  const s = steps[i];
5028
5442
  if (!s || typeof s.task !== "string" || !s.task.trim()) {
5029
- return `\u274C [\u5DE5\u4F5C\u6D41] steps[${i}].task \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u3002`;
5443
+ return toolFailure(`[\u5DE5\u4F5C\u6D41] steps[${i}].task \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32\u3002`);
5030
5444
  }
5031
5445
  }
5032
5446
  return null;
@@ -5143,14 +5557,14 @@ var init_workflow = __esm({
5143
5557
  // 聚合输出整体兜底截断(各子 agent 结果已按 workflowPerStepChars 单独截断)。
5144
5558
  maxOutputCharacters: 18e3,
5145
5559
  async execute(args, ctx) {
5146
- if (!ctx) return "\u274C [\u5DE5\u4F5C\u6D41]\uFF1Arun_workflow \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002";
5560
+ if (!ctx) return toolFailure("[\u5DE5\u4F5C\u6D41]\uFF1Arun_workflow \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002");
5147
5561
  const maxSteps = appConfig.workflowMaxSteps;
5148
5562
  const validationError = validateWorkflowArgs(args, maxSteps);
5149
5563
  if (validationError) return validationError;
5150
5564
  const mode = args.mode === "pipeline" ? "pipeline" : "parallel";
5151
5565
  const isolation = args.isolation === "worktree" ? "worktree" : "none";
5152
5566
  if (isolation === "worktree" && mode === "pipeline") {
5153
- return '\u274C [\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';
5567
+ return 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');
5154
5568
  }
5155
5569
  const steps = args.steps.map((s) => ({
5156
5570
  task: String(s.task),
@@ -5159,7 +5573,7 @@ var init_workflow = __esm({
5159
5573
  key: s.key ? String(s.key) : void 0
5160
5574
  }));
5161
5575
  if (ctx.depth >= MAX_AGENT_DEPTH) {
5162
- return `\u274C [\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`;
5576
+ return 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`);
5163
5577
  }
5164
5578
  const approvalMutex = createSemaphore(1);
5165
5579
  const serializedApproval = ctx.requestApproval ? async (...raArgs) => {
@@ -5193,7 +5607,7 @@ var init_workflow = __esm({
5193
5607
  try {
5194
5608
  wt = await createWorktree(ctx.sessionId, String(index));
5195
5609
  } catch (e) {
5196
- const msg = `\u274C [worktree \u521B\u5EFA\u5931\u8D25]\uFF1A${e?.message ?? e}\uFF08worktree \u9694\u79BB\u9700\u8981\u4E3B\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\uFF09\u3002\u8BE5\u6B65\u9AA4\u672A\u6267\u884C\u3002`;
5610
+ const msg = toolFailure(`[worktree \u521B\u5EFA\u5931\u8D25]\uFF1A${e?.message ?? e}\uFF08worktree \u9694\u79BB\u9700\u8981\u4E3B\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\uFF09\u3002\u8BE5\u6B65\u9AA4\u672A\u6267\u884C\u3002`).content;
5197
5611
  done++;
5198
5612
  ctx.emitProgress?.(`\u5E76\u884C\u7F16\u6392\uFF1A${done}/${total} worktree \u521B\u5EFA\u5931\u8D25\uFF08${stepLabel(step, index)}\uFF09`);
5199
5613
  return { ok: false, output: msg, label: stepLabel(step, index) };
@@ -5243,7 +5657,7 @@ var init_workflow = __esm({
5243
5657
  outcomes = await Promise.all(running);
5244
5658
  }
5245
5659
  } catch (err) {
5246
- return `\u274C [\u5DE5\u4F5C\u6D41\u7F16\u6392\u5F02\u5E38]\uFF1A${err?.message ?? err}`;
5660
+ return toolFailure(`[\u5DE5\u4F5C\u6D41\u7F16\u6392\u5F02\u5E38]\uFF1A${err?.message ?? err}`);
5247
5661
  }
5248
5662
  return formatWorkflowResult(mode, outcomes);
5249
5663
  }
@@ -5256,11 +5670,12 @@ var init_workflow = __esm({
5256
5670
  // ../core/src/tool/tsHost.ts
5257
5671
  import fsSync3 from "fs";
5258
5672
  import path16 from "path";
5259
- var _ts, _tsTried, getTs, buildDefaultOptions, findTsConfigBounded, resolveTsConfig, createProjectHost, lsCache, getLanguageService, realpathNative, posToLineCol, SUPPORTED_SOURCE_EXTS, checkSupportedSourceExt, lineColToPos;
5673
+ var _ts, _tsTried, getTs, buildDefaultOptions, findTsConfigBounded, tsconfigWalkCache, tsconfigParseCache, resolveTsConfig, createProjectHost, lsCache, getLanguageService, realpathNative, posToLineCol, SUPPORTED_SOURCE_EXTS, checkSupportedSourceExt, lineColToPos;
5260
5674
  var init_tsHost = __esm({
5261
5675
  "../core/src/tool/tsHost.ts"() {
5262
5676
  "use strict";
5263
5677
  init_guard();
5678
+ init_type();
5264
5679
  _ts = null;
5265
5680
  _tsTried = false;
5266
5681
  getTs = async () => {
@@ -5303,16 +5718,38 @@ var init_tsHost = __esm({
5303
5718
  }
5304
5719
  return void 0;
5305
5720
  };
5721
+ tsconfigWalkCache = /* @__PURE__ */ new Map();
5722
+ tsconfigParseCache = /* @__PURE__ */ new Map();
5306
5723
  resolveTsConfig = (ts, absPath) => {
5307
- const tsConfigPath = findTsConfigBounded(ts, absPath);
5724
+ const startDir = path16.dirname(absPath);
5725
+ let tsConfigPath = tsconfigWalkCache.get(startDir);
5726
+ if (!tsConfigPath) {
5727
+ tsConfigPath = findTsConfigBounded(ts, absPath) ?? "";
5728
+ if (tsConfigPath) tsconfigWalkCache.set(startDir, tsConfigPath);
5729
+ }
5308
5730
  if (!tsConfigPath) {
5309
5731
  return { options: buildDefaultOptions(ts), fileNames: [], projectRoot: getContainingRoot(absPath) };
5310
5732
  }
5733
+ let st;
5734
+ try {
5735
+ st = fsSync3.statSync(tsConfigPath, { throwIfNoEntry: false });
5736
+ } catch {
5737
+ st = void 0;
5738
+ }
5739
+ const cached2 = tsconfigParseCache.get(tsConfigPath);
5740
+ if (cached2 && st && st.mtimeMs === cached2.mtimeMs) return cached2.resolved;
5741
+ if (!st) {
5742
+ tsconfigParseCache.delete(tsConfigPath);
5743
+ for (const [dir, p] of tsconfigWalkCache) if (p === tsConfigPath) tsconfigWalkCache.delete(dir);
5744
+ return resolveTsConfig(ts, absPath);
5745
+ }
5311
5746
  const basePath = path16.dirname(tsConfigPath);
5312
5747
  const raw = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
5313
5748
  if (raw.error) return { options: buildDefaultOptions(ts), fileNames: [], projectRoot: basePath };
5314
5749
  const parsed = ts.parseJsonConfigFileContent(raw.config, ts.sys, basePath);
5315
- return { options: parsed.options, fileNames: parsed.fileNames, projectRoot: basePath };
5750
+ const resolved = { options: parsed.options, fileNames: parsed.fileNames, projectRoot: basePath };
5751
+ tsconfigParseCache.set(tsConfigPath, { mtimeMs: st.mtimeMs, resolved });
5752
+ return resolved;
5316
5753
  };
5317
5754
  createProjectHost = (ts, projectRoot, options, rootFileNames) => {
5318
5755
  const cache = /* @__PURE__ */ new Map();
@@ -5406,7 +5843,7 @@ var init_tsHost = __esm({
5406
5843
  checkSupportedSourceExt = (displayPath) => {
5407
5844
  const ext = path16.extname(displayPath).toLowerCase();
5408
5845
  if (SUPPORTED_SOURCE_EXTS.has(ext)) return null;
5409
- return `\u274C [\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301]\uFF1A[${displayPath}] \u7684\u6269\u5C55\u540D ${ext || "(\u65E0)"} \u4E0D\u5728 TS \u5F15\u64CE\u652F\u6301\u8303\u56F4\uFF08\u4EC5 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF09\u3002\u8BF7\u6539\u7528 read_file/grep \u67E5\u770B\u5185\u5BB9\uFF1B\u7F16\u8BD1/\u7C7B\u578B\u7C7B\u9A8C\u8BC1\u7528 run_command \u8DD1\u5BF9\u5E94\u8BED\u8A00\u5DE5\u5177\u94FE\uFF08\u5982 mvn compile / tsc --noEmit\uFF09\u3002`;
5846
+ return toolFailure(`[\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301]\uFF1A[${displayPath}] \u7684\u6269\u5C55\u540D ${ext || "(\u65E0)"} \u4E0D\u5728 TS \u5F15\u64CE\u652F\u6301\u8303\u56F4\uFF08\u4EC5 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF09\u3002\u8BF7\u6539\u7528 read_file/grep \u67E5\u770B\u5185\u5BB9\uFF1B\u7F16\u8BD1/\u7C7B\u578B\u7C7B\u9A8C\u8BC1\u7528 run_command \u8DD1\u5BF9\u5E94\u8BED\u8A00\u5DE5\u5177\u94FE\uFF08\u5982 mvn compile / tsc --noEmit\uFF09\u3002`);
5410
5847
  };
5411
5848
  lineColToPos = (ts, sf, line1, col1) => ts.getPositionOfLineAndCharacter(sf, Math.max(0, line1 - 1), Math.max(0, col1 - 1));
5412
5849
  }
@@ -5417,13 +5854,22 @@ import fs13 from "fs/promises";
5417
5854
  import path17 from "path";
5418
5855
  import { createReadStream } from "fs";
5419
5856
  import * as readline from "readline";
5420
- var isSensitiveReadTarget, stripReadFileLineNumbers, maskSecretsInContent, makeTmpPath, ATOMIC_TMP_RE, sweepStaleAtomicTmp, assertReadable, stripEdgeBlankLines, matchLineBlockWith, diagnoseOldStr, reindentToBase, alignIndentsByAnchor, applyOneEditToContent, fsTools;
5857
+ var IMAGE_MIME_BY_EXT, isSensitiveReadTarget, stripReadFileLineNumbers, maskSecretsInContent, makeTmpPath, ATOMIC_TMP_RE, sweepStaleAtomicTmp, assertReadable, stripEdgeBlankLines, matchLineBlockWith, diagnoseOldStr, reindentToBase, alignIndentsByAnchor, applyOneEditToContent, fsTools;
5421
5858
  var init_fs = __esm({
5422
5859
  "../core/src/tool/registry/fs.ts"() {
5423
5860
  "use strict";
5424
5861
  init_type();
5425
5862
  init_tsHost();
5426
5863
  init_guard();
5864
+ init_contentParts();
5865
+ IMAGE_MIME_BY_EXT = {
5866
+ ".png": "image/png",
5867
+ ".jpg": "image/jpeg",
5868
+ ".jpeg": "image/jpeg",
5869
+ ".gif": "image/gif",
5870
+ ".webp": "image/webp",
5871
+ ".bmp": "image/bmp"
5872
+ };
5427
5873
  isSensitiveReadTarget = (resolvedPath) => {
5428
5874
  const p = resolvedPath.toLowerCase().replace(/\\/g, "/");
5429
5875
  return [
@@ -5639,15 +6085,15 @@ var init_fs = __esm({
5639
6085
  };
5640
6086
  }
5641
6087
  if (lm.reason === "conflict") {
5642
- return { ok: false, error: `\u274C [\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1A\u4EE3\u7801\u51B2\u7A81\uFF01old_str\uFF08${tag}\u5F52\u4E00\u540E\uFF09\u5728\u5168\u6587\u4E2D\u4E0D\u552F\u4E00\uFF08\u5171 ${lm.count} \u5904\uFF09\u3002\u8BF7\u591A\u5305\u88F9\u51E0\u884C\u4E0A\u4E0B\u6587\uFF0C\u6216\u663E\u5F0F\u8BBE replace_all=true \u6279\u91CF\u66FF\u6362\u3002` };
6088
+ return { ok: false, error: toolFailure(`[\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1A\u4EE3\u7801\u51B2\u7A81\uFF01old_str\uFF08${tag}\u5F52\u4E00\u540E\uFF09\u5728\u5168\u6587\u4E2D\u4E0D\u552F\u4E00\uFF08\u5171 ${lm.count} \u5904\uFF09\u3002\u8BF7\u591A\u5305\u88F9\u51E0\u884C\u4E0A\u4E0B\u6587\uFF0C\u6216\u663E\u5F0F\u8BBE replace_all=true \u6279\u91CF\u66FF\u6362\u3002`) };
5643
6089
  }
5644
6090
  }
5645
6091
  const diag = diagnoseOldStr(normalizedContent, normalizedOldRaw);
5646
- return { ok: false, error: `\u274C [\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1A\u672A\u80FD\u5728\u6587\u4EF6\u4E2D\u627E\u5230\u6307\u5B9A\u7684 old_str \u65E7\u4EE3\u7801\u5757\u3002${diag}\u5E38\u89C1\u539F\u56E0\uFF1A\u2460 \u8BEF\u5E26\u4E86 read_file \u7684\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\uFF1B\u2461 \u7F29\u8FDB\uFF08Tab/\u7A7A\u683C\uFF09\u4E0D\u4E00\u81F4\uFF1B\u2462 \u884C\u5C3E\u591A\u4F59\u7A7A\u683C\uFF1B\u2463 \u6587\u4EF6\u5DF2\u88AB\u6539\u52A8/old_str \u975E\u8FDE\u7EED\u6574\u6BB5\u3002\u8BF7\u7528 read_file \u91CD\u65B0\u6838\u5BF9\u8BE5\u5904\u7247\u6BB5\uFF08\u53BB\u6389\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\u3001\u4FDD\u7559\u539F\u59CB Tab/\u7A7A\u683C\u7F29\u8FDB\uFF09\u540E\u91CD\u8BD5\u3002\u26A0\uFE0F \u4E25\u7981\u6539\u7528 run_command \u8C03\u7528 python/node/sed/awk \u7B49\u811A\u672C\u7ED5\u8FC7\u672C\u5DE5\u5177\u4FEE\u6539\u6587\u4EF6\u3002` };
6092
+ return { ok: false, error: toolFailure(`[\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1A\u672A\u80FD\u5728\u6587\u4EF6\u4E2D\u627E\u5230\u6307\u5B9A\u7684 old_str \u65E7\u4EE3\u7801\u5757\u3002${diag}\u5E38\u89C1\u539F\u56E0\uFF1A\u2460 \u8BEF\u5E26\u4E86 read_file \u7684\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\uFF1B\u2461 \u7F29\u8FDB\uFF08Tab/\u7A7A\u683C\uFF09\u4E0D\u4E00\u81F4\uFF1B\u2462 \u884C\u5C3E\u591A\u4F59\u7A7A\u683C\uFF1B\u2463 \u6587\u4EF6\u5DF2\u88AB\u6539\u52A8/old_str \u975E\u8FDE\u7EED\u6574\u6BB5\u3002\u8BF7\u7528 read_file \u91CD\u65B0\u6838\u5BF9\u8BE5\u5904\u7247\u6BB5\uFF08\u53BB\u6389\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\u3001\u4FDD\u7559\u539F\u59CB Tab/\u7A7A\u683C\u7F29\u8FDB\uFF09\u540E\u91CD\u8BD5\u3002\u26A0\uFE0F \u4E25\u7981\u6539\u7528 run_command \u8C03\u7528 python/node/sed/awk \u7B49\u811A\u672C\u7ED5\u8FC7\u672C\u5DE5\u5177\u4FEE\u6539\u6587\u4EF6\u3002`) };
5647
6093
  }
5648
6094
  const matchCount = normalizedContent.split(normalizedOld).length - 1;
5649
6095
  if (!replaceAll && matchCount > 1) {
5650
- return { ok: false, error: `\u274C [\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1A\u4EE3\u7801\u51B2\u7A81\uFF01old_str \u5728\u5168\u6587\u4E2D\u4E0D\u552F\u4E00\uFF08\u5171\u53D1\u73B0\u4E86 ${matchCount} \u5904\uFF09\u3002\u8BF7\u5411\u4E0A\u6216\u5411\u4E0B\u591A\u5305\u88F9\u51E0\u884C\u4E0A\u4E0B\u6587\u518D\u63D0\u8BF7\u4FEE\u6539\uFF0C\u6216\u663E\u5F0F\u8BBE replace_all=true \u6279\u91CF\u66FF\u6362\u3002` };
6096
+ return { ok: false, error: toolFailure(`[\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1A\u4EE3\u7801\u51B2\u7A81\uFF01old_str \u5728\u5168\u6587\u4E2D\u4E0D\u552F\u4E00\uFF08\u5171\u53D1\u73B0\u4E86 ${matchCount} \u5904\uFF09\u3002\u8BF7\u5411\u4E0A\u6216\u5411\u4E0B\u591A\u5305\u88F9\u51E0\u884C\u4E0A\u4E0B\u6587\u518D\u63D0\u8BF7\u4FEE\u6539\uFF0C\u6216\u663E\u5F0F\u8BBE replace_all=true \u6279\u91CF\u66FF\u6362\u3002`) };
5651
6097
  }
5652
6098
  const oldFirstNonBlank = normalizedOld.split("\n").find((l) => l.trim() !== "") ?? "";
5653
6099
  const baseIndent = oldFirstNonBlank.match(/^[ \t]*/)?.[0] ?? "";
@@ -5664,7 +6110,10 @@ var init_fs = __esm({
5664
6110
  type: "function",
5665
6111
  function: {
5666
6112
  name: "read_file",
5667
- description: "\u8BFB\u53D6\u6307\u5B9A\u3010\u6587\u672C\u3011\u6587\u4EF6\u7684\u6587\u672C\u5185\u5BB9\uFF0C\u5E76\u81EA\u52A8\u5E26\u4E0A\u7528\u4E8E\u5BF9\u9F50\u5B9A\u4F4D\u7684\u7269\u7406\u884C\u53F7\u3002\u652F\u6301\u5927\u6587\u4EF6\u5206\u7247\u8BFB\u53D6\uFF0C\u9632\u6B62 Token \u7206\u70B8\u3002\u2605 \u4EC5\u5904\u7406\u6587\u672C\u6587\u4EF6\u2014\u2014\u4E8C\u8FDB\u5236\u6587\u4EF6\u4F1A\u8BFB\u51FA\u4E71\u7801\uFF1A.xlsx \u6539\u7528 read_xlsx\uFF0C.docx \u6539\u7528 read_docx\uFF0C.pdf \u6539\u7528 read_pdf\u3002",
6113
+ // ★ #8a 策略声明:计划模式可用;权限规则作用域主参数
6114
+ planAllowed: true,
6115
+ primaryArg: "path",
6116
+ description: "\u8BFB\u53D6\u6307\u5B9A\u3010\u6587\u672C\u3011\u6587\u4EF6\u7684\u6587\u672C\u5185\u5BB9\uFF0C\u5E76\u81EA\u52A8\u5E26\u4E0A\u7528\u4E8E\u5BF9\u9F50\u5B9A\u4F4D\u7684\u7269\u7406\u884C\u53F7\u3002\u652F\u6301\u5927\u6587\u4EF6\u5206\u7247\u8BFB\u53D6\uFF0C\u9632\u6B62 Token \u7206\u70B8\u3002\u2605 \u4EC5\u5904\u7406\u6587\u672C\u6587\u4EF6\u2014\u2014\u4E8C\u8FDB\u5236\u6587\u4EF6\u4F1A\u8BFB\u51FA\u4E71\u7801\uFF1A.xlsx \u6539\u7528 read_xlsx\uFF0C.docx \u6539\u7528 read_docx\uFF0C.pdf \u6539\u7528 read_pdf\u3002\u2605 \u9700\u8BFB\u591A\u4E2A\u6587\u4EF6\u65F6\uFF0C\u628A\u591A\u4E2A read_file \u8C03\u7528\u653E\u8FDB\u540C\u4E00\u6761\u6D88\u606F\u5E76\u884C\u53D1\u51FA\uFF08\u81EA\u52A8\u5E76\u53D1\u6267\u884C\uFF09\uFF0C\u52FF\u8BFB\u5B8C\u4E00\u4E2A\u518D\u53D1\u4E0B\u4E00\u4E2A\u3002",
5668
6117
  parameters: {
5669
6118
  type: "object",
5670
6119
  properties: {
@@ -5685,6 +6134,7 @@ var init_fs = __esm({
5685
6134
  try {
5686
6135
  const absPath = resolveReadablePath(args.path);
5687
6136
  const lowerExt = absPath.toLowerCase();
6137
+ if (IMAGE_MIME_BY_EXT[path17.extname(lowerExt)]) return `\u{1F5BC} [${args.path}] \u662F\u56FE\u7247\u6587\u4EF6\uFF0Cread_file \u8BFB\u4E0D\u51FA\u6709\u7528\u5185\u5BB9\u3002\u8BF7\u6539\u7528 read_image \u8BFB\u53D6\uFF08\u56FE\u50CF\u4F1A\u4EE5\u89C6\u89C9\u9644\u4EF6\u76F4\u63A5\u63D0\u4F9B\u7ED9\u6A21\u578B\u5206\u6790\uFF09\u3002`;
5688
6138
  if (lowerExt.endsWith(".xlsx")) return `\u26A0\uFE0F [${args.path}] \u662F Excel \u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0Cread_file \u8BFB\u4E0D\u4E86\uFF08UTF-8 \u89E3\u7801\u6EE1\u5C4F\u4E71\u7801\uFF09\u3002\u8BF7\u6539\u7528 read_xlsx\uFF08\u652F\u6301 sheet \u9009\u62E9 / \u7FFB\u9875 / Markdown \u8868\u683C\u8F93\u51FA\uFF09\u3002`;
5689
6139
  if (lowerExt.endsWith(".docx")) return `\u26A0\uFE0F [${args.path}] \u662F Word \u4E8C\u8FDB\u5236\u6587\u6863\uFF0Cread_file \u8BFB\u4E0D\u4E86\u3002\u8BF7\u6539\u7528 read_docx \u63D0\u53D6\u6B63\u6587\u6587\u672C\u3002`;
5690
6140
  if (lowerExt.endsWith(".pdf")) return `\u26A0\uFE0F [${args.path}] \u662F PDF \u4E8C\u8FDB\u5236\u6587\u6863\uFF0Cread_file \u8BFB\u4E0D\u4E86\u3002\u8BF7\u6539\u7528 read_pdf \u63D0\u53D6\u6587\u5B57\u5C42\u3002`;
@@ -5719,7 +6169,7 @@ ${formattedCode}${hasMore ? `
5719
6169
 
5720
6170
  [... \u540E\u9762\u8FD8\u6709\u4EE3\u7801\u5DF2\u88AB\u9690\u85CF\uFF0C\u4F60\u53EF\u4EE5\u8C03\u6574 start_line \u7EE7\u7EED\u5206\u7247\u8BFB\u53D6\u3002]` : ""}`;
5721
6171
  } catch (error) {
5722
- return `\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25 [${args.path}]: ${error.message}`;
6172
+ return toolFailure(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25 [${args.path}]: ${error.message}`);
5723
6173
  }
5724
6174
  }
5725
6175
  }
@@ -5728,7 +6178,9 @@ ${formattedCode}${hasMore ? `
5728
6178
  type: "function",
5729
6179
  function: {
5730
6180
  name: "list_dir",
5731
- description: "\u626B\u63CF\u5E76\u7CBE\u7B80\u5217\u51FA\u5F53\u524D\u9879\u76EE\u7684\u5DE5\u4F5C\u533A\u76EE\u5F55\u6811\u3002\u672C\u5DE5\u5177\u81EA\u52A8\u5408\u5E76\u901A\u7528\u5FFD\u7565\u89C4\u5219\u4E0E\u591A\u5C42\u5B50\u76EE\u5F55\u7EA7 .gitignore \u89C4\u8303\u3002",
6181
+ planAllowed: true,
6182
+ primaryArg: "path",
6183
+ description: "\u626B\u63CF\u5E76\u7CBE\u7B80\u5217\u51FA\u5F53\u524D\u9879\u76EE\u7684\u5DE5\u4F5C\u533A\u76EE\u5F55\u6811\u3002\u672C\u5DE5\u5177\u81EA\u52A8\u5408\u5E76\u901A\u7528\u5FFD\u7565\u89C4\u5219\u4E0E\u591A\u5C42\u5B50\u76EE\u5F55\u7EA7 .gitignore \u89C4\u8303\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
5732
6184
  parameters: {
5733
6185
  type: "object",
5734
6186
  properties: {
@@ -5781,7 +6233,7 @@ ${formattedCode}${hasMore ? `
5781
6233
  return `[Workspace Universal Tree | Max Depth: ${maxDepth}]
5782
6234
  ${treeResult}`;
5783
6235
  } catch (error) {
5784
- return `\u9879\u76EE\u6811\u626B\u63CF\u5931\u8D25: ${error.message}`;
6236
+ return toolFailure(`\u9879\u76EE\u6811\u626B\u63CF\u5931\u8D25: ${error.message}`);
5785
6237
  }
5786
6238
  }
5787
6239
  }
@@ -5790,6 +6242,10 @@ ${treeResult}`;
5790
6242
  type: "function",
5791
6243
  function: {
5792
6244
  name: "edit_file",
6245
+ // ★ #8a 策略声明:undo 备份(快照原内容)+ 分类器作用域 + 权限主参数;计划模式缺省拒绝
6246
+ triggersUndo: "overwrite",
6247
+ autoApproval: "file",
6248
+ primaryArg: "path",
5793
6249
  description: "\u5BF9\u6587\u4EF6\u505A\u5C40\u90E8\u7CBE\u51C6\u4FEE\u6539\u3002\u2605 \u540C\u4E00\u6587\u4EF6\u591A\u5904\u8981\u6539\u65F6\u4E00\u6B21\u4F20 edits \u6570\u7EC4\u5168\u90E8\u5B8C\u6210\uFF08\u6309\u5E8F\u5E94\u7528\u3001\u6574\u4F53\u539F\u5B50\uFF1A\u4EFB\u4E00\u5904\u5931\u8D25\u5168\u90E8\u4E0D\u5199\u5165\uFF09\uFF0C\u52FF\u9010\u5904\u591A\u6B21\u8C03\u7528\u3002old_str \u987B\u4E0E\u6587\u4EF6\u539F\u6587\u9010\u5B57\u7B26\u4E00\u81F4\uFF1A\u53BB\u6389 read_file \u8FD4\u56DE\u7684\u300C<\u884C\u53F7>: \u300D\u524D\u7F00\uFF0C\u4FDD\u7559\u539F\u7F29\u8FDB\uFF08Tab/\u7A7A\u683C\uFF09\u4E0E\u884C\u5C3E\u7A7A\u767D\u3002\u9ED8\u8BA4\u987B\u5168\u6587\u552F\u4E00\uFF1Breplace_all=true \u66FF\u6362\u5168\u90E8\u5339\u914D\uFF08\u6279\u91CF\u91CD\u547D\u540D/\u7EDF\u4E00\u6539\u5199\uFF09\u3002",
5794
6250
  parameters: {
5795
6251
  type: "object",
@@ -5830,7 +6286,7 @@ ${patches}`;
5830
6286
  try {
5831
6287
  const editList = args.edits && args.edits.length > 0 ? args.edits : [{ old_str: args.old_str ?? "", new_str: args.new_str ?? "", replace_all: args.replace_all }];
5832
6288
  if (editList.length === 0 || editList.some((e) => typeof e.old_str !== "string" || e.old_str.length === 0)) {
5833
- return `\u274C [\u53C2\u6570\u7F3A\u5931]\uFF1A\u8BF7\u4F20\u5165 edits \u6570\u7EC4\uFF08\u6BCF\u6761\u542B old_str/new_str\uFF09\u4E00\u6B21\u5B8C\u6210\u591A\u5904\u4FEE\u6539\uFF0C\u6216\u9876\u5C42 old_str + new_str \u4FEE\u6539\u5355\u5904\u3002old_str \u4E0D\u80FD\u4E3A\u7A7A\u3002`;
6289
+ return toolFailure(`[\u53C2\u6570\u7F3A\u5931]\uFF1A\u8BF7\u4F20\u5165 edits \u6570\u7EC4\uFF08\u6BCF\u6761\u542B old_str/new_str\uFF09\u4E00\u6B21\u5B8C\u6210\u591A\u5904\u4FEE\u6539\uFF0C\u6216\u9876\u5C42 old_str + new_str \u4FEE\u6539\u5355\u5904\u3002old_str \u4E0D\u80FD\u4E3A\u7A7A\u3002`);
5834
6290
  }
5835
6291
  const absPath = resolveSafePath(args.path);
5836
6292
  const rawContent = await fs13.readFile(absPath, "utf-8");
@@ -5840,7 +6296,7 @@ ${patches}`;
5840
6296
  for (let i = 0; i < editList.length; i++) {
5841
6297
  const r = applyOneEditToContent(content, editList[i].old_str, editList[i].new_str ?? "", !!editList[i].replace_all);
5842
6298
  if (!r.ok) {
5843
- return editList.length === 1 ? r.error : `\u274C [\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1Aedits \u7B2C ${i + 1}/${editList.length} \u6761\u672A\u547D\u4E2D\uFF0C\u5DF2\u6574\u4F53\u653E\u5F03\uFF08\u6587\u4EF6\u672A\u505A\u4EFB\u4F55\u6539\u52A8\uFF0C\u53EF\u4FEE\u6B63\u8BE5\u6761\u540E\u6574\u7EC4\u91CD\u8BD5\uFF09\u3002${r.error.replace(/^❌ \[代码修补失败\]:/, "")}`;
6299
+ return editList.length === 1 ? r.error : toolFailure(`[\u4EE3\u7801\u4FEE\u8865\u5931\u8D25]\uFF1Aedits \u7B2C ${i + 1}/${editList.length} \u6761\u672A\u547D\u4E2D\uFF0C\u5DF2\u6574\u4F53\u653E\u5F03\uFF08\u6587\u4EF6\u672A\u505A\u4EFB\u4F55\u6539\u52A8\uFF0C\u53EF\u4FEE\u6B63\u8BE5\u6761\u540E\u6574\u7EC4\u91CD\u8BD5\uFF09\u3002${r.error.content.replace(/^❌ \[代码修补失败\]:/, "")}`);
5844
6300
  }
5845
6301
  content = r.content;
5846
6302
  summaries.push(editList.length === 1 ? r.summary : ` ${i + 1}. ${r.summary}`);
@@ -5850,7 +6306,7 @@ ${patches}`;
5850
6306
  return editList.length === 1 ? `\u2705 [\u4EE3\u7801\u4FEE\u8865\u6210\u529F]\uFF1A\u6587\u4EF6 [${args.path}] ${summaries[0]}` : `\u2705 [\u4EE3\u7801\u4FEE\u8865\u6210\u529F]\uFF1A\u6587\u4EF6 [${args.path}] \u5DF2\u6309\u5E8F\u5B8C\u6210 ${editList.length} \u5904\u4FEE\u6539\uFF08\u5355\u6B21\u539F\u5B50\u5199\u5165\uFF09\uFF1A
5851
6307
  ${summaries.join("\n")}`;
5852
6308
  } catch (error) {
5853
- return `\u64CD\u4F5C\u5931\u8D25: ${error.message}`;
6309
+ return toolFailure(`\u64CD\u4F5C\u5931\u8D25: ${error.message}`);
5854
6310
  }
5855
6311
  }
5856
6312
  }
@@ -5859,6 +6315,9 @@ ${summaries.join("\n")}`;
5859
6315
  type: "function",
5860
6316
  function: {
5861
6317
  name: "create_file",
6318
+ triggersUndo: "create",
6319
+ autoApproval: "file",
6320
+ primaryArg: "path",
5862
6321
  description: "\u5728\u5DE5\u4F5C\u533A\u5185\u521B\u5EFA\u4E00\u4E2A\u5168\u65B0\u7684\u6587\u4EF6\uFF0C\u5E76\u5199\u5165\u521D\u59CB\u5185\u5BB9\u3002\u5982\u679C\u6587\u4EF6\u5DF2\u5B58\u5728\uFF0C\u672C\u5DE5\u5177\u4F1A\u62D2\u7EDD\u6267\u884C\u4EE5\u9632\u6B62\u6E90\u7801\u88AB\u5168\u91CF\u8BEF\u8986\u76D6\u3002\u8BF7\u76F4\u63A5\u5199\u5165\u7528\u6237\u6307\u5B9A\u7684\u6700\u7EC8\u8DEF\u5F84\uFF0C\u52FF\u81EA\u884C\u53D1\u660E\u6682\u5B58/\u4E34\u65F6\u76EE\u5F55\uFF08\u5982 .dsc_tmp\uFF09\u2014\u2014\u4EA7\u7269\u4F1A\u771F\u5B9E\u843D\u76D8\u4E14\u5BF9\u7528\u6237\u53EF\u89C1\u3002",
5863
6322
  parameters: {
5864
6323
  type: "object",
@@ -5877,7 +6336,7 @@ ${summaries.join("\n")}`;
5877
6336
  const absPath = resolveSafePath(args.path);
5878
6337
  try {
5879
6338
  await fs13.access(absPath);
5880
- return `\u274C [\u521B\u5EFA\u5931\u8D25]\uFF1A\u6587\u4EF6 [${args.path}] \u5DF2\u5B58\u5728\u3002create_file \u4EC5\u7528\u4E8E\u65B0\u5EFA\u6587\u4EF6\uFF1B\u5982\u9700\u4FEE\u6539\u8BF7\u7528 edit_file\uFF0C\u5982\u9700\u8986\u76D6\u8BF7\u7528 write_file\u3002`;
6339
+ return toolFailure(`[\u521B\u5EFA\u5931\u8D25]\uFF1A\u6587\u4EF6 [${args.path}] \u5DF2\u5B58\u5728\u3002create_file \u4EC5\u7528\u4E8E\u65B0\u5EFA\u6587\u4EF6\uFF1B\u5982\u9700\u4FEE\u6539\u8BF7\u7528 edit_file\uFF0C\u5982\u9700\u8986\u76D6\u8BF7\u7528 write_file\u3002`);
5881
6340
  } catch (e) {
5882
6341
  if (e?.code !== "ENOENT") throw e;
5883
6342
  }
@@ -5889,7 +6348,7 @@ ${summaries.join("\n")}`;
5889
6348
  await fs13.rename(tmpPath, absPath);
5890
6349
  return `\u2705 [\u521B\u5EFA\u6210\u529F]\uFF1A\u65B0\u6587\u4EF6 [${args.path}] \u5DF2\u521B\u5EFA\uFF0C\u5199\u5165 ${content.length} \u5B57\u7B26\u3002`;
5891
6350
  } catch (error) {
5892
- return `\u64CD\u4F5C\u5931\u8D25: ${error.message}`;
6351
+ return toolFailure(`\u64CD\u4F5C\u5931\u8D25: ${error.message}`);
5893
6352
  } finally {
5894
6353
  if (tmpPath) {
5895
6354
  try {
@@ -5905,6 +6364,9 @@ ${summaries.join("\n")}`;
5905
6364
  type: "function",
5906
6365
  function: {
5907
6366
  name: "delete_path",
6367
+ triggersUndo: "delete",
6368
+ autoApproval: "file",
6369
+ primaryArg: "path",
5908
6370
  description: "\u4ECE\u672C\u5730\u78C1\u76D8\u5185\u6C38\u4E45\u5220\u9664\u4E00\u4E2A\u6307\u5B9A\u7684\u6587\u4EF6\u6216\u8005\u4E00\u6574\u4E2A\u6587\u4EF6\u5939\u76EE\u5F55\u3002\u5982\u679C\u662F\u76EE\u5F55\uFF0C\u5DE5\u5177\u4F1A\u81EA\u52A8\u6267\u884C\u6DF1\u5EA6\u9012\u5F52\u5F3A\u884C\u9500\u6BC1\u3002\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\uFF0C\u8BF7\u4E07\u5206\u5C0F\u5FC3\u3002",
5909
6371
  parameters: {
5910
6372
  type: "object",
@@ -5940,18 +6402,18 @@ ${summaries.join("\n")}`;
5940
6402
  try {
5941
6403
  const cleanPath = (args.path || "").trim();
5942
6404
  if (!cleanPath || cleanPath === "." || cleanPath === "./" || cleanPath === "/") {
5943
- return `\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u7981\u6B62\u901A\u8FC7\u672C\u5DE5\u5177\u76F4\u63A5\u6467\u6BC1\u9879\u76EE\u6839\u76EE\u5F55\u6216\u4F20\u5165\u7A7A\u8DEF\u5F84\uFF01`;
6405
+ return toolFailure(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u7981\u6B62\u901A\u8FC7\u672C\u5DE5\u5177\u76F4\u63A5\u6467\u6BC1\u9879\u76EE\u6839\u76EE\u5F55\u6216\u4F20\u5165\u7A7A\u8DEF\u5F84\uFF01`);
5944
6406
  }
5945
6407
  const absPath = resolveSafePath(cleanPath);
5946
6408
  const rel = path17.relative(getActiveWorkspaceRoot(), absPath);
5947
6409
  if (!rel || rel === "" || rel === "src" || rel.startsWith("..") || path17.isAbsolute(rel)) {
5948
- return `\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u62D2\u7EDD\u9500\u6BC1\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55 / src \u6E90\u7801\u6839 / \u8D8A\u754C\u8DEF\u5F84 [${cleanPath}]\uFF01`;
6410
+ return toolFailure(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u62D2\u7EDD\u9500\u6BC1\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55 / src \u6E90\u7801\u6839 / \u8D8A\u754C\u8DEF\u5F84 [${cleanPath}]\uFF01`);
5949
6411
  }
5950
6412
  let stat;
5951
6413
  try {
5952
6414
  stat = await fs13.stat(absPath);
5953
6415
  } catch {
5954
- return `\u274C [\u9500\u6BC1\u5931\u8D25]\uFF1A\u5728\u5DE5\u4F5C\u533A\u5185\u672A\u627E\u5230\u6307\u5B9A\u7684\u8DEF\u5F84 [${cleanPath}]\u3002`;
6416
+ return toolFailure(`[\u9500\u6BC1\u5931\u8D25]\uFF1A\u5728\u5DE5\u4F5C\u533A\u5185\u672A\u627E\u5230\u6307\u5B9A\u7684\u8DEF\u5F84 [${cleanPath}]\u3002`);
5955
6417
  }
5956
6418
  const isDirectory = stat.isDirectory();
5957
6419
  assertWithinWorkspace(absPath);
@@ -5963,7 +6425,7 @@ ${summaries.join("\n")}`;
5963
6425
  const targetTypeLabel = isDirectory ? "\u4E00\u6574\u4E2A\u6587\u4EF6\u5939\u76EE\u5F55" : "\u7EAF\u7269\u7406\u6587\u4EF6";
5964
6426
  return `\u2705 [\u8DEF\u5F84\u9500\u6BC1\u6210\u529F]\uFF1A\u5DF2\u6210\u529F\u6C38\u4E45\u9500\u6BC1${targetTypeLabel} [${cleanPath}]\u3002`;
5965
6427
  } catch (error) {
5966
- return `\u64CD\u4F5C\u5931\u8D25: ${error.message}`;
6428
+ return toolFailure(`\u64CD\u4F5C\u5931\u8D25: ${error.message}`);
5967
6429
  }
5968
6430
  }
5969
6431
  }
@@ -5972,6 +6434,9 @@ ${summaries.join("\n")}`;
5972
6434
  type: "function",
5973
6435
  function: {
5974
6436
  name: "write_file",
6437
+ triggersUndo: "overwrite",
6438
+ autoApproval: "file",
6439
+ primaryArg: "path",
5975
6440
  description: "\u5C06\u5B8C\u6574\u5185\u5BB9\u5168\u91CF\u5199\u5165\u6307\u5B9A\u6587\u4EF6\uFF08\u8986\u76D6\uFF09\u3002\u6587\u4EF6\u4E0D\u5B58\u5728\u5219\u65B0\u5EFA\uFF08\u542B\u7236\u76EE\u5F55\uFF09\uFF1B\u5DF2\u5B58\u5728\u5219\u6574\u4F53\u8986\u76D6\u3002\u2605 \u4EC5\u7528\u4E8E\u3010\u65B0\u5EFA\u6587\u4EF6\u3011\u6216\u3010\u5F7B\u5E95\u91CD\u5199\u6574\u4E2A\u6587\u4EF6\u3011\uFF1B\u4FEE\u6539\u65E2\u6709\u6587\u4EF6\u4E00\u5F8B\u6539\u7528 edit_file\uFF08\u5C40\u90E8\u7CBE\u51C6\u66FF\u6362\u3001\u6700\u5C0F diff\uFF09\u3002",
5976
6441
  parameters: {
5977
6442
  type: "object",
@@ -6001,7 +6466,7 @@ ${summaries.join("\n")}`;
6001
6466
  } catch {
6002
6467
  }
6003
6468
  }
6004
- return `\u64CD\u4F5C\u5931\u8D25: ${error.message}`;
6469
+ return toolFailure(`\u64CD\u4F5C\u5931\u8D25: ${error.message}`);
6005
6470
  }
6006
6471
  }
6007
6472
  }
@@ -6010,7 +6475,8 @@ ${summaries.join("\n")}`;
6010
6475
  type: "function",
6011
6476
  function: {
6012
6477
  name: "view_symbol_outline",
6013
- description: "\u901A\u8FC7\u62BD\u8C61\u8BED\u6CD5\u6811(AST)\u5FEB\u901F\u63D0\u53D6\u6307\u5B9A TS/JS \u6E90\u6587\u4EF6\u4E2D\u7684\u7B26\u53F7\u5927\u7EB2\uFF08\u7C7B\u3001\u63A5\u53E3\u3001\u51FD\u6570\u540D\u3001\u5BFC\u51FA\u9879\u3001\u5165\u53C2\u7B7E\u540D\u7B49\uFF09\u3002\u9002\u5408\u5728\u4E0D\u8BFB\u53D6\u51E0\u5343\u884C\u5177\u4F53\u4EE3\u7801\u7684\u524D\u63D0\u4E0B\uFF0C\u5B8F\u89C2\u4E86\u89E3\u6587\u4EF6\u67B6\u6784\u3002\u4EC5\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B\u5176\u4ED6\u7C7B\u578B\u6587\u4EF6\uFF08.java/.vue/.py \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\uFF0C\u8BF7\u6539\u7528 read_file\u3002",
6478
+ planAllowed: true,
6479
+ description: "\u901A\u8FC7\u62BD\u8C61\u8BED\u6CD5\u6811(AST)\u5FEB\u901F\u63D0\u53D6\u6307\u5B9A TS/JS \u6E90\u6587\u4EF6\u4E2D\u7684\u7B26\u53F7\u5927\u7EB2\uFF08\u7C7B\u3001\u63A5\u53E3\u3001\u51FD\u6570\u540D\u3001\u5BFC\u51FA\u9879\u3001\u5165\u53C2\u7B7E\u540D\u7B49\uFF09\u3002\u9002\u5408\u5728\u4E0D\u8BFB\u53D6\u51E0\u5343\u884C\u5177\u4F53\u4EE3\u7801\u7684\u524D\u63D0\u4E0B\uFF0C\u5B8F\u89C2\u4E86\u89E3\u6587\u4EF6\u67B6\u6784\u3002\u4EC5\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B\u5176\u4ED6\u7C7B\u578B\u6587\u4EF6\uFF08.java/.vue/.py \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\uFF0C\u8BF7\u6539\u7528 read_file\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
6014
6480
  parameters: {
6015
6481
  type: "object",
6016
6482
  properties: {
@@ -6084,7 +6550,7 @@ ${summaries.join("\n")}`;
6084
6550
  return `[File Symbol Outline: ${args.path}]
6085
6551
  ` + outlineLines.join("\n");
6086
6552
  } catch (error) {
6087
- return `\u7B26\u53F7\u5927\u7EB2\u5206\u6790\u5931\u8D25 [${args.path}]: ${error.message}`;
6553
+ return toolFailure(`\u7B26\u53F7\u5927\u7EB2\u5206\u6790\u5931\u8D25 [${args.path}]: ${error.message}`);
6088
6554
  }
6089
6555
  }
6090
6556
  }
@@ -6097,6 +6563,11 @@ ${summaries.join("\n")}`;
6097
6563
  // 注:move 涉及双路径(source+destination),超出当前 Undo 单路径 schema,暂不纳入写前备份(不可回退),
6098
6564
  // 已在描述中明示;后续若扩展 UndoRecord 双路径字段可再接入。
6099
6565
  name: "move_file",
6566
+ // ★ #8a 策略声明:双路径都受保护路径硬规则检查(P0-3 洞的声明化收口;特判已退役)。
6567
+ // 刻意不声明 triggersUndo:move 语义可手动移回,维持「不在 Undo 回退范围」现状。
6568
+ primaryArg: "src",
6569
+ autoApproval: "file",
6570
+ pathArgs: ["source", "destination"],
6100
6571
  description: "\u79FB\u52A8\u6216\u91CD\u547D\u540D\u6587\u4EF6/\u76EE\u5F55\uFF08\u540C\u5377\u539F\u5B50\u64CD\u4F5C\uFF09\u3002\u6E90\u4E0D\u5B58\u5728\u5219\u5931\u8D25\uFF1B\u76EE\u6807\u5DF2\u5B58\u5728\u5219\u62D2\u7EDD\uFF08\u9632\u8BEF\u8986\u76D6\uFF0C\u5982\u9700\u8986\u76D6\u8BF7\u5148 delete_path \u518D move_file\uFF09\u3002\u81EA\u52A8\u521B\u5EFA\u76EE\u6807\u7236\u76EE\u5F55\u3002\u26A0\uFE0F \u672C\u64CD\u4F5C\u6682\u4E0D\u5728 Undo \u56DE\u9000\u8303\u56F4\u5185\uFF08\u4E0D\u53EF\u64A4\u9500\uFF09\u3002",
6101
6572
  parameters: {
6102
6573
  type: "object",
@@ -6116,11 +6587,11 @@ ${summaries.join("\n")}`;
6116
6587
  try {
6117
6588
  await fs13.access(srcAbs);
6118
6589
  } catch {
6119
- return `\u274C [\u79FB\u52A8\u5931\u8D25]\uFF1A\u6E90\u8DEF\u5F84 [${args.source}] \u4E0D\u5B58\u5728\u3002`;
6590
+ return toolFailure(`[\u79FB\u52A8\u5931\u8D25]\uFF1A\u6E90\u8DEF\u5F84 [${args.source}] \u4E0D\u5B58\u5728\u3002`);
6120
6591
  }
6121
6592
  try {
6122
6593
  await fs13.access(dstAbs);
6123
- return `\u274C [\u79FB\u52A8\u5931\u8D25]\uFF1A\u76EE\u6807 [${args.destination}] \u5DF2\u5B58\u5728\u3002\u5982\u9700\u8986\u76D6\u8BF7\u5148 delete_path \u518D move_file\u3002`;
6594
+ return toolFailure(`[\u79FB\u52A8\u5931\u8D25]\uFF1A\u76EE\u6807 [${args.destination}] \u5DF2\u5B58\u5728\u3002\u5982\u9700\u8986\u76D6\u8BF7\u5148 delete_path \u518D move_file\u3002`);
6124
6595
  } catch {
6125
6596
  }
6126
6597
  assertWithinWorkspace(srcAbs);
@@ -6129,7 +6600,51 @@ ${summaries.join("\n")}`;
6129
6600
  await fs13.rename(srcAbs, dstAbs);
6130
6601
  return `\u2705 [\u79FB\u52A8\u6210\u529F]\uFF1A[${args.source}] \u2192 [${args.destination}]\u3002`;
6131
6602
  } catch (error) {
6132
- return `\u64CD\u4F5C\u5931\u8D25: ${error.message}`;
6603
+ return toolFailure(`\u64CD\u4F5C\u5931\u8D25: ${error.message}`);
6604
+ }
6605
+ }
6606
+ }
6607
+ },
6608
+ {
6609
+ type: "function",
6610
+ function: {
6611
+ name: "read_image",
6612
+ // ★ #8a 策略声明:计划模式可用(只读);权限规则作用域主参数
6613
+ planAllowed: true,
6614
+ primaryArg: "path",
6615
+ description: "\u8BFB\u53D6\u6307\u5B9A\u3010\u56FE\u7247\u3011\u6587\u4EF6\u5E76\u628A\u56FE\u50CF\u5185\u5BB9\u63D0\u4F9B\u7ED9\u6A21\u578B\u505A\u89C6\u89C9\u5206\u6790\uFF08png/jpg/jpeg/gif/webp/bmp\uFF0C\u22648MB\uFF09\u3002\u9002\u7528\uFF1A\u622A\u56FE\u8C03\u8BD5\u3001UI \u5BF9\u7167\u8FD8\u539F\u3001\u67E5\u770B\u56FE\u7247\u7D20\u6750\u3002\u56FE\u50CF\u4EE5\u89C6\u89C9\u9644\u4EF6\u6CE8\u5165\u5BF9\u8BDD\uFF08\u968F\u540E\u7684\u4E00\u6761\u6D88\u606F\uFF09\uFF0C\u4E0D\u8BA1\u5165\u672C\u5DE5\u5177\u7684\u6587\u672C\u8F93\u51FA\uFF1B\u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u89C6\u89C9\u65F6\u4F1A\u660E\u786E\u63D0\u793A\u3002\u6587\u672C\u6587\u4EF6\u8BF7\u7528 read_file\u3002",
6616
+ parameters: {
6617
+ type: "object",
6618
+ properties: {
6619
+ path: { type: "string", description: "\u56FE\u7247\u8DEF\u5F84\uFF1A\u76F8\u5BF9\u6D3B\u52A8\u5DE5\u4F5C\u533A\u6839\uFF08\u652F\u6301 ../ \u8DE8\u5144\u5F1F\u9879\u76EE\uFF09\u3001\u6216\u7EDD\u5BF9\u8DEF\u5F84" }
6620
+ },
6621
+ required: ["path"]
6622
+ },
6623
+ safetyLevel: "safe" /* SAFE */,
6624
+ isSync: true,
6625
+ async execute(args) {
6626
+ try {
6627
+ const absPath = resolveReadablePath(args.path);
6628
+ const readBlock = await assertReadable(absPath, args.path);
6629
+ if (readBlock) return readBlock;
6630
+ const mime = IMAGE_MIME_BY_EXT[path17.extname(absPath).toLowerCase()];
6631
+ if (!mime) return toolFailure(`[read_image] \u4E0D\u652F\u6301\u7684\u56FE\u7247\u683C\u5F0F [${args.path}]\uFF1A\u4EC5\u652F\u6301 png/jpg/jpeg/gif/webp/bmp\u3002\u6587\u672C\u6587\u4EF6\u8BF7\u7528 read_file\u3002`);
6632
+ const st = await fs13.stat(absPath).catch(() => null);
6633
+ if (!st || !st.isFile()) return toolFailure(`[read_image] \u56FE\u7247\u4E0D\u5B58\u5728\u6216\u4E0D\u662F\u6587\u4EF6\uFF1A${args.path}`);
6634
+ if (st.size > MAX_IMAGE_BYTES) {
6635
+ return toolFailure(`[read_image] \u56FE\u7247 ${(st.size / 1024 / 1024).toFixed(1)}MB \u8D85\u8FC7 ${Math.round(MAX_IMAGE_BYTES / 1024 / 1024)}MB \u4E0A\u9650\uFF1A\u8BF7\u538B\u7F29\u540E\u91CD\u8BD5\uFF0C\u6216\u88C1\u526A\u5230\u9700\u8981\u7684\u533A\u57DF\u3002`);
6636
+ }
6637
+ if (!isVisionEnabled()) {
6638
+ return toolFailure(`[read_image] \u5F53\u524D\u6A21\u578B\u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF08\u89C6\u89C9\u80FD\u529B\u6309\u7AEF\u70B9\u5B9E\u6D4B\u81EA\u52A8\u5224\u5B9A\uFF1B\u53EF\u5207\u6362 vision \u6A21\u578B\u6216\u8BBE DEEP_SEEK_VISION=1 \u5F3A\u5F00\u540E\u91CD\u8BD5\uFF09\u3002\u56FE\u7247\u672A\u8BFB\u53D6\uFF1A${args.path}`);
6639
+ }
6640
+ const buf = await fs13.readFile(absPath);
6641
+ return {
6642
+ content: `\u{1F5BC} [read_image] \u5DF2\u8BFB\u53D6\u56FE\u7247 ${args.path}\uFF08${mime}\uFF0C${st.size} \u5B57\u8282\uFF09\u3002\u56FE\u50CF\u5185\u5BB9\u89C1\u968F\u540E\u7684\u9644\u4EF6\u6D88\u606F\uFF0C\u8BF7\u76F4\u63A5\u5BF9\u56FE\u7247\u8FDB\u884C\u5206\u6790/\u5BF9\u7167\u3002`,
6643
+ status: "success",
6644
+ images: [{ name: path17.basename(absPath), mime, base64: buf.toString("base64") }]
6645
+ };
6646
+ } catch (error) {
6647
+ return toolFailure(`\u8BFB\u53D6\u56FE\u7247\u5931\u8D25 [${args.path}]: ${error.message}`);
6133
6648
  }
6134
6649
  }
6135
6650
  }
@@ -6158,7 +6673,9 @@ var init_search = __esm({
6158
6673
  type: "function",
6159
6674
  function: {
6160
6675
  name: "search_grep",
6161
- description: "\u5728\u5DE5\u4F5C\u533A\u6587\u4EF6\u4E2D\u68C0\u7D22\u5339\u914D\u7684\u4EE3\u7801\u884C\uFF0C\u6BCF\u4E2A\u547D\u4E2D\u9ED8\u8BA4\u5E26\u524D\u540E\u5404 2 \u884C\u4E0A\u4E0B\u6587\uFF08\u53EF\u76F4\u63A5\u5224\u65AD\u7528\u6CD5\uFF1B\u6784\u9020 edit_file \u7684 old_str \u524D\u53EF\u7528 context \u8C03\u5927\u4E00\u6B21\u62FF\u8DB3\uFF09\u3002\u9ED8\u8BA4\u5B57\u9762\u91CF\u5339\u914D\uFF0C\u6B63\u5219\u4F20 is_regex=true\u3002\u2605 \u591A\u5173\u952E\u8BCD\u7528 is_regex=true \u4EE5 | \u5408\u5E76\uFF08\u5982 'uploadVisible|uploadTip'\uFF09\u4E00\u6B21\u67E5\u5B8C\uFF0C\u52FF\u9010\u8BCD\u591A\u6B21\u8C03\u7528\u3002path \u53EF\u9650\u5B9A\u76EE\u5F55\u6216\u6587\u4EF6\uFF08\u5355\u5927\u6587\u4EF6\u591A\u5904\u5B9A\u4F4D\u4F20\u6587\u4EF6\u8DEF\u5F84\u6700\u9AD8\u6548\uFF09\u3002\u591A\u6839\u5DE5\u4F5C\u533A\uFF1Apath \u53EF\u4F20\u9879\u76EE\u76EE\u5F55\u540D\uFF08\u5982 'frontend'\uFF09\u3001\u7EDD\u5BF9\u8DEF\u5F84\u6216 ../\u5144\u5F1F\u76EE\u5F55\u3002\u2605 \u7528\u6237\u6307\u660E\u4E86\u9879\u76EE\u65F6\u52A1\u5FC5\u628A\u8BE5\u9879\u76EE\u4F5C\u4E3A path \u4F20\u5165\u2014\u2014\u7F3A\u7701\u53EA\u641C\u6D3B\u52A8\u6839\uFF0C\u5E38\u4E0E\u610F\u56FE\u4E0D\u7B26\u3002",
6676
+ planAllowed: true,
6677
+ primaryArg: "query",
6678
+ description: "\u5728\u5DE5\u4F5C\u533A\u6587\u4EF6\u4E2D\u68C0\u7D22\u5339\u914D\u7684\u4EE3\u7801\u884C\uFF0C\u6BCF\u4E2A\u547D\u4E2D\u9ED8\u8BA4\u5E26\u524D\u540E\u5404 2 \u884C\u4E0A\u4E0B\u6587\uFF08\u53EF\u76F4\u63A5\u5224\u65AD\u7528\u6CD5\uFF1B\u6784\u9020 edit_file \u7684 old_str \u524D\u53EF\u7528 context \u8C03\u5927\u4E00\u6B21\u62FF\u8DB3\uFF09\u3002\u9ED8\u8BA4\u5B57\u9762\u91CF\u5339\u914D\uFF0C\u6B63\u5219\u4F20 is_regex=true\u3002\u2605 \u591A\u5173\u952E\u8BCD\u7528 is_regex=true \u4EE5 | \u5408\u5E76\uFF08\u5982 'uploadVisible|uploadTip'\uFF09\u4E00\u6B21\u67E5\u5B8C\uFF0C\u52FF\u9010\u8BCD\u591A\u6B21\u8C03\u7528\u3002path \u53EF\u9650\u5B9A\u76EE\u5F55\u6216\u6587\u4EF6\uFF08\u5355\u5927\u6587\u4EF6\u591A\u5904\u5B9A\u4F4D\u4F20\u6587\u4EF6\u8DEF\u5F84\u6700\u9AD8\u6548\uFF09\u3002\u591A\u6839\u5DE5\u4F5C\u533A\uFF1Apath \u53EF\u4F20\u9879\u76EE\u76EE\u5F55\u540D\uFF08\u5982 'frontend'\uFF09\u3001\u7EDD\u5BF9\u8DEF\u5F84\u6216 ../\u5144\u5F1F\u76EE\u5F55\u3002\u2605 \u7528\u6237\u6307\u660E\u4E86\u9879\u76EE\u65F6\u52A1\u5FC5\u628A\u8BE5\u9879\u76EE\u4F5C\u4E3A path \u4F20\u5165\u2014\u2014\u7F3A\u7701\u53EA\u641C\u6D3B\u52A8\u6839\uFF0C\u5E38\u4E0E\u610F\u56FE\u4E0D\u7B26\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
6162
6679
  parameters: {
6163
6680
  type: "object",
6164
6681
  properties: {
@@ -6178,7 +6695,7 @@ var init_search = __esm({
6178
6695
  async execute(args, ctx) {
6179
6696
  try {
6180
6697
  const cleanQuery = (args.query || "").trim();
6181
- if (!cleanQuery) return "\u274C [\u68C0\u7D22\u5931\u8D25]\uFF1A\u4F20\u5165\u7684\u68C0\u7D22\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\u3002";
6698
+ if (!cleanQuery) return toolFailure("[\u68C0\u7D22\u5931\u8D25]\uFF1A\u4F20\u5165\u7684\u68C0\u7D22\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\u3002");
6182
6699
  const pattern = args.is_regex ? cleanQuery : escapeRegExp(cleanQuery);
6183
6700
  const resolveSearchRoot = () => {
6184
6701
  if (!args.path) return ctx?.cwd ?? getActiveWorkspaceRoot();
@@ -6253,8 +6770,8 @@ var init_search = __esm({
6253
6770
  return resultLines.join("\n");
6254
6771
  } catch (error) {
6255
6772
  if (error.code === 1) return `\u672A\u627E\u5230\u4E0E "${args.query}" \u76F8\u5173\u7684\u4EFB\u4F55\u4EE3\u7801\u5339\u914D\u9879\u3002`;
6256
- if (error.killed || error.signal) return `\u23F3 [\u68C0\u7D22\u8D85\u65F6]\uFF1A30s \u5185\u672A\u5B8C\u6210\uFF08\u7591\u4F3C\u547D\u4E2D\u5DE8\u578B/\u5F02\u5E38\u6587\u4EF6\uFF09\u3002\u5EFA\u8BAE\u7F29\u5C0F\u5173\u952E\u8BCD\u6216\u9650\u5B9A\u76EE\u5F55\u540E\u91CD\u8BD5\u3002`;
6257
- return `\u68C0\u7D22\u5931\u8D25: ${error.message}`;
6773
+ if (error.killed || error.signal) return toolFailure(`\u23F3 [\u68C0\u7D22\u8D85\u65F6]\uFF1A30s \u5185\u672A\u5B8C\u6210\uFF08\u7591\u4F3C\u547D\u4E2D\u5DE8\u578B/\u5F02\u5E38\u6587\u4EF6\uFF09\u3002\u5EFA\u8BAE\u7F29\u5C0F\u5173\u952E\u8BCD\u6216\u9650\u5B9A\u76EE\u5F55\u540E\u91CD\u8BD5\u3002`);
6774
+ return toolFailure(`\u68C0\u7D22\u5931\u8D25: ${error.message}`);
6258
6775
  }
6259
6776
  }
6260
6777
  }
@@ -6263,6 +6780,7 @@ var init_search = __esm({
6263
6780
  type: "function",
6264
6781
  function: {
6265
6782
  name: "read_project_guide",
6783
+ planAllowed: true,
6266
6784
  description: "\u8BFB\u53D6\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u7684\u4E13\u5C5E AI \u884C\u4E3A\u6307\u5F15\u4E0E\u5F00\u53D1\u6307\u5357\uFF08\u4F9D\u6B21\u5C1D\u8BD5 CLAUDE.md / AGENTS.md / AGENT.md\uFF0C\u547D\u4E2D\u7B2C\u4E00\u4E2A\uFF09\u3002\u8BE5\u6307\u5357\u56FA\u5316\u4E86\u672C\u9879\u76EE\u7684\u6784\u5EFA\u547D\u4EE4\u3001\u8FD0\u884C\u6D4B\u8BD5\u89C4\u8303\u3001\u6838\u5FC3\u6280\u672F\u6808\u53CA\u4EE3\u7801\u98CE\u683C\u9650\u5236\u3002\u5728\u5BF9\u964C\u751F\u9879\u76EE\u5B9E\u65BD\u4EFB\u4F55\u6784\u5EFA\u6216\u6D4B\u8BD5\u547D\u4EE4\u524D\uFF0C\u5FC5\u987B\u4F18\u5148\u8BFB\u53D6\u6B64\u5DE5\u5177\u3002",
6267
6785
  parameters: {
6268
6786
  type: "object",
@@ -6328,6 +6846,8 @@ var init_command = __esm({
6328
6846
  type: "function",
6329
6847
  function: {
6330
6848
  name: "run_command",
6849
+ primaryArg: "command",
6850
+ autoApproval: "command",
6331
6851
  description: "\u5728\u5DE5\u4F5C\u533A\u6267\u884C shell \u547D\u4EE4\uFF08\u77ED\u5E73\u5FEB\u7684\u6D4B\u8BD5 / \u6784\u5EFA / git \u7B49\uFF09\uFF0C\u6D41\u5F0F\u8FD4\u56DE stdout/stderr \u5E76\u9644\u5E26\u9000\u51FA\u7801\u3002\u5C5E\u4E8E\u9AD8\u5371\u64CD\u4F5C\uFF0C\u6BCF\u6B21\u6267\u884C\u90FD\u9700\u8981\u7528\u6237\u5BA1\u6279\u3002\u9884\u8BA1\u8FD0\u884C\u6570\u5206\u949F\u4EE5\u4E0A\u7684\u957F\u4EFB\u52A1\uFF08\u5168\u91CF\u6D4B\u8BD5 / \u5927\u6784\u5EFA / \u5927\u4F53\u79EF\u5B89\u88C5\uFF09\u8BF7\u6539\u7528 run_in_background \u540E\u53F0\u6267\u884C\uFF1B\u82E5\u672C\u5DE5\u5177\u6267\u884C\u8FC7\u4E45\u4ECD\u672A\u7ED3\u675F\uFF0C\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u628A\u8FDB\u7A0B\u8F6C\u5165\u540E\u53F0\u5E76\u8FD4\u56DE task_id\uFF08\u7528 get_background_output \u67E5\u7ED3\u679C\u3001stop_background_task \u7EC8\u6B62\uFF09\u3002",
6332
6852
  parameters: {
6333
6853
  type: "object",
@@ -6384,7 +6904,7 @@ ${tail}`,
6384
6904
  let degradeReason = null;
6385
6905
  const isWin = process.platform === "win32";
6386
6906
  if (ctx?.abortSignal?.aborted) {
6387
- yield `\u274C [\u5DF2\u4E2D\u6B62]\uFF1A\u547D\u4EE4 [${args.command}] \u672A\u6267\u884C\uFF08\u7528\u6237\u5DF2\u4E2D\u65AD\uFF09\u3002`;
6907
+ yield toolFailure(`[\u5DF2\u4E2D\u6B62]\uFF1A\u547D\u4EE4 [${args.command}] \u672A\u6267\u884C\uFF08\u7528\u6237\u5DF2\u4E2D\u65AD\uFF09\u3002`).content;
6388
6908
  yield EXIT_SENTINEL(-1);
6389
6909
  return;
6390
6910
  }
@@ -6401,7 +6921,7 @@ ${tail}`,
6401
6921
  signal: ctx?.abortSignal
6402
6922
  });
6403
6923
  } catch (e) {
6404
- yield `\u274C [\u542F\u52A8\u5931\u8D25]\uFF1Aspawn \u629B\u51FA\u5F02\u5E38\uFF08signal \u5DF2\u4E2D\u6B62\u6216\u547D\u4EE4\u975E\u6CD5\uFF09: ${e?.message ?? e}`;
6924
+ yield toolFailure(`[\u542F\u52A8\u5931\u8D25]\uFF1Aspawn \u629B\u51FA\u5F02\u5E38\uFF08signal \u5DF2\u4E2D\u6B62\u6216\u547D\u4EE4\u975E\u6CD5\uFF09: ${e?.message ?? e}`).content;
6405
6925
  yield EXIT_SENTINEL(-1);
6406
6926
  return;
6407
6927
  }
@@ -6643,7 +7163,9 @@ var init_glob = __esm({
6643
7163
  type: "function",
6644
7164
  function: {
6645
7165
  name: "glob",
6646
- description: "\u6309\u6587\u4EF6\u540D glob \u6A21\u5F0F\u5FEB\u901F\u67E5\u627E\u6587\u4EF6\uFF08\u5982 '**/*.ts'\u3001'src/**/test*'\u3001'**/*.test.ts'\uFF09\u3002\u2605 \u652F\u6301\u82B1\u62EC\u53F7\u591A\u9009\uFF1A'**/*.{ts,tsx,vue}'\u3001'src/{api,components}/**' \u4E00\u6B21\u67E5\u591A\u7C7B\u578B/\u591A\u76EE\u5F55\uFF0C\u52FF\u5206\u591A\u6B21\u8C03\u7528\u3002\u4E0E search_grep\uFF08\u6309\u5185\u5BB9\u68C0\u7D22\uFF09\u4E92\u8865\u3002\u81EA\u52A8\u5957\u7528 .gitignore / \u901A\u7528\u9ED1\u540D\u5355\u8FC7\u6EE4\u3002",
7166
+ planAllowed: true,
7167
+ primaryArg: "pattern",
7168
+ description: "\u6309\u6587\u4EF6\u540D glob \u6A21\u5F0F\u5FEB\u901F\u67E5\u627E\u6587\u4EF6\uFF08\u5982 '**/*.ts'\u3001'src/**/test*'\u3001'**/*.test.ts'\uFF09\u3002\u2605 \u652F\u6301\u82B1\u62EC\u53F7\u591A\u9009\uFF1A'**/*.{ts,tsx,vue}'\u3001'src/{api,components}/**' \u4E00\u6B21\u67E5\u591A\u7C7B\u578B/\u591A\u76EE\u5F55\uFF0C\u52FF\u5206\u591A\u6B21\u8C03\u7528\u3002\u4E0E search_grep\uFF08\u6309\u5185\u5BB9\u68C0\u7D22\uFF09\u4E92\u8865\u3002\u81EA\u52A8\u5957\u7528 .gitignore / \u901A\u7528\u9ED1\u540D\u5355\u8FC7\u6EE4\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
6647
7169
  parameters: {
6648
7170
  type: "object",
6649
7171
  properties: {
@@ -6657,7 +7179,7 @@ var init_glob = __esm({
6657
7179
  async execute(args, ctx) {
6658
7180
  try {
6659
7181
  const cleanPattern = (args.pattern || "").trim();
6660
- if (!cleanPattern) return "\u274C [Glob\u5931\u8D25]\uFF1A\u4F20\u5165\u7684\u68C0\u7D22 pattern \u4E0D\u80FD\u4E3A\u7A7A\u3002";
7182
+ if (!cleanPattern) return toolFailure("[Glob\u5931\u8D25]\uFF1A\u4F20\u5165\u7684\u68C0\u7D22 pattern \u4E0D\u80FD\u4E3A\u7A7A\u3002");
6661
7183
  const activeRoot = ctx?.cwd ?? getActiveWorkspaceRoot();
6662
7184
  const root = args.path ? resolveReadablePath(args.path) : activeRoot;
6663
7185
  const re = globToRegex2(cleanPattern);
@@ -6701,7 +7223,7 @@ var init_glob = __esm({
6701
7223
  return `${prefix}[Glob: ${cleanPattern} | ${hits.length} \u4E2A\u5339\u914D]
6702
7224
  ` + hits.join("\n");
6703
7225
  } catch (error) {
6704
- return `glob \u68C0\u7D22\u5931\u8D25: ${error.message}`;
7226
+ return toolFailure(`glob \u68C0\u7D22\u5931\u8D25: ${error.message}`);
6705
7227
  }
6706
7228
  }
6707
7229
  }
@@ -6730,7 +7252,8 @@ var init_git = __esm({
6730
7252
  type: "function",
6731
7253
  function: {
6732
7254
  name: "get_git_diff",
6733
- description: "\u83B7\u53D6\u5F53\u524D\u5DE5\u4F5C\u533A\u4E2D\u6240\u6709\u76F8\u6BD4\u4E8E Git \u6682\u5B58\u533A/\u6700\u8FD1\u4E00\u6B21\u63D0\u4EA4\u7684\u672A\u63D0\u4EA4\u4EE3\u7801\u6539\u52A8\uFF08\u7EA2\u7EFF\u8272 Diff \u5DEE\u5F02\uFF09\u3002\u9002\u5408\u5728\u4FEE\u6539\u4EE3\u7801\u540E\u3001\u6216\u8FD0\u884C\u6D4B\u8BD5\u524D\uFF0C\u81EA\u4E3B\u8D70\u67E5\u4FEE\u6539\u662F\u5426\u7CBE\u51C6\u65E0\u4F4E\u7EA7\u9519\u8BEF\u3002",
7255
+ planAllowed: true,
7256
+ description: "\u83B7\u53D6\u5F53\u524D\u5DE5\u4F5C\u533A\u4E2D\u6240\u6709\u76F8\u6BD4\u4E8E Git \u6682\u5B58\u533A/\u6700\u8FD1\u4E00\u6B21\u63D0\u4EA4\u7684\u672A\u63D0\u4EA4\u4EE3\u7801\u6539\u52A8\uFF08\u7EA2\u7EFF\u8272 Diff \u5DEE\u5F02\uFF09\u3002\u9002\u5408\u5728\u4FEE\u6539\u4EE3\u7801\u540E\u3001\u6216\u8FD0\u884C\u6D4B\u8BD5\u524D\uFF0C\u81EA\u4E3B\u8D70\u67E5\u4FEE\u6539\u662F\u5426\u7CBE\u51C6\u65E0\u4F4E\u7EA7\u9519\u8BEF\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
6734
7257
  parameters: {
6735
7258
  type: "object",
6736
7259
  properties: {
@@ -6774,9 +7297,9 @@ var init_git = __esm({
6774
7297
  ${stdout}`;
6775
7298
  } catch (error) {
6776
7299
  if (isNotARepoError2(error)) {
6777
- return `\u274C [Diff \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u55C5\u63A2\u4EE3\u7801\u7248\u672C\u6539\u52A8\u5DEE\u5F02\u3002`;
7300
+ return toolFailure(`[Diff \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u55C5\u63A2\u4EE3\u7801\u7248\u672C\u6539\u52A8\u5DEE\u5F02\u3002`);
6778
7301
  }
6779
- return `\u8BFB\u53D6\u4EE3\u7801\u5DEE\u5F02\u5931\u8D25: ${error.message}`;
7302
+ return toolFailure(`\u8BFB\u53D6\u4EE3\u7801\u5DEE\u5F02\u5931\u8D25: ${error.message}`);
6780
7303
  }
6781
7304
  }
6782
7305
  }
@@ -6785,7 +7308,8 @@ ${stdout}`;
6785
7308
  type: "function",
6786
7309
  function: {
6787
7310
  name: "git_status",
6788
- description: "\u67E5\u770B\u5F53\u524D Git \u5DE5\u4F5C\u533A\u72B6\u6001\uFF08\u5F53\u524D\u5206\u652F\u3001\u4E0E\u8FDC\u7AEF\u7684\u9886\u5148/\u843D\u540E\u3001\u4EE5\u53CA\u6539\u52A8/\u6682\u5B58/\u672A\u8DDF\u8E2A\u6587\u4EF6\u7684\u7CBE\u7B80\u6E05\u5355\uFF09\u3002\u7EAF\u8BFB\uFF0C\u514D\u5BA1\u6279\uFF0C\u7528\u4E8E\u66FF\u4EE3\u8D70 run_command \u8DD1 git status \u7684\u7E41\u7410\u3002",
7311
+ planAllowed: true,
7312
+ description: "\u67E5\u770B\u5F53\u524D Git \u5DE5\u4F5C\u533A\u72B6\u6001\uFF08\u5F53\u524D\u5206\u652F\u3001\u4E0E\u8FDC\u7AEF\u7684\u9886\u5148/\u843D\u540E\u3001\u4EE5\u53CA\u6539\u52A8/\u6682\u5B58/\u672A\u8DDF\u8E2A\u6587\u4EF6\u7684\u7CBE\u7B80\u6E05\u5355\uFF09\u3002\u7EAF\u8BFB\uFF0C\u514D\u5BA1\u6279\uFF0C\u7528\u4E8E\u66FF\u4EE3\u8D70 run_command \u8DD1 git status \u7684\u7E41\u7410\u3002\u2605 \u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
6789
7313
  parameters: {
6790
7314
  type: "object",
6791
7315
  properties: {}
@@ -6802,9 +7326,9 @@ ${stdout}`;
6802
7326
  ${stdout.trim()}`;
6803
7327
  } catch (error) {
6804
7328
  if (isNotARepoError2(error)) {
6805
- return `\u274C [Git Status \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\u3002`;
7329
+ return toolFailure(`[Git Status \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\u3002`);
6806
7330
  }
6807
- return `\u8BFB\u53D6 Git \u72B6\u6001\u5931\u8D25: ${error.message}`;
7331
+ return toolFailure(`\u8BFB\u53D6 Git \u72B6\u6001\u5931\u8D25: ${error.message}`);
6808
7332
  }
6809
7333
  }
6810
7334
  }
@@ -6813,7 +7337,8 @@ ${stdout.trim()}`;
6813
7337
  type: "function",
6814
7338
  function: {
6815
7339
  name: "git_log",
6816
- description: "\u67E5\u770B\u5F53\u524D\u5206\u652F\u7684\u6700\u8FD1\u82E5\u5E72\u6761\u63D0\u4EA4\u5386\u53F2\uFF08\u77ED\u54C8\u5E0C + \u65E5\u671F + \u63D0\u4EA4\u8BF4\u660E\uFF09\u3002\u7EAF\u8BFB\uFF0C\u514D\u5BA1\u6279\u3002\u7528\u4E8E\u4E86\u89E3\u9879\u76EE\u6F14\u8FDB\u3001\u5B9A\u4F4D\u67D0\u6B21\u6539\u52A8\u6216\u786E\u8BA4\u63D0\u4EA4\u662F\u5426\u6210\u529F\u3002",
7340
+ planAllowed: true,
7341
+ description: "\u67E5\u770B\u5F53\u524D\u5206\u652F\u7684\u6700\u8FD1\u82E5\u5E72\u6761\u63D0\u4EA4\u5386\u53F2\uFF08\u77ED\u54C8\u5E0C + \u65E5\u671F + \u63D0\u4EA4\u8BF4\u660E\uFF09\u3002\u7EAF\u8BFB\uFF0C\u514D\u5BA1\u6279\u3002\u7528\u4E8E\u4E86\u89E3\u9879\u76EE\u6F14\u8FDB\u3001\u5B9A\u4F4D\u67D0\u6B21\u6539\u52A8\u6216\u786E\u8BA4\u63D0\u4EA4\u662F\u5426\u6210\u529F\u3002\u2605 \u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
6817
7342
  parameters: {
6818
7343
  type: "object",
6819
7344
  properties: {
@@ -6840,9 +7365,9 @@ ${stdout.trim()}`;
6840
7365
  ${stdout.trim()}`;
6841
7366
  } catch (error) {
6842
7367
  if (isNotARepoError2(error)) {
6843
- return `\u274C [Git Log \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\u3002`;
7368
+ return toolFailure(`[Git Log \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\u3002`);
6844
7369
  }
6845
- return `\u8BFB\u53D6 Git \u5386\u53F2\u5931\u8D25: ${error.message}`;
7370
+ return toolFailure(`\u8BFB\u53D6 Git \u5386\u53F2\u5931\u8D25: ${error.message}`);
6846
7371
  }
6847
7372
  }
6848
7373
  }
@@ -6851,6 +7376,8 @@ ${stdout.trim()}`;
6851
7376
  type: "function",
6852
7377
  function: {
6853
7378
  name: "git_commit",
7379
+ // ★ #8a:分类器仅 /auto 激进档纳入(不可 undo,显式 opt-in)
7380
+ autoApproval: "aggressive",
6854
7381
  description: "\u5C06\u5F53\u524D\u6682\u5B58\u533A\u7684\u6539\u52A8\u63D0\u4EA4\u4E3A\u4E00\u6B21\u65B0\u7684 Git commit\u3002\u53EF\u9009 stage_all=true \u4E00\u5E76\u6267\u884C git add -A\uFF08\u6682\u5B58\u5168\u90E8\u6539\u52A8\u542B\u65B0\u589E/\u5220\u9664\uFF09\u540E\u518D\u63D0\u4EA4\u3002\u6CE8\u610F\uFF1A\u4E0D\u4F1A\u81EA\u52A8 push\u3002",
6855
7382
  parameters: {
6856
7383
  type: "object",
@@ -6878,13 +7405,13 @@ ${stdout.trim()}`;
6878
7405
  ${summary}`;
6879
7406
  } catch (error) {
6880
7407
  if (isNotARepoError2(error)) {
6881
- return `\u274C [Git Commit \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\u3002`;
7408
+ return toolFailure(`[Git Commit \u5931\u8D25]\uFF1A\u5F53\u524D\u5DE5\u4F5C\u533A\u5C1A\u672A\u521D\u59CB\u5316 Git \u4ED3\u5E93\u3002`);
6882
7409
  }
6883
7410
  const text = `${error?.stderr || error?.message || ""}`;
6884
7411
  if (text.toLowerCase().includes("nothing to commit")) {
6885
7412
  return `\u2139\uFE0F [git_commit]\uFF1A\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u6539\u52A8\uFF08nothing to commit\uFF09\u3002\u5982\u9700\u63D0\u4EA4\u8BF7\u5148\u6682\u5B58\uFF08stage_all=true \u6216 git add\uFF09\u3002`;
6886
7413
  }
6887
- return `\u274C [git_commit \u5931\u8D25]\uFF1A${text.trim()}`;
7414
+ return toolFailure(`[git_commit \u5931\u8D25]\uFF1A${text.trim()}`);
6888
7415
  }
6889
7416
  }
6890
7417
  }
@@ -6925,7 +7452,7 @@ var init_inspect_dependencies = __esm({
6925
7452
  };
6926
7453
  return JSON.stringify(result, null, 2);
6927
7454
  } catch (error) {
6928
- return `\u274C \u8BFB\u53D6\u4F9D\u8D56\u6E05\u5355\u5931\u8D25\uFF0C\u672A\u627E\u5230 package.json \u6216\u6587\u4EF6\u683C\u5F0F\u635F\u574F: ${error.message}`;
7455
+ return toolFailure(`\u8BFB\u53D6\u4F9D\u8D56\u6E05\u5355\u5931\u8D25\uFF0C\u672A\u627E\u5230 package.json \u6216\u6587\u4EF6\u683C\u5F0F\u635F\u574F: ${error.message}`);
6929
7456
  }
6930
7457
  }
6931
7458
  }
@@ -7325,6 +7852,9 @@ var init_web = __esm({
7325
7852
  type: "function",
7326
7853
  function: {
7327
7854
  name: "web_fetch",
7855
+ planAllowed: true,
7856
+ primaryArg: "url",
7857
+ autoApproval: "aggressive",
7328
7858
  description: "\u6293\u53D6\u6307\u5B9A\u516C\u5F00 URL \u7684\u7F51\u9875\u5185\u5BB9\u5E76\u8F6C\u4E3A\u5E72\u51C0\u7684 Markdown \u6587\u672C\uFF0C\u7528\u4E8E\u67E5\u9605 API \u6587\u6863\u3001\u62A5\u9519\u8BF4\u660E\u3001\u5B98\u65B9 changelog \u7B49\u5916\u90E8\u8D44\u6599\u3002\u4EC5\u652F\u6301 http/https\uFF0C\u9ED8\u8BA4\u62E6\u622A\u5185\u7F51/\u56DE\u73AF/\u94FE\u8DEF\u672C\u5730\u5730\u5740\uFF08\u9632 SSRF\uFF09\uFF1B\u672C\u5730\u5F00\u53D1/\u81EA\u52A8\u5316\u6D4B\u8BD5\u9700\u6293 localhost \u670D\u52A1\u6216\u5185\u7F51\u9875\u9762\u65F6\uFF0C\u4F20 allow_private=true \u653E\u884C\uFF08\u4E91\u5143\u6570\u636E\u7AEF\u70B9\u4ECD\u62E6\uFF09\u3002",
7329
7859
  parameters: {
7330
7860
  type: "object",
@@ -7349,10 +7879,10 @@ var init_web = __esm({
7349
7879
  try {
7350
7880
  parsed = new URL(args.url);
7351
7881
  } catch {
7352
- return `\u274C [\u6293\u53D6\u5931\u8D25]\uFF1AURL \u683C\u5F0F\u4E0D\u5408\u6CD5\uFF1A${args.url}`;
7882
+ return toolFailure(`[\u6293\u53D6\u5931\u8D25]\uFF1AURL \u683C\u5F0F\u4E0D\u5408\u6CD5\uFF1A${args.url}`);
7353
7883
  }
7354
7884
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
7355
- return `\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u4EC5\u5141\u8BB8 http/https \u534F\u8BAE\uFF0C\u62D2\u7EDD ${parsed.protocol}`;
7885
+ return toolFailure(`[\u5B89\u5168\u7194\u65AD]\uFF1A\u4EC5\u5141\u8BB8 http/https \u534F\u8BAE\uFF0C\u62D2\u7EDD ${parsed.protocol}`);
7356
7886
  }
7357
7887
  const signals = [AbortSignal.timeout(FETCH_TIMEOUT_MS2)];
7358
7888
  if (ctx?.abortSignal) signals.push(ctx.abortSignal);
@@ -7362,7 +7892,7 @@ var init_web = __esm({
7362
7892
  "Accept": "text/html,application/xhtml+xml,text/plain,application/json,application/xml;q=0.9,*/*;q=0.8"
7363
7893
  }, signals, allowPrivate);
7364
7894
  if (!res.ok) {
7365
- return `\u274C [\u6293\u53D6\u5931\u8D25]\uFF1AHTTP ${res.status} ${res.statusText}\uFF08${parsed.href}\uFF09`;
7895
+ return toolFailure(`[\u6293\u53D6\u5931\u8D25]\uFF1AHTTP ${res.status} ${res.statusText}\uFF08${parsed.href}\uFF09`);
7366
7896
  }
7367
7897
  const contentType = res.headers.get("content-type") || "";
7368
7898
  if (!/(text|html|json|xml|plain|markdown)/i.test(contentType)) {
@@ -7391,12 +7921,12 @@ var init_web = __esm({
7391
7921
  ${body}${truncNote}`;
7392
7922
  } catch (error) {
7393
7923
  if (error?.name === "TimeoutError") {
7394
- return `\u274C [\u6293\u53D6\u8D85\u65F6]\uFF1A${FETCH_TIMEOUT_MS2 / 1e3}s \u5185\u672A\u54CD\u5E94\uFF1A${parsed.href}`;
7924
+ return toolFailure(`[\u6293\u53D6\u8D85\u65F6]\uFF1A${FETCH_TIMEOUT_MS2 / 1e3}s \u5185\u672A\u54CD\u5E94\uFF1A${parsed.href}`);
7395
7925
  }
7396
7926
  if (error?.name === "AbortError") {
7397
7927
  return `\u23F9\uFE0F [\u5DF2\u4E2D\u6B62]\uFF1A\u7528\u6237\u4E2D\u65AD\u4E86\u6293\u53D6\uFF1A${parsed.href}`;
7398
7928
  }
7399
- return `\u274C [\u6293\u53D6\u5931\u8D25]\uFF1A${error.message}`;
7929
+ return toolFailure(`[\u6293\u53D6\u5931\u8D25]\uFF1A${error.message}`);
7400
7930
  }
7401
7931
  }
7402
7932
  }
@@ -7405,6 +7935,9 @@ ${body}${truncNote}`;
7405
7935
  type: "function",
7406
7936
  function: {
7407
7937
  name: "web_search",
7938
+ planAllowed: true,
7939
+ primaryArg: "query",
7940
+ autoApproval: "aggressive",
7408
7941
  description: "\u8054\u7F51\u641C\u7D22\u5916\u90E8\u8D44\u6599\uFF08API \u6587\u6863\u3001\u62A5\u9519\u89E3\u6CD5\u3001\u5E93\u7528\u6CD5\u3001\u6700\u65B0\u4FE1\u606F\u7B49\uFF09\uFF0C\u8FD4\u56DE\u7ED3\u679C\u5217\u8868\uFF08\u6807\u9898/URL/\u7247\u6BB5\uFF09\u3002\u9ED8\u8BA4\u7528 Bing\uFF08\u514D\u6CE8\u518C\u3001\u4E2D\u56FD/\u5168\u7403\u7686\u53EF\u7528\uFF09\uFF1B\u914D\u7F6E\u4E86 TAVILY_API_KEY \u4F1A\u81EA\u52A8\u5347\u7EA7\u5230 Tavily\uFF08\u7ED3\u679C\u66F4\u5E72\u51C0\u3001\u5E26\u6458\u8981\uFF09\uFF1B\u53EF\u7528\u73AF\u5883\u53D8\u91CF SEARCH_PROVIDER=bing|ddg|tavily \u5F3A\u5236\u6307\u5B9A\u3002\u67E5\u8BE2\u964C\u751F\u5E93\u6216\u62A5\u9519\u65F6\u4F18\u5148\u7528\u672C\u5DE5\u5177\u66FF\u4EE3\u51ED\u8BB0\u5FC6\u4F5C\u7B54\u3002\u7ED3\u679C\u6309\u957F\u5EA6\u7194\u65AD\u3002",
7409
7942
  parameters: {
7410
7943
  type: "object",
@@ -7432,12 +7965,12 @@ ${body}${truncNote}`;
7432
7965
  return formatSearchResults(args.query, provider, resp);
7433
7966
  } catch (error) {
7434
7967
  if (error?.name === "TimeoutError") {
7435
- return `\u274C [web_search \u8D85\u65F6]\uFF1A${SEARCH_TIMEOUT_MS / 1e3}s \u5185\u672A\u54CD\u5E94\u3002`;
7968
+ return toolFailure(`[web_search \u8D85\u65F6]\uFF1A${SEARCH_TIMEOUT_MS / 1e3}s \u5185\u672A\u54CD\u5E94\u3002`);
7436
7969
  }
7437
7970
  if (error?.name === "AbortError") {
7438
7971
  return `\u23F9\uFE0F [\u5DF2\u4E2D\u6B62]\uFF1A\u7528\u6237\u4E2D\u65AD\u4E86\u641C\u7D22\u3002`;
7439
7972
  }
7440
- return `\u274C [web_search \u5931\u8D25 | ${provider}]\uFF1A${error.message}`;
7973
+ return toolFailure(`[web_search \u5931\u8D25 | ${provider}]\uFF1A${error.message}`);
7441
7974
  }
7442
7975
  }
7443
7976
  }
@@ -7495,17 +8028,17 @@ var init_http = __esm({
7495
8028
  async execute(args, ctx) {
7496
8029
  const method = (args.method || "GET").toUpperCase();
7497
8030
  if (!ALLOWED_METHODS.includes(method)) {
7498
- return `\u274C [http_request] \u4E0D\u652F\u6301\u7684\u65B9\u6CD5\uFF1A${args.method}\uFF08\u5141\u8BB8 ${ALLOWED_METHODS.join("/")})`;
8031
+ return toolFailure(`[http_request] \u4E0D\u652F\u6301\u7684\u65B9\u6CD5\uFF1A${args.method}\uFF08\u5141\u8BB8 ${ALLOWED_METHODS.join("/")})`);
7499
8032
  }
7500
8033
  const maxChars = args.max_length && args.max_length > 0 ? args.max_length : DEFAULT_MAX_CHARS2;
7501
8034
  let parsed;
7502
8035
  try {
7503
8036
  parsed = new URL(args.url);
7504
8037
  } catch {
7505
- return `\u274C [http_request] URL \u4E0D\u5408\u6CD5\uFF1A${args.url}`;
8038
+ return toolFailure(`[http_request] URL \u4E0D\u5408\u6CD5\uFF1A${args.url}`);
7506
8039
  }
7507
8040
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
7508
- return `\u274C [http_request] \u4EC5\u5141\u8BB8 http/https\uFF0C\u62D2\u7EDD ${parsed.protocol}`;
8041
+ return toolFailure(`[http_request] \u4EC5\u5141\u8BB8 http/https\uFF0C\u62D2\u7EDD ${parsed.protocol}`);
7509
8042
  }
7510
8043
  const headers = {
7511
8044
  "User-Agent": USER_AGENT2,
@@ -7544,12 +8077,12 @@ var init_http = __esm({
7544
8077
  ].join("\n");
7545
8078
  } catch (error) {
7546
8079
  if (error?.name === "TimeoutError") {
7547
- return `\u274C [http_request \u8D85\u65F6]\uFF1A${HTTP_TIMEOUT_MS / 1e3}s \u5185\u672A\u54CD\u5E94\uFF1A${parsed.href}`;
8080
+ return toolFailure(`[http_request \u8D85\u65F6]\uFF1A${HTTP_TIMEOUT_MS / 1e3}s \u5185\u672A\u54CD\u5E94\uFF1A${parsed.href}`);
7548
8081
  }
7549
8082
  if (error?.name === "AbortError") {
7550
8083
  return `\u23F9\uFE0F [http_request \u5DF2\u4E2D\u6B62]\uFF1A\u7528\u6237\u4E2D\u65AD\uFF1A${parsed.href}`;
7551
8084
  }
7552
- return `\u274C [http_request \u5931\u8D25 | ${method}]\uFF1A${error.message}`;
8085
+ return toolFailure(`[http_request \u5931\u8D25 | ${method}]\uFF1A${error.message}`);
7553
8086
  }
7554
8087
  }
7555
8088
  }
@@ -7595,7 +8128,7 @@ var init_typescript = __esm({
7595
8128
  try {
7596
8129
  const absPath = realpathNative(resolveReadablePath(displayPath));
7597
8130
  const block = await precheck(displayPath, absPath);
7598
- if (block) return { ok: false, text: block };
8131
+ if (block) return { ok: false, result: typeof block === "string" ? asToolFailure(block) : block };
7599
8132
  const { ls } = await getLanguageService(TS, absPath, { checkJs });
7600
8133
  const syntactic = ls.getSyntacticDiagnostics(absPath);
7601
8134
  const semantic = ls.getSemanticDiagnostics(absPath);
@@ -7637,7 +8170,7 @@ var init_typescript = __esm({
7637
8170
  return { ok: true, errors, warnings, text: `[Diagnostics: ${displayPath}] ${errors} error(s), ${warnings} warning(s)
7638
8171
  ${body}${tail}` };
7639
8172
  } catch (e) {
7640
- return { ok: false, text: `\u274C \u7C7B\u578B\u8BCA\u65AD\u5931\u8D25 [${displayPath}]: ${e?.message ?? e}` };
8173
+ return { ok: false, result: toolFailure(`\u7C7B\u578B\u8BCA\u65AD\u5931\u8D25 [${displayPath}]: ${e?.message ?? e}`) };
7641
8174
  }
7642
8175
  };
7643
8176
  typescriptTools = [
@@ -7645,7 +8178,8 @@ ${body}${tail}` };
7645
8178
  type: "function",
7646
8179
  function: {
7647
8180
  name: "get_diagnostics",
7648
- description: "\u5BF9\u4E00\u4E2A\u6216\u591A\u4E2A TS/JS \u6587\u4EF6\u53D6\u7C7B\u578B+\u8BED\u4E49\u8BCA\u65AD\uFF08\u7C7B\u578B\u9519\u8BEF/\u672A\u7528\u53D8\u91CF/\u4E0D\u53EF\u8FBE\u4EE3\u7801\uFF09\uFF0C\u8F93\u51FA\u884C:\u5217/\u4E25\u91CD\u5EA6/TS \u9519\u8BEF\u7801\uFF0C\u683C\u5F0F\u5BF9\u9F50 tsc\u3002\u2605 \u6539\u5B8C\u591A\u4E2A\u6587\u4EF6\u540E\u9A8C\u8BC1\uFF1A\u4F20 paths \u6570\u7EC4\u4E00\u6B21\u8BCA\u65AD\u5168\u90E8\uFF08\u6700\u591A 20 \u4E2A\uFF09\uFF0C\u52FF\u9010\u6587\u4EF6\u591A\u6B21\u8C03\u7528\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\uFF08.java/.py \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u9879\u76EE\u7EA7\u5168\u91CF\u8BCA\u65AD\u7528 run_command \u8DD1 `tsc --noEmit`\u3002",
8181
+ planAllowed: true,
8182
+ description: "\u5BF9\u4E00\u4E2A\u6216\u591A\u4E2A TS/JS \u6587\u4EF6\u53D6\u7C7B\u578B+\u8BED\u4E49\u8BCA\u65AD\uFF08\u7C7B\u578B\u9519\u8BEF/\u672A\u7528\u53D8\u91CF/\u4E0D\u53EF\u8FBE\u4EE3\u7801\uFF09\uFF0C\u8F93\u51FA\u884C:\u5217/\u4E25\u91CD\u5EA6/TS \u9519\u8BEF\u7801\uFF0C\u683C\u5F0F\u5BF9\u9F50 tsc\u3002\u2605 \u6539\u5B8C\u591A\u4E2A\u6587\u4EF6\u540E\u9A8C\u8BC1\uFF1A\u4F20 paths \u6570\u7EC4\u4E00\u6B21\u8BCA\u65AD\u5168\u90E8\uFF08\u6700\u591A 20 \u4E2A\uFF09\uFF0C\u52FF\u9010\u6587\u4EF6\u591A\u6B21\u8C03\u7528\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\uFF08.java/.py \u7B49\uFF09\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u9879\u76EE\u7EA7\u5168\u91CF\u8BCA\u65AD\u7528 run_command \u8DD1 `tsc --noEmit`\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
7649
8183
  parameters: {
7650
8184
  type: "object",
7651
8185
  properties: {
@@ -7666,11 +8200,11 @@ ${body}${tail}` };
7666
8200
  const TS = await getTs();
7667
8201
  if (!TS) return `\u26A0\uFE0F [\u7C7B\u578B\u8BCA\u65AD\u4E0D\u53EF\u7528]\uFF1Atypescript \u6A21\u5757\u672A\u52A0\u8F7D\uFF08VSCode \u6269\u5C55\u5185\u53EF\u7528\uFF1BCLI \u73AF\u5883\u672A\u5FC5\u5B89\u88C5 typescript\uFF09\u3002\u53EF\u6539\u7528 run_command \u8DD1 \`tsc --noEmit\`\u3002`;
7668
8202
  const targets = args.paths && args.paths.length > 0 ? args.paths : args.path ? [args.path] : [];
7669
- if (targets.length === 0) return `\u274C [\u53C2\u6570\u7F3A\u5931]\uFF1A\u8BF7\u4F20 path\uFF08\u5355\u6587\u4EF6\uFF09\u6216 paths\uFF08\u6279\u91CF\uFF0C\u6539\u5B8C\u591A\u4E2A\u6587\u4EF6\u540E\u4E00\u6B21\u9A8C\u8BC1\uFF09\u3002`;
7670
- if (targets.length > MAX_DIAG_FILES) return `\u274C [\u53C2\u6570\u8D85\u9650]\uFF1A\u5355\u6B21\u6279\u91CF\u8BCA\u65AD\u6700\u591A ${MAX_DIAG_FILES} \u4E2A\u6587\u4EF6\uFF08\u6536\u5230 ${targets.length} \u4E2A\uFF09\u3002\u8BF7\u5206\u6279\u8C03\u7528\uFF0C\u6216\u7528 run_command \u8DD1 \`tsc --noEmit\` \u505A\u9879\u76EE\u7EA7\u5168\u91CF\u8BCA\u65AD\u3002`;
8203
+ if (targets.length === 0) return toolFailure(`[\u53C2\u6570\u7F3A\u5931]\uFF1A\u8BF7\u4F20 path\uFF08\u5355\u6587\u4EF6\uFF09\u6216 paths\uFF08\u6279\u91CF\uFF0C\u6539\u5B8C\u591A\u4E2A\u6587\u4EF6\u540E\u4E00\u6B21\u9A8C\u8BC1\uFF09\u3002`);
8204
+ if (targets.length > MAX_DIAG_FILES) return toolFailure(`[\u53C2\u6570\u8D85\u9650]\uFF1A\u5355\u6B21\u6279\u91CF\u8BCA\u65AD\u6700\u591A ${MAX_DIAG_FILES} \u4E2A\u6587\u4EF6\uFF08\u6536\u5230 ${targets.length} \u4E2A\uFF09\u3002\u8BF7\u5206\u6279\u8C03\u7528\uFF0C\u6216\u7528 run_command \u8DD1 \`tsc --noEmit\` \u505A\u9879\u76EE\u7EA7\u5168\u91CF\u8BCA\u65AD\u3002`);
7671
8205
  const results = [];
7672
8206
  for (const p of targets) results.push(await diagnoseOneFile(TS, p, args.check_js));
7673
- if (results.length === 1) return results[0].text;
8207
+ if (results.length === 1) return results[0].ok ? results[0].text : results[0].result;
7674
8208
  let totalErr = 0, totalWarn = 0, clean = 0, failed = 0;
7675
8209
  const sections = [];
7676
8210
  for (let i = 0; i < results.length; i++) {
@@ -7678,7 +8212,7 @@ ${body}${tail}` };
7678
8212
  if (!r.ok) {
7679
8213
  failed++;
7680
8214
  sections.push(`\u2014\u2014 ${targets[i]}\uFF08\u8BCA\u65AD\u672A\u4EA7\u51FA\uFF09\u2014\u2014
7681
- ${r.text}`);
8215
+ ${r.result.content}`);
7682
8216
  continue;
7683
8217
  }
7684
8218
  totalErr += r.errors;
@@ -7691,7 +8225,7 @@ ${r.text}`);
7691
8225
 
7692
8226
  ${sections.join("\n\n")}`;
7693
8227
  } catch (e) {
7694
- return `\u274C \u7C7B\u578B\u8BCA\u65AD\u5931\u8D25: ${e?.message ?? e}`;
8228
+ return toolFailure(`\u7C7B\u578B\u8BCA\u65AD\u5931\u8D25: ${e?.message ?? e}`);
7695
8229
  }
7696
8230
  }
7697
8231
  }
@@ -7700,7 +8234,8 @@ ${sections.join("\n\n")}`;
7700
8234
  type: "function",
7701
8235
  function: {
7702
8236
  name: "goto_definition",
7703
- description: "\u8DF3\u8F6C\u5230 TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5B9A\u4E49\u4F4D\u7F6E\uFF0C\u8FD4\u56DE rel/path:line:col\uFF08\u53EF\u591A\u5904\uFF09\u3002\u8DE8\u6587\u4EF6\u8FFD\u8E2A\u51FD\u6570/\u7C7B\u578B/\u53D8\u91CF\u7684\u6765\u6E90\uFF0C\u66FF\u4EE3 grep \u731C\u6D4B\u3002\u843D\u70B9\u5728 node_modules/.d.ts \u65F6\u6807\u6CE8 (declaration/library)\u3002\u884C\u5217 1-based\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002",
8237
+ planAllowed: true,
8238
+ description: "\u8DF3\u8F6C\u5230 TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5B9A\u4E49\u4F4D\u7F6E\uFF0C\u8FD4\u56DE rel/path:line:col\uFF08\u53EF\u591A\u5904\uFF09\u3002\u8DE8\u6587\u4EF6\u8FFD\u8E2A\u51FD\u6570/\u7C7B\u578B/\u53D8\u91CF\u7684\u6765\u6E90\uFF0C\u66FF\u4EE3 grep \u731C\u6D4B\u3002\u843D\u70B9\u5728 node_modules/.d.ts \u65F6\u6807\u6CE8 (declaration/library)\u3002\u884C\u5217 1-based\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
7704
8239
  parameters: {
7705
8240
  type: "object",
7706
8241
  properties: {
@@ -7724,9 +8259,9 @@ ${sections.join("\n\n")}`;
7724
8259
  if (block) return block;
7725
8260
  const { ls } = await getLanguageService(TS, absPath);
7726
8261
  const program = ls.getProgram();
7727
- if (!program) return `\u274C \u65E0\u6CD5\u53D6\u5F97 TS program\uFF08\u5185\u90E8\u9519\u8BEF\uFF09\u3002`;
8262
+ if (!program) return toolFailure(`\u65E0\u6CD5\u53D6\u5F97 TS program\uFF08\u5185\u90E8\u9519\u8BEF\uFF09\u3002`);
7728
8263
  const sf = program.getSourceFile(absPath);
7729
- if (!sf) return `\u274C \u6587\u4EF6\u672A\u7EB3\u5165 program\uFF1A${displayPath}\uFF08\u53EF\u80FD\u65E0\u5BF9\u5E94 tsconfig \u6216\u89E3\u6790\u5931\u8D25\uFF09\u3002`;
8264
+ if (!sf) return toolFailure(`\u6587\u4EF6\u672A\u7EB3\u5165 program\uFF1A${displayPath}\uFF08\u53EF\u80FD\u65E0\u5BF9\u5E94 tsconfig \u6216\u89E3\u6790\u5931\u8D25\uFF09\u3002`);
7730
8265
  const pos = lineColToPos(TS, sf, line, column);
7731
8266
  const defs = ls.getDefinitionAtPosition(absPath, pos);
7732
8267
  if (!defs || defs.length === 0) {
@@ -7748,7 +8283,7 @@ ${sections.join("\n\n")}`;
7748
8283
  return `[Goto Definition: ${displayPath}:${line}:${column}]
7749
8284
  ${out.join("\n")}`;
7750
8285
  } catch (e) {
7751
- return `\u274C \u8DF3\u8F6C\u5B9A\u4E49\u5931\u8D25 [${displayPath}:${line}:${column}]: ${e?.message ?? e}`;
8286
+ return toolFailure(`\u8DF3\u8F6C\u5B9A\u4E49\u5931\u8D25 [${displayPath}:${line}:${column}]: ${e?.message ?? e}`);
7752
8287
  }
7753
8288
  }
7754
8289
  }
@@ -7757,7 +8292,7 @@ ${out.join("\n")}`;
7757
8292
  type: "function",
7758
8293
  function: {
7759
8294
  name: "find_references",
7760
- description: "\u67E5\u627E TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5168\u9879\u76EE\u5F15\u7528\uFF08\u8C03\u7528/\u5BFC\u5165/\u8BFB\u5199\uFF09\uFF0C\u8F93\u51FA rel/path:line:col + \u8BFB\u5199\u6807\u6CE8 + \u884C\u6458\u5F55\u3002\u7C7B\u578B\u611F\u77E5\uFF1A\u53EA\u8FD4\u56DE\u771F\u5B9E\u7ED1\u5B9A\uFF0C\u4E0D\u542B\u6CE8\u91CA/\u5B57\u7B26\u4E32\u91CC\u7684\u540C\u540D\u8BCD\uFF08grep \u6838\u5FC3\u8BEF\u62A5\u6E90\uFF09\u3002\u7528\u4E8E\u6539\u7B7E\u540D/\u91CD\u547D\u540D\u524D\u7684\u5F71\u54CD\u9762\u6392\u67E5\u3001\u8FFD\u8E2A\u8C03\u7528\u65B9\uFF1B\u4E0E goto_definition \u4E92\u4E3A\u53CD\u5411\u3002\u8986\u76D6\u4EE5 program \u5185\u6587\u4EF6\u4E3A\u754C\uFF08tsconfig include + import \u94FE\uFF09\uFF1B\u5355\u7B26\u53F7\u8D85 200 \u5904\u5F15\u7528\u622A\u65AD\u3002\u884C\u5217 1-based\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002",
8295
+ description: "\u67E5\u627E TS/JS \u6587\u4EF6\u67D0\u884C\u67D0\u5217\u7B26\u53F7\u7684\u5168\u9879\u76EE\u5F15\u7528\uFF08\u8C03\u7528/\u5BFC\u5165/\u8BFB\u5199\uFF09\uFF0C\u8F93\u51FA rel/path:line:col + \u8BFB\u5199\u6807\u6CE8 + \u884C\u6458\u5F55\u3002\u7C7B\u578B\u611F\u77E5\uFF1A\u53EA\u8FD4\u56DE\u771F\u5B9E\u7ED1\u5B9A\uFF0C\u4E0D\u542B\u6CE8\u91CA/\u5B57\u7B26\u4E32\u91CC\u7684\u540C\u540D\u8BCD\uFF08grep \u6838\u5FC3\u8BEF\u62A5\u6E90\uFF09\u3002\u7528\u4E8E\u6539\u7B7E\u540D/\u91CD\u547D\u540D\u524D\u7684\u5F71\u54CD\u9762\u6392\u67E5\u3001\u8FFD\u8E2A\u8C03\u7528\u65B9\uFF1B\u4E0E goto_definition \u4E92\u4E3A\u53CD\u5411\u3002\u8986\u76D6\u4EE5 program \u5185\u6587\u4EF6\u4E3A\u754C\uFF08tsconfig include + import \u94FE\uFF09\uFF1B\u5355\u7B26\u53F7\u8D85 200 \u5904\u5F15\u7528\u622A\u65AD\u3002\u884C\u5217 1-based\u3002\u652F\u6301 .ts/.tsx/.js/.jsx/.mjs/.cjs\uFF1B.vue SFC \u4E0E\u5176\u4ED6\u8BED\u8A00\u6269\u5C55\u540D\u76F4\u63A5\u62D2\u7EDD\u3001\u52FF\u4F20\u5165\u3002\u2605 \u53EA\u8BFB\u5B89\u5168\uFF0C\u53EF\u4E0E\u5176\u5B83\u53EA\u8BFB\u5DE5\u5177\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u5E76\u884C\u8C03\u7528\u3002",
7761
8296
  parameters: {
7762
8297
  type: "object",
7763
8298
  properties: {
@@ -7783,9 +8318,9 @@ ${out.join("\n")}`;
7783
8318
  if (block) return block;
7784
8319
  const { ls } = await getLanguageService(TS, absPath);
7785
8320
  const program = ls.getProgram();
7786
- if (!program) return `\u274C \u65E0\u6CD5\u53D6\u5F97 TS program\uFF08\u5185\u90E8\u9519\u8BEF\uFF09\u3002`;
8321
+ if (!program) return toolFailure(`\u65E0\u6CD5\u53D6\u5F97 TS program\uFF08\u5185\u90E8\u9519\u8BEF\uFF09\u3002`);
7787
8322
  const sf = program.getSourceFile(absPath);
7788
- if (!sf) return `\u274C \u6587\u4EF6\u672A\u7EB3\u5165 program\uFF1A${displayPath}\uFF08\u53EF\u80FD\u65E0\u5BF9\u5E94 tsconfig \u6216\u89E3\u6790\u5931\u8D25\uFF09\u3002`;
8323
+ if (!sf) return toolFailure(`\u6587\u4EF6\u672A\u7EB3\u5165 program\uFF1A${displayPath}\uFF08\u53EF\u80FD\u65E0\u5BF9\u5E94 tsconfig \u6216\u89E3\u6790\u5931\u8D25\uFF09\u3002`);
7789
8324
  const pos = lineColToPos(TS, sf, line, column);
7790
8325
  const groups = ls.findReferences(absPath, pos);
7791
8326
  if (!groups || groups.length === 0) {
@@ -7839,7 +8374,7 @@ ${out.join("\n")}`;
7839
8374
  return `[References: ${displayPath}:${line}:${column}]
7840
8375
  ${sections.join("\n")}${tail}`;
7841
8376
  } catch (e) {
7842
- return `\u274C \u67E5\u627E\u5F15\u7528\u5931\u8D25 [${displayPath}:${line}:${column}]: ${e?.message ?? e}`;
8377
+ return toolFailure(`\u67E5\u627E\u5F15\u7528\u5931\u8D25 [${displayPath}:${line}:${column}]: ${e?.message ?? e}`);
7843
8378
  }
7844
8379
  }
7845
8380
  }
@@ -7887,11 +8422,11 @@ var init_todo = __esm({
7887
8422
  isSync: true,
7888
8423
  async execute(args, ctx) {
7889
8424
  if (!ctx?.sessionId) {
7890
- return `\u274C [todo_write \u5931\u8D25]\uFF1A\u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\uFF0C\u65E0\u6CD5\u6301\u4E45\u5316\u4EFB\u52A1\u6E05\u5355\u3002`;
8425
+ return toolFailure(`[todo_write \u5931\u8D25]\uFF1A\u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\uFF0C\u65E0\u6CD5\u6301\u4E45\u5316\u4EFB\u52A1\u6E05\u5355\u3002`);
7891
8426
  }
7892
8427
  const todos = Array.isArray(args.todos) ? args.todos : [];
7893
8428
  if (todos.length > MAX_TODOS) {
7894
- return `\u274C [todo_write \u5931\u8D25]\uFF1A\u4EFB\u52A1\u6761\u6570 ${todos.length} \u8D85\u8FC7\u4E0A\u9650 ${MAX_TODOS}\uFF0C\u8BF7\u5408\u7406\u62C6\u5206\u3002`;
8429
+ return toolFailure(`[todo_write \u5931\u8D25]\uFF1A\u4EFB\u52A1\u6761\u6570 ${todos.length} \u8D85\u8FC7\u4E0A\u9650 ${MAX_TODOS}\uFF0C\u8BF7\u5408\u7406\u62C6\u5206\u3002`);
7895
8430
  }
7896
8431
  const cleaned = todos.map((t) => {
7897
8432
  const item = {
@@ -7904,7 +8439,7 @@ var init_todo = __esm({
7904
8439
  });
7905
8440
  const emptyContent = cleaned.filter((t) => !t.content);
7906
8441
  if (emptyContent.length) {
7907
- return `\u274C [todo_write \u5931\u8D25]\uFF1A\u6709 ${emptyContent.length} \u6761\u4EFB\u52A1\u7F3A\u5C11 content\uFF0C\u8BF7\u8865\u5168\u540E\u518D\u63D0\u4EA4\u3002`;
8442
+ return toolFailure(`[todo_write \u5931\u8D25]\uFF1A\u6709 ${emptyContent.length} \u6761\u4EFB\u52A1\u7F3A\u5C11 content\uFF0C\u8BF7\u8865\u5168\u540E\u518D\u63D0\u4EA4\u3002`);
7908
8443
  }
7909
8444
  const inProgressCount = cleaned.filter((t) => t.status === "in_progress").length;
7910
8445
  const completedCount = cleaned.filter((t) => t.status === "completed").length;
@@ -8343,11 +8878,8 @@ var init_cleanup = __esm({
8343
8878
  import fs22 from "fs/promises";
8344
8879
  import path26 from "path";
8345
8880
  import crypto from "crypto";
8346
- async function beforeMutationBackup(toolName, args, toolCallId, sessionId) {
8881
+ async function beforeMutationBackup(strategy, toolName, args, toolCallId, sessionId) {
8347
8882
  if (appConfig.undoEnabled === false) return null;
8348
- if (!MUTATION_TOOLS.has(toolName)) {
8349
- throw new Error(`[undo] \u4E0D\u5728\u53D8\u66F4\u5DE5\u5177\u540D\u5355: ${toolName}`);
8350
- }
8351
8883
  const relativePath = String(args?.path ?? "").replace(/\\/g, "/");
8352
8884
  if (relativePath && isSensitivePath(relativePath)) {
8353
8885
  const policy = appConfig.undoBackupSensitive ?? "skip";
@@ -8368,20 +8900,18 @@ async function beforeMutationBackup(toolName, args, toolCallId, sessionId) {
8368
8900
  bornDate: getTodayDateString()
8369
8901
  };
8370
8902
  let record;
8371
- switch (toolName) {
8372
- case "edit_file":
8373
- case "write_file":
8374
- case "notebook_edit":
8903
+ switch (strategy) {
8904
+ case "overwrite":
8375
8905
  record = await backupFileOverwrite(common, undoDir, args);
8376
8906
  break;
8377
- case "create_file":
8907
+ case "create":
8378
8908
  record = await backupFileCreate(common);
8379
8909
  break;
8380
- case "delete_path":
8910
+ case "delete":
8381
8911
  record = await backupDelete(common, undoDir, relativePath);
8382
8912
  break;
8383
8913
  default:
8384
- throw new Error(`[undo] \u672A\u77E5\u5DE5\u5177: ${toolName}`);
8914
+ throw new Error(`[undo] \u672A\u77E5\u5907\u4EFD\u7B56\u7565: ${strategy}`);
8385
8915
  }
8386
8916
  await appendUndoRecord(record, sessionId);
8387
8917
  void maybeCleanupAfterUndoAppend(sessionId);
@@ -8415,8 +8945,9 @@ async function backupFileOverwrite(common, undoDir, args) {
8415
8945
  contentHashBefore: sha1Buf(content),
8416
8946
  fileSizeBefore: content.byteLength,
8417
8947
  argsSnapshot: {
8418
- old_str: common.operationType === "edit_file" ? truncate2(args?.old_str) : void 0,
8419
- new_str: common.operationType === "edit_file" ? truncate2(args?.new_str) : void 0,
8948
+ // ★ #8a:原按 operationType==='edit_file' 特判,改按参数存在性判定——行为不变,泛化到任何声明式 overwrite 写工具
8949
+ old_str: typeof args?.old_str === "string" ? truncate2(args.old_str) : void 0,
8950
+ new_str: typeof args?.new_str === "string" ? truncate2(args.new_str) : void 0,
8420
8951
  contentLength: typeof args?.content === "string" ? args.content.length : void 0
8421
8952
  }
8422
8953
  };
@@ -8478,7 +9009,7 @@ async function backupDelete(common, undoDir, relativePath) {
8478
9009
  fileSizeBefore: content.byteLength
8479
9010
  };
8480
9011
  }
8481
- var MUTATION_TOOLS, isUndoTrigger, sha1Buf, truncate2, isSensitivePath, hashTreeManifest, getDirTreeBytes, collectSensitiveInDir;
9012
+ var sha1Buf, truncate2, isSensitivePath, hashTreeManifest, getDirTreeBytes, collectSensitiveInDir;
8482
9013
  var init_backup = __esm({
8483
9014
  "../core/src/tool/undo/backup.ts"() {
8484
9015
  "use strict";
@@ -8489,8 +9020,6 @@ var init_backup = __esm({
8489
9020
  init_undo();
8490
9021
  init_cleanup();
8491
9022
  init_traceCalculate();
8492
- MUTATION_TOOLS = /* @__PURE__ */ new Set(["edit_file", "write_file", "create_file", "delete_path", "notebook_edit"]);
8493
- isUndoTrigger = (toolName) => MUTATION_TOOLS.has(toolName);
8494
9023
  sha1Buf = (buf) => crypto.createHash("sha1").update(buf).digest("hex");
8495
9024
  truncate2 = (s, n = 200) => s === void 0 ? void 0 : s.length > n ? s.slice(0, n) + "\u2026" : s;
8496
9025
  isSensitivePath = (rel) => {
@@ -8760,7 +9289,7 @@ var init_undo2 = __esm({
8760
9289
  safetyLevel: "safe" /* SAFE */,
8761
9290
  isSync: true,
8762
9291
  async execute(_args, ctx) {
8763
- if (!ctx?.sessionId) return `\u274C [undo_list \u5931\u8D25]\uFF1A\u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002`;
9292
+ if (!ctx?.sessionId) return toolFailure(`[undo_list \u5931\u8D25]\uFF1A\u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002`);
8764
9293
  const list = await listUndoable(ctx.sessionId);
8765
9294
  if (!list.length) return `\uFF08\u5F53\u524D\u4F1A\u8BDD\u6CA1\u6709\u53EF\u56DE\u9000\u7684\u53D8\u66F4\u3002\uFF09`;
8766
9295
  const lines = list.map(
@@ -8789,15 +9318,15 @@ ${lines.join("\n")}
8789
9318
  isSync: true,
8790
9319
  requireApproval: (args) => args?.restore_last ? `\u7533\u8BF7\u56DE\u9000\u6700\u8FD1\u4E00\u6B21\u6587\u4EF6\u53D8\u66F4\uFF08restore_last\uFF09\u3002\u8BE5\u64CD\u4F5C\u4F1A\u6539\u5199\u78C1\u76D8\u6587\u4EF6\u3002` : `\u7533\u8BF7\u56DE\u9000\u6587\u4EF6\u53D8\u66F4\uFF08undoId=${String(args?.undoId ?? "").slice(0, 8)}\uFF09\u3002\u8BE5\u64CD\u4F5C\u4F1A\u6539\u5199\u78C1\u76D8\u6587\u4EF6\u3002`,
8791
9320
  async execute(args, ctx) {
8792
- if (!ctx?.sessionId) return `\u274C [undo_restore \u5931\u8D25]\uFF1A\u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002`;
9321
+ if (!ctx?.sessionId) return toolFailure(`[undo_restore \u5931\u8D25]\uFF1A\u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002`);
8793
9322
  try {
8794
9323
  if (args?.restore_last) return await restoreLast(ctx.sessionId);
8795
- if (!args?.undoId) return `\u274C [undo_restore \u5931\u8D25]\uFF1A\u9700\u63D0\u4F9B undoId\uFF0C\u6216\u8BBE restore_last=true\u3002`;
9324
+ if (!args?.undoId) return toolFailure(`[undo_restore \u5931\u8D25]\uFF1A\u9700\u63D0\u4F9B undoId\uFF0C\u6216\u8BBE restore_last=true\u3002`);
8796
9325
  const target = await findByUndoIdPrefix(ctx.sessionId, String(args.undoId));
8797
- if (!target) return `\u274C [undo_restore \u5931\u8D25]\uFF1A\u627E\u4E0D\u5230 undoId \u524D\u7F00 "${args.undoId}" \u5BF9\u5E94\u7684\u8BB0\u5F55\uFF0C\u8BF7\u7528 undo_list \u6838\u5BF9\u3002`;
9326
+ if (!target) return toolFailure(`[undo_restore \u5931\u8D25]\uFF1A\u627E\u4E0D\u5230 undoId \u524D\u7F00 "${args.undoId}" \u5BF9\u5E94\u7684\u8BB0\u5F55\uFF0C\u8BF7\u7528 undo_list \u6838\u5BF9\u3002`);
8798
9327
  return await restoreByUndoId(target.undoId, ctx.sessionId);
8799
9328
  } catch (e) {
8800
- return `\u274C [undo_restore \u5931\u8D25]\uFF1A${e?.message ?? e}`;
9329
+ return toolFailure(`[undo_restore \u5931\u8D25]\uFF1A${e?.message ?? e}`);
8801
9330
  }
8802
9331
  }
8803
9332
  }
@@ -8850,10 +9379,10 @@ var init_ask = __esm({
8850
9379
  const question = (args?.question ?? "").trim();
8851
9380
  const options = Array.isArray(args?.options) ? args.options : [];
8852
9381
  const cleanOptions = options.map((o) => ({ label: String(o?.label ?? "").trim(), description: o?.description ? String(o.description).trim() : void 0 })).filter((o) => o.label);
8853
- if (!question) return "\u274C [\u63D0\u95EE\u5931\u8D25]\uFF1Aquestion \u4E0D\u80FD\u4E3A\u7A7A\u3002";
8854
- if (cleanOptions.length < 2 || cleanOptions.length > 4) return "\u274C [\u63D0\u95EE\u5931\u8D25]\uFF1Aoptions \u987B\u4E3A 2-4 \u4E2A\u975E\u7A7A\u9009\u9879\u3002";
9382
+ if (!question) return toolFailure("[\u63D0\u95EE\u5931\u8D25]\uFF1Aquestion \u4E0D\u80FD\u4E3A\u7A7A\u3002");
9383
+ if (cleanOptions.length < 2 || cleanOptions.length > 4) return toolFailure("[\u63D0\u95EE\u5931\u8D25]\uFF1Aoptions \u987B\u4E3A 2-4 \u4E2A\u975E\u7A7A\u9009\u9879\u3002");
8855
9384
  if (!ctx?.requestQuestion) {
8856
- return "\u26A0\uFE0F [\u63D0\u95EE\u4E0D\u652F\u6301]\uFF1A\u5F53\u524D\u5BBF\u4E3B\u4E0D\u652F\u6301\u7ED3\u6784\u5316\u63D0\u95EE\uFF08\u591A\u4E3A\u975E\u4EA4\u4E92\u73AF\u5883\uFF09\u3002\u8BF7\u6539\u7528\u7EAF\u6587\u672C\u76F4\u63A5\u5411\u7528\u6237\u5217\u51FA\u9009\u9879\u5E76\u63D0\u95EE\u3002";
9385
+ return toolFailure("[\u63D0\u95EE\u4E0D\u652F\u6301]\uFF1A\u5F53\u524D\u5BBF\u4E3B\u4E0D\u652F\u6301\u7ED3\u6784\u5316\u63D0\u95EE\uFF08\u591A\u4E3A\u975E\u4EA4\u4E92\u73AF\u5883\uFF09\u3002\u8BF7\u6539\u7528\u7EAF\u6587\u672C\u76F4\u63A5\u5411\u7528\u6237\u5217\u51FA\u9009\u9879\u5E76\u63D0\u95EE\u3002");
8857
9386
  }
8858
9387
  let answer;
8859
9388
  try {
@@ -8863,7 +9392,7 @@ var init_ask = __esm({
8863
9392
  multiSelect: !!args?.multiSelect
8864
9393
  });
8865
9394
  } catch (e) {
8866
- return `\u274C [\u63D0\u95EE\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u8BF7\u6539\u7528\u7EAF\u6587\u672C\u5411\u7528\u6237\u63D0\u95EE\u3002`;
9395
+ return toolFailure(`[\u63D0\u95EE\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u8BF7\u6539\u7528\u7EAF\u6587\u672C\u5411\u7528\u6237\u63D0\u95EE\u3002`);
8867
9396
  }
8868
9397
  if (!answer.selected || answer.selected.length === 0) {
8869
9398
  return "\uFF08\u7528\u6237\u53D6\u6D88\u4E86\u672C\u6B21\u63D0\u95EE\uFF0C\u672A\u4F5C\u9009\u62E9\u3002\u53EF\u6309\u9ED8\u8BA4\u7EE7\u7EED\uFF0C\u6216\u91CD\u65B0\u7EC4\u7EC7\u95EE\u9898\u518D\u6B21\u8BE2\u95EE\u3002\uFF09";
@@ -8904,7 +9433,7 @@ var init_worktree = __esm({
8904
9433
  \uFF08\u5C06\u521B\u5EFA\u4E34\u65F6\u5206\u652F\u7684 git worktree \u5E76\u628A\u4F1A\u8BDD\u5DE5\u4F5C\u533A\u5207\u6362\u8FC7\u53BB\uFF1B\u6539\u52A8\u9694\u79BB\uFF0C\u9700 commit/merge \u624D\u80FD\u5E76\u56DE\u4E3B\u4ED3\uFF09`,
8905
9434
  async execute(args, ctx) {
8906
9435
  const sessionId = ctx?.sessionId;
8907
- if (!sessionId) return "\u274C [enter_worktree] \u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\u3002";
9436
+ if (!sessionId) return toolFailure("[enter_worktree] \u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\u3002");
8908
9437
  if (getSessionWorktree(sessionId)) {
8909
9438
  return `\u26A0\uFE0F \u5F53\u524D\u4F1A\u8BDD\u5DF2\u5728 worktree \u5185\uFF08${getSessionWorktree(sessionId)?.path}\uFF09\u3002\u8BF7\u5148 exit_worktree \u518D\u91CD\u65B0\u8FDB\u5165\uFF0C\u907F\u514D\u6CC4\u6F0F\u3002`;
8910
9439
  }
@@ -8912,8 +9441,8 @@ var init_worktree = __esm({
8912
9441
  try {
8913
9442
  wt = await createWorktree(sessionId, "session");
8914
9443
  } catch (e) {
8915
- if (isNotARepoError(e)) return "\u274C [enter_worktree] \u4E3B\u5DE5\u4F5C\u533A\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u521B\u5EFA worktree\u3002";
8916
- return `\u274C [enter_worktree] \u521B\u5EFA worktree \u5931\u8D25\uFF1A${e?.message ?? e}`;
9444
+ if (isNotARepoError(e)) return toolFailure("[enter_worktree] \u4E3B\u5DE5\u4F5C\u533A\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u65E0\u6CD5\u521B\u5EFA worktree\u3002");
9445
+ return toolFailure(`[enter_worktree] \u521B\u5EFA worktree \u5931\u8D25\uFF1A${e?.message ?? e}`);
8917
9446
  }
8918
9447
  setSessionWorktree(sessionId, wt);
8919
9448
  return [
@@ -8942,7 +9471,7 @@ var init_worktree = __esm({
8942
9471
  \uFF08\u5C06\u5220\u9664\u5F53\u524D worktree \u53CA\u5176\u4E34\u65F6\u5206\u652F\uFF1B\u672A\u63D0\u4EA4\u6539\u52A8\u4F1A\u4E22\u5931\u3002\u5982\u9700\u4FDD\u7559\u8BF7\u5148 commit\u3002\uFF09`,
8943
9472
  async execute(_args, ctx) {
8944
9473
  const sessionId = ctx?.sessionId;
8945
- if (!sessionId) return "\u274C [exit_worktree] \u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\u3002";
9474
+ if (!sessionId) return toolFailure("[exit_worktree] \u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\u3002");
8946
9475
  const wt = clearSessionWorktree(sessionId);
8947
9476
  if (!wt) return "\u2139\uFE0F \u5F53\u524D\u4F1A\u8BDD\u4E0D\u5728\u4EFB\u4F55 worktree \u5185\uFF08\u65E0\u9700\u9000\u51FA\uFF09\u3002";
8948
9477
  const diff = await harvestDiff(wt).catch(() => "");
@@ -9049,6 +9578,9 @@ var init_notebook = __esm({
9049
9578
  type: "function",
9050
9579
  function: {
9051
9580
  name: "notebook_edit",
9581
+ // ★ #8a:undo 备份同 edit_file(快照原内容);刻意不声明 autoApproval——维持「分类器不覆盖、恒人工审批」现状
9582
+ triggersUndo: "overwrite",
9583
+ primaryArg: "path",
9052
9584
  description: "\u7F16\u8F91 Jupyter notebook\uFF08.ipynb\uFF09\u7684\u5355\u5143\u683C\u3002\u652F\u6301\u4E09\u79CD\u6A21\u5F0F\uFF1Areplace\uFF08\u66FF\u6362\u67D0 cell \u5185\u5BB9/\u7C7B\u578B\uFF09\u3001insert\uFF08\u63D2\u5165\u65B0 cell\uFF09\u3001delete\uFF08\u5220\u9664 cell\uFF09\u3002\u7528 cell_id \u6216 cell_index \u5B9A\u4F4D\u3002\u4EC5\u652F\u6301 .ipynb \u6587\u4EF6\u3002",
9053
9585
  parameters: {
9054
9586
  type: "object",
@@ -9071,28 +9603,28 @@ var init_notebook = __esm({
9071
9603
  ${String(args.new_source).slice(0, 500)}` : ""}`,
9072
9604
  async execute(args) {
9073
9605
  const relPath = args?.path;
9074
- if (typeof relPath !== "string" || !relPath.trim()) return "\u274C [notebook_edit] \u7F3A\u5C11\u53C2\u6570 path\u3002";
9075
- if (!relPath.toLowerCase().endsWith(".ipynb")) return `\u274C [notebook_edit] \u4EC5\u652F\u6301 .ipynb \u6587\u4EF6\uFF1A${relPath}`;
9606
+ if (typeof relPath !== "string" || !relPath.trim()) return toolFailure("[notebook_edit] \u7F3A\u5C11\u53C2\u6570 path\u3002");
9607
+ if (!relPath.toLowerCase().endsWith(".ipynb")) return toolFailure(`[notebook_edit] \u4EC5\u652F\u6301 .ipynb \u6587\u4EF6\uFF1A${relPath}`);
9076
9608
  const edit_mode = args?.edit_mode === "insert" || args?.edit_mode === "delete" ? args.edit_mode : "replace";
9077
9609
  let absPath;
9078
9610
  try {
9079
9611
  absPath = resolveSafePath(relPath);
9080
9612
  } catch (e) {
9081
- return `\u274C [notebook_edit] \u8DEF\u5F84\u89E3\u6790\u5931\u8D25\uFF1A${e?.message ?? e}`;
9613
+ return toolFailure(`[notebook_edit] \u8DEF\u5F84\u89E3\u6790\u5931\u8D25\uFF1A${e?.message ?? e}`);
9082
9614
  }
9083
9615
  let raw;
9084
9616
  try {
9085
9617
  raw = await fs24.readFile(absPath, "utf-8");
9086
9618
  } catch (e) {
9087
- return `\u274C [notebook_edit] \u8BFB\u53D6\u5931\u8D25\uFF1A${e?.message ?? e}`;
9619
+ return toolFailure(`[notebook_edit] \u8BFB\u53D6\u5931\u8D25\uFF1A${e?.message ?? e}`);
9088
9620
  }
9089
9621
  let nb;
9090
9622
  try {
9091
9623
  nb = JSON.parse(raw);
9092
9624
  } catch (e) {
9093
- return `\u274C [notebook_edit] JSON \u89E3\u6790\u5931\u8D25\uFF08\u975E\u5408\u6CD5 .ipynb\uFF09\uFF1A${e?.message ?? e}`;
9625
+ return toolFailure(`[notebook_edit] JSON \u89E3\u6790\u5931\u8D25\uFF08\u975E\u5408\u6CD5 .ipynb\uFF09\uFF1A${e?.message ?? e}`);
9094
9626
  }
9095
- if (!nb || !Array.isArray(nb.cells)) return "\u274C [notebook_edit] \u975E\u5408\u6CD5 notebook\uFF08\u7F3A cells \u6570\u7EC4\uFF09\u3002";
9627
+ if (!nb || !Array.isArray(nb.cells)) return toolFailure("[notebook_edit] \u975E\u5408\u6CD5 notebook\uFF08\u7F3A cells \u6570\u7EC4\uFF09\u3002");
9096
9628
  let summary;
9097
9629
  try {
9098
9630
  summary = applyNotebookEdit(nb, {
@@ -9103,12 +9635,12 @@ ${String(args.new_source).slice(0, 500)}` : ""}`,
9103
9635
  edit_mode
9104
9636
  });
9105
9637
  } catch (e) {
9106
- return `\u274C [notebook_edit] ${e?.message ?? e}`;
9638
+ return toolFailure(`[notebook_edit] ${e?.message ?? e}`);
9107
9639
  }
9108
9640
  try {
9109
9641
  assertWithinWorkspace(absPath);
9110
9642
  } catch (e) {
9111
- return `\u274C [notebook_edit] \u4E8C\u6B21\u56F4\u680F\u590D\u68C0\u5931\u8D25\uFF1A${e?.message ?? e}`;
9643
+ return toolFailure(`[notebook_edit] \u4E8C\u6B21\u56F4\u680F\u590D\u68C0\u5931\u8D25\uFF1A${e?.message ?? e}`);
9112
9644
  }
9113
9645
  const tmpPath = makeTmpPath(absPath);
9114
9646
  try {
@@ -9117,7 +9649,7 @@ ${String(args.new_source).slice(0, 500)}` : ""}`,
9117
9649
  } catch (e) {
9118
9650
  await fs24.unlink(tmpPath).catch(() => {
9119
9651
  });
9120
- return `\u274C [notebook_edit] \u5199\u5165\u5931\u8D25\uFF1A${e?.message ?? e}`;
9652
+ return toolFailure(`[notebook_edit] \u5199\u5165\u5931\u8D25\uFF1A${e?.message ?? e}`);
9121
9653
  }
9122
9654
  return `\u2705 [notebook_edit] ${summary}\uFF08${relPath}\uFF09`;
9123
9655
  }
@@ -9225,10 +9757,41 @@ var init_loader3 = __esm({
9225
9757
  }
9226
9758
  });
9227
9759
 
9228
- // ../core/src/tool/registry/memory.ts
9760
+ // ../core/src/memory/usage.ts
9229
9761
  import fs26 from "fs/promises";
9230
9762
  import path29 from "path";
9231
- var MAX_BODY_BYTES, NAME_RE, VALID_TYPES2, memoryFilePath, sanitizeLine, buildMemoryMd, writeAndRegister, memoryTools;
9763
+ var USAGE_FILE, readUsage, recordMemoryUse;
9764
+ var init_usage = __esm({
9765
+ "../core/src/memory/usage.ts"() {
9766
+ "use strict";
9767
+ init_loader3();
9768
+ init_common();
9769
+ USAGE_FILE = path29.join(GLOBAL_MEMORY_DIR, ".usage.json");
9770
+ readUsage = async () => {
9771
+ const raw = await readJSONFile(USAGE_FILE);
9772
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
9773
+ };
9774
+ recordMemoryUse = async (name, kind) => {
9775
+ try {
9776
+ const map = await readUsage();
9777
+ const prev = map[name];
9778
+ map[name] = {
9779
+ lastUsed: (/* @__PURE__ */ new Date()).toISOString(),
9780
+ reads: (prev?.reads ?? 0) + (kind === "read" ? 1 : 0)
9781
+ };
9782
+ await fs26.mkdir(GLOBAL_MEMORY_DIR, { recursive: true });
9783
+ await fs26.writeFile(USAGE_FILE, JSON.stringify(map, null, 2), "utf-8");
9784
+ } catch (e) {
9785
+ console.warn(`\u26A0\uFE0F [memory] \u4F7F\u7528\u753B\u50CF\u8BB0\u5F55\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09: ${e?.message ?? e}`);
9786
+ }
9787
+ };
9788
+ }
9789
+ });
9790
+
9791
+ // ../core/src/tool/registry/memory.ts
9792
+ import fs27 from "fs/promises";
9793
+ import path30 from "path";
9794
+ var MAX_BODY_BYTES, MAX_DESCRIPTION_CHARS, MEMORY_LIMITS, NAME_RE, VALID_TYPES2, memoryFilePath, sanitizeLine, buildMemoryMd, writeAndRegister, indexLineBytes, memoryTools;
9232
9795
  var init_memory = __esm({
9233
9796
  "../core/src/tool/registry/memory.ts"() {
9234
9797
  "use strict";
@@ -9236,10 +9799,13 @@ var init_memory = __esm({
9236
9799
  init_fs();
9237
9800
  init_loader3();
9238
9801
  init_registry6();
9802
+ init_usage();
9239
9803
  MAX_BODY_BYTES = 64 * 1024;
9804
+ MAX_DESCRIPTION_CHARS = 200;
9805
+ MEMORY_LIMITS = { maxCount: 200, maxIndexBytes: 16 * 1024 };
9240
9806
  NAME_RE = /^[a-z0-9-]+$/;
9241
9807
  VALID_TYPES2 = ["user", "feedback", "project", "reference"];
9242
- memoryFilePath = (name, project) => project ? path29.join(process.cwd(), ".deepseeker-code", "memory", `${name}.md`) : path29.join(GLOBAL_MEMORY_DIR, `${name}.md`);
9808
+ memoryFilePath = (name, project) => project ? path30.join(process.cwd(), ".deepseeker-code", "memory", `${name}.md`) : path30.join(GLOBAL_MEMORY_DIR, `${name}.md`);
9243
9809
  sanitizeLine = (s) => String(s ?? "").replace(/[\r\n]+/g, " ").trim();
9244
9810
  buildMemoryMd = (name, description, type, body) => `---
9245
9811
  name: ${name}
@@ -9251,13 +9817,14 @@ ${body.trim()}
9251
9817
  `;
9252
9818
  writeAndRegister = async (name, description, type, body, project) => {
9253
9819
  const file = memoryFilePath(name, project);
9254
- await fs26.mkdir(path29.dirname(file), { recursive: true });
9820
+ await fs27.mkdir(path30.dirname(file), { recursive: true });
9255
9821
  const tmp = makeTmpPath(file);
9256
- await fs26.writeFile(tmp, buildMemoryMd(name, description, type, body), "utf-8");
9257
- await fs26.rename(tmp, file);
9822
+ await fs27.writeFile(tmp, buildMemoryMd(name, description, type, body), "utf-8");
9823
+ await fs27.rename(tmp, file);
9258
9824
  registerMemory({ name, description, type, body: body.trim(), source: project ? "project" : "global", file });
9259
9825
  return file;
9260
9826
  };
9827
+ indexLineBytes = (m) => Buffer.byteLength(memoryIndexLine(m), "utf-8");
9261
9828
  memoryTools = [
9262
9829
  {
9263
9830
  type: "function",
@@ -9273,7 +9840,7 @@ ${body.trim()}
9273
9840
  },
9274
9841
  description: {
9275
9842
  type: "string",
9276
- description: "\u4E00\u53E5\u8BDD\u6982\u62EC\uFF08\u6CE8\u5165\u7D22\u5F15\u4F9B\u65E5\u540E\u5224\u65AD\u662F\u5426\u53EC\u56DE\uFF09\u3002\u52FF\u6362\u884C"
9843
+ description: "\u4E00\u53E5\u8BDD\u6982\u62EC\uFF08\u6CE8\u5165\u7D22\u5F15\u4F9B\u65E5\u540E\u5224\u65AD\u662F\u5426\u53EC\u56DE\uFF09\u3002\u52FF\u6362\u884C\uFF0C\u4E0A\u9650 200 \u5B57\u7B26"
9277
9844
  },
9278
9845
  body: {
9279
9846
  type: "string",
@@ -9299,13 +9866,25 @@ ${body.trim()}
9299
9866
  const body = typeof args?.body === "string" ? args.body : "";
9300
9867
  const type = sanitizeLine(args?.type) || "reference";
9301
9868
  const project = args?.project === true;
9302
- if (!NAME_RE.test(name)) return `\u274C [memory_save] name \u975E\u6CD5\uFF1A\u4EC5\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD/\u6570\u5B57/\u8FDE\u5B57\u7B26\uFF0C\u6536\u5230 "${name}"\u3002`;
9303
- if (!description) return "\u274C [memory_save] description \u4E0D\u80FD\u4E3A\u7A7A\u3002";
9304
- if (!body.trim()) return "\u274C [memory_save] body \u4E0D\u80FD\u4E3A\u7A7A\u3002";
9305
- if (!VALID_TYPES2.includes(type)) return `\u274C [memory_save] type \u975E\u6CD5\uFF1A\u987B user/feedback/project/reference\uFF0C\u6536\u5230 "${type}"\u3002`;
9869
+ if (!NAME_RE.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`);
9870
+ if (!description) return toolFailure("[memory_save] description \u4E0D\u80FD\u4E3A\u7A7A\u3002");
9871
+ 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`);
9872
+ if (!body.trim()) return toolFailure("[memory_save] body \u4E0D\u80FD\u4E3A\u7A7A\u3002");
9873
+ if (!VALID_TYPES2.includes(type)) return toolFailure(`[memory_save] type \u975E\u6CD5\uFF1A\u987B user/feedback/project/reference\uFF0C\u6536\u5230 "${type}"\u3002`);
9306
9874
  const bodyBytes = Buffer.byteLength(body, "utf-8");
9307
- if (bodyBytes > MAX_BODY_BYTES) return `\u274C [memory_save] body \u8D85\u8FC7 ${MAX_BODY_BYTES} \u5B57\u8282\u4E0A\u9650\uFF08\u5F53\u524D ${bodyBytes}\uFF09\u3002\u8BF7\u7CBE\u7B80\u6216\u62C6\u5206\u3002`;
9875
+ if (bodyBytes > MAX_BODY_BYTES) return toolFailure(`[memory_save] body \u8D85\u8FC7 ${MAX_BODY_BYTES} \u5B57\u8282\u4E0A\u9650\uFF08\u5F53\u524D ${bodyBytes}\uFF09\u3002\u8BF7\u7CBE\u7B80\u6216\u62C6\u5206\u3002`);
9876
+ const all = listMemories();
9877
+ const existing = getMemory(name);
9878
+ if (!existing && all.length >= MEMORY_LIMITS.maxCount) {
9879
+ return toolFailure(`[memory_save] \u8BB0\u5FC6\u6761\u6570\u5DF2\u8FBE\u4E0A\u9650 ${MEMORY_LIMITS.maxCount}\uFF08\u73B0\u6709 ${all.length} \u6761\uFF09\u3002\u7D22\u5F15\u6BCF\u8F6E\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\uFF0C\u53EA\u589E\u4E0D\u51CF\u4F1A\u6301\u7EED\u4FB5\u8680\u4E0A\u4E0B\u6587\u2014\u2014\u8BF7\u5148 memory_list \u68C0\u67E5\uFF0C\u7528 memory_delete \u6E05\u7406\u5931\u6548\u6761\u76EE\u6216\u5408\u5E76\u76F8\u8FD1\u8BB0\u5FC6\u540E\u518D\u4FDD\u5B58\u3002`);
9880
+ }
9881
+ const curBytes = all.length === 0 ? 0 : Buffer.byteLength(all.map(memoryIndexLine).join("\n"), "utf-8");
9882
+ const delta = indexLineBytes({ name, type, description }) - (existing ? indexLineBytes(existing) : 0);
9883
+ if (delta > 0 && curBytes + delta > MEMORY_LIMITS.maxIndexBytes) {
9884
+ return toolFailure(`[memory_save] \u8BB0\u5FC6\u7D22\u5F15\u5C06\u8D85\u5B57\u8282\u4E0A\u9650\uFF08\u5F53\u524D ${curBytes}/${MEMORY_LIMITS.maxIndexBytes} \u5B57\u8282\uFF0C\u672C\u6761\u51C0\u589E ${delta} \u5B57\u8282\uFF09\u3002\u7D22\u5F15\u6BCF\u8F6E\u6CE8\u5165\u7CFB\u7EDF\u63D0\u793A\u8BCD\u2014\u2014\u8BF7\u7CBE\u7B80 description\uFF0C\u6216\u7528 memory_delete \u6E05\u7406/\u5408\u5E76\u65E7\u8BB0\u5FC6\u540E\u518D\u4FDD\u5B58\u3002`);
9885
+ }
9308
9886
  const file = await writeAndRegister(name, description, type, body, project);
9887
+ await recordMemoryUse(name, "save");
9309
9888
  return `\u2705 \u5DF2\u4FDD\u5B58\u8BB0\u5FC6 **${name}**\uFF08${type}${project ? "\uFF0C\u9879\u76EE\u6E90" : "\uFF0C\u5168\u5C40\u6E90"}\uFF09\u2192 ${file}`;
9310
9889
  }
9311
9890
  }
@@ -9327,7 +9906,8 @@ ${body.trim()}
9327
9906
  async execute(args) {
9328
9907
  const name = sanitizeLine(args?.name);
9329
9908
  const m = getMemory(name);
9330
- if (!m) return `\u274C \u672A\u627E\u5230\u8BB0\u5FC6\uFF1A${name}\u3002\u53EF\u8C03\u7528 memory_list \u67E5\u770B\u5168\u90E8\u8BB0\u5FC6\u540D\u3002`;
9909
+ if (!m) return toolFailure(`\u672A\u627E\u5230\u8BB0\u5FC6\uFF1A${name}\u3002\u53EF\u8C03\u7528 memory_list \u67E5\u770B\u5168\u90E8\u8BB0\u5FC6\u540D\u3002`);
9910
+ await recordMemoryUse(name, "read");
9331
9911
  return `## ${m.name}\uFF08${m.type}\uFF09
9332
9912
 
9333
9913
  ${m.body}`;
@@ -9345,7 +9925,7 @@ ${m.body}`;
9345
9925
  async execute() {
9346
9926
  const all = listMemories();
9347
9927
  if (all.length === 0) return "\uFF08\u6682\u65E0\u8BB0\u5FC6\uFF09";
9348
- const lines = all.slice().sort((a, b) => a.name.localeCompare(b.name)).map((m) => `- **${m.name}** (${m.type}) \u2014 ${m.description}`);
9928
+ const lines = all.slice().sort((a, b) => a.name.localeCompare(b.name)).map(memoryIndexLine);
9349
9929
  return `\u5171 ${all.length} \u6761\u8BB0\u5FC6\uFF1A
9350
9930
  ${lines.join("\n")}`;
9351
9931
  }
@@ -9355,7 +9935,7 @@ ${lines.join("\n")}`;
9355
9935
  type: "function",
9356
9936
  function: {
9357
9937
  name: "memory_delete",
9358
- description: "\u5220\u9664\u6307\u5B9A\u8BB0\u5FC6\uFF08\u5220\u9664\u78C1\u76D8\u6587\u4EF6\u5E76\u4ECE\u8FD0\u884C\u65F6\u6CE8\u518C\u8868\u6CE8\u9500\uFF09\u3002\u4EC5\u5F53\u4F60\u786E\u8BA4\u67D0\u6761\u8BB0\u5FC6\u5DF2\u5931\u6548/\u6709\u8BEF\u65F6\u4F7F\u7528\u3002",
9938
+ description: "\u5220\u9664\u6307\u5B9A\u8BB0\u5FC6\uFF08\u5220\u9664\u78C1\u76D8\u6587\u4EF6\u5E76\u4ECE\u8FD0\u884C\u65F6\u6CE8\u518C\u8868\u6CE8\u9500\uFF0C\u9700\u7528\u6237\u5BA1\u6279\uFF09\u3002\u4EC5\u5F53\u4F60\u786E\u8BA4\u67D0\u6761\u8BB0\u5FC6\u5DF2\u5931\u6548/\u6709\u8BEF\u65F6\u4F7F\u7528\u3002",
9359
9939
  parameters: {
9360
9940
  type: "object",
9361
9941
  properties: {
@@ -9363,14 +9943,19 @@ ${lines.join("\n")}`;
9363
9943
  },
9364
9944
  required: ["name"]
9365
9945
  },
9366
- safetyLevel: "safe" /* SAFE */,
9946
+ // ★ #9①(2026-09-16):SAFE → MUTATION。记忆目录在工作区沙箱外,被注入的工具结果可诱导
9947
+ // 删除跨会话记忆;删除又不可 undo(不在工作区备份范围)——必须人工过目。未声明
9948
+ // autoApproval → 分类器恒 'ask' → 转人工;allow-always 经 primaryArg 落精确名作用域规则。
9949
+ safetyLevel: "mutation" /* MUTATION */,
9367
9950
  isSync: true,
9951
+ primaryArg: "name",
9952
+ 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`,
9368
9953
  async execute(args) {
9369
9954
  const name = sanitizeLine(args?.name);
9370
9955
  const m = getMemory(name);
9371
- if (!m) return `\u274C \u672A\u627E\u5230\u8BB0\u5FC6\uFF1A${name}\uFF08\u53EF\u80FD\u5DF2\u88AB\u5220\u9664\uFF09\u3002`;
9956
+ if (!m) return toolFailure(`\u672A\u627E\u5230\u8BB0\u5FC6\uFF1A${name}\uFF08\u53EF\u80FD\u5DF2\u88AB\u5220\u9664\uFF09\u3002`);
9372
9957
  try {
9373
- await fs26.unlink(m.file);
9958
+ await fs27.unlink(m.file);
9374
9959
  } catch {
9375
9960
  }
9376
9961
  unregisterMemory(name);
@@ -9382,9 +9967,74 @@ ${lines.join("\n")}`;
9382
9967
  }
9383
9968
  });
9384
9969
 
9970
+ // ../core/src/session/sidecar.ts
9971
+ import fs28 from "fs/promises";
9972
+ import path31 from "path";
9973
+ var SIDECAR_ARCHIVED_MARK, sidecarArchivedNote, safeSidecarId, getToolOutputSidecarPath, getToolOutputSidecarDir, ensuredSidecarDirs, SIDECAR_SESSION_CAP_BYTES, listSidecarFiles, writeSidecarArchive;
9974
+ var init_sidecar = __esm({
9975
+ "../core/src/session/sidecar.ts"() {
9976
+ "use strict";
9977
+ init_store();
9978
+ SIDECAR_ARCHIVED_MARK = "\u5B8C\u6574\u539F\u6587\u5DF2\u5B58\u6863";
9979
+ sidecarArchivedNote = (toolCallId) => `${SIDECAR_ARCHIVED_MARK}\uFF0Crecall \u5DE5\u5177\u4F20 with_full="${toolCallId}" \u53EF\u53D6\u56DE`;
9980
+ safeSidecarId = (id) => String(id).replace(/[^A-Za-z0-9_-]/g, "");
9981
+ getToolOutputSidecarPath = (sessionId, toolCallId) => path31.join(getSessionsDirPath(sessionId), "tool-outputs", `${safeSidecarId(toolCallId)}.txt`);
9982
+ getToolOutputSidecarDir = (sessionId) => path31.join(getSessionsDirPath(sessionId), "tool-outputs");
9983
+ ensuredSidecarDirs = /* @__PURE__ */ new Set();
9984
+ SIDECAR_SESSION_CAP_BYTES = 64 * 1024 * 1024;
9985
+ listSidecarFiles = async (dir) => {
9986
+ let names;
9987
+ try {
9988
+ names = await fs28.readdir(dir);
9989
+ } catch {
9990
+ return [];
9991
+ }
9992
+ const out = [];
9993
+ for (const name of names) {
9994
+ try {
9995
+ const st = await fs28.stat(path31.join(dir, name));
9996
+ if (st.isFile()) out.push({ name, size: st.size, mtimeMs: st.mtimeMs });
9997
+ } catch {
9998
+ }
9999
+ }
10000
+ return out;
10001
+ };
10002
+ writeSidecarArchive = async (sessionId, toolCallId, content, opts) => {
10003
+ try {
10004
+ const dir = getToolOutputSidecarDir(sessionId);
10005
+ if (!ensuredSidecarDirs.has(dir)) {
10006
+ await fs28.mkdir(dir, { recursive: true });
10007
+ ensuredSidecarDirs.add(dir);
10008
+ }
10009
+ const cap = opts?.capBytes ?? SIDECAR_SESSION_CAP_BYTES;
10010
+ const incoming = Buffer.byteLength(content, "utf-8");
10011
+ const files = await listSidecarFiles(dir);
10012
+ const total = files.reduce((s, f) => s + f.size, 0);
10013
+ if (total + incoming > cap) {
10014
+ const byOldest = [...files].sort((a, b) => a.mtimeMs - b.mtimeMs);
10015
+ let freed = 0;
10016
+ for (const f of byOldest) {
10017
+ if (total - freed + incoming <= cap) break;
10018
+ try {
10019
+ await fs28.unlink(path31.join(dir, f.name));
10020
+ freed += f.size;
10021
+ } catch {
10022
+ }
10023
+ }
10024
+ }
10025
+ await fs28.writeFile(getToolOutputSidecarPath(sessionId, toolCallId), content, "utf-8");
10026
+ return sidecarArchivedNote(toolCallId);
10027
+ } catch (e) {
10028
+ console.warn("\u26A0\uFE0F [sidecar] \u5DE5\u5177\u539F\u6587\u5B58\u6863\u5931\u8D25\uFF08\u5DF2\u964D\u7EA7\u4E3A\u666E\u901A\u622A\u65AD\u63D0\u793A\uFF09:", e instanceof Error ? e.message : e);
10029
+ return void 0;
10030
+ }
10031
+ };
10032
+ }
10033
+ });
10034
+
9385
10035
  // ../core/src/tool/registry/recall.ts
9386
- import fs27 from "fs/promises";
9387
- import path30 from "path";
10036
+ import fs29 from "fs/promises";
10037
+ import path32 from "path";
9388
10038
  var escapeRegExp2, lineText, primaryPathArg, annotateStaleness, snippetAround, recallTools;
9389
10039
  var init_recall = __esm({
9390
10040
  "../core/src/tool/registry/recall.ts"() {
@@ -9392,7 +10042,7 @@ var init_recall = __esm({
9392
10042
  init_type();
9393
10043
  init_guard();
9394
10044
  init_transcript();
9395
- init_store();
10045
+ init_sidecar();
9396
10046
  escapeRegExp2 = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9397
10047
  lineText = (l) => {
9398
10048
  let t = "";
@@ -9415,9 +10065,9 @@ var init_recall = __esm({
9415
10065
  annotateStaleness = async (args, observedAt) => {
9416
10066
  const p = primaryPathArg(args);
9417
10067
  if (!p) return "\u5FEB\u7167\uFF08\u65E0\u8DEF\u5F84\u5B9E\u4F53\uFF0C\u672A\u7ECF\u91CD\u9A8C\u2014\u2014\u636E\u4EE5\u884C\u52A8\u524D\u8BF7\u91CD\u8DD1\u9A8C\u8BC1\uFF09";
9418
- const abs = path30.isAbsolute(p) ? p : path30.resolve(getActiveWorkspaceRoot(), p);
10068
+ const abs = path32.isAbsolute(p) ? p : path32.resolve(getActiveWorkspaceRoot(), p);
9419
10069
  try {
9420
- const st = await fs27.stat(abs);
10070
+ const st = await fs29.stat(abs);
9421
10071
  if (observedAt && st.mtime.toISOString() > observedAt) {
9422
10072
  return `\u26A0\uFE0F \u89C2\u5BDF\u540E\u8BE5\u6587\u4EF6\u5DF2\u88AB\u4FEE\u6539\uFF08${p}\uFF09\u2014\u2014\u5FC5\u987B\u91CD\u65B0 read_file \u540E\u624D\u80FD\u636E\u4EE5\u4FEE\u6539`;
9423
10073
  }
@@ -9457,17 +10107,17 @@ var init_recall = __esm({
9457
10107
  // ★ 检索结果预算与 web 等工具对齐 16000 口径;命中数 × 单片段双重限流防 context thrashing
9458
10108
  maxOutputCharacters: 16e3,
9459
10109
  async execute(args, ctx) {
9460
- if (!ctx?.sessionId) return "\u274C [recall] \u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\uFF0C\u65E0\u6CD5\u68C0\u7D22\u3002";
10110
+ if (!ctx?.sessionId) return toolFailure("[recall] \u7F3A\u5C11\u4F1A\u8BDD\u4E0A\u4E0B\u6587\uFF08sessionId\uFF09\uFF0C\u65E0\u6CD5\u68C0\u7D22\u3002");
9461
10111
  try {
9462
10112
  if (args.with_full) {
9463
- const safeId = String(args.with_full).replace(/[^A-Za-z0-9_-]/g, "");
9464
- if (!safeId) return "\u274C [recall] with_full \u542B\u975E\u6CD5\u5B57\u7B26\u3002";
9465
- const sidecar = path30.join(getSessionsDirPath(ctx.sessionId), "tool-outputs", `${safeId}.txt`);
10113
+ const safeId = safeSidecarId(args.with_full);
10114
+ if (!safeId) return toolFailure("[recall] with_full \u542B\u975E\u6CD5\u5B57\u7B26\u3002");
10115
+ const sidecar = getToolOutputSidecarPath(ctx.sessionId, args.with_full);
9466
10116
  let full = "";
9467
10117
  try {
9468
- full = await fs27.readFile(sidecar, "utf-8");
10118
+ full = await fs29.readFile(sidecar, "utf-8");
9469
10119
  } catch {
9470
- return `\u274C [recall] \u672A\u627E\u5230\u8BE5 id \u7684\u5B58\u6863\u539F\u6587\uFF08\u53EF\u80FD\u672A\u66FE\u89E6\u53D1\u622A\u65AD\u5B58\u6863\u3001\u6216\u4F1A\u8BDD\u5DF2\u6E05\u7406\uFF09\uFF1A${args.with_full}`;
10120
+ return toolFailure(`[recall] \u672A\u627E\u5230\u8BE5 id \u7684\u5B58\u6863\u539F\u6587\uFF08\u53EF\u80FD\u672A\u66FE\u89E6\u53D1\u622A\u65AD\u5B58\u6863\u3001\u6216\u4F1A\u8BDD\u5DF2\u6E05\u7406\uFF09\uFF1A${args.with_full}`);
9471
10121
  }
9472
10122
  const CHUNK = 14e3;
9473
10123
  const off = Math.max(0, Math.floor(args.full_offset ?? 0));
@@ -9480,7 +10130,7 @@ var init_recall = __esm({
9480
10130
  ${slice}${more}`;
9481
10131
  }
9482
10132
  const cleanQuery = (args.query || "").trim();
9483
- if (!cleanQuery) return "\u274C [recall] \u68C0\u7D22\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\uFF08\u6216\u6539\u7528 with_full \u53D6\u5B58\u6863\u5168\u6587\uFF09\u3002";
10133
+ if (!cleanQuery) return toolFailure("[recall] \u68C0\u7D22\u5173\u952E\u8BCD\u4E0D\u80FD\u4E3A\u7A7A\uFF08\u6216\u6539\u7528 with_full \u53D6\u5B58\u6863\u5168\u6587\uFF09\u3002");
9484
10134
  const re = new RegExp(args.is_regex ? cleanQuery : escapeRegExp2(cleanQuery), "i");
9485
10135
  const lines = await readTranscriptLines(ctx.sessionId);
9486
10136
  const toolCallInfo = /* @__PURE__ */ new Map();
@@ -9497,6 +10147,7 @@ ${slice}${more}`;
9497
10147
  }
9498
10148
  const limit = Math.min(Math.max(Math.floor(args.limit ?? 6), 1), 12);
9499
10149
  const runFilter = args.run_id?.trim();
10150
+ const hasRunEvents = lines.some((l) => isEventLine(l) && l.dscEvent === "run.start");
9500
10151
  const blocks = [];
9501
10152
  let runN = 0;
9502
10153
  let runId = "";
@@ -9510,7 +10161,7 @@ ${slice}${more}`;
9510
10161
  }
9511
10162
  const role = l.role;
9512
10163
  if (role === "system") continue;
9513
- if (runFilter && !(runId && runId.startsWith(runFilter))) continue;
10164
+ if (runFilter && hasRunEvents && !(runId && runId.startsWith(runFilter))) continue;
9514
10165
  let info;
9515
10166
  if (role === "tool") {
9516
10167
  info = toolCallInfo.get(l.tool_call_id);
@@ -9524,7 +10175,7 @@ ${slice}${more}`;
9524
10175
  if (role === "tool") {
9525
10176
  header += `
9526
10177
  \u6821\u9A8C\uFF1A${await annotateStaleness(info?.args, ts)}`;
9527
- if (typeof l.content === "string" && l.content.includes("\u5B8C\u6574\u539F\u6587\u5DF2\u5B58\u6863")) {
10178
+ if (typeof l.content === "string" && l.content.includes(SIDECAR_ARCHIVED_MARK)) {
9528
10179
  header += `
9529
10180
  \u2702\uFE0F \u8BE5\u7ED3\u679C\u66FE\u88AB\u622A\u65AD\uFF0C\u4F20 with_full="${l.tool_call_id}" \u53EF\u53D6\u5B58\u6863\u5168\u6587`;
9530
10181
  }
@@ -9549,7 +10200,7 @@ ${snippetAround(text, re, 700)}`);
9549
10200
  "\u63D0\u793A\uFF1A\u26A0\uFE0F \u6807\u6CE8\u7684\u6587\u4EF6\u5FC5\u987B\u91CD\u65B0 read_file \u9A8C\u8BC1\u540E\u624D\u80FD\u636E\u4EE5\u4FEE\u6539\uFF1B\u672C\u5DE5\u5177\u7ED3\u679C\u4EC5\u4F5C\u5386\u53F2\u7EBF\u7D22\uFF0C\u4E0D\u66FF\u4EE3\u5F53\u524D\u6587\u4EF6\u72B6\u6001\u3002"
9550
10201
  ].join("\n");
9551
10202
  } catch (e) {
9552
- return `\u274C [recall] \u68C0\u7D22\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`;
10203
+ return toolFailure(`[recall] \u68C0\u7D22\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
9553
10204
  }
9554
10205
  }
9555
10206
  }
@@ -9620,7 +10271,7 @@ var init_spreadsheet = __esm({
9620
10271
  const readBlock = await assertReadable(absPath, args.path);
9621
10272
  if (readBlock) return readBlock;
9622
10273
  if (!absPath.toLowerCase().endsWith(".xlsx")) {
9623
- return `\u274C [\u683C\u5F0F\u4E0D\u652F\u6301]\uFF1A[${args.path}] \u4E0D\u662F .xlsx\u3002read_xlsx \u4EC5\u89E3\u6790 Excel \u5DE5\u4F5C\u7C3F\uFF08.xls \u8BF7\u5148\u53E6\u5B58\u4E3A .xlsx\uFF09\uFF1B\u5176\u5B83\u6587\u4EF6\u8BF7\u7528 read_file\u3002`;
10274
+ return toolFailure(`[\u683C\u5F0F\u4E0D\u652F\u6301]\uFF1A[${args.path}] \u4E0D\u662F .xlsx\u3002read_xlsx \u4EC5\u89E3\u6790 Excel \u5DE5\u4F5C\u7C3F\uFF08.xls \u8BF7\u5148\u53E6\u5B58\u4E3A .xlsx\uFF09\uFF1B\u5176\u5B83\u6587\u4EF6\u8BF7\u7528 read_file\u3002`);
9624
10275
  }
9625
10276
  const startRow = args.start_row ? Math.max(1, Math.floor(args.start_row)) : 1;
9626
10277
  const maxRows = Math.min(args.max_rows ? Math.max(1, Math.floor(args.max_rows)) : 200, 1e3);
@@ -9664,7 +10315,7 @@ var init_spreadsheet = __esm({
9664
10315
  [... \u540E\u9762\u8FD8\u6709 ${rowCount - endRow} \u884C\uFF0C\u53EF\u628A start_row \u8BBE\u4E3A ${endRow + 1} \u7EE7\u7EED\u7FFB\u9875\u3002]`);
9665
10316
  return lines.join("\n");
9666
10317
  } catch (error) {
9667
- return `\u8BFB\u53D6 Excel \u5931\u8D25 [${args.path}]: ${error.message}`;
10318
+ return toolFailure(`\u8BFB\u53D6 Excel \u5931\u8D25 [${args.path}]: ${error.message}`);
9668
10319
  }
9669
10320
  }
9670
10321
  }
@@ -9674,7 +10325,7 @@ var init_spreadsheet = __esm({
9674
10325
  });
9675
10326
 
9676
10327
  // ../core/src/tool/registry/document.ts
9677
- import fs28 from "fs/promises";
10328
+ import fs30 from "fs/promises";
9678
10329
  var getMammoth, getUnpdf, documentTools;
9679
10330
  var init_document = __esm({
9680
10331
  "../core/src/tool/registry/document.ts"() {
@@ -9713,7 +10364,7 @@ var init_document = __esm({
9713
10364
  const readBlock = await assertReadable(absPath, args.path);
9714
10365
  if (readBlock) return readBlock;
9715
10366
  if (!absPath.toLowerCase().endsWith(".docx")) {
9716
- return `\u274C [\u683C\u5F0F\u4E0D\u652F\u6301]\uFF1A[${args.path}] \u4E0D\u662F .docx\u3002read_docx \u4EC5\u89E3\u6790 Word \u6587\u6863\uFF08.doc \u8001\u683C\u5F0F\u8BF7\u5148\u53E6\u5B58\u4E3A .docx\uFF09\uFF1B\u5176\u5B83\u6587\u4EF6\u7528 read_file\u3002`;
10367
+ return toolFailure(`[\u683C\u5F0F\u4E0D\u652F\u6301]\uFF1A[${args.path}] \u4E0D\u662F .docx\u3002read_docx \u4EC5\u89E3\u6790 Word \u6587\u6863\uFF08.doc \u8001\u683C\u5F0F\u8BF7\u5148\u53E6\u5B58\u4E3A .docx\uFF09\uFF1B\u5176\u5B83\u6587\u4EF6\u7528 read_file\u3002`);
9717
10368
  }
9718
10369
  const start = args.start_char ? Math.max(0, Math.floor(args.start_char)) : 0;
9719
10370
  const maxChars = Math.min(args.max_chars ? Math.max(1, Math.floor(args.max_chars)) : 3e4, 5e4);
@@ -9731,7 +10382,7 @@ var init_document = __esm({
9731
10382
  [... \u540E\u9762\u8FD8\u6709 ${total - (start + slice.length)} \u5B57\u7B26\uFF0C\u53EF\u628A start_char \u8BBE\u4E3A ${start + slice.length} \u7EE7\u7EED\u7FFB\u9875\u3002]`);
9732
10383
  return lines.join("\n");
9733
10384
  } catch (error) {
9734
- return `\u8BFB\u53D6 Word \u6587\u6863\u5931\u8D25 [${args.path}]: ${error.message}`;
10385
+ return toolFailure(`\u8BFB\u53D6 Word \u6587\u6863\u5931\u8D25 [${args.path}]: ${error.message}`);
9735
10386
  }
9736
10387
  }
9737
10388
  }
@@ -9760,11 +10411,11 @@ var init_document = __esm({
9760
10411
  const readBlock = await assertReadable(absPath, args.path);
9761
10412
  if (readBlock) return readBlock;
9762
10413
  if (!absPath.toLowerCase().endsWith(".pdf")) {
9763
- return `\u274C [\u683C\u5F0F\u4E0D\u652F\u6301]\uFF1A[${args.path}] \u4E0D\u662F .pdf\u3002read_pdf \u4EC5\u89E3\u6790 PDF \u6587\u6863\uFF1B\u5176\u5B83\u6587\u4EF6\u7528 read_file\u3002`;
10414
+ return toolFailure(`[\u683C\u5F0F\u4E0D\u652F\u6301]\uFF1A[${args.path}] \u4E0D\u662F .pdf\u3002read_pdf \u4EC5\u89E3\u6790 PDF \u6587\u6863\uFF1B\u5176\u5B83\u6587\u4EF6\u7528 read_file\u3002`);
9764
10415
  }
9765
10416
  const start = args.start_char ? Math.max(0, Math.floor(args.start_char)) : 0;
9766
10417
  const maxChars = Math.min(args.max_chars ? Math.max(1, Math.floor(args.max_chars)) : 3e4, 5e4);
9767
- const buf = await fs28.readFile(absPath);
10418
+ const buf = await fs30.readFile(absPath);
9768
10419
  const unpdf = await getUnpdf();
9769
10420
  const pdf = await unpdf.getDocumentProxy(new Uint8Array(buf));
9770
10421
  const { text, totalPages } = await unpdf.extractText(pdf, { mergePages: true });
@@ -9780,7 +10431,7 @@ var init_document = __esm({
9780
10431
  [... \u540E\u9762\u8FD8\u6709 ${total - (start + slice.length)} \u5B57\u7B26\uFF0C\u53EF\u628A start_char \u8BBE\u4E3A ${start + slice.length} \u7EE7\u7EED\u7FFB\u9875\u3002]`);
9781
10432
  return lines.join("\n");
9782
10433
  } catch (error) {
9783
- return `\u8BFB\u53D6 PDF \u5931\u8D25 [${args.path}]: ${error.message}`;
10434
+ return toolFailure(`\u8BFB\u53D6 PDF \u5931\u8D25 [${args.path}]: ${error.message}`);
9784
10435
  }
9785
10436
  }
9786
10437
  }
@@ -9795,7 +10446,9 @@ __export(tool_exports, {
9795
10446
  MAX_AGENT_DEPTH: () => MAX_AGENT_DEPTH,
9796
10447
  ToolExecutionResultStatus: () => ToolExecutionResultStatus,
9797
10448
  ToolSafetyLevel: () => ToolSafetyLevel,
9798
- agentTools: () => agentTools
10449
+ agentTools: () => agentTools,
10450
+ asToolFailure: () => asToolFailure,
10451
+ toolFailure: () => toolFailure
9799
10452
  });
9800
10453
  var agentTools, agentSubTools, workflowTools;
9801
10454
  var init_tool = __esm({
@@ -9887,9 +10540,9 @@ var init_lockManager = __esm({
9887
10540
 
9888
10541
  // ../core/src/agent/backgroundTool.ts
9889
10542
  function normalizeYield(v) {
9890
- if (typeof v === "string") return v;
9891
- if (v && typeof v === "object" && "content" in v) return String(v.content ?? "");
9892
- return String(v ?? "");
10543
+ if (typeof v === "string") return { content: v, status: "success" };
10544
+ if (v && typeof v === "object" && typeof v.content === "string" && (v.status === "success" || v.status === "failed")) return v;
10545
+ return { content: String(v ?? ""), status: "success" };
9893
10546
  }
9894
10547
  async function runBackgroundTool(gen, lockKey, toolName, signal) {
9895
10548
  let aborted = false;
@@ -9922,11 +10575,11 @@ async function runBackgroundTool(gen, lockKey, toolName, signal) {
9922
10575
  } catch (e) {
9923
10576
  console.warn("\u26A0\uFE0F \u5173\u95ED\u672A\u6D88\u8D39\u7684\u540E\u53F0 generator \u5931\u8D25:", e instanceof Error ? e.message : e);
9924
10577
  }
9925
- return `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u9501 [${lockKey}] \u5DF2\u88AB\u5360\u7528\uFF0C[${toolName}] \u672A\u542F\u52A8\u3002`;
10578
+ return { content: `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u9501 [${lockKey}] \u5DF2\u88AB\u5360\u7528\uFF0C[${toolName}] \u672A\u542F\u52A8\u3002`, status: "failed", errorCategory: "runtime" };
9926
10579
  }
9927
10580
  if (signal?.aborted) {
9928
10581
  await finalize();
9929
- return `\u274C [\u5DF2\u4E2D\u6B62]\uFF1A[${toolName}] \u540E\u53F0\u4EFB\u52A1\u672A\u542F\u52A8\uFF08\u7528\u6237\u5DF2\u4E2D\u65AD\uFF09\u3002`;
10582
+ return toolFailure(`[\u5DF2\u4E2D\u6B62]\uFF1A[${toolName}] \u540E\u53F0\u4EFB\u52A1\u672A\u542F\u52A8\uFF08\u7528\u6237\u5DF2\u4E2D\u65AD\uFF09\u3002`);
9930
10583
  }
9931
10584
  if (signal) signal.addEventListener("abort", onAbort, { once: true });
9932
10585
  let first;
@@ -9934,9 +10587,9 @@ async function runBackgroundTool(gen, lockKey, toolName, signal) {
9934
10587
  first = await gen.next();
9935
10588
  } catch (e) {
9936
10589
  await finalize();
9937
- return `\u274C [\u540E\u53F0\u542F\u52A8\u5931\u8D25]\uFF1A${e?.message ?? e}`;
10590
+ return toolFailure(`[\u540E\u53F0\u542F\u52A8\u5931\u8D25]\uFF1A${e?.message ?? e}`);
9938
10591
  }
9939
- const immediate = first.done ? "(\u540E\u53F0\u4EFB\u52A1\u672A\u4EA7\u51FA\u5373\u65F6\u7ED3\u679C\uFF0C\u5DF2\u76F4\u63A5\u7ED3\u675F)" : normalizeYield(first.value);
10592
+ const immediate = first.done ? { content: "(\u540E\u53F0\u4EFB\u52A1\u672A\u4EA7\u51FA\u5373\u65F6\u7ED3\u679C\uFF0C\u5DF2\u76F4\u63A5\u7ED3\u675F)", status: "success" } : normalizeYield(first.value);
9940
10593
  if (first.done) {
9941
10594
  await finalize();
9942
10595
  return immediate;
@@ -9966,19 +10619,17 @@ var init_backgroundTool = __esm({
9966
10619
  "use strict";
9967
10620
  init_lockManager();
9968
10621
  init_config();
10622
+ init_type();
9969
10623
  }
9970
10624
  });
9971
10625
 
9972
10626
  // ../core/src/tool/autoPermission.ts
9973
- import path31 from "path";
9974
- var AUTO_SCOPE, AUTO_DEFAULT_EXTRA, AUTO_AGGRESSIVE_EXTRA, isMcpTool, BUILTIN_AUTO_DENY, matchBuiltinDeny, COMMAND_DENY, matchCommandDeny, hasShellMetachars, READONLY_HEADS, PKG_MGR, SCRIPT_RUNNER_HEADS, SCRIPT_RUNNER_READONLY, isScriptRunnerCommand, hasWriteModifier, SENSITIVE_FILE_ARG_RE, hasSensitiveFileArg, isReadOnlyCommand, isWithinWorkspace, runAutoCheck;
10627
+ import path33 from "path";
10628
+ var isMcpTool, BUILTIN_AUTO_DENY, matchBuiltinDeny, COMMAND_DENY, matchCommandDeny, hasShellMetachars, READONLY_HEADS, PKG_MGR, SCRIPT_RUNNER_HEADS, SCRIPT_RUNNER_READONLY, isScriptRunnerCommand, hasWriteModifier, SENSITIVE_FILE_ARG_RE, hasSensitiveFileArg, isReadOnlyCommand, isWithinWorkspace, runAutoCheck;
9975
10629
  var init_autoPermission = __esm({
9976
10630
  "../core/src/tool/autoPermission.ts"() {
9977
10631
  "use strict";
9978
10632
  init_model();
9979
- AUTO_SCOPE = /* @__PURE__ */ new Set(["edit_file", "write_file", "create_file", "delete_path", "move_file"]);
9980
- AUTO_DEFAULT_EXTRA = /* @__PURE__ */ new Set(["run_command", "run_in_background"]);
9981
- AUTO_AGGRESSIVE_EXTRA = /* @__PURE__ */ new Set(["web_fetch", "web_search", "git_commit"]);
9982
10633
  isMcpTool = (name) => name.startsWith("mcp__");
9983
10634
  BUILTIN_AUTO_DENY = [
9984
10635
  /(^|[\\/])\.env(\.|$)/i,
@@ -10118,27 +10769,28 @@ var init_autoPermission = __esm({
10118
10769
  isWithinWorkspace = (p, cwd) => {
10119
10770
  if (!p || !cwd) return false;
10120
10771
  try {
10121
- const abs = path31.isAbsolute(p) ? p : path31.resolve(cwd, p);
10122
- const rel = path31.relative(cwd, abs);
10123
- return rel === "" || !rel.startsWith("..") && !path31.isAbsolute(rel);
10772
+ const abs = path33.isAbsolute(p) ? p : path33.resolve(cwd, p);
10773
+ const rel = path33.relative(cwd, abs);
10774
+ return rel === "" || !rel.startsWith("..") && !path33.isAbsolute(rel);
10124
10775
  } catch {
10125
10776
  return false;
10126
10777
  }
10127
10778
  };
10128
- runAutoCheck = async (name, args, ctx, aggressive = false) => {
10129
- const isFileTool = AUTO_SCOPE.has(name);
10130
- const inScope = isFileTool || AUTO_DEFAULT_EXTRA.has(name) || aggressive && (AUTO_AGGRESSIVE_EXTRA.has(name) || isMcpTool(name));
10779
+ runAutoCheck = async (name, args, ctx, aggressive = false, decl) => {
10780
+ const isFileTool = decl?.autoApproval === "file";
10781
+ const inScope = isFileTool || decl?.autoApproval === "command" || aggressive && (decl?.autoApproval === "aggressive" || isMcpTool(name));
10131
10782
  if (!inScope) return "ask";
10132
10783
  if (isFileTool) {
10133
- const paths = name === "move_file" ? [args?.source, args?.destination] : [args?.path];
10784
+ const paths = (decl.pathArgs ?? ["path"]).map((k) => args?.[k]);
10134
10785
  for (const p of paths) {
10135
10786
  const s = typeof p === "string" ? p : "";
10136
10787
  if (!isWithinWorkspace(s, ctx.cwd)) return "ask";
10137
10788
  if (matchBuiltinDeny(s)) return "deny";
10138
10789
  }
10139
10790
  }
10140
- if (name === "run_command" || name === "run_in_background") {
10141
- const cmd = typeof args?.command === "string" ? args.command : "";
10791
+ if (decl?.autoApproval === "command") {
10792
+ const argKey = decl.primaryArg ?? "command";
10793
+ const cmd = typeof args?.[argKey] === "string" ? args[argKey] : "";
10142
10794
  if (matchCommandDeny(cmd)) return "deny";
10143
10795
  }
10144
10796
  const v = await classifyToolRisk(name, args, "", ctx.abortSignal);
@@ -10148,14 +10800,15 @@ var init_autoPermission = __esm({
10148
10800
  });
10149
10801
 
10150
10802
  // ../core/src/agent/toolExecution.ts
10151
- import fs29 from "fs/promises";
10152
- import path32 from "path";
10803
+ import path34 from "path";
10153
10804
  var applyPrivacyMasking, processToolCall;
10154
10805
  var init_toolExecution = __esm({
10155
10806
  "../core/src/agent/toolExecution.ts"() {
10156
10807
  "use strict";
10157
10808
  init_truncate();
10809
+ init_toolResultCollect();
10158
10810
  init_tool();
10811
+ init_argsValidator();
10159
10812
  init_guard();
10160
10813
  init_permissions();
10161
10814
  init_hooks();
@@ -10165,8 +10818,7 @@ var init_toolExecution = __esm({
10165
10818
  init_autoPermission();
10166
10819
  init_loader();
10167
10820
  init_config();
10168
- init_store();
10169
- init_planMode();
10821
+ init_sidecar();
10170
10822
  applyPrivacyMasking = (rules3, args, output) => {
10171
10823
  if (!rules3) return output;
10172
10824
  try {
@@ -10231,18 +10883,25 @@ var init_toolExecution = __esm({
10231
10883
  const placeholder = "\uFF08\u5DF2\u4E2D\u6B62\uFF0C\u672A\u6267\u884C\uFF09";
10232
10884
  return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: placeholder, resultForUser: placeholder, ok: false, aborted: true };
10233
10885
  }
10234
- if (!parseFailed && planMode && appConfig.planEnforcement === "runtime" && calledName !== "enter_plan_mode" && calledName !== "exit_plan_mode" && !PLAN_ALLOWED_TOOLS.has(calledName)) {
10886
+ const matchedTool = rawTools.find((t) => t.function.name === calledName);
10887
+ if (!parseFailed && planMode && appConfig.planEnforcement === "runtime" && calledName !== "enter_plan_mode" && calledName !== "exit_plan_mode" && matchedTool?.function?.planAllowed !== true) {
10235
10888
  const note = `\u274C [\u8BA1\u5212\u6A21\u5F0F] \u5F53\u524D\u4E3A\u53EA\u8BFB\u8C03\u7814\u9636\u6BB5\uFF0C\u7981\u6B62 ${calledName}\u3002\u5B8C\u6210\u65B9\u6848\u8BF7\u8C03 exit_plan_mode \u63D0\u4EA4\uFF0C\u7ECF\u7528\u6237\u5BA1\u6279\u540E\u8FDB\u5165\u5B9E\u73B0\u9636\u6BB5\u3002`;
10236
10889
  events({ sessionId, eventType: "tool.validation.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: note } });
10237
10890
  return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel: note, resultForUser: note, ok: false };
10238
10891
  }
10239
- const matchedTool = rawTools.find((t) => t.function.name === calledName);
10240
10892
  const toolCtx = { sessionId, cwd: getActiveWorkspaceRoot(), abortSignal: signal, depth, keepRecentUnits, compactRatio, modelWindow, parentSystemPrompt, events, onUIEvent, requestApproval: hostRequestApproval, requestQuestion: hostRequestQuestion, emitProgress: (m) => onUIEvent?.({ type: "tool.progress", toolsId: toolCall.id, toolName: calledName, message: m }), permissionMode };
10241
10893
  let result = "";
10242
- let explicitOk = null;
10894
+ let resultStatus = "success";
10895
+ let errorCategory;
10896
+ const failResult = (text, cat = "runtime") => {
10897
+ result = text;
10898
+ resultStatus = "failed";
10899
+ errorCategory = cat;
10900
+ };
10243
10901
  let postHookOverride;
10902
+ let imageAttachments;
10244
10903
  if (parseFailed) {
10245
- result = `\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u6A21\u578B\u8FD4\u56DE\u7684 arguments \u4E0D\u662F\u5408\u6CD5 JSON${JSON.stringify(toolCall).slice(0, 300)}`;
10904
+ failResult(`\u53C2\u6570\u89E3\u6790\u5931\u8D25\uFF1A\u6A21\u578B\u8FD4\u56DE\u7684 arguments \u4E0D\u662F\u5408\u6CD5 JSON${JSON.stringify(toolCall).slice(0, 300)}`, "syntax");
10246
10905
  events({ sessionId, eventType: "tool.validation.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: result } });
10247
10906
  } else if (matchedTool && typeof matchedTool.function.execute === "function") {
10248
10907
  const level = matchedTool.function.safetyLevel;
@@ -10259,24 +10918,36 @@ var init_toolExecution = __esm({
10259
10918
  }
10260
10919
  if (veto.deny) {
10261
10920
  denied = true;
10262
- result = veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`;
10921
+ failResult(veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
10263
10922
  } else if (veto.argsOverride !== void 0 && veto.argsOverride !== null && typeof veto.argsOverride === "object" && !Array.isArray(veto.argsOverride)) {
10264
10923
  console.log(`\u270F\uFE0F [Pre-hook] [${calledName}] args \u5DF2\u88AB hook \u6539\u5199`);
10265
10924
  calledArgs = veto.argsOverride;
10266
10925
  }
10267
10926
  }
10268
- const protectedPaths = calledName === "move_file" ? [calledArgs?.source, calledArgs?.destination] : isUndoTrigger(calledName) ? [calledArgs?.path] : [];
10927
+ {
10928
+ const paramsSchema = matchedTool.function.parameters;
10929
+ if (paramsSchema && typeof paramsSchema === "object" && !Array.isArray(paramsSchema)) {
10930
+ const verdict = validateToolArgs(paramsSchema, calledArgs);
10931
+ if (!verdict.ok) {
10932
+ denied = true;
10933
+ failResult(`\u274C \u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A${verdict.message}\uFF1B\u8BF7\u4FEE\u6B63\u53C2\u6570\u540E\u91CD\u8BD5\uFF08schema \u8981\u6C42\u89C1\u5DE5\u5177 parameters\uFF09\u3002`, "syntax");
10934
+ events({ sessionId, eventType: "tool.validation.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: result } });
10935
+ }
10936
+ }
10937
+ }
10938
+ const pathArgs = matchedTool.function.pathArgs ?? (matchedTool.function.triggersUndo ? ["path"] : []);
10939
+ const protectedPaths = pathArgs.map((k) => calledArgs?.[k]);
10269
10940
  const hitProtected = protectedPaths.find((p) => typeof p === "string" && isProtectedWrite(p, toolCtx.cwd));
10270
10941
  if (hitProtected) {
10271
10942
  denied = true;
10272
- result = `\u274C [\u4FDD\u62A4\u8DEF\u5F84] \u7981\u6B62\u4FEE\u6539\u53D7\u4FDD\u62A4\u76EE\u5F55\uFF08.git/.ssh/.aws/.deepseeker-code \u7B49 VCS/\u51ED\u8BC1/\u914D\u7F6E\uFF09\uFF1A${hitProtected}\u3002`;
10943
+ failResult(`\u274C [\u4FDD\u62A4\u8DEF\u5F84] \u7981\u6B62\u4FEE\u6539\u53D7\u4FDD\u62A4\u76EE\u5F55\uFF08.git/.ssh/.aws/.deepseeker-code \u7B49 VCS/\u51ED\u8BC1/\u914D\u7F6E\uFF09\uFF1A${hitProtected}\u3002`, "permission");
10273
10944
  }
10274
10945
  let perm = null;
10275
10946
  try {
10276
- perm = checkPermission(resolveMcpPermissionName(calledName, calledArgs), calledArgs);
10947
+ perm = checkPermission(resolveMcpPermissionName(calledName, calledArgs), calledArgs, matchedTool.function.primaryArg);
10277
10948
  if (perm === "deny") {
10278
10949
  denied = true;
10279
- result = `\u274C [\u6743\u9650\u89C4\u5219]\uFF1A[${calledName}] \u88AB\u6743\u9650\u89C4\u5219 deny \u62D2\u7EDD\u3002`;
10950
+ failResult(`\u274C [\u6743\u9650\u89C4\u5219]\uFF1A[${calledName}] \u88AB\u6743\u9650\u89C4\u5219 deny \u62D2\u7EDD\u3002`, "permission");
10280
10951
  } else if (perm === "allow") {
10281
10952
  needApproval = false;
10282
10953
  } else if (perm === "ask") {
@@ -10289,7 +10960,7 @@ var init_toolExecution = __esm({
10289
10960
  const argCwd = typeof calledArgs?.cwd === "string" ? calledArgs.cwd.trim() : "";
10290
10961
  const samePath = (a, b) => {
10291
10962
  try {
10292
- const ra = path32.resolve(a), rb = path32.resolve(b);
10963
+ const ra = path34.resolve(a), rb = path34.resolve(b);
10293
10964
  return process.platform === "win32" ? ra.toLowerCase() === rb.toLowerCase() : ra === rb;
10294
10965
  } catch {
10295
10966
  return false;
@@ -10303,21 +10974,27 @@ var init_toolExecution = __esm({
10303
10974
  }
10304
10975
  if ((calledName === "run_command" || calledName === "run_in_background") && matchCommandDeny(typeof calledArgs?.command === "string" ? calledArgs.command : "")) {
10305
10976
  denied = true;
10306
- result = `\u274C [\u5B89\u5168] \u707E\u96BE\u547D\u4EE4\u6E05\u5355\u62E6\u622A\uFF08\u4E0D\u53EF\u88AB allow \u89C4\u5219\u7ED5\u8FC7\uFF09\uFF1A[${calledName}] ${String(calledArgs?.command ?? "").slice(0, 100)}`;
10977
+ failResult(`\u274C [\u5B89\u5168] \u707E\u96BE\u547D\u4EE4\u6E05\u5355\u62E6\u622A\uFF08\u4E0D\u53EF\u88AB allow \u89C4\u5219\u7ED5\u8FC7\uFF09\uFF1A[${calledName}] ${String(calledArgs?.command ?? "").slice(0, 100)}`, "permission");
10307
10978
  }
10308
10979
  const isBgTool = matchedTool.function.isSync === false;
10309
10980
  const lockKey = isBgTool ? computeLockKey(matchedTool.function.exclusiveLock, calledArgs, toolCtx) : null;
10310
10981
  if (lockKey && isLockHeld(lockKey)) {
10311
10982
  denied = true;
10312
- result = `\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u5DF2\u6709\u540E\u53F0\u4EFB\u52A1\u6301\u6709\u9501 [${lockKey}]\uFF0C[${calledName}] \u8C03\u7528\u88AB\u8DF3\u8FC7\u3002`;
10983
+ failResult(`\u{1F512} [\u4E92\u65A5\u9501\u963B\u585E]\uFF1A\u5DF2\u6709\u540E\u53F0\u4EFB\u52A1\u6301\u6709\u9501 [${lockKey}]\uFF0C[${calledName}] \u8C03\u7528\u88AB\u8DF3\u8FC7\u3002`);
10313
10984
  }
10314
10985
  if (needApproval && !denied && !scriptRunnerBlocked) {
10315
- const auto = await runAutoCheck(resolveMcpPermissionName(calledName, calledArgs), calledArgs, toolCtx, permissionMode === "auto");
10986
+ const auto = await runAutoCheck(
10987
+ resolveMcpPermissionName(calledName, calledArgs),
10988
+ calledArgs,
10989
+ toolCtx,
10990
+ permissionMode === "auto",
10991
+ { autoApproval: matchedTool.function.autoApproval, pathArgs: matchedTool.function.pathArgs, primaryArg: matchedTool.function.primaryArg }
10992
+ );
10316
10993
  if (auto === "allow") {
10317
10994
  needApproval = false;
10318
10995
  } else if (auto === "deny") {
10319
10996
  denied = true;
10320
- result = `\u274C [auto] \u5185\u7F6E\u9AD8\u5371\u6E05\u5355\u62E6\u622A\uFF1A[${calledName}] ${calledArgs?.path ?? ""}\u3002`;
10997
+ failResult(`\u274C [auto] \u5185\u7F6E\u9AD8\u5371\u6E05\u5355\u62E6\u622A\uFF1A[${calledName}] ${calledArgs?.path ?? ""}\u3002`, "permission");
10321
10998
  }
10322
10999
  }
10323
11000
  if (needApproval && !denied) {
@@ -10327,19 +11004,19 @@ var init_toolExecution = __esm({
10327
11004
  if (ra) detail = typeof ra === "function" ? await ra(calledArgs, toolCtx) : ra;
10328
11005
  } catch (e) {
10329
11006
  denied = true;
10330
- result = `\u274C [\u5BA1\u6279\u63CF\u8FF0\u751F\u6210\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`;
11007
+ failResult(`\u274C [\u5BA1\u6279\u63CF\u8FF0\u751F\u6210\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
10331
11008
  }
10332
11009
  if (!denied) {
10333
- let approved = false;
11010
+ let approved2 = false;
10334
11011
  try {
10335
- approved = await requestApproval(calledName, toolCall.id, detail, toolCtx, level, calledArgs);
11012
+ approved2 = await requestApproval(calledName, toolCall.id, detail, toolCtx, level, calledArgs, matchedTool.function.primaryArg);
10336
11013
  } catch (e) {
10337
11014
  denied = true;
10338
- result = `\u274C [\u5BA1\u6279\u6D41\u7A0B\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`;
11015
+ failResult(`\u274C [\u5BA1\u6279\u6D41\u7A0B\u5F02\u5E38]\uFF1A${e?.message ?? e}\u3002\u51FA\u4E8E\u5B89\u5168\u9ED8\u8BA4\u62D2\u7EDD [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
10339
11016
  }
10340
- if (!approved && !denied) {
11017
+ if (!approved2 && !denied) {
10341
11018
  denied = true;
10342
- result = `\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u7528\u6237\u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u7533\u8BF7\u3002`;
11019
+ failResult(`\u274C [\u5B89\u5168\u7194\u65AD]\uFF1A\u7528\u6237\u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u7533\u8BF7\u3002`, "permission");
10343
11020
  }
10344
11021
  }
10345
11022
  }
@@ -10352,26 +11029,26 @@ var init_toolExecution = __esm({
10352
11029
  }
10353
11030
  if (veto.deny) {
10354
11031
  denied = true;
10355
- result = veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`;
11032
+ failResult(veto.reason || `\u274C [Hook \u62E6\u622A]\uFF1Apre-hook \u62D2\u7EDD\u4E86 [${calledName}] \u7684\u6267\u884C\u3002`, "permission");
10356
11033
  }
10357
11034
  }
10358
- if (!denied && isUndoTrigger(calledName)) {
11035
+ if (!denied && matchedTool.function.triggersUndo) {
10359
11036
  try {
10360
- await beforeMutationBackup(calledName, calledArgs, toolCall.id, sessionId);
11037
+ await beforeMutationBackup(matchedTool.function.triggersUndo, calledName, calledArgs, toolCall.id, sessionId);
10361
11038
  } catch (e) {
10362
11039
  denied = true;
10363
- result = `\u274C [Undo \u5907\u4EFD\u5931\u8D25\xB7\u5B89\u5168\u7194\u65AD]\uFF1A${e?.message ?? e}\u3002\u5199\u5165\u5DF2\u963B\u6B62\uFF08\u51E1\u6539\u5FC5\u53EF\u56DE\u9000\u539F\u5219\uFF09\u3002`;
11040
+ failResult(`\u274C [Undo \u5907\u4EFD\u5931\u8D25\xB7\u5B89\u5168\u7194\u65AD]\uFF1A${e?.message ?? e}\u3002\u5199\u5165\u5DF2\u963B\u6B62\uFF08\u51E1\u6539\u5FC5\u53EF\u56DE\u9000\u539F\u5219\uFF09\u3002`);
10364
11041
  }
10365
11042
  }
10366
11043
  if (!denied) {
10367
11044
  try {
10368
11045
  events({ sessionId, eventType: "tool.execute.start", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin" }, payload: { output: JSON.stringify(calledArgs) } });
10369
11046
  const execRet = matchedTool.function.execute(calledArgs, toolCtx);
10370
- if (isBgTool) {
10371
- result = await runBackgroundTool(execRet, lockKey, calledName, signal);
10372
- } else {
10373
- result = await collectToolResult(execRet, (chunk) => toolCtx.emitProgress?.(chunk), signal);
10374
- }
11047
+ const collected = isBgTool ? await runBackgroundTool(execRet, lockKey, calledName, signal) : await collectToolResult(execRet, (chunk) => toolCtx.emitProgress?.(chunk), signal);
11048
+ result = collected.content;
11049
+ resultStatus = collected.status;
11050
+ imageAttachments = collected.images;
11051
+ if (collected.status === "failed") errorCategory = collected.errorCategory ?? "runtime";
10375
11052
  if (matchedTool.function.verifyResult) {
10376
11053
  const verdict = matchedTool.function.verifyResult(result, toolCtx);
10377
11054
  if (verdict.status === "failed" /* FAILED */) {
@@ -10379,13 +11056,19 @@ var init_toolExecution = __esm({
10379
11056
  \u8BF7\u6B63\u89C6\u4E0B\u65B9\u8F93\u51FA\uFF0C\u4E0D\u8981\u4E50\u89C2\u5047\u8BBE\u6210\u529F\u3002
10380
11057
 
10381
11058
  ${result}`;
11059
+ resultStatus = "failed";
11060
+ errorCategory = verdict.errorCategory ?? "unknown";
10382
11061
  }
10383
11062
  }
10384
- events({ sessionId, eventType: "tool.execute.end", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: true }, payload: { output: result } });
11063
+ if (resultStatus === "success") {
11064
+ events({ sessionId, eventType: "tool.execute.end", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: true }, payload: { output: result } });
11065
+ } else {
11066
+ events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round, errorCategory: errorCategory ?? "unknown" }, payload: { output: result } });
11067
+ }
10385
11068
  } catch (err) {
10386
11069
  console.error(`\u274C \u6267\u884C\u5DE5\u5177 ${calledName} \u65F6\u53D1\u751F\u9519\u8BEF:`, err);
10387
- result = `\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`;
10388
- events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: result } });
11070
+ failResult(`\u5DE5\u5177\u6267\u884C\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`);
11071
+ events({ sessionId, eventType: "tool.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round, errorCategory: errorCategory ?? "runtime" }, payload: { output: result } });
10389
11072
  }
10390
11073
  try {
10391
11074
  const post = await runPostHooks(resolveMcpPermissionName(calledName, calledArgs), calledArgs, result, toolCtx);
@@ -10395,27 +11078,14 @@ ${result}`;
10395
11078
  }
10396
11079
  }
10397
11080
  } else {
10398
- result = `\u9519\u8BEF\uFF1A\u672A\u77E5\u5DE5\u5177 "${calledName}" \u6216\u8BE5\u5DE5\u5177\u65E0\u53EF\u6267\u884C\u51FD\u6570`;
10399
- explicitOk = false;
11081
+ failResult(`\u9519\u8BEF\uFF1A\u672A\u77E5\u5DE5\u5177 "${calledName}" \u6216\u8BE5\u5DE5\u5177\u65E0\u53EF\u6267\u884C\u51FD\u6570`, "syntax");
10400
11082
  events({ sessionId, eventType: "tool.validation.failed", metadata: { depth, decisionSource: llmDecisionSource, durationMs: performance.now() - startTime, round, tools_id: toolCall.id, toolName: calledName, toolSource: "builtin", ok: false, attempt: round }, payload: { output: result } });
10401
11083
  }
10402
11084
  result = applyPrivacyMasking(matchedTool?.function?.privacyMaskingRules, calledArgs, result);
10403
- let sidecarNote;
10404
11085
  const truncBudget = matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS;
10405
- if (result.length > truncBudget) {
10406
- try {
10407
- const sidecarDir = path32.join(getSessionsDirPath(sessionId), "tool-outputs");
10408
- await fs29.mkdir(sidecarDir, { recursive: true });
10409
- const safeId = String(toolCall.id).replace(/[^A-Za-z0-9_-]/g, "");
10410
- await fs29.writeFile(path32.join(sidecarDir, `${safeId}.txt`), result, "utf-8");
10411
- sidecarNote = `\u5B8C\u6574\u539F\u6587\u5DF2\u5B58\u6863\uFF0Crecall \u5DE5\u5177\u4F20 with_full="${toolCall.id}" \u53EF\u53D6\u56DE`;
10412
- } catch (e) {
10413
- console.warn(`\u26A0\uFE0F [sidecar] \u5DE5\u5177\u539F\u6587\u5B58\u6863\u5931\u8D25\uFF08\u5DF2\u964D\u7EA7\u4E3A\u666E\u901A\u622A\u65AD\u63D0\u793A\uFF09:`, e instanceof Error ? e.message : e);
10414
- }
10415
- }
10416
- result = truncateToolResult(result, matchedTool?.function?.maxOutputCharacters, sidecarNote);
10417
- const FAILED_PREFIXES = ["\u5DE5\u5177\u6267\u884C\u5931\u8D25", "\u53C2\u6570\u89E3\u6790\u5931\u8D25", "\u274C", "\u3010\u7CFB\u7EDF\u5224\u5B9A", "\u{1F512}", "\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25", "\u9879\u76EE\u6811\u626B\u63CF\u5931\u8D25", "\u7B26\u53F7\u5927\u7EB2\u5206\u6790\u5931\u8D25", "\u64CD\u4F5C\u5931\u8D25:"];
10418
- const ok = explicitOk ?? !FAILED_PREFIXES.some((p) => result.startsWith(p));
11086
+ const sidecarNote = result.length > truncBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), result) : void 0;
11087
+ result = truncateToolResult(result, matchedTool?.function.maxOutputCharacters, sidecarNote);
11088
+ const ok = resultStatus === "success";
10419
11089
  let resultForModel = result;
10420
11090
  let resultForUser = result;
10421
11091
  if (matchedTool?.function?.outputFilter) {
@@ -10428,9 +11098,11 @@ ${result}`;
10428
11098
  }
10429
11099
  if (postHookOverride !== void 0) {
10430
11100
  console.log(`\u270F\uFE0F [Post-hook] [${calledName}] \u6A21\u578B\u89C6\u56FE\u7ED3\u679C\u5DF2\u88AB hook \u6539\u5199`);
10431
- resultForModel = truncateToolResult(postHookOverride, matchedTool?.function?.maxOutputCharacters);
11101
+ const overrideBudget = matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS;
11102
+ const overrideNote = postHookOverride.length > overrideBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), postHookOverride) : void 0;
11103
+ resultForModel = truncateToolResult(postHookOverride, matchedTool?.function?.maxOutputCharacters, overrideNote);
10432
11104
  }
10433
- return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel, resultForUser, ok };
11105
+ return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel, resultForUser, ok, imageAttachments };
10434
11106
  };
10435
11107
  }
10436
11108
  });
@@ -10442,13 +11114,14 @@ var init_toolScheduling = __esm({
10442
11114
  "use strict";
10443
11115
  init_toolExecution();
10444
11116
  init_transcript();
11117
+ init_contentParts();
10445
11118
  init_config();
10446
11119
  init_tool();
10447
11120
  init_permissions();
10448
- init_backup();
10449
11121
  scheduleToolCalls = async function* (assistantMessage, message, ctx) {
10450
11122
  const { sessionId, signal, rawTools } = ctx;
10451
11123
  let abortedDuringTools = false;
11124
+ const pendingImages = [];
10452
11125
  const fillRestPlaceholders = async (fromIdx) => {
10453
11126
  const tcs2 = assistantMessage.tool_calls;
10454
11127
  for (let j = fromIdx + 1; j < tcs2.length; j++) {
@@ -10472,13 +11145,13 @@ var init_toolScheduling = __esm({
10472
11145
  const canParallelize = (name, args, parseFailed) => {
10473
11146
  if (!parallelSafeToolsEnabled || parseFailed || signal?.aborted) return false;
10474
11147
  if (name === "exit_plan_mode" || name === "enter_plan_mode" || name === "ask_question") return false;
10475
- if (isUndoTrigger(name)) return false;
10476
11148
  const matched = rawTools.find((t) => t.function.name === name);
10477
11149
  if (!matched) return false;
11150
+ if (matched.function.triggersUndo) return false;
10478
11151
  if (matched.function.safetyLevel !== "safe" /* SAFE */) return false;
10479
11152
  if (matched.function.isSync === false) return false;
10480
11153
  try {
10481
- const perm = checkPermission(name, args);
11154
+ const perm = checkPermission(name, args, matched.function.primaryArg);
10482
11155
  if (perm === "ask" || perm === "deny") return false;
10483
11156
  } catch {
10484
11157
  return false;
@@ -10516,6 +11189,7 @@ var init_toolScheduling = __esm({
10516
11189
  yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
10517
11190
  message.push({ role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
10518
11191
  await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
11192
+ if (oc.imageAttachments?.length) pendingImages.push(...oc.imageAttachments);
10519
11193
  if (oc.aborted) abortedDuringTools = true;
10520
11194
  }
10521
11195
  } else {
@@ -10537,10 +11211,16 @@ var init_toolScheduling = __esm({
10537
11211
  yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
10538
11212
  message.push({ role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
10539
11213
  await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
11214
+ if (oc.imageAttachments?.length) pendingImages.push(...oc.imageAttachments);
10540
11215
  if (oc.aborted) abortedDuringTools = true;
10541
11216
  idx++;
10542
11217
  }
10543
11218
  }
11219
+ if (!abortedDuringTools && pendingImages.length > 0) {
11220
+ const parts = buildImageFollowUpParts(pendingImages);
11221
+ message.push({ role: "user", content: parts });
11222
+ await appendMessage({ sessionId, role: "user", content: parts });
11223
+ }
10544
11224
  if (abortedDuringTools) return { kind: "aborted" };
10545
11225
  return { kind: "completed" };
10546
11226
  };
@@ -10549,7 +11229,7 @@ var init_toolScheduling = __esm({
10549
11229
 
10550
11230
  // ../core/src/agent/streamInference.ts
10551
11231
  import { createHash as createHash2 } from "node:crypto";
10552
- var sha1short, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
11232
+ var sha1short, toolsFingerprintCache, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
10553
11233
  var init_streamInference = __esm({
10554
11234
  "../core/src/agent/streamInference.ts"() {
10555
11235
  "use strict";
@@ -10561,6 +11241,7 @@ var init_streamInference = __esm({
10561
11241
  init_visionCapability();
10562
11242
  init_createModel();
10563
11243
  sha1short = (s) => createHash2("sha1").update(s).digest("hex").slice(0, 10);
11244
+ toolsFingerprintCache = /* @__PURE__ */ new WeakMap();
10564
11245
  RETRY_BACKOFF_CAP_MS = 3e4;
10565
11246
  extractRetryAfterMs = (e) => {
10566
11247
  try {
@@ -10610,7 +11291,14 @@ var init_streamInference = __esm({
10610
11291
  let assistantMessage = { role: "assistant", content: null };
10611
11292
  try {
10612
11293
  const prefixFingerprint = {
10613
- toolsHash: sha1short(JSON.stringify(cleanedToolSchemas)),
11294
+ toolsHash: (() => {
11295
+ let h = toolsFingerprintCache.get(cleanedToolSchemas);
11296
+ if (h === void 0) {
11297
+ h = sha1short(JSON.stringify(cleanedToolSchemas));
11298
+ toolsFingerprintCache.set(cleanedToolSchemas, h);
11299
+ }
11300
+ return h;
11301
+ })(),
10614
11302
  sysHash: sha1short(String(message[0]?.content ?? "")),
10615
11303
  sumHash: sha1short(String(message[1]?.content ?? "")),
10616
11304
  msgCount: message.length
@@ -10701,11 +11389,15 @@ var init_streamInference = __esm({
10701
11389
  const raHint = retryAfterMs != null ? `\uFF0C\u9075 Retry-After=${retryAfterMs}ms` : "";
10702
11390
  console.warn(`\u26A0\uFE0F API \u77AC\u65F6\u9519\u8BEF\uFF08${statusHint}${streamErr instanceof Error ? streamErr.message : String(streamErr)}\uFF09\uFF0C${backoffMs}ms \u540E\u7B2C ${apiRetries}/${MAX_API_RETRIES} \u6B21\u91CD\u8BD5${raHint}...`);
10703
11391
  await new Promise((resolve, reject) => {
10704
- const t = setTimeout(resolve, backoffMs);
10705
- signal?.addEventListener("abort", () => {
11392
+ const onAbort = () => {
10706
11393
  clearTimeout(t);
10707
11394
  reject(new Error("aborted"));
10708
- }, { once: true });
11395
+ };
11396
+ const t = setTimeout(() => {
11397
+ signal?.removeEventListener("abort", onAbort);
11398
+ resolve();
11399
+ }, backoffMs);
11400
+ signal?.addEventListener("abort", onAbort, { once: true });
10709
11401
  });
10710
11402
  contentBuf = "";
10711
11403
  reasoningBuf = "";
@@ -10806,7 +11498,7 @@ var init_streamInference = __esm({
10806
11498
  });
10807
11499
 
10808
11500
  // ../core/src/agent/agentNudges.ts
10809
- var NUDGE_EVERY, NUDGE_FENCE, PHANTOM_FENCE, PHANTOM_RETRY_MAX, EARLY_FINAL_FENCE, EARLY_FINAL_TURN_THRESHOLD, EARLY_FINAL_MAX, TOOL_DIGEST_FENCE, TOOL_DIGEST_MAX, TOOL_DIGEST_WINDOW, TOOL_DIGEST_TEXT_MAX_LEN, PLAN_FIRST_FENCE, REPEAT_FENCE, READ_REPEAT_THRESHOLD, GREP_REPEAT_THRESHOLD, REPEAT_NUDGE_MAX, PHANTOM_TEXT, EARLY_FINAL_TEXT, TOOL_DIGEST_TEXT, NUDGE_TEXT, COMPLEX_VERBS, COMPLEX_OBJECTS, COMPLEX_MARKERS, QUERY_LEAD, looksComplex, PLAN_FIRST_TEXT, looksComplete, parseToolCall, normPath, normQuery, REPEAT_READ_TEXT, REPEAT_GREP_TEXT, createNudgeScheduler;
11501
+ var NUDGE_EVERY, NUDGE_FENCE, PHANTOM_FENCE, PHANTOM_RETRY_MAX, EARLY_FINAL_FENCE, EARLY_FINAL_TURN_THRESHOLD, EARLY_FINAL_MAX, TOOL_DIGEST_FENCE, TOOL_DIGEST_MAX, TOOL_DIGEST_WINDOW, TOOL_DIGEST_TEXT_MAX_LEN, PLAN_FIRST_FENCE, REPEAT_FENCE, READ_REPEAT_THRESHOLD, GREP_REPEAT_THRESHOLD, REPEAT_NUDGE_MAX, TODO_INCOMPLETE_FENCE, TODO_INCOMPLETE_MAX, PHANTOM_TEXT, EARLY_FINAL_TEXT, TOOL_DIGEST_TEXT, NUDGE_TEXT, COMPLEX_VERBS, COMPLEX_OBJECTS, COMPLEX_MARKERS, QUERY_LEAD, looksComplex, PLAN_FIRST_TEXT, looksComplete, parseToolCall, normPath, normQuery, REPEAT_READ_TEXT, REPEAT_GREP_TEXT, TODO_INCOMPLETE_TEXT, createNudgeScheduler;
10810
11502
  var init_agentNudges = __esm({
10811
11503
  "../core/src/agent/agentNudges.ts"() {
10812
11504
  "use strict";
@@ -10826,6 +11518,8 @@ var init_agentNudges = __esm({
10826
11518
  READ_REPEAT_THRESHOLD = 3;
10827
11519
  GREP_REPEAT_THRESHOLD = 2;
10828
11520
  REPEAT_NUDGE_MAX = 3;
11521
+ TODO_INCOMPLETE_FENCE = "\u27E6DSC:TODO_INCOMPLETE\u27E7";
11522
+ TODO_INCOMPLETE_MAX = 1;
10829
11523
  PHANTOM_TEXT = "\u4F60\u7684\u4E0A\u4E00\u6761\u56DE\u590D\u6CA1\u6709\u4EFB\u4F55\u5185\u5BB9\u3001\u4E5F\u6CA1\u6709\u8C03\u7528\u4EFB\u4F55\u5DE5\u5177\uFF0C\u4F46\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\u3002\u8BF7\u7EE7\u7EED\u63A8\u8FDB\uFF08\u8C03\u7528\u5DE5\u5177\u6216\u7ED9\u51FA\u5B9E\u8D28\u56DE\u7B54\uFF09\uFF1B\u82E5\u786E\u5B9E\u53D7\u963B\u3001\u9700\u8981\u7528\u6237\u51B3\u7B56\uFF0C\u7528 ask_question \u8BF4\u660E\u5177\u4F53\u963B\u585E\u70B9\u3002\u4E0D\u8981\u8FD4\u56DE\u7A7A\u56DE\u590D\u3002";
10830
11524
  EARLY_FINAL_TEXT = (round) => `\u4F60\u4EC5 ${round} \u8F6E\u5C31\u51C6\u5907\u6536\u5C3E\uFF0C\u4E14\u56DE\u7B54\u4E2D\u6CA1\u6709\u660E\u786E\u7684\u5B8C\u6210\u58F0\u660E\u3002\u8BF7\u81EA\u68C0\uFF1A\u82E5\u7528\u6237\u8F93\u5165\u672C\u5C31\u65E0\u9700\u5DE5\u5177\uFF08\u5BD2\u6684/\u95F2\u804A/\u7EAF\u95EE\u7B54\uFF09\u3001\u6216\u4F60\u5728\u7B49\u7528\u6237\u6F84\u6E05\u51B3\u7B56\uFF0C\u76F4\u63A5\u6B63\u5E38\u56DE\u5E94\u5373\u53EF\uFF0C\u5FFD\u7565\u672C\u81EA\u68C0\u3001\u4E0D\u8981\u8F93\u51FA\u4EFB\u4F55\u81EA\u68C0\u8BF4\u660E\uFF1B\u82E5\u786E\u6709\u4EFB\u52A1\u672A\u843D\u5730\uFF08\u6240\u9700\u4FE1\u606F\u672A\u83B7\u53D6\u9F50 / \u8BE5\u6539\u7684\u6587\u4EF6\u672A\u6539\u5B8C / \u672A\u9A8C\u8BC1\uFF09\uFF0C\u7ACB\u5373\u7EE7\u7EED\u8C03\u7528\u5DE5\u5177\u63A8\u8FDB\uFF0C\u4E0D\u8981\u8349\u7387\u603B\u7ED3\u6536\u5C3E\uFF1B\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7B80\u8FF0\u6210\u679C\u3002`;
10831
11525
  TOOL_DIGEST_TEXT = "\u4F60\u521A\u6267\u884C\u5B8C\u5DE5\u5177\u62FF\u5230\u7ED3\u679C\uFF0C\u5374\u672A\u57FA\u4E8E\u8BE5\u7ED3\u679C\u7ED9\u51FA\u5B9E\u8D28\u56DE\u5E94\u5C31\u51C6\u5907\u6536\u5C3E\u3002\u8BF7\u7ED3\u5408\u5DE5\u5177\u8FD4\u56DE\u7ED3\u679C\u7EE7\u7EED\u63A8\u8FDB\uFF1B\u82E5\u7ED3\u679C\u8868\u660E\u4EFB\u52A1\u5C1A\u672A\u5B8C\u6210\uFF08\u5982\u4ECD\u5728\u7F16\u8BD1/\u8FD0\u884C\u3001\u9700\u7EE7\u7EED\u8F6E\u8BE2\uFF09\uFF0C\u7ACB\u5373\u91C7\u53D6\u4E0B\u4E00\u6B65\u884C\u52A8\uFF0C\u4E0D\u8981\u7A7A\u624B\u6536\u5C3E\u3002\u82E5\u786E\u5DF2\u5168\u90E8\u5B8C\u6210\uFF0C\u8BF7\u660E\u786E\u56DE\u590D\u300C\u5DF2\u5B8C\u6210\u300D\u5E76\u7B80\u8FF0\u6210\u679C\u3002";
@@ -10862,6 +11556,7 @@ var init_agentNudges = __esm({
10862
11556
  normQuery = (q) => String(q ?? "").trim().toLowerCase().replace(/\s+/g, " ");
10863
11557
  REPEAT_READ_TEXT = (p, n) => `\u4F60\u5DF2\u7B2C ${n} \u6B21\u8BFB\u53D6\u300C${p}\u300D\u2014\u2014\u8BE5\u6587\u4EF6\u5185\u5BB9\u4F60\u65E9\u5DF2\u62E5\u6709\uFF0C\u65E0\u9700\u6574\u6587\u4EF6\u91CD\u8BFB\u3002\u5982\u9700\u67D0\u5904\u7EC6\u8282\u8BF7\u56DE\u770B\u4E4B\u524D\u7684\u5DE5\u5177\u7ED3\u679C\uFF08\u65E9\u524D\u8BFB\u53D6\u53EF\u80FD\u5DF2\u88AB\u5F52\u6863\uFF0C\u53EF\u7528 recall \u5DE5\u5177\u68C0\u7D22\u53D6\u56DE\u539F\u6587\uFF09\uFF1B\u82E5\u8BE5\u6587\u4EF6\u521A\u88AB\u6539\u52A8\u3001\u786E\u9700\u786E\u8BA4\uFF0C\u53EA\u8BFB\u6539\u52A8\u9644\u8FD1\u51E0\u884C\u5373\u53EF\uFF0C\u4E0D\u8981\u6574\u6587\u4EF6\u91CD\u8BFB\u3002`;
10864
11558
  REPEAT_GREP_TEXT = (q, n) => `\u4F60\u5DF2\u7B2C ${n} \u6B21\u68C0\u7D22\u300C${q}\u300D\u2014\u2014\u4E4B\u524D\u7684\u7ED3\u679C\u4F60\u5DF2\u62E5\u6709\u3002\u8BF7\u6539\u7528 read_file \u8BFB\u5B8C\u6574\u76EE\u6807\u6587\u4EF6\u7406\u89E3\u4E0A\u4E0B\u6587\uFF0C\u6216\u7F29\u5C0F/\u8C03\u6574\u68C0\u7D22\u8303\u56F4\uFF0C\u4E0D\u8981\u91CD\u590D\u540C\u4E00\u68C0\u7D22\u3002`;
11559
+ TODO_INCOMPLETE_TEXT = (openItems) => `\u4F60\u7684\u4EFB\u52A1\u6E05\u5355\u4ECD\u6709 ${openItems.length} \u9879\u672A\u5B8C\u6210${openItems.length ? `\uFF08\u5982\uFF1A${openItems.slice(0, 3).join("\uFF1B")}\uFF09` : ""}\u3002\u8BF7\u6838\u5BF9\uFF1A\u82E5\u8FD9\u4E9B\u5B50\u76EE\u6807\u786E\u5DF2\u843D\u5730\uFF0C\u5148\u8C03 todo_write \u628A\u5BF9\u5E94\u9879\u7F6E completed \u518D\u6536\u5C3E\uFF1B\u82E5\u786E\u672A\u5B8C\u6210\uFF0C\u7EE7\u7EED\u63A8\u8FDB\uFF0C\u4E0D\u8981\u62FF\u7740\u672A\u52FE\u5B8C\u7684\u6E05\u5355\u8349\u7387\u603B\u7ED3\uFF1B\u82E5\u4EFB\u52A1\u5DF2\u88AB\u7528\u6237\u53D6\u6D88\u6216\u53D8\u66F4\uFF0C\u4E5F\u7528 todo_write \u66F4\u65B0\u6E05\u5355\u4EE5\u53CD\u6620\u6700\u65B0\u8BA1\u5212\u3002`;
10865
11560
  createNudgeScheduler = (opts = {}) => {
10866
11561
  const firstPrompt = opts.firstPrompt ?? "";
10867
11562
  const planMode = !!opts.planMode;
@@ -10881,6 +11576,10 @@ var init_agentNudges = __esm({
10881
11576
  const nudgedGreps = /* @__PURE__ */ new Set();
10882
11577
  let repeatNudges = 0;
10883
11578
  let repeatRetrievalPending = null;
11579
+ let todosTouchedThisRun = false;
11580
+ let lastTodos = [];
11581
+ let todoNudges = 0;
11582
+ let todoPending = null;
10884
11583
  return {
10885
11584
  pickNudge(round) {
10886
11585
  if (!planFirstSent && round === 1 && !planMode && looksComplex(firstPrompt)) {
@@ -10903,6 +11602,11 @@ ${PLAN_FIRST_TEXT}` };
10903
11602
  earlyFinalPending = null;
10904
11603
  return m;
10905
11604
  }
11605
+ if (todoPending) {
11606
+ const m = todoPending;
11607
+ todoPending = null;
11608
+ return m;
11609
+ }
10906
11610
  if (repeatRetrievalPending) {
10907
11611
  const m = repeatRetrievalPending;
10908
11612
  repeatRetrievalPending = null;
@@ -10934,6 +11638,15 @@ ${PHANTOM_TEXT}` };
10934
11638
  ${EARLY_FINAL_TEXT(round)}` };
10935
11639
  return true;
10936
11640
  }
11641
+ if (todosTouchedThisRun && todoNudges < TODO_INCOMPLETE_MAX) {
11642
+ const open = lastTodos.filter((t) => t && t.status !== "completed" && t.content).map((t) => t.content);
11643
+ if (open.length > 0) {
11644
+ todoNudges++;
11645
+ todoPending = { role: "system", content: `${TODO_INCOMPLETE_FENCE}
11646
+ ${TODO_INCOMPLETE_TEXT(open)}` };
11647
+ return true;
11648
+ }
11649
+ }
10937
11650
  return false;
10938
11651
  },
10939
11652
  noteToolCall(round, toolCalls) {
@@ -10943,6 +11656,11 @@ ${EARLY_FINAL_TEXT(round)}` };
10943
11656
  if (!Array.isArray(toolCalls)) return;
10944
11657
  for (const tc of toolCalls) {
10945
11658
  const { name, args } = parseToolCall(tc);
11659
+ if (name === "todo_write") {
11660
+ todosTouchedThisRun = true;
11661
+ lastTodos = Array.isArray(args?.todos) ? args.todos : [];
11662
+ continue;
11663
+ }
10946
11664
  if (name === "edit_file" || name === "write_file" || name === "create_file") {
10947
11665
  const p = normPath(args?.path);
10948
11666
  if (p) {
@@ -10985,6 +11703,113 @@ ${REPEAT_GREP_TEXT(q, n)}` };
10985
11703
  }
10986
11704
  });
10987
11705
 
11706
+ // ../core/src/observability/usageLog.ts
11707
+ import fs31 from "fs/promises";
11708
+ import path35 from "path";
11709
+ var USAGE_DISABLED, usageDir, ensuredDir, getUsageLogPath, recordUsageRun, USAGE_FILE_RE, SYNTHETIC_SESSION_RE, readUsageRecords, summarizeUsage, topTools;
11710
+ var init_usageLog = __esm({
11711
+ "../core/src/observability/usageLog.ts"() {
11712
+ "use strict";
11713
+ init_config();
11714
+ USAGE_DISABLED = process.env.DEEPSEEKER_CODE_USAGE_LOG === "0";
11715
+ usageDir = () => path35.join(appConfig.dataDir, "usage");
11716
+ ensuredDir = null;
11717
+ getUsageLogPath = (month) => path35.join(usageDir(), `usage-${month}.jsonl`);
11718
+ recordUsageRun = async (rec) => {
11719
+ if (USAGE_DISABLED) return;
11720
+ try {
11721
+ const full = { workspace: appConfig.userWorkspaceDir, ...rec };
11722
+ const month = (full.ts || (/* @__PURE__ */ new Date()).toISOString()).slice(0, 7);
11723
+ const dir = usageDir();
11724
+ if (ensuredDir !== dir) {
11725
+ await fs31.mkdir(dir, { recursive: true });
11726
+ ensuredDir = dir;
11727
+ }
11728
+ await fs31.appendFile(getUsageLogPath(month), JSON.stringify(full) + "\n", "utf-8");
11729
+ } catch (e) {
11730
+ 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);
11731
+ }
11732
+ };
11733
+ USAGE_FILE_RE = /^usage-\d{4}-\d{2}\.jsonl$/;
11734
+ SYNTHETIC_SESSION_RE = /^(selftest-|eval-)/;
11735
+ readUsageRecords = async (days) => {
11736
+ let files;
11737
+ try {
11738
+ files = (await fs31.readdir(usageDir())).filter((f) => USAGE_FILE_RE.test(f)).sort();
11739
+ } catch {
11740
+ return [];
11741
+ }
11742
+ let cutoff = null;
11743
+ if (days && days > 0) {
11744
+ cutoff = /* @__PURE__ */ new Date();
11745
+ cutoff.setHours(0, 0, 0, 0);
11746
+ cutoff.setDate(cutoff.getDate() - (days - 1));
11747
+ }
11748
+ const out = [];
11749
+ for (const f of files) {
11750
+ let text;
11751
+ try {
11752
+ text = await fs31.readFile(path35.join(usageDir(), f), "utf-8");
11753
+ } catch {
11754
+ continue;
11755
+ }
11756
+ for (const line of text.split("\n")) {
11757
+ if (!line.trim()) continue;
11758
+ let rec;
11759
+ try {
11760
+ rec = JSON.parse(line);
11761
+ } catch {
11762
+ continue;
11763
+ }
11764
+ if (!rec || typeof rec !== "object" || !rec.sessionId || !rec.ts) continue;
11765
+ if (SYNTHETIC_SESSION_RE.test(rec.sessionId)) continue;
11766
+ if (cutoff && new Date(rec.ts) < cutoff) continue;
11767
+ out.push(rec);
11768
+ }
11769
+ }
11770
+ return out.sort((a, b) => String(a.ts).localeCompare(String(b.ts)));
11771
+ };
11772
+ summarizeUsage = (records) => {
11773
+ const s = {
11774
+ runs: 0,
11775
+ mainRuns: 0,
11776
+ subRuns: 0,
11777
+ stopReasons: { normal: 0, aborted: 0, error: 0, repeat: 0, limit: 0 },
11778
+ rounds: 0,
11779
+ mainRounds: 0,
11780
+ durationMs: 0,
11781
+ promptTokens: 0,
11782
+ completionTokens: 0,
11783
+ totalTokens: 0,
11784
+ cachedTokens: 0,
11785
+ toolCalls: 0,
11786
+ tools: {}
11787
+ };
11788
+ for (const r of records) {
11789
+ s.runs++;
11790
+ if (r.depth > 0) s.subRuns++;
11791
+ else s.mainRuns++;
11792
+ if (r.stopReason in s.stopReasons) s.stopReasons[r.stopReason]++;
11793
+ s.rounds += r.rounds || 0;
11794
+ if (r.depth === 0) s.mainRounds += r.rounds || 0;
11795
+ s.durationMs += r.durationMs || 0;
11796
+ s.promptTokens += r.promptTokens ?? 0;
11797
+ s.completionTokens += r.completionTokens ?? 0;
11798
+ s.totalTokens += r.totalTokens ?? 0;
11799
+ s.cachedTokens += r.cachedTokens ?? 0;
11800
+ for (const [name, n] of Object.entries(r.tools ?? {})) {
11801
+ s.tools[name] = (s.tools[name] ?? 0) + (n || 0);
11802
+ s.toolCalls += n || 0;
11803
+ }
11804
+ if (!s.firstTs || r.ts < s.firstTs) s.firstTs = r.ts;
11805
+ if (!s.lastTs || r.ts > s.lastTs) s.lastTs = r.ts;
11806
+ }
11807
+ return s;
11808
+ };
11809
+ topTools = (tools, n) => Object.entries(tools).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, n);
11810
+ }
11811
+ });
11812
+
10988
11813
  // ../core/src/agent/runAgent.ts
10989
11814
  async function* runAgent(message, options) {
10990
11815
  const sessionId = options.sessionId;
@@ -11033,6 +11858,7 @@ async function* runAgent(message, options) {
11033
11858
  cached_tokens: (usageSum?.cached_tokens ?? 0) + (u.cached_tokens ?? 0)
11034
11859
  };
11035
11860
  };
11861
+ const toolCounts = {};
11036
11862
  await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
11037
11863
  try {
11038
11864
  while (true) {
@@ -11153,6 +11979,10 @@ async function* runAgent(message, options) {
11153
11979
  return;
11154
11980
  }
11155
11981
  nudges.noteToolCall(round, assistantMessage.tool_calls);
11982
+ for (const tc of assistantMessage.tool_calls) {
11983
+ const name = tc?.function?.name || "unknown";
11984
+ toolCounts[name] = (toolCounts[name] ?? 0) + 1;
11985
+ }
11156
11986
  const repeatVerdict = breaker.check(assistantMessage.tool_calls, round, lastContent);
11157
11987
  if (repeatVerdict.tripped) {
11158
11988
  stopReason = "repeat";
@@ -11216,6 +12046,25 @@ async function* runAgent(message, options) {
11216
12046
  await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: usageSum });
11217
12047
  } catch {
11218
12048
  }
12049
+ try {
12050
+ void recordUsageRun({
12051
+ v: 1,
12052
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
12053
+ sessionId,
12054
+ runId,
12055
+ depth,
12056
+ model: options.model?.trim() || MODEL_NAME,
12057
+ stopReason: signal?.aborted ? "aborted" : stopReason,
12058
+ rounds: round,
12059
+ durationMs: Math.round(performance.now() - startTime),
12060
+ promptTokens: usageSum?.prompt_tokens,
12061
+ completionTokens: usageSum?.completion_tokens,
12062
+ totalTokens: usageSum?.total_tokens,
12063
+ cachedTokens: usageSum?.cached_tokens,
12064
+ tools: toolCounts
12065
+ });
12066
+ } catch {
12067
+ }
11219
12068
  try {
11220
12069
  const reason = signal?.aborted ? "aborted" : stopReason;
11221
12070
  await dispatch("Stop", { sessionId, cwd, lastText: lastContent || "", reason });
@@ -11245,163 +12094,13 @@ var init_runAgent = __esm({
11245
12094
  init_toolScheduling();
11246
12095
  init_streamInference();
11247
12096
  init_agentNudges();
11248
- }
11249
- });
11250
-
11251
- // ../core/src/session/recovery.ts
11252
- var detectInterruption, repairOrphanToolCalls, reconcileCompaction, recoverSession;
11253
- var init_recovery = __esm({
11254
- "../core/src/session/recovery.ts"() {
11255
- "use strict";
11256
- init_transcript();
11257
- init_store();
11258
- detectInterruption = (lines) => {
11259
- let sawEvents = false;
11260
- let lastStart;
11261
- const closed = /* @__PURE__ */ new Set();
11262
- for (const l of lines) {
11263
- if (!isEventLine(l)) continue;
11264
- sawEvents = true;
11265
- if (l.dscEvent === "run.start") lastStart = l;
11266
- else if (l.dscEvent === "run.end" || l.dscEvent === "run.abandoned") closed.add(l.runId);
11267
- }
11268
- if (!sawEvents || !lastStart) return { kind: "legacy" };
11269
- return closed.has(lastStart.runId) ? { kind: "clean" } : { kind: "crashed", runId: lastStart.runId };
11270
- };
11271
- repairOrphanToolCalls = (msgs, confirmedCrash) => {
11272
- const placeholder = confirmedCrash ? "\uFF08\u4E0A\u6B21\u4F1A\u8BDD\u5728\u5DE5\u5177\u6267\u884C\u4E2D\u5D29\u6E83\uFF08run \u672A\u95ED\u5408\uFF09\uFF0C\u8BE5\u8C03\u7528\u672A\u6267\u884C\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";
11273
- const out = [];
11274
- const repairedToolCallIds = [];
11275
- for (let i = 0; i < msgs.length; i++) {
11276
- const m = msgs[i];
11277
- out.push(m);
11278
- if (m.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0) {
11279
- const answered = /* @__PURE__ */ new Set();
11280
- let j = i + 1;
11281
- while (j < msgs.length && msgs[j].role === "tool") {
11282
- answered.add(msgs[j].tool_call_id);
11283
- j++;
11284
- }
11285
- for (const tc of m.tool_calls) {
11286
- if (tc.id && !answered.has(tc.id)) {
11287
- out.push({ role: "tool", tool_call_id: tc.id, content: placeholder });
11288
- repairedToolCallIds.push(tc.id);
11289
- }
11290
- }
11291
- }
11292
- }
11293
- return { msgs: out, repairedToolCallIds, confirmedCrash };
11294
- };
11295
- reconcileCompaction = (state, lines) => {
11296
- let last;
11297
- for (const l of lines) {
11298
- if (isEventLine(l) && l.dscEvent === "compaction") last = l;
11299
- }
11300
- if (!last) return { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary, desync: "legacy" };
11301
- let desync = "none";
11302
- if (last.archivedMessageCount !== state.archivedMessageCount) {
11303
- desync = state.archivedMessageCount > last.archivedMessageCount ? "state-ahead" : "event-ahead";
11304
- console.warn(
11305
- `\u26A0\uFE0F [recovery] \u538B\u7F29\u72B6\u6001 desync\uFF08${desync}\uFF09\uFF1Astate.json archivedMessageCount=${state.archivedMessageCount} vs \u672B\u4E2A compaction \u4E8B\u4EF6=${last.archivedMessageCount}\u3002\u4EE5 state \u4E3A\u51C6\uFF08slice \u6267\u884C\u95F8\u95E8\uFF09\uFF0C\u4E8B\u4EF6\u4FA7\u4EC5\u955C\u50CF\u3002`
11306
- );
11307
- }
11308
- return { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary, desync };
11309
- };
11310
- recoverSession = async (sessionId) => {
11311
- const lines = await readTranscriptLines(sessionId);
11312
- const interruption = detectInterruption(lines);
11313
- if (interruption.kind === "crashed") {
11314
- await appendEvent(sessionId, {
11315
- dscEvent: "run.abandoned",
11316
- runId: interruption.runId,
11317
- reason: "run.end missing: interrupted by process exit/kill"
11318
- });
11319
- 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\u5D29\u6E83\u786E\u8BA4\u300D\u6863\u6267\u884C\u3002`);
11320
- }
11321
- const state = await getRollingState(sessionId);
11322
- const compaction = reconcileCompaction(
11323
- { archivedMessageCount: state.archivedMessageCount, rollingSummary: state.rollingSummary },
11324
- lines
11325
- );
11326
- return { interruption, compaction };
11327
- };
11328
- }
11329
- });
11330
-
11331
- // ../core/src/session/content.ts
11332
- var decayOldToolResults, enforceVisionGate, buildContextMessages;
11333
- var init_content = __esm({
11334
- "../core/src/session/content.ts"() {
11335
- "use strict";
11336
- init_contextCore();
11337
- init_contentParts();
11338
- init_transcript();
11339
- init_store();
11340
- init_config();
11341
- init_visionCapability();
11342
- init_recovery();
11343
- decayOldToolResults = (msgs) => {
11344
- const units = groupUnits(msgs);
11345
- const cutoffUnitIdx = Math.max(0, units.length - appConfig.KEEP_RECENT_UNITS);
11346
- if (cutoffUnitIdx === 0) return msgs;
11347
- const decayIds = /* @__PURE__ */ new Set();
11348
- let hasOldImage = false;
11349
- for (let u = 0; u < cutoffUnitIdx; u++) {
11350
- for (const m of units[u]) {
11351
- const mm = m;
11352
- if (mm.role === "tool" && typeof mm.tool_call_id === "string") decayIds.add(mm.tool_call_id);
11353
- if (hasImagePart(m.content)) hasOldImage = true;
11354
- }
11355
- }
11356
- if (decayIds.size === 0 && !hasOldImage) return msgs;
11357
- const keep = appConfig.BOUNDARY_TOOL_KEEP_CHARS;
11358
- const oldRefs = /* @__PURE__ */ new Set();
11359
- for (let u = 0; u < cutoffUnitIdx; u++) for (const m of units[u]) oldRefs.add(m);
11360
- return msgs.map((m) => {
11361
- if (!oldRefs.has(m)) return m;
11362
- const mm = m;
11363
- if (mm.role === "tool" && decayIds.has(mm.tool_call_id) && typeof mm.content === "string" && mm.content.length > keep) {
11364
- const head = mm.content.slice(0, keep);
11365
- return { ...mm, content: `${head}
11366
-
11367
- [\u2026 \u8BE5\u5386\u53F2\u5DE5\u5177\u8F93\u51FA\u5DF2\u6298\u53E0\uFF08\u5171 ${mm.content.length} \u5B57\u7B26\uFF09\uFF0C\u5982\u9700\u7EC6\u8282\u8BF7\u91CD\u65B0\u8C03\u7528\u5DE5\u5177 \u2026]` };
11368
- }
11369
- return replaceImageParts(mm, "[\u5386\u53F2\u56FE\u7247\u5DF2\u6298\u53E0\uFF1A\u539F\u56FE\u4ECD\u5728\u4F1A\u8BDD\u5F52\u6863\u4E2D\uFF0C\u5982\u9700\u518D\u6B21\u67E5\u770B\u8BF7\u91CD\u65B0\u63D0\u4F9B\u8BE5\u56FE\u7247]");
11370
- });
11371
- };
11372
- enforceVisionGate = (msgs, modelId) => isVisionEnabled(modelId) ? msgs : msgs.map((m) => collapseToText(m, VISION_DEGRADE_NOTE));
11373
- buildContextMessages = async (sessionId, currentUserMsg, systemPrompt, modelId) => {
11374
- await ensureVisionCacheLoaded();
11375
- const lines = await readTranscriptLines(sessionId);
11376
- const store = await getRollingState(sessionId);
11377
- const eventAware = appConfig.transcriptEvents || lines.some(isEventLine);
11378
- const recovery = eventAware ? await recoverSession(sessionId) : {
11379
- interruption: { kind: "legacy" },
11380
- compaction: { archivedMessageCount: store.archivedMessageCount, rollingSummary: store.rollingSummary, desync: "legacy" }
11381
- };
11382
- const all = lines.filter((l) => !isEventLine(l)).map(cleanMsg);
11383
- const result = [
11384
- { role: "system", content: systemPrompt },
11385
- // 存储系统提示词
11386
- { role: "system", content: recovery.compaction.rollingSummary }
11387
- // 后续存储摘要使用
11388
- ];
11389
- const archivedMessageCount = recovery.compaction.archivedMessageCount;
11390
- const messageAll = all.slice(archivedMessageCount);
11391
- if (messageAll.length === 0) {
11392
- result.push(currentUserMsg);
11393
- return enforceVisionGate(result, modelId);
11394
- }
11395
- const repair = repairOrphanToolCalls(messageAll, recovery.interruption.kind === "crashed");
11396
- result.push(...decayOldToolResults(repair.msgs));
11397
- result.push(currentUserMsg);
11398
- return enforceVisionGate(result, modelId);
11399
- };
12097
+ init_usageLog();
12098
+ init_createModel();
11400
12099
  }
11401
12100
  });
11402
12101
 
11403
12102
  // ../core/src/session/lineage.ts
11404
- import fs30 from "node:fs";
12103
+ import fs32 from "node:fs";
11405
12104
  var parentOfSubsession, stringAncestorsOf, readForkedFromSync, lineageClosureOf, ownerCanApprove;
11406
12105
  var init_lineage = __esm({
11407
12106
  "../core/src/session/lineage.ts"() {
@@ -11424,7 +12123,7 @@ var init_lineage = __esm({
11424
12123
  };
11425
12124
  readForkedFromSync = (sessionId) => {
11426
12125
  try {
11427
- const raw = fs30.readFileSync(getStatePath(sessionId), "utf-8");
12126
+ const raw = fs32.readFileSync(getStatePath(sessionId), "utf-8");
11428
12127
  const v = JSON.parse(raw)?.forkedFrom;
11429
12128
  return typeof v === "string" && v.length > 0 ? v : void 0;
11430
12129
  } catch {
@@ -11454,7 +12153,7 @@ var init_lineage = __esm({
11454
12153
  });
11455
12154
 
11456
12155
  // ../core/src/agent/subagent.ts
11457
- var SUBAGENT_DENYLIST, activeSubagents, RESUME_NOTE, runSubagent;
12156
+ var SUBAGENT_DENYLIST, activeSubagents, failText, RESUME_NOTE, runSubagent;
11458
12157
  var init_subagent = __esm({
11459
12158
  "../core/src/agent/subagent.ts"() {
11460
12159
  "use strict";
@@ -11468,6 +12167,7 @@ var init_subagent = __esm({
11468
12167
  init_lineage();
11469
12168
  SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
11470
12169
  activeSubagents = /* @__PURE__ */ new Set();
12170
+ failText = (msg) => toolFailure(msg).content;
11471
12171
  RESUME_NOTE = [
11472
12172
  `
11473
12173
  # \u3010\u7EED\u8DD1\u8BF4\u660E\u3011`,
@@ -11480,7 +12180,7 @@ var init_subagent = __esm({
11480
12180
  if (ctx.depth >= MAX_AGENT_DEPTH) {
11481
12181
  return {
11482
12182
  ok: false,
11483
- output: `\u274C [\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`,
12183
+ 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`),
11484
12184
  sessionId: ""
11485
12185
  };
11486
12186
  }
@@ -11489,21 +12189,21 @@ var init_subagent = __esm({
11489
12189
  if (!target.includes("__sub__")) {
11490
12190
  return {
11491
12191
  ok: false,
11492
- output: `\u274C [\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`,
12192
+ 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`),
11493
12193
  sessionId: ""
11494
12194
  };
11495
12195
  }
11496
12196
  if (!ownerCanApprove(target, ctx.sessionId)) {
11497
12197
  return {
11498
12198
  ok: false,
11499
- output: `\u274C [\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`,
12199
+ 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`),
11500
12200
  sessionId: ""
11501
12201
  };
11502
12202
  }
11503
12203
  if (activeSubagents.has(target)) {
11504
12204
  return {
11505
12205
  ok: false,
11506
- output: `\u274C [\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`,
12206
+ 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`),
11507
12207
  sessionId: ""
11508
12208
  };
11509
12209
  }
@@ -11511,7 +12211,7 @@ var init_subagent = __esm({
11511
12211
  if (existing.length === 0) {
11512
12212
  return {
11513
12213
  ok: false,
11514
- output: `\u274C [\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`,
12214
+ 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`),
11515
12215
  sessionId: ""
11516
12216
  };
11517
12217
  }
@@ -11522,7 +12222,7 @@ var init_subagent = __esm({
11522
12222
  if (name && !manifest) {
11523
12223
  return {
11524
12224
  ok: false,
11525
- output: `\u274C [\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`,
12225
+ 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`),
11526
12226
  sessionId: ""
11527
12227
  };
11528
12228
  }
@@ -11589,7 +12289,7 @@ var init_subagent = __esm({
11589
12289
  try {
11590
12290
  for await (const e of runAgent(subMessages, subOptions)) {
11591
12291
  if (ctx.abortSignal?.aborted) {
11592
- result = { ok: false, output: `\u274C [\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 };
12292
+ 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 };
11593
12293
  break;
11594
12294
  }
11595
12295
  if (e.type === "final") {
@@ -11604,15 +12304,15 @@ var init_subagent = __esm({
11604
12304
  result = { ok: true, output: subResult, sessionId: subSessionId, manifestName: manifest?.name };
11605
12305
  }
11606
12306
  } catch (streamError) {
11607
- result = { ok: false, output: `\u274C [\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 };
12307
+ 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 };
11608
12308
  } finally {
11609
12309
  if (result) {
11610
12310
  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}`));
11611
12311
  }
11612
12312
  }
11613
- return result ?? { ok: false, output: `\u274C [\u6D3E\u751F\u6267\u884C\u5931\u8D25]: \u672A\u77E5\u9519\u8BEF`, sessionId: subSessionId, manifestName: manifest?.name };
12313
+ return result ?? { ok: false, output: failText(`[\u6D3E\u751F\u6267\u884C\u5931\u8D25]: \u672A\u77E5\u9519\u8BEF`), sessionId: subSessionId, manifestName: manifest?.name };
11614
12314
  } catch (error) {
11615
- return { ok: false, output: `\u274C [\u6D3E\u751F\u6267\u884C\u5931\u8D25]: ${error.message}`, sessionId: subSessionId, manifestName: manifest?.name };
12315
+ return { ok: false, output: failText(`[\u6D3E\u751F\u6267\u884C\u5931\u8D25]: ${error.message}`), sessionId: subSessionId, manifestName: manifest?.name };
11616
12316
  } finally {
11617
12317
  activeSubagents.delete(subSessionId);
11618
12318
  }
@@ -11620,10 +12320,48 @@ var init_subagent = __esm({
11620
12320
  }
11621
12321
  });
11622
12322
 
12323
+ // ../core/src/hooks/trust.ts
12324
+ import path36 from "path";
12325
+ import fs33 from "fs/promises";
12326
+ var TRUSTED_HOOKS_FILE, approved, loaded, ensureLoaded, isHookCommandApproved, approveHookCommand;
12327
+ var init_trust2 = __esm({
12328
+ "../core/src/hooks/trust.ts"() {
12329
+ "use strict";
12330
+ init_config();
12331
+ init_common();
12332
+ TRUSTED_HOOKS_FILE = path36.join(appConfig.dataDir, "trusted_hooks.json");
12333
+ approved = /* @__PURE__ */ new Set();
12334
+ loaded = false;
12335
+ ensureLoaded = async () => {
12336
+ if (loaded) return;
12337
+ const list = await readJSONFile(TRUSTED_HOOKS_FILE);
12338
+ if (Array.isArray(list)) {
12339
+ for (const x of list) if (typeof x === "string") approved.add(x);
12340
+ }
12341
+ loaded = true;
12342
+ };
12343
+ isHookCommandApproved = async (ident) => {
12344
+ await ensureLoaded();
12345
+ return approved.has(ident);
12346
+ };
12347
+ approveHookCommand = async (ident) => {
12348
+ await ensureLoaded();
12349
+ if (approved.has(ident)) return;
12350
+ approved.add(ident);
12351
+ try {
12352
+ await fs33.mkdir(appConfig.dataDir, { recursive: true });
12353
+ await atomicWriteJSON(TRUSTED_HOOKS_FILE, [...approved]);
12354
+ } catch (e) {
12355
+ console.warn(`\u26A0\uFE0F [hooks] \u5BA1\u6279\u8BB0\u5FC6\u843D\u76D8\u5931\u8D25\uFF08\u672C\u6B21\u5DF2\u653E\u884C\uFF0C\u4E0B\u6B21\u9996\u8DD1\u4F1A\u518D\u95EE\uFF09: ${e?.message ?? e}`);
12356
+ }
12357
+ };
12358
+ }
12359
+ });
12360
+
11623
12361
  // ../core/src/hooks/loader.ts
11624
- import fs31 from "fs/promises";
11625
- import path33 from "path";
11626
- var readHooksConfig, appendValidated, validateRule, parseStdoutDecision, parseAgentDecision, compileRule2, loadHooks, initHooks;
12362
+ import fs34 from "fs/promises";
12363
+ import path37 from "path";
12364
+ var slimHookContext, readHooksConfig, appendValidated, validateRule, parseStdoutDecision, parseAgentDecision, gateSeq, firstRunGate, compileRule2, loadHooks, initHooks;
11627
12365
  var init_loader4 = __esm({
11628
12366
  "../core/src/hooks/loader.ts"() {
11629
12367
  "use strict";
@@ -11634,18 +12372,32 @@ var init_loader4 = __esm({
11634
12372
  init_subagent();
11635
12373
  init_tool();
11636
12374
  init_types();
12375
+ init_trust2();
12376
+ slimHookContext = (ctx) => {
12377
+ if (!ctx || typeof ctx !== "object" || !ctx.toolContext) return ctx;
12378
+ const { toolContext, ...rest } = ctx;
12379
+ return {
12380
+ ...rest,
12381
+ toolContext: {
12382
+ sessionId: toolContext.sessionId,
12383
+ cwd: toolContext.cwd,
12384
+ depth: toolContext.depth,
12385
+ permissionMode: toolContext.permissionMode
12386
+ }
12387
+ };
12388
+ };
11637
12389
  readHooksConfig = async (includeProject) => {
11638
12390
  const merged = {};
11639
12391
  const paths = [
11640
- path33.join(appConfig.dataDir, "settings.json"),
12392
+ path37.join(appConfig.dataDir, "settings.json"),
11641
12393
  // 全局用户级
11642
- ...includeProject ? [path33.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
12394
+ ...includeProject ? [path37.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
11643
12395
  // 项目级(叠加);未信任时省略
11644
12396
  ];
11645
12397
  for (const configPath of paths) {
11646
12398
  let raw;
11647
12399
  try {
11648
- raw = await fs31.readFile(configPath, "utf-8");
12400
+ raw = await fs34.readFile(configPath, "utf-8");
11649
12401
  } catch {
11650
12402
  continue;
11651
12403
  }
@@ -11658,14 +12410,14 @@ var init_loader4 = __esm({
11658
12410
  }
11659
12411
  const hooksBlock = parsed?.hooks;
11660
12412
  if (!hooksBlock || typeof hooksBlock !== "object") continue;
11661
- appendValidated(merged, hooksBlock, configPath);
12413
+ appendValidated(merged, hooksBlock, configPath, configPath === paths[1]);
11662
12414
  if (configPath === paths[1] && Object.keys(hooksBlock).length > 0) {
11663
12415
  console.warn(`\u26A0\uFE0F\u3010\u5B89\u5168\u63D0\u793A\u3011\u5DF2\u52A0\u8F7D\u9879\u76EE\u7EA7 hook \u914D\u7F6E [${configPath}]\uFF0C\u5176 command \u5C06\u4EE5 shell \u6267\u884C\u3002\u4EC5\u5728\u4FE1\u4EFB\u8BE5\u9879\u76EE\u65F6\u542F\u7528\uFF1B\u514B\u9686\u672A\u77E5\u4ED3\u5E93\u524D\u8BF7\u6838\u67E5\u8BE5\u6587\u4EF6\u4EE5\u9632\u4EFB\u610F\u547D\u4EE4\u6267\u884C\u3002`);
11664
12416
  }
11665
12417
  }
11666
12418
  return merged;
11667
12419
  };
11668
- appendValidated = (merged, hooksBlock, src) => {
12420
+ appendValidated = (merged, hooksBlock, src, isProject) => {
11669
12421
  for (const [key, val] of Object.entries(hooksBlock)) {
11670
12422
  if (!ALL_EVENTS.includes(key)) {
11671
12423
  console.warn(`\u26A0\uFE0F [hooks] \u672A\u77E5\u4E8B\u4EF6\u7C7B\u578B "${key}"\uFF08${src}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
@@ -11679,7 +12431,7 @@ var init_loader4 = __esm({
11679
12431
  const list = merged[event] ??= [];
11680
12432
  val.forEach((item, i) => {
11681
12433
  const rule = validateRule(item, event, src, i);
11682
- if (rule) list.push(rule);
12434
+ if (rule) list.push(isProject ? { ...rule, projectSource: true } : rule);
11683
12435
  });
11684
12436
  }
11685
12437
  };
@@ -11802,10 +12554,52 @@ var init_loader4 = __esm({
11802
12554
  }
11803
12555
  return { deny: false, explicit: false };
11804
12556
  };
12557
+ gateSeq = 0;
12558
+ firstRunGate = (raw, ident, run) => {
12559
+ const gated = raw.requireApproval === true || raw.projectSource === true;
12560
+ if (!gated) return run;
12561
+ return async (ctx) => {
12562
+ if (await isHookCommandApproved(ident)) return run(ctx);
12563
+ const req = ctx?.toolContext?.requestApproval;
12564
+ if (typeof req !== "function") {
12565
+ console.warn(`\u23ED\uFE0F [hooks] ${raw.type ?? "command"} hook \u9996\u8DD1\u5F85\u5BA1\u6279\u4E14\u5F53\u524D\u4E8B\u4EF6\u65E0\u5BA1\u6279\u901A\u9053\uFF0C\u5DF2\u8DF3\u8FC7\uFF1A${ident.slice(0, 120)}\uFF08\u53EF\u5728 CLI \u5DE5\u5177\u4E8B\u4EF6\u89E6\u53D1\u65F6\u6279\u51C6\u300C\u59CB\u7EC8\u5141\u8BB8\u300D\uFF0C\u6216\u624B\u52A8\u52A0\u5165 ~/.deepseeker-code/trusted_hooks.json\uFF09`);
12566
+ return { deny: false };
12567
+ }
12568
+ const kindLabel = raw.projectSource ? "\u9879\u76EE\u7EA7" : "\u5168\u5C40";
12569
+ const detail = raw.type === "http" ? `${kindLabel} hook \u9996\u6B21\u8FD0\u884C\uFF1A\u8BF7\u6C42\u5C06\u4E0A\u4E0B\u6587 POST \u5230\u5916\u90E8 URL
12570
+ ${ident}` : raw.type === "agent" ? `${kindLabel} hook \u9996\u6B21\u8FD0\u884C\uFF1A\u6D3E\u5B50 agent \u8BC4\u5224\u5DE5\u5177\u8C03\u7528
12571
+ \u4EFB\u52A1\uFF1A${ident}` : `${kindLabel} hook \u9996\u6B21\u8FD0\u884C\uFF1A\u6267\u884C shell \u547D\u4EE4
12572
+ ${ident}`;
12573
+ let decision;
12574
+ try {
12575
+ decision = await req(detail, {
12576
+ toolName: `hook:${raw.type ?? "command"}`,
12577
+ toolCallId: `hook-${++gateSeq}`,
12578
+ sessionId: ctx?.toolContext?.sessionId ?? ""
12579
+ });
12580
+ } catch (e) {
12581
+ console.warn(`\u23ED\uFE0F [hooks] \u9996\u8DD1\u5BA1\u6279\u901A\u9053\u5F02\u5E38\uFF0C\u5DF2\u8DF3\u8FC7\u8BE5 hook: ${e?.message ?? e}`);
12582
+ return { deny: false };
12583
+ }
12584
+ if (decision === "deny") {
12585
+ console.warn(`\u23ED\uFE0F [hooks] \u9996\u8DD1\u5BA1\u6279\u88AB\u62D2\u7EDD\uFF0C\u672C\u6B21\u8DF3\u8FC7\u8BE5 hook\uFF1A${ident.slice(0, 120)}`);
12586
+ return { deny: false };
12587
+ }
12588
+ if (decision === "allow-always") await approveHookCommand(ident);
12589
+ return run(ctx);
12590
+ };
12591
+ };
11805
12592
  compileRule2 = (event, raw) => {
11806
12593
  const denyOnNonZero = raw.denyOnNonZero ?? event === "PreToolUse";
11807
12594
  const timeoutMs2 = raw.timeoutMs ?? DEFAULT_TIMEOUT_BY_EVENT[event];
11808
- const base = { event, matcher: raw.matcher, source: "config", onError: raw.onError };
12595
+ const base = {
12596
+ event,
12597
+ matcher: raw.matcher,
12598
+ source: "config",
12599
+ onError: raw.onError,
12600
+ // #9 可观测:首跑审批门是否启用(/hooks 展示)
12601
+ firstRunApproval: raw.requireApproval === true || raw.projectSource === true
12602
+ };
11809
12603
  if (raw.type === "prompt") {
11810
12604
  const text = raw.text;
11811
12605
  return { ...base, run: async () => ({ contextAdditions: [text] }) };
@@ -11814,7 +12608,7 @@ var init_loader4 = __esm({
11814
12608
  const task = raw.task;
11815
12609
  return {
11816
12610
  ...base,
11817
- run: async (ctx) => {
12611
+ run: firstRunGate(raw, task, async (ctx) => {
11818
12612
  const tc = ctx?.toolContext;
11819
12613
  if (!tc || typeof tc.depth === "number" && tc.depth > 0) return { deny: false };
11820
12614
  const fullTask = [
@@ -11835,20 +12629,20 @@ var init_loader4 = __esm({
11835
12629
  return decision.deny ? { deny: true, reason: decision.reason ?? "hook agent \u62D2\u7EDD\uFF08\u672A\u7ED9\u7406\u7531\uFF09" } : { deny: false };
11836
12630
  }
11837
12631
  return denyOnNonZero ? { deny: true, reason: "hook agent \u672A\u8F93\u51FA\u660E\u786E\u51B3\u7B56\uFF08DENY/ALLOW\uFF09\uFF0C\u6309 fail-closed \u62D2\u7EDD" } : { deny: false };
11838
- }
12632
+ })
11839
12633
  };
11840
12634
  }
11841
12635
  if (raw.type === "http") {
11842
12636
  const url = raw.url;
11843
12637
  return {
11844
12638
  ...base,
11845
- run: async (ctx) => {
12639
+ run: firstRunGate(raw, url, async (ctx) => {
11846
12640
  const res = await executeHttpHook({
11847
12641
  url,
11848
12642
  method: raw.method,
11849
12643
  headers: raw.headers,
11850
- body: ctx,
11851
- // 整个 hook 上下文 JSON 化发出(含 sessionId/toolName/args/prompt 等)
12644
+ body: slimHookContext(ctx),
12645
+ // hook 上下文 JSON 化发出(含 sessionId/toolName/args/prompt 等;toolContext 走瘦投影)
11852
12646
  timeoutMs: timeoutMs2
11853
12647
  });
11854
12648
  if (!res.ok) {
@@ -11872,19 +12666,19 @@ var init_loader4 = __esm({
11872
12666
  return { deny: true, reason: `hook http \u72B6\u6001\u7801 ${res.status}` };
11873
12667
  }
11874
12668
  return { deny: false };
11875
- }
12669
+ })
11876
12670
  };
11877
12671
  }
11878
12672
  const command = raw.command;
11879
12673
  return {
11880
12674
  ...base,
11881
- run: async (ctx) => {
12675
+ run: firstRunGate(raw, command, async (ctx) => {
11882
12676
  const res = await executeHookCommand({
11883
12677
  command,
11884
12678
  cwd: ctx?.cwd,
11885
12679
  env: ctx?.env,
11886
12680
  timeoutMs: timeoutMs2,
11887
- stdinPayload: ctx
12681
+ stdinPayload: slimHookContext(ctx)
11888
12682
  });
11889
12683
  if (res.timedOut) {
11890
12684
  return denyOnNonZero ? { deny: true, reason: `hook \u6267\u884C\u8D85\u65F6\uFF08>${timeoutMs2}ms\uFF09` } : { deny: false };
@@ -11898,19 +12692,19 @@ var init_loader4 = __esm({
11898
12692
  if (out) return out;
11899
12693
  }
11900
12694
  return { deny: false };
11901
- }
12695
+ })
11902
12696
  };
11903
12697
  };
11904
12698
  loadHooks = async (includeProject) => {
11905
12699
  const cfg = await readHooksConfig(includeProject);
11906
- const compiled = [];
12700
+ const compiled2 = [];
11907
12701
  for (const event of ALL_EVENTS) {
11908
12702
  const list = cfg[event];
11909
12703
  if (!list || list.length === 0) continue;
11910
- for (const raw of list) compiled.push(compileRule2(event, raw));
12704
+ for (const raw of list) compiled2.push(compileRule2(event, raw));
11911
12705
  }
11912
- replaceDeclarativeHooks(compiled);
11913
- return compiled.length;
12706
+ replaceDeclarativeHooks(compiled2);
12707
+ return compiled2.length;
11914
12708
  };
11915
12709
  initHooks = async (includeProject) => {
11916
12710
  try {
@@ -11961,11 +12755,11 @@ ${m.context}`;
11961
12755
  async execute(args) {
11962
12756
  const name = args?.name;
11963
12757
  if (typeof name !== "string" || !name.trim()) {
11964
- return "\u274C [load_skill] \u7F3A\u5C11\u53C2\u6570 name\u3002\u8BF7\u5148\u67E5\u770B\u3010\u53EF\u7528\u6280\u80FD\u76EE\u5F55\u3011\u4E2D\u7684\u6280\u80FD\u540D\u3002";
12758
+ return toolFailure("[load_skill] \u7F3A\u5C11\u53C2\u6570 name\u3002\u8BF7\u5148\u67E5\u770B\u3010\u53EF\u7528\u6280\u80FD\u76EE\u5F55\u3011\u4E2D\u7684\u6280\u80FD\u540D\u3002");
11965
12759
  }
11966
12760
  const manifest = getSkillManifest(name.trim());
11967
12761
  if (!manifest) {
11968
- return `\u274C \u672A\u627E\u5230\u6280\u80FD\uFF1A${name}\u3002\u8BF7\u6838\u5BF9\u3010\u53EF\u7528\u6280\u80FD\u76EE\u5F55\u3011\u4E2D\u7684\u540D\u79F0\u62FC\u5199\u3002`;
12762
+ return toolFailure(`\u672A\u627E\u5230\u6280\u80FD\uFF1A${name}\u3002\u8BF7\u6838\u5BF9\u3010\u53EF\u7528\u6280\u80FD\u76EE\u5F55\u3011\u4E2D\u7684\u540D\u79F0\u62FC\u5199\u3002`);
11969
12763
  }
11970
12764
  return assembleSkillContent(manifest);
11971
12765
  }
@@ -11976,8 +12770,8 @@ ${m.context}`;
11976
12770
  });
11977
12771
 
11978
12772
  // ../core/src/skills/loader.ts
11979
- import fs32 from "fs/promises";
11980
- import path34 from "path";
12773
+ import fs35 from "fs/promises";
12774
+ import path38 from "path";
11981
12775
  import { fileURLToPath as fileURLToPath2 } from "url";
11982
12776
  var BUILTIN_DIR, SOURCES2, MAX_SKILL_BODY_BYTES, splitList, parseSkillAt, loadSkills, initSkills;
11983
12777
  var init_loader5 = __esm({
@@ -11988,18 +12782,18 @@ var init_loader5 = __esm({
11988
12782
  init_registry3();
11989
12783
  init_skill();
11990
12784
  init_registry2();
11991
- BUILTIN_DIR = path34.join(path34.dirname(fileURLToPath2(import.meta.url)), "builtin");
12785
+ BUILTIN_DIR = path38.join(path38.dirname(fileURLToPath2(import.meta.url)), "builtin");
11992
12786
  SOURCES2 = [
11993
12787
  { dir: BUILTIN_DIR, source: "builtin" },
11994
- { dir: path34.join(appConfig.dataDir, "skills"), source: "global" },
11995
- { dir: path34.join(process.cwd(), ".deepseeker-code", "skills"), source: "project" }
12788
+ { dir: path38.join(appConfig.dataDir, "skills"), source: "global" },
12789
+ { dir: path38.join(process.cwd(), ".deepseeker-code", "skills"), source: "project" }
11996
12790
  ];
11997
12791
  MAX_SKILL_BODY_BYTES = 64 * 1024;
11998
12792
  splitList = (raw) => (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
11999
12793
  parseSkillAt = async (skillFile, dir, source) => {
12000
12794
  let raw;
12001
12795
  try {
12002
- raw = await fs32.readFile(skillFile, "utf-8");
12796
+ raw = await fs35.readFile(skillFile, "utf-8");
12003
12797
  } catch {
12004
12798
  return null;
12005
12799
  }
@@ -12038,7 +12832,7 @@ var init_loader5 = __esm({
12038
12832
  loadSkills = async (includeProject) => {
12039
12833
  const picked = await scanSources(
12040
12834
  filterSources(SOURCES2, includeProject),
12041
- (e, dir) => e.isDirectory() ? path34.join(dir, e.name, "SKILL.md") : void 0,
12835
+ (e, dir) => e.isDirectory() ? path38.join(dir, e.name, "SKILL.md") : void 0,
12042
12836
  parseSkillAt
12043
12837
  );
12044
12838
  for (const { item } of picked) registerSkill(item);
@@ -12069,8 +12863,8 @@ var init_loader5 = __esm({
12069
12863
  });
12070
12864
 
12071
12865
  // ../core/src/agents/loader.ts
12072
- import fs33 from "fs/promises";
12073
- import path35 from "path";
12866
+ import fs36 from "fs/promises";
12867
+ import path39 from "path";
12074
12868
  import { fileURLToPath as fileURLToPath3 } from "url";
12075
12869
  var BUILTIN_DIR2, SOURCES3, parseAgentAt, initAgents;
12076
12870
  var init_loader6 = __esm({
@@ -12080,16 +12874,16 @@ var init_loader6 = __esm({
12080
12874
  init_frontmatter();
12081
12875
  init_registry4();
12082
12876
  init_registry2();
12083
- BUILTIN_DIR2 = path35.join(path35.dirname(fileURLToPath3(import.meta.url)), "builtin");
12877
+ BUILTIN_DIR2 = path39.join(path39.dirname(fileURLToPath3(import.meta.url)), "builtin");
12084
12878
  SOURCES3 = [
12085
12879
  { dir: BUILTIN_DIR2, source: "builtin" },
12086
- { dir: path35.join(appConfig.dataDir, "agents"), source: "global" },
12087
- { dir: path35.join(process.cwd(), ".deepseeker-code", "agents"), source: "project" }
12880
+ { dir: path39.join(appConfig.dataDir, "agents"), source: "global" },
12881
+ { dir: path39.join(process.cwd(), ".deepseeker-code", "agents"), source: "project" }
12088
12882
  ];
12089
12883
  parseAgentAt = async (file, dir, source) => {
12090
12884
  let raw;
12091
12885
  try {
12092
- raw = await fs33.readFile(file, "utf-8");
12886
+ raw = await fs36.readFile(file, "utf-8");
12093
12887
  } catch {
12094
12888
  return null;
12095
12889
  }
@@ -12124,7 +12918,7 @@ var init_loader6 = __esm({
12124
12918
  const known = new Set(into.map((t) => t.function.name));
12125
12919
  const picked = await scanSources(
12126
12920
  filterSources(SOURCES3, includeProject),
12127
- (e, dir) => e.isFile() && e.name.endsWith(".agent.md") ? path35.join(dir, e.name) : void 0,
12921
+ (e, dir) => e.isFile() && e.name.endsWith(".agent.md") ? path39.join(dir, e.name) : void 0,
12128
12922
  parseAgentAt
12129
12923
  );
12130
12924
  for (const { item: m, file, source } of picked) {
@@ -12161,8 +12955,8 @@ var init_registry7 = __esm({
12161
12955
  });
12162
12956
 
12163
12957
  // ../core/src/commands/loader.ts
12164
- import fs34 from "fs/promises";
12165
- import path36 from "path";
12958
+ import fs37 from "fs/promises";
12959
+ import path40 from "path";
12166
12960
  import { fileURLToPath as fileURLToPath4 } from "url";
12167
12961
  var BUILTIN_DIR3, SOURCES4, MAX_COMMAND_BODY_BYTES, parseCommandAt, loadCommands, initCommands;
12168
12962
  var init_loader7 = __esm({
@@ -12172,17 +12966,17 @@ var init_loader7 = __esm({
12172
12966
  init_frontmatter();
12173
12967
  init_registry7();
12174
12968
  init_registry2();
12175
- BUILTIN_DIR3 = path36.join(path36.dirname(fileURLToPath4(import.meta.url)), "builtin");
12969
+ BUILTIN_DIR3 = path40.join(path40.dirname(fileURLToPath4(import.meta.url)), "builtin");
12176
12970
  SOURCES4 = [
12177
12971
  { dir: BUILTIN_DIR3, source: "builtin" },
12178
- { dir: path36.join(appConfig.dataDir, "commands"), source: "global" },
12179
- { dir: path36.join(process.cwd(), ".deepseeker-code", "commands"), source: "project" }
12972
+ { dir: path40.join(appConfig.dataDir, "commands"), source: "global" },
12973
+ { dir: path40.join(process.cwd(), ".deepseeker-code", "commands"), source: "project" }
12180
12974
  ];
12181
12975
  MAX_COMMAND_BODY_BYTES = 64 * 1024;
12182
12976
  parseCommandAt = async (file, _dir, source) => {
12183
12977
  let raw;
12184
12978
  try {
12185
- raw = await fs34.readFile(file, "utf-8");
12979
+ raw = await fs37.readFile(file, "utf-8");
12186
12980
  } catch {
12187
12981
  return null;
12188
12982
  }
@@ -12193,7 +12987,7 @@ var init_loader7 = __esm({
12193
12987
  }
12194
12988
  const name = parsed.frontmatter.name;
12195
12989
  const description = parsed.frontmatter.description;
12196
- const fallbackName = path36.basename(file, ".md");
12990
+ const fallbackName = path40.basename(file, ".md");
12197
12991
  const finalName = name || fallbackName;
12198
12992
  if (!description) {
12199
12993
  console.warn(`\u26A0\uFE0F [commands] \u7F3A\u5C11 description\uFF08${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
@@ -12219,7 +13013,7 @@ var init_loader7 = __esm({
12219
13013
  loadCommands = async (includeProject) => {
12220
13014
  const picked = await scanSources(
12221
13015
  filterSources(SOURCES4, includeProject),
12222
- (e, dir) => e.isFile() && e.name.endsWith(".md") ? path36.join(dir, e.name) : void 0,
13016
+ (e, dir) => e.isFile() && e.name.endsWith(".md") ? path40.join(dir, e.name) : void 0,
12223
13017
  parseCommandAt
12224
13018
  );
12225
13019
  for (const { item } of picked) registerCommand(item);
@@ -12237,8 +13031,8 @@ var init_loader7 = __esm({
12237
13031
  });
12238
13032
 
12239
13033
  // ../core/src/outputStyles/loader.ts
12240
- import fs35 from "fs/promises";
12241
- import path37 from "path";
13034
+ import fs38 from "fs/promises";
13035
+ import path41 from "path";
12242
13036
  import { fileURLToPath as fileURLToPath5 } from "url";
12243
13037
  var BUILTIN_DIR4, MAX_STYLE_BODY_BYTES, SOURCES5, parseStyleAt, loadOutputStyles, initOutputStyles;
12244
13038
  var init_loader8 = __esm({
@@ -12248,17 +13042,17 @@ var init_loader8 = __esm({
12248
13042
  init_frontmatter();
12249
13043
  init_registry5();
12250
13044
  init_registry2();
12251
- BUILTIN_DIR4 = path37.join(path37.dirname(fileURLToPath5(import.meta.url)), "builtin");
13045
+ BUILTIN_DIR4 = path41.join(path41.dirname(fileURLToPath5(import.meta.url)), "builtin");
12252
13046
  MAX_STYLE_BODY_BYTES = 64 * 1024;
12253
13047
  SOURCES5 = [
12254
13048
  { dir: BUILTIN_DIR4, source: "builtin" },
12255
- { dir: path37.join(appConfig.dataDir, "output-styles"), source: "global" },
12256
- { dir: path37.join(process.cwd(), ".deepseeker-code", "output-styles"), source: "project" }
13049
+ { dir: path41.join(appConfig.dataDir, "output-styles"), source: "global" },
13050
+ { dir: path41.join(process.cwd(), ".deepseeker-code", "output-styles"), source: "project" }
12257
13051
  ];
12258
13052
  parseStyleAt = async (file, _dir, source) => {
12259
13053
  let raw;
12260
13054
  try {
12261
- raw = await fs35.readFile(file, "utf-8");
13055
+ raw = await fs38.readFile(file, "utf-8");
12262
13056
  } catch {
12263
13057
  return null;
12264
13058
  }
@@ -12290,7 +13084,7 @@ var init_loader8 = __esm({
12290
13084
  loadOutputStyles = async (includeProject) => {
12291
13085
  const picked = await scanSources(
12292
13086
  filterSources(SOURCES5, includeProject),
12293
- (e, dir) => e.isFile() && e.name.endsWith(".md") ? path37.join(dir, e.name) : void 0,
13087
+ (e, dir) => e.isFile() && e.name.endsWith(".md") ? path41.join(dir, e.name) : void 0,
12294
13088
  parseStyleAt
12295
13089
  );
12296
13090
  for (const { item } of picked) registerOutputStyle(item);
@@ -12371,8 +13165,8 @@ var init_bootstrap = __esm({
12371
13165
  });
12372
13166
 
12373
13167
  // ../core/src/statusLine/config.ts
12374
- import fs36 from "fs/promises";
12375
- import path38 from "path";
13168
+ import fs39 from "fs/promises";
13169
+ import path42 from "path";
12376
13170
  var parseBlock, readStatusLineConfig;
12377
13171
  var init_config2 = __esm({
12378
13172
  "../core/src/statusLine/config.ts"() {
@@ -12388,16 +13182,16 @@ var init_config2 = __esm({
12388
13182
  };
12389
13183
  readStatusLineConfig = async (includeProject) => {
12390
13184
  const paths = [
12391
- path38.join(appConfig.dataDir, "settings.json"),
13185
+ path42.join(appConfig.dataDir, "settings.json"),
12392
13186
  // 全局用户级
12393
- ...includeProject ? [path38.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
13187
+ ...includeProject ? [path42.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
12394
13188
  // 项目级(覆盖);未信任时省略
12395
13189
  ];
12396
13190
  let result = null;
12397
13191
  for (const configPath of paths) {
12398
13192
  let raw;
12399
13193
  try {
12400
- raw = await fs36.readFile(configPath, "utf-8");
13194
+ raw = await fs39.readFile(configPath, "utf-8");
12401
13195
  } catch {
12402
13196
  continue;
12403
13197
  }
@@ -12417,8 +13211,8 @@ var init_config2 = __esm({
12417
13211
 
12418
13212
  // ../core/src/statusLine/runner.ts
12419
13213
  import { spawn as spawn5 } from "child_process";
12420
- import fs37 from "fs";
12421
- import path39 from "path";
13214
+ import fs40 from "fs";
13215
+ import path43 from "path";
12422
13216
  import { fileURLToPath as fileURLToPath6 } from "url";
12423
13217
  var STATUSLINE_TIMEOUT_MS, cachedVersion, runStatusLine;
12424
13218
  var init_runner = __esm({
@@ -12427,16 +13221,16 @@ var init_runner = __esm({
12427
13221
  init_guard();
12428
13222
  STATUSLINE_TIMEOUT_MS = 3e3;
12429
13223
  try {
12430
- const here = path39.dirname(fileURLToPath6(import.meta.url));
13224
+ const here = path43.dirname(fileURLToPath6(import.meta.url));
12431
13225
  const candidates = [
12432
- path39.join(here, "../../../package.json"),
13226
+ path43.join(here, "../../../package.json"),
12433
13227
  // src/core/package.json
12434
- path39.join(here, "../../../../package.json")
13228
+ path43.join(here, "../../../../package.json")
12435
13229
  // 仓库根 package.json
12436
13230
  ];
12437
13231
  for (const p of candidates) {
12438
13232
  try {
12439
- const pkg = JSON.parse(fs37.readFileSync(p, "utf-8"));
13233
+ const pkg = JSON.parse(fs40.readFileSync(p, "utf-8"));
12440
13234
  if (typeof pkg.version === "string" && pkg.version) {
12441
13235
  cachedVersion = pkg.version;
12442
13236
  break;
@@ -12487,8 +13281,8 @@ var init_runner = __esm({
12487
13281
  });
12488
13282
 
12489
13283
  // ../core/src/observability/store.ts
12490
- import path40 from "path";
12491
- import fs38 from "fs/promises";
13284
+ import path44 from "path";
13285
+ import fs41 from "fs/promises";
12492
13286
  var getTraceDirPath, getTracePath, tracePathCache, ensuredTraceDirs, ensureTraceDir, getGlobalClockPath, getTraceStorePath;
12493
13287
  var init_store3 = __esm({
12494
13288
  "../core/src/observability/store.ts"() {
@@ -12498,21 +13292,21 @@ var init_store3 = __esm({
12498
13292
  init_common();
12499
13293
  getTraceDirPath = (mainTraceId) => {
12500
13294
  assertSafeSessionId(mainTraceId, "traceId");
12501
- return path40.join(appConfig.dataDir, "trace", appConfig.userWorkspaceDir, getFileName(mainTraceId));
13295
+ return path44.join(appConfig.dataDir, "trace", appConfig.userWorkspaceDir, getFileName(mainTraceId));
12502
13296
  };
12503
13297
  getTracePath = () => {
12504
- return path40.join(appConfig.dataDir, "trace");
13298
+ return path44.join(appConfig.dataDir, "trace");
12505
13299
  };
12506
13300
  tracePathCache = /* @__PURE__ */ new Map();
12507
13301
  ensuredTraceDirs = /* @__PURE__ */ new Set();
12508
13302
  ensureTraceDir = async (mainTraceId) => {
12509
13303
  const dir = getTraceDirPath(mainTraceId);
12510
13304
  if (ensuredTraceDirs.has(dir)) return;
12511
- await fs38.mkdir(dir, { recursive: true });
13305
+ await fs41.mkdir(dir, { recursive: true });
12512
13306
  ensuredTraceDirs.add(dir);
12513
13307
  };
12514
13308
  getGlobalClockPath = () => {
12515
- return path40.join(getTracePath(), "trace-cleanup-clock.json");
13309
+ return path44.join(getTracePath(), "trace-cleanup-clock.json");
12516
13310
  };
12517
13311
  getTraceStorePath = async (mainTraceId) => {
12518
13312
  assertSafeSessionId(mainTraceId, "traceId");
@@ -12521,7 +13315,7 @@ var init_store3 = __esm({
12521
13315
  if (cached2) return cached2;
12522
13316
  await ensureTraceDir(rootId);
12523
13317
  const dir = getTraceDirPath(rootId);
12524
- const files = await fs38.readdir(dir);
13318
+ const files = await fs41.readdir(dir);
12525
13319
  const existedFile = files.find((f) => f.endsWith(`__${rootId}.jsonl`));
12526
13320
  let fileName = "";
12527
13321
  if (existedFile) {
@@ -12530,7 +13324,7 @@ var init_store3 = __esm({
12530
13324
  const todayStr = getTodayDateString();
12531
13325
  fileName = `trace-${todayStr}__${rootId}.jsonl`;
12532
13326
  }
12533
- const resolved = path40.join(dir, fileName);
13327
+ const resolved = path44.join(dir, fileName);
12534
13328
  tracePathCache.set(rootId, resolved);
12535
13329
  return resolved;
12536
13330
  };
@@ -12538,8 +13332,8 @@ var init_store3 = __esm({
12538
13332
  });
12539
13333
 
12540
13334
  // ../core/src/observability/trace.ts
12541
- import fs39 from "fs/promises";
12542
- import path41 from "path";
13335
+ import fs42 from "fs/promises";
13336
+ import path45 from "path";
12543
13337
  async function emitTrace(base) {
12544
13338
  const event = {
12545
13339
  sessionId: base.sessionId,
@@ -12561,7 +13355,7 @@ async function emitTrace(base) {
12561
13355
  async function appendTraceEvent(event) {
12562
13356
  const p = await getTraceStorePath(event.sessionId);
12563
13357
  const line = JSON.stringify(event) + "\n";
12564
- await fs39.appendFile(p, line, "utf-8");
13358
+ await fs42.appendFile(p, line, "utf-8");
12565
13359
  if (workspaceTraceBytes < 0) {
12566
13360
  workspaceTraceBytes = await getDirBytes(getTracePath());
12567
13361
  }
@@ -12593,8 +13387,8 @@ async function maybeCleanupAfterAppend(sessionId) {
12593
13387
  lastSizeCleanupAt2 = now;
12594
13388
  diskLastCleanupAt2 = now;
12595
13389
  const clockPath = getGlobalClockPath();
12596
- await fs39.mkdir(path41.dirname(clockPath), { recursive: true });
12597
- await fs39.writeFile(clockPath, JSON.stringify({ lastCleanupAt: now, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
13390
+ await fs42.mkdir(path45.dirname(clockPath), { recursive: true });
13391
+ await fs42.writeFile(clockPath, JSON.stringify({ lastCleanupAt: now, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
12598
13392
  if (deletedCount > 0) {
12599
13393
  console.log(`\u2728 [Trace\u8FD0\u7EF4] \u5171\u9500\u6BC1 ${deletedCount} \u4E2A\u4F1A\u8BDD\u65E5\u5FD7\uFF08\u8F6F\u89C4\u5219\u8FC7\u671F\u6E05\u7406 + \u5BB9\u91CF\u786C\u9A71\u9010\uFF09\uFF0C\u78C1\u76D8\u6C34\u4F4D\u56DE\u843D\u3002`);
12600
13394
  }
@@ -12614,22 +13408,22 @@ async function cleanupOldTraceFiles() {
12614
13408
  let deleted = 0;
12615
13409
  let relativePaths;
12616
13410
  try {
12617
- relativePaths = await fs39.readdir(dir, { recursive: true });
13411
+ relativePaths = await fs42.readdir(dir, { recursive: true });
12618
13412
  } catch {
12619
13413
  return 0;
12620
13414
  }
12621
13415
  const re = /^trace-(\d{4}-\d{2}-\d{2})__([\w-]+)\.jsonl$/;
12622
13416
  for (const relativePath of relativePaths) {
12623
- const fileName = path41.basename(relativePath);
13417
+ const fileName = path45.basename(relativePath);
12624
13418
  const m = fileName.match(re);
12625
13419
  if (!m) continue;
12626
13420
  const fileBornDay = /* @__PURE__ */ new Date(`${m[1]}T00:00:00`);
12627
13421
  if (fileBornDay >= cutoff) continue;
12628
- const fullPath = path41.join(dir, relativePath);
13422
+ const fullPath = path45.join(dir, relativePath);
12629
13423
  try {
12630
- const st = await fs39.stat(fullPath);
13424
+ const st = await fs42.stat(fullPath);
12631
13425
  if (now - st.mtimeMs < ACTIVE_GRACE_MS2) continue;
12632
- await fs39.unlink(fullPath);
13426
+ await fs42.unlink(fullPath);
12633
13427
  deleted += 1;
12634
13428
  } catch {
12635
13429
  }
@@ -12640,18 +13434,18 @@ async function evictOldestForSize2(targetBytes) {
12640
13434
  const dir = getTracePath();
12641
13435
  let relativePaths;
12642
13436
  try {
12643
- relativePaths = await fs39.readdir(dir, { recursive: true });
13437
+ relativePaths = await fs42.readdir(dir, { recursive: true });
12644
13438
  } catch {
12645
13439
  return 0;
12646
13440
  }
12647
13441
  const re = /^trace-(\d{4}-\d{2}-\d{2})__([\w-]+)\.jsonl$/;
12648
13442
  const candidates = [];
12649
13443
  for (const relativePath of relativePaths) {
12650
- const fileName = path41.basename(relativePath);
13444
+ const fileName = path45.basename(relativePath);
12651
13445
  if (!re.test(fileName)) continue;
12652
13446
  try {
12653
- const st = await fs39.stat(path41.join(dir, relativePath));
12654
- candidates.push({ mtime: st.mtimeMs, fullPath: path41.join(dir, relativePath), size: st.size });
13447
+ const st = await fs42.stat(path45.join(dir, relativePath));
13448
+ candidates.push({ mtime: st.mtimeMs, fullPath: path45.join(dir, relativePath), size: st.size });
12655
13449
  } catch {
12656
13450
  }
12657
13451
  }
@@ -12661,7 +13455,7 @@ async function evictOldestForSize2(targetBytes) {
12661
13455
  for (const c of candidates) {
12662
13456
  if (totalBytes <= targetBytes) break;
12663
13457
  try {
12664
- await fs39.unlink(c.fullPath);
13458
+ await fs42.unlink(c.fullPath);
12665
13459
  totalBytes -= c.size;
12666
13460
  deleted += 1;
12667
13461
  } catch {
@@ -12693,7 +13487,7 @@ var init_trace = __esm({
12693
13487
  if (clockLoaded2) return;
12694
13488
  clockLoaded2 = true;
12695
13489
  try {
12696
- const rawClock = await fs39.readFile(getGlobalClockPath(), "utf-8");
13490
+ const rawClock = await fs42.readFile(getGlobalClockPath(), "utf-8");
12697
13491
  diskLastCleanupAt2 = JSON.parse(rawClock).lastCleanupAt || 0;
12698
13492
  } catch {
12699
13493
  diskLastCleanupAt2 = 0;
@@ -12819,7 +13613,7 @@ var init_systemPrompt = __esm({
12819
13613
  - **\u6539\u5B8C\u52FF\u7ACB\u523B\u91CD\u8BFB\uFF08\u53CD\u788E\u6B65\xB7\u91CD\u8981\uFF09**\uFF1Aedit_file / write_file \u6210\u529F\u8FD4\u56DE\u540E\uFF0C\u5DE5\u5177\u7ED3\u679C\u5DF2\u8BF4\u660E\u6539\u4E86\u4EC0\u4E48\uFF0C\u4E0D\u8981\u9A6C\u4E0A read_file \u628A\u6574\u4E2A\u6587\u4EF6\u91CD\u8BFB\u4E00\u904D\u6765"\u786E\u8BA4"\u3002\u786E\u9700\u6838\u9A8C\u65F6\uFF0C\u53EA\u8BFB\u6539\u52A8\u9644\u8FD1\u51E0\u884C\u6216\u8DD1\u7C7B\u578B\u68C0\u67E5/\u6D4B\u8BD5\uFF0C\u800C\u975E\u6574\u6587\u4EF6\u91CD\u8BFB\u3002
12820
13614
  - \u4E0D\u91CD\u590D\u6293\u53D6\u5DF2\u6293\u53D6\u8FC7\u7684 URL\uFF1B\u4E0D\u7528\u8FD1\u4E49\u5173\u952E\u8BCD\u53CD\u590D\u641C\u7D22\u540C\u4E00\u4E2A\u610F\u56FE\u3002
12821
13615
  - \u8054\u7F51/\u641C\u7D22\u7C7B\u8C03\u7814\u4EFB\u52A1\uFF0C\u901A\u5E38 1~3 \u4E2A\u6765\u6E90\u5DF2\u8DB3\u591F\u652F\u6491\u56DE\u7B54\u2014\u2014\u62FF\u5230\u5373\u7EFC\u5408\u4F5C\u7B54\uFF0C\u4E0D\u8981\u4E3A\u8FFD\u6C42"\u66F4\u5168\u9762"\u800C\u65E0\u9650\u8FFD\u52A0\u68C0\u7D22\u8F6E\u6B21\u3002
12822
- - \u4E00\u6B21\u53EA\u63A8\u8FDB\u4E00\u4E2A\u660E\u786E\u7684\u5B50\u76EE\u6807\uFF0C\u907F\u514D\u5728\u540C\u4E00\u8F6E\u91CC\u5806\u780C\u5927\u91CF\u5DE5\u5177\u8C03\u7528\u3002
13616
+ - **\u72EC\u7ACB\u8BFB\u64CD\u4F5C\u5E76\u884C\u6253\u5305\uFF08\u91CD\u8981\uFF09**\uFF1A\u591A\u4E2A\u4E92\u4E0D\u4F9D\u8D56\u7684\u53EA\u8BFB\u8C03\u7528\uFF08\u8BFB\u591A\u4E2A\u6587\u4EF6\u3001\u591A\u4E2A\u68C0\u7D22\u3001\u591A\u4E2A\u5B9A\u4E49\u67E5\u8BE2\uFF09\u5E94\u5728\u540C\u4E00\u6761\u6D88\u606F\u91CC\u4E00\u6B21\u6027\u5168\u90E8\u53D1\u51FA\uFF0C\u6267\u884C\u5C42\u4F1A\u81EA\u52A8\u5E76\u53D1\u6267\u884C\uFF0C\u4E00\u8F6E\u9876\u591A\u8F6E\uFF1B\u6709\u4F9D\u8D56\uFF08\u540E\u4E00\u6B65\u8981\u7528\u524D\u4E00\u6B65\u7ED3\u679C\uFF09\u6216\u4F1A\u6539\u72B6\u6001\u7684\u64CD\u4F5C\uFF0C\u5FC5\u987B\u7B49\u524D\u5E8F\u7ED3\u679C\u8FD4\u56DE\u518D\u53D1\u3002
12823
13617
  - \u591A\u6B65\u9AA4\u7684\u590D\u6742\u4EFB\u52A1\uFF08\u22653 \u6B65\u3001\u8DE8\u591A\u6587\u4EF6\u6216\u591A\u9636\u6BB5\uFF09\u5148\u8C03\u7528 todo_write \u628A\u8BA1\u5212\u62C6\u6210\u53EF\u52FE\u9009\u7684\u4EFB\u52A1\u6E05\u5355\uFF0C\u518D\u9010\u6B65\u63A8\u8FDB\u5E76\u5373\u65F6\u66F4\u65B0\u72B6\u6001\u2014\u2014\u8BA9\u4F60\u548C\u7528\u6237\u90FD\u770B\u6E05\u8FDB\u5EA6\u3001\u4E0D\u9057\u6F0F\u6B65\u9AA4\uFF1B\u7B80\u5355 1-2 \u6B65\u7684\u4EFB\u52A1\u65E0\u9700\u5EFA\u6E05\u5355\u3002
12824
13618
 
12825
13619
  \u3010\u64CD\u4F5C\u786E\u8BA4\u7EA6\u5B9A\u3011
@@ -12840,8 +13634,8 @@ ${PLAN_MODE_AUTO_ENTER_HINT}
12840
13634
  });
12841
13635
 
12842
13636
  // ../core/src/serve/chatProcessing.ts
12843
- import path42 from "path";
12844
- import fs40 from "fs";
13637
+ import path46 from "path";
13638
+ import fs43 from "fs";
12845
13639
  var handleUnifiedChat;
12846
13640
  var init_chatProcessing = __esm({
12847
13641
  "../core/src/serve/chatProcessing.ts"() {
@@ -12897,17 +13691,17 @@ ${inject}`;
12897
13691
  if (roots.length > 1) {
12898
13692
  let activeReal = getActiveWorkspaceRoot();
12899
13693
  try {
12900
- activeReal = fs40.realpathSync(activeReal);
13694
+ activeReal = fs43.realpathSync(activeReal);
12901
13695
  } catch {
12902
13696
  }
12903
- const lines = roots.map((r) => `- ${path42.basename(r) || r}: ${r}${r === activeReal ? "\uFF08\u9ED8\u8BA4\u6839\uFF1A\u76F8\u5BF9\u8DEF\u5F84/\u547D\u4EE4\u57FA\u51C6\uFF09" : "\uFF08\u53EF\u76F4\u63A5\u8BFB\u5199\uFF09"}`);
13697
+ const lines = roots.map((r) => `- ${path46.basename(r) || r}: ${r}${r === activeReal ? "\uFF08\u9ED8\u8BA4\u6839\uFF1A\u76F8\u5BF9\u8DEF\u5F84/\u547D\u4EE4\u57FA\u51C6\uFF09" : "\uFF08\u53EF\u76F4\u63A5\u8BFB\u5199\uFF09"}`);
12904
13698
  sysPrompt = sysPrompt + `
12905
13699
 
12906
13700
  \u3010\u5DE5\u4F5C\u533A\uFF08\u591A\u9879\u76EE\uFF09\u3011
12907
13701
  \u65E0\u9700\u5207\u6362\u9879\u76EE\uFF0C\u4EE5\u4E0B\u6240\u6709\u6839\u90FD\u53EF\u76F4\u63A5\u7528\u7EDD\u5BF9\u8DEF\u5F84\u8BBF\u95EE\uFF1B\u8DE8\u9879\u76EE\u7528\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u9ED8\u8BA4\u6839\u7684 ../<\u5144\u5F1F\u76EE\u5F55>:
12908
13702
  ${lines.join("\n")}
12909
- \u2605 \u8DEF\u5F84\u951A\u5B9A\u94C1\u5F8B\uFF1A\u5DE5\u5177\u7F3A\u7701\u53EA\u4F5C\u7528\u4E8E\u9ED8\u8BA4\u6839\uFF08${path42.basename(activeReal) || activeReal} = IDE \u5934\u90E8\u6240\u793A\u6839\uFF09\u3002\u5F53\u7528\u6237\u5728\u5BF9\u8BDD\u4E2D\u6307\u660E\u4E86\u67D0\u4E2A\u9879\u76EE\uFF08\u5982\u300C\u5728 A \u9879\u76EE\u300D\u300C\u6539\u524D\u7AEF\u7684 xxx\u300D\u300C\u641C\u540E\u7AEF\u300D\uFF09\uFF0C\u8BE5\u8F6E\u6240\u6709\u5E26 path \u53C2\u6570\u7684\u5DE5\u5177\u8C03\u7528\uFF08read_file / edit_file / search_grep / glob / list_dir / read_xlsx / read_docx \u7B49\uFF09\u5FC5\u987B\u628A\u5BF9\u5E94\u9879\u76EE\u6839\u4F5C\u4E3A path \u4F20\u5165\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u3001../<\u5144\u5F1F\u76EE\u5F55>\u3001\u6216\u9879\u76EE\u76EE\u5F55\u540D\uFF09\uFF0C\u5426\u5219\u53EA\u4F1A\u8BFB\u5199/\u641C\u7D22\u9ED8\u8BA4\u6839\uFF0C\u4E0E\u7528\u6237\u610F\u56FE\u4E0D\u7B26\u3002
12910
- \u6CE8\u610F\uFF1A\u9879\u76EE\u7EA7\u914D\u7F6E\uFF08.deepseeker-code/\uFF09\u4EC5\u5728\u9ED8\u8BA4\u6839 ${path42.basename(activeReal) || activeReal} \u751F\u6548\u3002`;
13703
+ \u2605 \u8DEF\u5F84\u951A\u5B9A\u94C1\u5F8B\uFF1A\u5DE5\u5177\u7F3A\u7701\u53EA\u4F5C\u7528\u4E8E\u9ED8\u8BA4\u6839\uFF08${path46.basename(activeReal) || activeReal} = IDE \u5934\u90E8\u6240\u793A\u6839\uFF09\u3002\u5F53\u7528\u6237\u5728\u5BF9\u8BDD\u4E2D\u6307\u660E\u4E86\u67D0\u4E2A\u9879\u76EE\uFF08\u5982\u300C\u5728 A \u9879\u76EE\u300D\u300C\u6539\u524D\u7AEF\u7684 xxx\u300D\u300C\u641C\u540E\u7AEF\u300D\uFF09\uFF0C\u8BE5\u8F6E\u6240\u6709\u5E26 path \u53C2\u6570\u7684\u5DE5\u5177\u8C03\u7528\uFF08read_file / edit_file / search_grep / glob / list_dir / read_xlsx / read_docx \u7B49\uFF09\u5FC5\u987B\u628A\u5BF9\u5E94\u9879\u76EE\u6839\u4F5C\u4E3A path \u4F20\u5165\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u3001../<\u5144\u5F1F\u76EE\u5F55>\u3001\u6216\u9879\u76EE\u76EE\u5F55\u540D\uFF09\uFF0C\u5426\u5219\u53EA\u4F1A\u8BFB\u5199/\u641C\u7D22\u9ED8\u8BA4\u6839\uFF0C\u4E0E\u7528\u6237\u610F\u56FE\u4E0D\u7B26\u3002
13704
+ \u6CE8\u610F\uFF1A\u9879\u76EE\u7EA7\u914D\u7F6E\uFF08.deepseeker-code/\uFF09\u4EC5\u5728\u9ED8\u8BA4\u6839 ${path46.basename(activeReal) || activeReal} \u751F\u6548\u3002`;
12911
13705
  }
12912
13706
  const osName = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : "Linux";
12913
13707
  const now = /* @__PURE__ */ new Date();
@@ -13012,7 +13806,7 @@ ${envLines.join("\n")}`;
13012
13806
  });
13013
13807
 
13014
13808
  // ../core/src/session/fork.ts
13015
- import fs41 from "fs/promises";
13809
+ import fs44 from "fs/promises";
13016
13810
  var listForkAnchors, derivePrefix, forkSession;
13017
13811
  var init_fork = __esm({
13018
13812
  "../core/src/session/fork.ts"() {
@@ -13085,7 +13879,7 @@ var init_fork = __esm({
13085
13879
  const prefix = derivePrefix(lines, upToLineId);
13086
13880
  const newId = createUUID();
13087
13881
  await ensureSessionsDir(newId);
13088
- const rawText = await fs41.readFile(getTranscriptPath(sourceId), "utf-8");
13882
+ const rawText = await fs44.readFile(getTranscriptPath(sourceId), "utf-8");
13089
13883
  const kept = [];
13090
13884
  let good = 0;
13091
13885
  for (const s of rawText.split("\n")) {
@@ -13101,7 +13895,7 @@ var init_fork = __esm({
13101
13895
  }
13102
13896
  let payload = kept.join("\n");
13103
13897
  if (payload.length > 0 && !payload.endsWith("\n")) payload += "\n";
13104
- await fs41.writeFile(getTranscriptPath(newId), payload, "utf-8");
13898
+ await fs44.writeFile(getTranscriptPath(newId), payload, "utf-8");
13105
13899
  const srcState = await readStore(sourceId);
13106
13900
  const srcRolling = await getRollingState(sourceId);
13107
13901
  const legacy = !lines.some(isEventLine);
@@ -13864,6 +14658,11 @@ ${r.output.trim() || "(\u65E0\u8F93\u51FA)"}`);
13864
14658
  const picked = await new Promise((resolve) => setPendingSessions({ sessions: sessions2, resolve }));
13865
14659
  if (picked) await loadSession(picked);
13866
14660
  }, [pushInfo, loadSession]);
14661
+ const newSession = useCallback(async () => {
14662
+ sessionIdRef.current = await getOrCreateSessionId(void 0);
14663
+ clearRows();
14664
+ pushInfo(S.sessionNew(truncateMiddle(sessionIdRef.current, 12)));
14665
+ }, [clearRows, pushInfo]);
13867
14666
  const resolveFork = useCallback((anchor) => {
13868
14667
  setPendingFork((prev) => {
13869
14668
  prev?.resolve(anchor);
@@ -13971,6 +14770,7 @@ ${r.output.trim() || "(\u65E0\u8F93\u51FA)"}`);
13971
14770
  loadSession,
13972
14771
  openForkPicker,
13973
14772
  resolveFork,
14773
+ newSession,
13974
14774
  openModelPicker,
13975
14775
  resolveModel
13976
14776
  };
@@ -15034,8 +15834,8 @@ var init_TopPanel = __esm({
15034
15834
  });
15035
15835
 
15036
15836
  // ../core/src/observability/inspect.ts
15037
- import fs42 from "fs";
15038
- var CWD, fmt, readTrace, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
15837
+ import fs45 from "fs";
15838
+ var CWD, fmt, readTrace, STOP_LABEL, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
15039
15839
  var init_inspect = __esm({
15040
15840
  "../core/src/observability/inspect.ts"() {
15041
15841
  "use strict";
@@ -15044,6 +15844,7 @@ var init_inspect = __esm({
15044
15844
  init_registry();
15045
15845
  init_loader();
15046
15846
  init_store3();
15847
+ init_usageLog();
15047
15848
  init_store();
15048
15849
  init_createModel();
15049
15850
  CWD = process.cwd();
@@ -15052,7 +15853,7 @@ var init_inspect = __esm({
15052
15853
  if (!sessionId) return [];
15053
15854
  try {
15054
15855
  const p = await getTraceStorePath(sessionId);
15055
- const text = await fs42.promises.readFile(p, "utf8");
15856
+ const text = await fs45.promises.readFile(p, "utf8");
15056
15857
  const out = [];
15057
15858
  for (const line of text.split("\n")) {
15058
15859
  if (!line.trim()) continue;
@@ -15066,10 +15867,13 @@ var init_inspect = __esm({
15066
15867
  return [];
15067
15868
  }
15068
15869
  };
15870
+ STOP_LABEL = { normal: "\u5B8C\u6210", aborted: "\u4E2D\u6B62", error: "\u9519\u8BEF", repeat: "\u7194\u65AD", limit: "\u8D85\u9650" };
15871
+ fmtK = (n) => n >= 1e4 ? `${(n / 1e3).toFixed(1)}k` : fmt(n);
15069
15872
  inspectUsage = async (sessionId) => {
15070
15873
  const trace = await readTrace(sessionId);
15071
15874
  const responses = trace.filter((t) => t?.eventType === "llm.response" && t?.usage);
15072
- if (responses.length === 0) return `\u{1F4CA} Token \u7528\u91CF\uFF1A\u6682\u65E0\u6570\u636E\uFF08\u672C\u4F1A\u8BDD\u5C1A\u65E0 LLM \u54CD\u5E94\u843D\u76D8\uFF09\u3002`;
15875
+ const sessionRuns = (await readUsageRecords()).filter((r) => r.sessionId === sessionId);
15876
+ if (responses.length === 0 && sessionRuns.length === 0) return `\u{1F4CA} Token \u7528\u91CF\uFF1A\u6682\u65E0\u6570\u636E\uFF08\u672C\u4F1A\u8BDD\u5C1A\u65E0 LLM \u54CD\u5E94\u843D\u76D8\uFF09\u3002`;
15073
15877
  let prompt = 0, completion = 0, total = 0, cacheHit = 0, cacheMiss = 0;
15074
15878
  let mainTotal = 0, subTotal = 0;
15075
15879
  for (const r of responses) {
@@ -15085,13 +15889,35 @@ var init_inspect = __esm({
15085
15889
  }
15086
15890
  const cacheRate = prompt > 0 ? (cacheHit / prompt * 100).toFixed(1) : "0.0";
15087
15891
  const rounds = responses.length;
15088
- return [
15892
+ const lines = [
15089
15893
  `\u{1F4CA} Token \u7528\u91CF\uFF08\u672C\u4F1A\u8BDD\uFF0C${rounds} \u6B21 LLM \u54CD\u5E94\uFF09`,
15090
15894
  ` \u8F93\u5165(prompt): ${fmt(prompt)} | \u8F93\u51FA(completion): ${fmt(completion)} | \u5408\u8BA1: ${fmt(total)}`,
15091
15895
  ` \u7F13\u5B58\u547D\u4E2D: ${fmt(cacheHit)}\uFF08${cacheRate}%\uFF09 | \u7F13\u5B58\u672A\u547D\u4E2D: ${fmt(cacheMiss)}`,
15092
- ` \u4E3B agent: ${fmt(mainTotal)} | \u5B50 agent(spawn/workflow): ${fmt(subTotal)}`,
15093
- ` \u63D0\u793A\uFF1A\u5E76\u884C workflow \u4F1A\u653E\u5927\u5B50 agent \u7528\u91CF\uFF1B\u7F13\u5B58\u547D\u4E2D\u7387\u8D8A\u9AD8\uFF0C\u8F93\u5165\u6210\u672C\u8D8A\u4F4E\u3002`
15094
- ].join("\n");
15896
+ ` \u4E3B agent: ${fmt(mainTotal)} | \u5B50 agent(spawn/workflow): ${fmt(subTotal)}`
15897
+ ];
15898
+ if (sessionRuns.length > 0) {
15899
+ const ss = summarizeUsage(sessionRuns);
15900
+ const abortedPct = ss.mainRuns > 0 ? Math.round(ss.stopReasons.aborted / ss.mainRuns * 100) : 0;
15901
+ const avgRounds = ss.mainRuns > 0 ? (ss.mainRounds / ss.mainRuns).toFixed(1) : "0";
15902
+ lines.push(` \u258E\u672C\u4F1A\u8BDD ${ss.mainRuns} \u4E2A run\uFF1A${ss.mainRounds} \u8F6E\uFF08\u5747 ${avgRounds} \u8F6E/run\uFF09\xB7 \u4E2D\u6B62 ${ss.stopReasons.aborted}/${ss.mainRuns}\uFF08${abortedPct}%\uFF09`);
15903
+ for (const r of sessionRuns.filter((x) => x.depth === 0).slice(-6)) {
15904
+ const tools = topTools(r.tools, 3).map(([n, c]) => `${n}\xD7${c}`).join(" ") || "\uFF08\u65E0\u5DE5\u5177\u8C03\u7528\uFF09";
15905
+ lines.push(` ${String(r.ts).slice(5, 16).replace("T", " ")} ${r.rounds}\u8F6E ${fmtK(r.totalTokens ?? 0)} tok ${STOP_LABEL[r.stopReason] ?? r.stopReason} ${tools}`);
15906
+ }
15907
+ }
15908
+ const week = summarizeUsage(await readUsageRecords(7));
15909
+ if (week.runs > 0) {
15910
+ const weekHit = week.promptTokens > 0 ? (week.cachedTokens / week.promptTokens * 100).toFixed(1) : "0.0";
15911
+ const weekAbort = week.mainRuns > 0 ? Math.round(week.stopReasons.aborted / week.mainRuns * 100) : 0;
15912
+ const weekAvg = week.mainRuns > 0 ? (week.mainRounds / week.mainRuns).toFixed(1) : "0";
15913
+ const tools = topTools(week.tools, 4).map(([n, c]) => `${n}\xD7${c}`).join("\u3001");
15914
+ lines.push(
15915
+ ` \u258E\u8FD1 7 \u5929\uFF08\u4F7F\u7528\u65E5\u5FD7 \xB7 \u5168\u9879\u76EE\uFF09\uFF1A${week.mainRuns} runs \xB7 ${week.mainRounds} \u8F6E\uFF08\u5747 ${weekAvg} \u8F6E/run\uFF09\xB7 \u4E2D\u6B62 ${weekAbort}% \xB7 ${fmtK(week.totalTokens)} tok\uFF08\u7F13\u5B58\u547D\u4E2D ${weekHit}%\uFF09`,
15916
+ ` \u5E38\u7528\u5DE5\u5177: ${tools || "\uFF08\u65E0\uFF09"}`
15917
+ );
15918
+ }
15919
+ lines.push(` \u63D0\u793A\uFF1A\u5E76\u884C workflow \u4F1A\u653E\u5927\u5B50 agent \u7528\u91CF\uFF1B\u7F13\u5B58\u547D\u4E2D\u7387\u8D8A\u9AD8\uFF0C\u8F93\u5165\u6210\u672C\u8D8A\u4F4E\u3002`);
15920
+ return lines.join("\n");
15095
15921
  };
15096
15922
  inspectContext = async (sessionId) => {
15097
15923
  const trace = await readTrace(sessionId);
@@ -15339,6 +16165,8 @@ var init_App = __esm({
15339
16165
  return S.cmdLang;
15340
16166
  case "output-style":
15341
16167
  return S.cmdOutputStyle;
16168
+ case "new":
16169
+ return S.cmdNew;
15342
16170
  case "sessions":
15343
16171
  return S.cmdSessions;
15344
16172
  case "fork":
@@ -15421,6 +16249,13 @@ var init_App = __esm({
15421
16249
  case "/clear":
15422
16250
  state.clearRows();
15423
16251
  return true;
16252
+ case "/new":
16253
+ if (state.busy) {
16254
+ state.pushInfo(S.sessionNewBusy);
16255
+ return true;
16256
+ }
16257
+ void state.newSession();
16258
+ return true;
15424
16259
  case "/sessions":
15425
16260
  void state.openSessionPicker();
15426
16261
  return true;
@@ -15863,7 +16698,7 @@ init_updateCheck();
15863
16698
  init_trust();
15864
16699
  import { render } from "ink";
15865
16700
  import os5 from "os";
15866
- import path43 from "path";
16701
+ import path47 from "path";
15867
16702
  import { emitKeypressEvents } from "node:readline";
15868
16703
  import { jsx as jsx19 } from "react/jsx-runtime";
15869
16704
  var parseArgs = (argv) => {
@@ -15996,7 +16831,7 @@ var main = async () => {
15996
16831
  process.stdout.write("\x1B[2J\x1B[3J\x1B[1;1H");
15997
16832
  const cwdIsHome = () => {
15998
16833
  try {
15999
- return path43.resolve(cwd).toLowerCase() === path43.resolve(os5.homedir()).toLowerCase();
16834
+ return path47.resolve(cwd).toLowerCase() === path47.resolve(os5.homedir()).toLowerCase();
16000
16835
  } catch {
16001
16836
  return false;
16002
16837
  }