deepseeker-code 1.0.55 → 1.0.56

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 +560 -196
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -225,6 +225,33 @@ ${cwd}
225
225
  DeepSeeker-Code \u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u3001\u7F16\u8F91\u548C\u6267\u884C\u6587\u4EF6\u3002`,
226
226
  optTrust: "\u4FE1\u4EFB\u6B64\u76EE\u5F55",
227
227
  optExit: "\u9000\u51FA",
228
+ trustEmpty: "\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF1A\uFF08\u65E0\uFF09\u3002\u9996\u6B21\u8FDB\u5165\u67D0\u76EE\u5F55\u65F6\u4F1A\u8BE2\u95EE\u662F\u5426\u4FE1\u4EFB\u3002",
229
+ trustList: (list) => `\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF08\u64A4\u9500\u7528 /trust <\u5E8F\u53F7 \u6216 \u8DEF\u5F84>\uFF09\uFF1A
230
+ ${list}
231
+
232
+ \u64A4\u9500\u540E\u9700\u91CD\u542F CLI \u751F\u6548\uFF08\u9879\u76EE\u7EA7\u914D\u7F6E\u4EC5\u5728\u542F\u52A8\u65F6\u52A0\u8F7D\uFF09\u3002`,
233
+ trustRemoved: (target) => `\u5DF2\u64A4\u9500\u4FE1\u4EFB\uFF1A${target}
234
+ \uFF08\u91CD\u542F CLI \u540E\u751F\u6548\u2014\u2014\u9879\u76EE\u7EA7\u914D\u7F6E\u4EC5\u5728\u542F\u52A8\u65F6\u52A0\u8F7D\uFF09`,
235
+ trustNotFound: (target) => `\u672A\u627E\u5230\u8BE5\u4FE1\u4EFB\u76EE\u5F55\uFF1A${target}`,
236
+ archiveSummary: (s) => `\u{1F4DC} \u6700\u8FD1\u4E00\u6B21\u538B\u7F29\u7684\u5F52\u6863\u6458\u8981\uFF1A
237
+ ${s}`,
238
+ archiveNone: "\u672C\u4F1A\u8BDD\u542F\u52A8\u4EE5\u6765\u5C1A\u672A\u53D1\u751F\u8FC7\u538B\u7F29\uFF08\u6458\u8981\u4EC5\u4FDD\u7559\u8FD0\u884C\u65F6\u6700\u8FD1\u4E00\u6B21\uFF09\u3002",
239
+ planModeSet: (on) => `\u8BA1\u5212\u6A21\u5F0F\uFF1A${on ? "\u5F00\uFF08\u4E0B\u6B21\u63D0\u95EE\u5148\u53EA\u8BFB\u8C03\u7814\u5E76\u4EA7\u51FA\u65B9\u6848\uFF0C\u5BA1\u6279\u540E\u5B9E\u73B0\uFF09" : "\u5173"}`,
240
+ budgetView: (budget, last) => `\u{1F4B0} \u9884\u7B97\uFF1A${budget != null ? `\xA5${budget}/run` : "\u672A\u8BBE\u7F6E\uFF08/budget 0.5 \u8BBE\u7F6E\u6BCF\u4E2A run \u7684\u82B1\u8D39\u4E0A\u9650\uFF09"}${last != null ? ` \xB7 \u4E0A\u4E2A run \u5DF2\u82B1 \xA5${last.toFixed(3)}` : " \xB7 \u5C1A\u65E0\u5B8C\u6210 run"}`,
241
+ budgetOff: "\u{1F4B0} \u9884\u7B97\u5DF2\u5173\u95ED\uFF08\u4E0D\u9650\u82B1\u8D39\uFF09\u3002",
242
+ budgetUsage: "\u7528\u6CD5\uFF1A/budget 0.5\uFF08\u5143/run\uFF09\xB7 /budget off \xB7 /budget \u67E5\u770B",
243
+ budgetSet: (n) => `\u{1F4B0} \u9884\u7B97\u5DF2\u8BBE\u7F6E\uFF1A\u6BCF run \xA5${n}\uFF08\u8D85\u9650\u5148\u63D0\u9192\u6536\u655B\uFF0C\u4ECD\u8D85 120% \u5C06\u622A\u65AD\u672C run\uFF09\u3002`,
244
+ autoModeSet: (on) => `\u81EA\u52A8\u6A21\u5F0F\uFF1A${on ? "\u5F00\uFF08\u5DE5\u4F5C\u533A\u5185\u6587\u4EF6\u7F16\u8F91\u7531\u5206\u7C7B\u5668\u81EA\u52A8\u653E\u884C\uFF0C\u9AD8\u5371\u8F6C\u4EBA\u5DE5\uFF09" : "\u5173"}`,
245
+ thinkingInvalid: (arg) => `\u65E0\u6548\u7B49\u7EA7\uFF1A${arg}\uFF08\u53EF\u9009\uFF1Aoff \u5173\u95ED / high \u5E38\u89C4 / max \u6DF1\u5EA6\uFF09`,
246
+ thinkingCurrent: (level) => `\u5F53\u524D\u601D\u8003\u7B49\u7EA7\uFF1A${level}\uFF08off \u5173\u95ED / high \u5E38\u89C4 / max \u6DF1\u5EA6\uFF09`,
247
+ thinkingSet: (lvl) => `\u601D\u8003\u7B49\u7EA7\uFF1A${lvl}${lvl === "off" ? "\uFF08\u5173\u95ED\uFF0C\u7B80\u5355\u4EFB\u52A1\u66F4\u5FEB\u66F4\u7701\uFF09" : lvl === "max" ? "\uFF08\u6DF1\u5EA6\uFF0C\u590D\u6742\u4EFB\u52A1\uFF09" : "\uFF08\u5E38\u89C4\uFF09"}`,
248
+ engineLoading: "\u23F3 \u5F15\u64CE\u52A0\u8F7D\u4E2D\uFF08MCP/skills/commands\uFF09\u2026",
249
+ unknownCmd: (name) => `\u672A\u77E5\u547D\u4EE4\uFF1A/${name || "(\u7A7A)"}\uFF08\u8F93\u5165 / \u67E5\u770B\u53EF\u7528\u547D\u4EE4\uFF09`,
250
+ bangUsage: "\u7528\u6CD5\uFF1A!<shell \u547D\u4EE4>\uFF08\u5982 !git status\uFF09\uFF1B\u8F93\u51FA\u4F1A\u5E26\u5165\u4E0B\u8F6E\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u3002",
251
+ bangHead: (timedOut, ok, exitCode) => timedOut ? "\u23F1 \u8D85\u65F6\u4E2D\u6B62" : ok ? "\u2705 \u9000\u51FA\u7801 0" : exitCode != null ? `\u274C \u9000\u51FA\u7801 ${exitCode}` : "\u274C \u5DF2\u4E2D\u6B62",
252
+ bangNoOutput: "(\u65E0\u8F93\u51FA)",
253
+ visionDowngraded: (model) => `\u{1F5BC} \u6A21\u578B ${model} \u4E0D\u652F\u6301\u56FE\u7247\uFF0C\u5DF2\u81EA\u52A8\u964D\u7EA7\u4E3A\u6587\u672C\u5904\u7406\uFF08\u5DF2\u8BB0\u4F4F\uFF0C\u540E\u7EED\u6D88\u606F\u76F4\u63A5\u6309\u6587\u672C\u53D1\u9001\uFF09`,
254
+ planEnterSwitched: (reason) => `\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F${reason ? `\uFF1A${reason}` : ""}\uFF0C\u5DF2\u5207\u6362\u2026`,
228
255
  helpText: (model, thinking, locale2) => [
229
256
  "/help \xB7 /status \xB7 /clear \xB7 /exit",
230
257
  "/plan \u2014 \u5207\u6362\u8BA1\u5212\u6A21\u5F0F\uFF08\u53EA\u8BFB\u8C03\u7814 \u2192 \u65B9\u6848\u5BA1\u6279 \u2192 \u5B9E\u73B0\uFF09",
@@ -380,6 +407,33 @@ ${cwd}
380
407
  DeepSeeker-Code will read, edit, and execute files here.`,
381
408
  optTrust: "Yes, I trust this folder",
382
409
  optExit: "No, exit",
410
+ trustEmpty: "Trusted dirs: (none). You'll be asked on first entry to a directory.",
411
+ trustList: (list) => `Trusted dirs (revoke with /trust <index or path>):
412
+ ${list}
413
+
414
+ Revocation takes effect after a CLI restart (project config loads at startup only).`,
415
+ trustRemoved: (target) => `Trust revoked: ${target}
416
+ (takes effect after a CLI restart \u2014 project config loads at startup only)`,
417
+ trustNotFound: (target) => `No such trusted dir: ${target}`,
418
+ archiveSummary: (s) => `\u{1F4DC} Latest compaction summary:
419
+ ${s}`,
420
+ archiveNone: "No compaction has happened since this session started (only the latest runtime summary is kept).",
421
+ planModeSet: (on) => `Plan mode: ${on ? "on (next question starts read-only research, plan review, then implementation)" : "off"}`,
422
+ budgetView: (budget, last) => `\u{1F4B0} Budget: ${budget != null ? `\xA5${budget}/run` : "not set (/budget 0.5 to cap per-run cost)"}${last != null ? ` \xB7 last run cost \xA5${last.toFixed(3)}` : " \xB7 no completed run yet"}`,
423
+ budgetOff: "\u{1F4B0} Budget disabled (no cap).",
424
+ budgetUsage: "Usage: /budget 0.5 (\xA5/run) \xB7 /budget off \xB7 /budget to view",
425
+ budgetSet: (n) => `\u{1F4B0} Budget set: \xA5${n} per run (soft nudge on breach, hard truncation at 120%).`,
426
+ autoModeSet: (on) => `Auto mode: ${on ? "on (in-workspace file edits auto-approved by classifier, risky ops to human)" : "off"}`,
427
+ thinkingInvalid: (arg) => `Invalid level: ${arg} (choose: off / high / max)`,
428
+ thinkingCurrent: (level) => `Current thinking level: ${level} (off / high / max)`,
429
+ thinkingSet: (lvl) => `Thinking level: ${lvl}${lvl === "off" ? " (off \u2014 faster and cheaper for simple tasks)" : lvl === "max" ? " (deep, for complex tasks)" : " (standard)"}`,
430
+ engineLoading: "\u23F3 Loading engine (MCP/skills/commands)\u2026",
431
+ unknownCmd: (name) => `Unknown command: /${name || "(empty)"} (type / to list commands)`,
432
+ bangUsage: "Usage: !<shell command> (e.g. !git status); output joins the next turn's context.",
433
+ bangHead: (timedOut, ok, exitCode) => timedOut ? "\u23F1 timed out" : ok ? "\u2705 exit code 0" : exitCode != null ? `\u274C exit code ${exitCode}` : "\u274C aborted",
434
+ bangNoOutput: "(no output)",
435
+ visionDowngraded: (model) => `\u{1F5BC} Model ${model} doesn't support images; downgraded to text this turn (remembered \u2014 future messages send as text)`,
436
+ planEnterSwitched: (reason) => `\u{1F4CB} Model requested plan mode${reason ? `: ${reason}` : ""}; switched\u2026`,
383
437
  helpText: (model, thinking, locale2) => [
384
438
  "/help \xB7 /status \xB7 /clear \xB7 /exit",
385
439
  "/plan \u2014 Toggle plan mode (read-only research \u2192 review \u2192 implement)",
@@ -796,10 +850,33 @@ var init_trust = __esm({
796
850
 
797
851
  // ../core/src/batch/ticket.ts
798
852
  import fs4 from "fs/promises";
799
- var generateBatchId, parseToolsValue, parsePositiveNumber, parseBatchTicket, readBatchTicket;
853
+ var TICKET_MSG, ticketMsg, generateBatchId, parseToolsValue, parsePositiveNumber, parseBatchTicket, readBatchTicket;
800
854
  var init_ticket = __esm({
801
855
  "../core/src/batch/ticket.ts"() {
802
856
  "use strict";
857
+ TICKET_MSG = {
858
+ zh: {
859
+ noFrontmatter: "\u7F3A\u5C11 frontmatter\uFF08--- \u5305\u88F9\u7684\u5934\u90E8\u5757\uFF09\u3002\u5DE5\u5355\u683C\u5F0F\u89C1 batch add \u5E2E\u52A9\u3002",
860
+ noAccept: "frontmatter \u7F3A accept\uFF08\u9A8C\u6536\u547D\u4EE4\uFF09\u3002\u6302\u673A\u4EFB\u52A1\u5FC5\u987B\u5E26\u53EF\u6267\u884C\u9A8C\u6536\uFF0C\u7F3A\u9A8C\u6536\u62D2\u7EDD\u5165\u961F\u3002",
861
+ badBudget: (v) => `budget-yuan \u975E\u6CD5\uFF08"${v}"\uFF09\uFF1A\u987B\u4E3A\u6B63\u6570\uFF08\u5143\uFF09\u3002`,
862
+ badTimeout: (v) => `timeout-min \u975E\u6CD5\uFF08"${v}"\uFF09\uFF1A\u987B\u4E3A\u6B63\u6574\u6570\uFF08\u5206\u949F\uFF09\u3002`,
863
+ badTools: "tools \u4E3A\u7A7A\uFF1A\u8981\u4E48\u5220\u6389\u8BE5\u884C\uFF08\u7528\u7F3A\u7701\u7981\u7528\u6E05\u5355\uFF09\uFF0C\u8981\u4E48\u5217\u5DE5\u5177\u540D\uFF08\u9017\u53F7\u5206\u9694\uFF09\u3002",
864
+ emptyBody: "\u4EFB\u52A1\u6B63\u6587\u4E3A\u7A7A\uFF1Afrontmatter \u4E4B\u540E\u5E94\u5199\u4EFB\u52A1\u63CF\u8FF0\u3002",
865
+ untitled: "(\u672A\u547D\u540D\u4EFB\u52A1)",
866
+ readFail: (e) => `\u8BFB\u5DE5\u5355\u5931\u8D25\uFF1A${e}`
867
+ },
868
+ en: {
869
+ noFrontmatter: "Missing frontmatter (a --- delimited header block). See `batch add` help for the ticket format.",
870
+ noAccept: "Frontmatter is missing `accept` (acceptance command). Batch tasks must carry an executable acceptance check \u2014 rejected without one.",
871
+ badBudget: (v) => `Invalid budget-yuan ("${v}"): must be a positive number (yuan).`,
872
+ badTimeout: (v) => `Invalid timeout-min ("${v}"): must be a positive integer (minutes).`,
873
+ badTools: "Empty tools: either remove the line (use the default denylist) or list tool names (comma-separated).",
874
+ emptyBody: "Empty task body: write the task description after the frontmatter.",
875
+ untitled: "(untitled task)",
876
+ readFail: (e) => `Failed to read ticket: ${e}`
877
+ }
878
+ };
879
+ ticketMsg = (locale2, key) => (TICKET_MSG[locale2] ?? TICKET_MSG.zh)[key] ?? TICKET_MSG.zh[key] ?? key;
803
880
  generateBatchId = (now = /* @__PURE__ */ new Date()) => {
804
881
  const p = (n, w = 2) => String(n).padStart(w, "0");
805
882
  const rand = Math.random().toString(36).slice(2, 6);
@@ -810,9 +887,9 @@ var init_ticket = __esm({
810
887
  const n = Number(raw.trim());
811
888
  return Number.isFinite(n) && n > 0 ? n : null;
812
889
  };
813
- parseBatchTicket = (source) => {
890
+ parseBatchTicket = (source, locale2 = "zh") => {
814
891
  const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(source);
815
- if (!m) return { ok: false, error: "\u7F3A\u5C11 frontmatter\uFF08--- \u5305\u88F9\u7684\u5934\u90E8\u5757\uFF09\u3002\u5DE5\u5355\u683C\u5F0F\u89C1 batch add \u5E2E\u52A9\u3002" };
892
+ if (!m) return { ok: false, error: ticketMsg(locale2, "noFrontmatter") };
816
893
  const [, fmRaw, bodyRaw] = m;
817
894
  const fm = {};
818
895
  for (const line of fmRaw.split(/\r?\n/)) {
@@ -821,34 +898,34 @@ var init_ticket = __esm({
821
898
  fm[kv[1].toLowerCase()] = kv[2].trim();
822
899
  }
823
900
  const accept = (fm["accept"] ?? "").trim();
824
- if (!accept) return { ok: false, error: "frontmatter \u7F3A accept\uFF08\u9A8C\u6536\u547D\u4EE4\uFF09\u3002\u6302\u673A\u4EFB\u52A1\u5FC5\u987B\u5E26\u53EF\u6267\u884C\u9A8C\u6536\uFF0C\u7F3A\u9A8C\u6536\u62D2\u7EDD\u5165\u961F\u3002" };
901
+ if (!accept) return { ok: false, error: ticketMsg(locale2, "noAccept") };
825
902
  let budgetYuan;
826
903
  if (fm["budget-yuan"] !== void 0) {
827
904
  const n = parsePositiveNumber(fm["budget-yuan"]);
828
- if (n === null) return { ok: false, error: `budget-yuan \u975E\u6CD5\uFF08"${fm["budget-yuan"]}"\uFF09\uFF1A\u987B\u4E3A\u6B63\u6570\uFF08\u5143\uFF09\u3002` };
905
+ if (n === null) return { ok: false, error: ticketMsg(locale2, "badBudget")(fm["budget-yuan"]) };
829
906
  budgetYuan = n;
830
907
  }
831
908
  let timeoutMin;
832
909
  if (fm["timeout-min"] !== void 0) {
833
910
  const n = parsePositiveNumber(fm["timeout-min"]);
834
- if (n === null) return { ok: false, error: `timeout-min \u975E\u6CD5\uFF08"${fm["timeout-min"]}"\uFF09\uFF1A\u987B\u4E3A\u6B63\u6574\u6570\uFF08\u5206\u949F\uFF09\u3002` };
911
+ if (n === null) return { ok: false, error: ticketMsg(locale2, "badTimeout")(fm["timeout-min"]) };
835
912
  timeoutMin = n;
836
913
  }
837
914
  const tools = fm["tools"] !== void 0 ? parseToolsValue(fm["tools"]) : void 0;
838
- if (tools && tools.length === 0) return { ok: false, error: "tools \u4E3A\u7A7A\uFF1A\u8981\u4E48\u5220\u6389\u8BE5\u884C\uFF08\u7528\u7F3A\u7701\u7981\u7528\u6E05\u5355\uFF09\uFF0C\u8981\u4E48\u5217\u5DE5\u5177\u540D\uFF08\u9017\u53F7\u5206\u9694\uFF09\u3002" };
915
+ if (tools && tools.length === 0) return { ok: false, error: ticketMsg(locale2, "badTools") };
839
916
  const body = bodyRaw.trim();
840
- if (!body) return { ok: false, error: "\u4EFB\u52A1\u6B63\u6587\u4E3A\u7A7A\uFF1Afrontmatter \u4E4B\u540E\u5E94\u5199\u4EFB\u52A1\u63CF\u8FF0\u3002" };
841
- const title = (fm["title"] ?? "").trim() || body.split(/\r?\n/).find((l) => l.trim())?.slice(0, 40) || "(\u672A\u547D\u540D\u4EFB\u52A1)";
917
+ if (!body) return { ok: false, error: ticketMsg(locale2, "emptyBody") };
918
+ const title = (fm["title"] ?? "").trim() || body.split(/\r?\n/).find((l) => l.trim())?.slice(0, 40) || ticketMsg(locale2, "untitled");
842
919
  return { ok: true, ticket: { title, accept, budgetYuan, timeoutMin, tools: tools && tools.length > 0 ? tools : void 0, body } };
843
920
  };
844
- readBatchTicket = async (filePath) => {
921
+ readBatchTicket = async (filePath, locale2 = "zh") => {
845
922
  let source;
846
923
  try {
847
924
  source = await fs4.readFile(filePath, "utf-8");
848
925
  } catch (e) {
849
- return { ok: false, error: `\u8BFB\u5DE5\u5355\u5931\u8D25\uFF1A${e?.message ?? e}` };
926
+ return { ok: false, error: ticketMsg(locale2, "readFail")(e?.message ?? e) };
850
927
  }
851
- return parseBatchTicket(source);
928
+ return parseBatchTicket(source, locale2);
852
929
  };
853
930
  }
854
931
  });
@@ -856,11 +933,21 @@ var init_ticket = __esm({
856
933
  // ../core/src/batch/queue.ts
857
934
  import fs5 from "fs/promises";
858
935
  import path6 from "path";
859
- var batchQueueRoot, stateDir, lockPath, listState, snapshotQueue, reportPath, enqueueTicketFile, takeNextTicket, requeueStaleRunning, finishTicket, cleanDone, pidAlive, acquireRunLock, releaseRunLock;
936
+ var LOCK_MSG, batchQueueRoot, stateDir, lockPath, listState, snapshotQueue, reportPath, enqueueTicketFile, takeNextTicket, requeueStaleRunning, finishTicket, cleanDone, pidAlive, acquireRunLock, releaseRunLock;
860
937
  var init_queue = __esm({
861
938
  "../core/src/batch/queue.ts"() {
862
939
  "use strict";
863
940
  init_ticket();
941
+ LOCK_MSG = {
942
+ zh: {
943
+ createFail: (e) => `\u9501\u521B\u5EFA\u5931\u8D25\uFF1A${e}`,
944
+ held: (pid) => `\u5DF2\u6709 batch runner \u5728\u8FD0\u884C\uFF08pid ${pid}\uFF09\u3002\u6302\u673A\u6A21\u5F0F\u4E32\u884C\uFF0C\u52FF\u5E76\u53D1\u8DD1\u4E24\u4E2A runner\u3002`
945
+ },
946
+ en: {
947
+ createFail: (e) => `Failed to create the lock file: ${e}`,
948
+ held: (pid) => `Another batch runner is already running (pid ${pid}). Batch mode is serial \u2014 don't run two runners at once.`
949
+ }
950
+ };
864
951
  batchQueueRoot = () => process.env.DEEPSEEKER_CODE_BATCH_DIR || path6.join(process.cwd(), ".deepseeker-code", "batch");
865
952
  stateDir = (s) => path6.join(batchQueueRoot(), s);
866
953
  lockPath = () => path6.join(batchQueueRoot(), ".lock");
@@ -877,8 +964,8 @@ var init_queue = __esm({
877
964
  done: (await listState("done")).map((f) => f.replace(/\.md$/, ""))
878
965
  });
879
966
  reportPath = (id) => path6.join(stateDir("done"), `${id}.report.md`);
880
- enqueueTicketFile = async (srcPath) => {
881
- const parsed = await readBatchTicket(srcPath);
967
+ enqueueTicketFile = async (srcPath, locale2 = "zh") => {
968
+ const parsed = await readBatchTicket(srcPath, locale2);
882
969
  if (!parsed.ok) return parsed;
883
970
  const source = await fs5.readFile(srcPath, "utf-8");
884
971
  const id = generateBatchId();
@@ -935,18 +1022,18 @@ var init_queue = __esm({
935
1022
  return e?.code === "EPERM";
936
1023
  }
937
1024
  };
938
- acquireRunLock = async () => {
1025
+ acquireRunLock = async (locale2 = "zh") => {
939
1026
  await fs5.mkdir(batchQueueRoot(), { recursive: true });
940
1027
  const payload = `${process.pid} ${(/* @__PURE__ */ new Date()).toISOString()}`;
941
1028
  try {
942
1029
  await fs5.writeFile(lockPath(), payload, { flag: "wx" });
943
1030
  return { ok: true };
944
1031
  } catch (e) {
945
- if (e?.code !== "EEXIST") return { ok: false, error: `\u9501\u521B\u5EFA\u5931\u8D25\uFF1A${e?.message ?? e}` };
1032
+ if (e?.code !== "EEXIST") return { ok: false, error: LOCK_MSG[locale2].createFail(e?.message ?? e) };
946
1033
  const raw = await fs5.readFile(lockPath(), "utf-8").catch(() => "");
947
1034
  const ownerPid = parseInt(raw.split(" ")[0], 10);
948
1035
  if (Number.isFinite(ownerPid) && pidAlive(ownerPid)) {
949
- return { ok: false, error: `\u5DF2\u6709 batch runner \u5728\u8FD0\u884C\uFF08pid ${ownerPid}\uFF09\u3002\u6302\u673A\u6A21\u5F0F\u4E32\u884C\uFF0C\u52FF\u5E76\u53D1\u8DD1\u4E24\u4E2A runner\u3002` };
1036
+ return { ok: false, error: LOCK_MSG[locale2].held(ownerPid) };
950
1037
  }
951
1038
  await fs5.writeFile(lockPath(), payload).catch(() => {
952
1039
  });
@@ -963,6 +1050,211 @@ var init_queue = __esm({
963
1050
  }
964
1051
  });
965
1052
 
1053
+ // src/batchStrings.ts
1054
+ var ZH, EN, DICT, batchT;
1055
+ var init_batchStrings = __esm({
1056
+ "src/batchStrings.ts"() {
1057
+ "use strict";
1058
+ ZH = {
1059
+ // —— batchAdmin(add / list / clean / help) ——
1060
+ usage: `deepseeker-code batch \u2014\u2014 \u6302\u673A\u6279\u5904\u7406\uFF08\u6210\u672C\u4FE1\u4EFB\u94FE\u7B2C\u56DB\u73AF\uFF09
1061
+
1062
+ \u7528\u6CD5\uFF1Adeepseeker-code batch <\u5B50\u547D\u4EE4>
1063
+ add <\u5DE5\u5355.md> [...] \u6295\u9012\u4EFB\u52A1\uFF08\u53EF\u4E00\u6B21\u591A\u4E2A\u6587\u4EF6\uFF09
1064
+ list \u67E5\u770B pending / running / done \u4E09\u6001\u961F\u5217
1065
+ run [--wait-window HH:MM-HH:MM] \u6267\u884C\u961F\u5217\uFF08\u4E32\u884C\uFF0C\u9010\u4EFB\u52A1\u51FA\u62A5\u544A\uFF1B--wait-window \u5230\u70B9\u518D\u5F00\u8DD1\uFF09
1066
+ clean \u6E05\u7A7A done/\uFF08\u5DF2\u8BFB\u8FC7\u7684\u62A5\u544A\u4E0E\u5DE5\u5355\u5B58\u6863\uFF09
1067
+
1068
+ \u5DE5\u5355\u683C\u5F0F\uFF08Markdown\uFF0Cfrontmatter \u7684 accept \u5FC5\u586B\u2014\u2014\u7F3A\u9A8C\u6536\u62D2\u7EDD\u5165\u961F\uFF09\uFF1A
1069
+ ---
1070
+ title: \u4FEE\u590D\u65E5\u671F\u89E3\u6790 bug
1071
+ accept: npm test
1072
+ budget-yuan: 0.5 # \u53EF\u9009\uFF1A\u672C\u4EFB\u52A1\u82B1\u8D39\u4E0A\u9650\uFF08\u5143\uFF09
1073
+ timeout-min: 30 # \u53EF\u9009\uFF1Aagent \u9636\u6BB5\u8D85\u65F6\uFF08\u5206\u949F\uFF0C\u7F3A\u7701 30\uFF09
1074
+ tools: run_command # \u53EF\u9009\uFF1A\u663E\u5F0F\u89E3\u9501\u9AD8\u5371\u5DE5\u5177\uFF08\u7F3A\u7701\u7981 run_command/delete_path\uFF09
1075
+ ---
1076
+ \u4EFB\u52A1\u63CF\u8FF0\u6B63\u6587\uFF08Markdown\uFF09\u2026
1077
+
1078
+ \u4EA7\u7269\uFF1A<\u9879\u76EE>/.deepseeker-code/batch/done/<id>.report.md\uFF08\u72B6\u6001/\u7ED3\u7B97\u5355/\u9A8C\u6536\u8F93\u51FA/diff \u6458\u8981\uFF09\uFF1B
1079
+ \u6539\u52A8\u6536\u5272\u5728\u72EC\u7ACB git worktree\uFF08\u4E34\u65F6\u5206\u652F\uFF09\u4E0A\uFF0C\u4FDD\u7559\u5F85\u4EBA\u5DE5 merge \u6216\u4E22\u5F03\u3002`,
1080
+ adminAddUsage: "\u7528\u6CD5\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md> [...]\uFF08\u7F3A\u9A8C\u6536\u547D\u4EE4\u7684\u5DE5\u5355\u4F1A\u88AB\u62D2\u7EDD\uFF09\n\u8BE6\u60C5\uFF1Adeepseeker-code batch help",
1081
+ readFileFail: (e) => `\u8BFB\u6587\u4EF6\u5931\u8D25\uFF1A${e}`,
1082
+ enqueued: (id, title) => `\u{1F4E5} \u5DF2\u5165\u961F [${id}] ${title}`,
1083
+ addRejected: (name, err) => `\u{1F6AB} \u62D2\u7EDD ${name}\uFF1A${err}`,
1084
+ nextStepRun: "\n\u8DD1\u6279\uFF1Adeepseeker-code batch run",
1085
+ adminQueueEmpty: "\u961F\u5217\u7A7A\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>\uFF08\u683C\u5F0F\u89C1 batch help\uFF09",
1086
+ listRunningHead: "\u25B6 \u8FD0\u884C\u4E2D\uFF1A",
1087
+ listPendingHead: (n) => `\u23F3 \u5F85\u8DD1 ${n}\uFF1A`,
1088
+ listDoneHead: (n) => `\u2714 \u5DF2\u5B8C\u6210 ${n}\uFF1A`,
1089
+ cleaned: (n) => `\u{1F9F9} \u5DF2\u6E05\u7A7A done/\uFF08${n} \u4E2A\u4EFB\u52A1\u5B58\u6863\uFF09\u3002pending/running \u4E0D\u53D7\u5F71\u54CD\u3002`,
1090
+ reportMissing: "\uFF08\u62A5\u544A\u7F3A\u5931\uFF09",
1091
+ // —— batchRunner 控制台进度 ——
1092
+ ticketRejected: (id, err) => `\u{1F6AB} [${id}] \u5DE5\u5355\u88AB\u62D2\uFF1A${err}`,
1093
+ taskMeta: (budget, timeoutMin, accept) => ` \u9884\u7B97 ${budget !== void 0 ? `\xA5${budget}` : "\u4E0D\u9650"} \xB7 \u8D85\u65F6 ${timeoutMin}min \xB7 \u9A8C\u6536 \`${accept}\``,
1094
+ roundLine: (ts2, round) => ` [${ts2}] \u8F6E ${round}`,
1095
+ wtCreateFail: (e) => `\u521B\u5EFA worktree \u5931\u8D25\uFF08\u9700\u8981\u4E3B\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\uFF09\uFF1A${e}`,
1096
+ agentErrorLine: (id, err) => `\u{1F4A5} [${id}] agent \u5F02\u5E38\uFF1A${err}`,
1097
+ timeoutLine: (id, min) => `\u23F1\uFE0F [${id}] \u8D85\u65F6\uFF08${min}min\uFF09\uFF0C\u622A\u65AD\u6536\u5C3E\uFF08\u9A8C\u6536\u4E0D\u8DD1\uFF09`,
1098
+ acceptLine: (passed, id, code) => `${passed ? "\u2705" : "\u274C"} [${id}] \u9A8C\u6536 exit ${code}`,
1099
+ acceptKilledSuffix: "\uFF08\u8D85\u65F6\u88AB\u6740\uFF09",
1100
+ timeoutAbort: (min) => `\u4EFB\u52A1\u8D85\u65F6\uFF08${min} \u5206\u949F\uFF09`,
1101
+ artifactLine: (p, branch) => ` \u4EA7\u7269\uFF1A${p}\uFF08\u5206\u652F ${branch}\uFF0C\u4FDD\u7559\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09`,
1102
+ inWindowNow: (hhmm) => `\u23F0 \u5F53\u524D ${hhmm} \u5DF2\u5728\u7A97\u53E3\u5185\uFF0C\u7ACB\u5373\u5F00\u8DD1\u3002`,
1103
+ waitWindowWait: (h, m) => `\u23F0 --wait-window\uFF1A${h} \u5C0F\u65F6 ${m} \u5206\u540E\uFF08\u7A97\u53E3\u8D77\u70B9\uFF09\u5F00\u8DD1\u3002\u6302\u673A\u7B49\u5F85\u4E2D\uFF0CCtrl+C \u53EF\u53D6\u6D88\u3002`,
1104
+ waitWindowInvalid: (s) => `\u274C --wait-window \u683C\u5F0F\u975E\u6CD5\uFF1A"${s}"\uFF08\u5E94\u4E3A HH:MM-HH:MM\uFF0C\u5982 23:30-06:30\uFF09`,
1105
+ runnerQueueEmpty: "\u961F\u5217\u7A7A\uFF1Apending/ \u65E0\u4EFB\u52A1\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>",
1106
+ summaryDone: (total, passed, mins) => `\u{1F319} \u6302\u673A\u7ED3\u675F\uFF1A${total} \u4EFB\u52A1 \xB7 ${passed} \u8FC7 \xB7 ${total - passed} \u672A\u8FC7\uFF08\u8017\u65F6 ${mins}min\uFF09`,
1107
+ summaryCost: (sum, tasks) => `\u{1F4B0} \u603B\u82B1\u8D39\uFF1A\xA5${sum}\uFF08${tasks} \u4E2A\u4EFB\u52A1\u8BA1\u4EF7\uFF09`,
1108
+ summaryCostFree: "\u{1F4B0} \u603B\u82B1\u8D39\uFF1A\u5168\u90E8\u65E0\u4EF7\u6A21\u578B\uFF0C\u672A\u8BA1\u94B1",
1109
+ failedHead: "\u26A0\uFE0F \u672A\u8FC7\u6E05\u5355\uFF1A",
1110
+ artifactsDir: "\u62A5\u544A\u4E0E\u6539\u52A8\uFF1A.deepseeker-code/batch/done/\uFF08worktree \u5747\u4FDD\u7559\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09",
1111
+ // —— 状态标签(控制台与报告共用) ——
1112
+ statusLabel: (s) => ({
1113
+ passed: "\u2705 \u901A\u8FC7\u9A8C\u6536",
1114
+ "accept-failed": "\u274C \u9A8C\u6536\u672A\u8FC7",
1115
+ timeout: "\u23F1\uFE0F \u8D85\u65F6\u622A\u65AD",
1116
+ error: "\u{1F4A5} agent \u5F02\u5E38",
1117
+ rejected: "\u{1F6AB} \u5DE5\u5355\u88AB\u62D2"
1118
+ })[s],
1119
+ // —— 验收命令执行(输出嵌入报告) ——
1120
+ spawnFail: (e) => `\u542F\u52A8\u5931\u8D25\uFF1A${e}`,
1121
+ execFail: (e) => `\u6267\u884C\u5931\u8D25\uFF1A${e}`,
1122
+ tailOmitted: (n) => `\uFF08\u524D ${n} \u884C\u5DF2\u7701\u7565\uFF09`,
1123
+ noOutput: "\uFF08\u65E0\u8F93\u51FA\uFF09",
1124
+ // —— 结算单聚合与 receipt 行(报告内嵌) ——
1125
+ costFree: "\uFF08\u65E0\u4EF7\u6A21\u578B\uFF0C\u4E0D\u8BA1\u94B1\uFF09",
1126
+ costSummary: (c) => {
1127
+ const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "\uFF08\u65E0\u4EF7\u6A21\u578B\uFF0C\u4E0D\u8BA1\u94B1\uFF09";
1128
+ const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
1129
+ const mins = Math.round(c.durationMs / 6e3) / 10;
1130
+ return `${c.rounds} \u8F6E \xB7 ${money} \xB7 \u7F13\u5B58\u547D\u4E2D ${hit}% \xB7 ${mins}min${c.partialPrice ? "\uFF08\u90E8\u5206\u8BB0\u5F55\u65E0\u4EF7\uFF0C\u94B1\u4E3A\u90E8\u5206\u548C\uFF09" : ""}${c.subRuns > 0 ? ` \xB7 \u5B50 run ${c.subRuns} \u4E2A\u5DF2\u8BA1\u5165` : ""}`;
1131
+ },
1132
+ noReceipt: "\uFF08\u65E0\u7ED3\u7B97\u5355\u2014\u2014run \u672A\u5230\u6536\u5C3E\u5373\u88AB\u622A\u65AD\uFF0C\u82B1\u8D39\u4EE5\u4E0A\u65B9 usageLog \u805A\u5408\u4E3A\u51C6\uFF09",
1133
+ receiptLine: (r) => `${r.rounds} \u8F6E \xB7 ${r.costYuan !== null ? `\xA5${r.costYuan.toFixed(4)}` : "\u65E0\u4EF7"} \xB7 \u7F13\u5B58\u547D\u4E2D ${Math.round(r.cacheHitRate * 100)}% \xB7 ${(r.durationMs / 1e3).toFixed(0)}s`,
1134
+ // —— 报告骨架 ——
1135
+ rejectedTitle: "# \u6302\u673A\u4EFB\u52A1\u88AB\u62D2",
1136
+ reportTitle: (title) => `# \u6302\u673A\u4EFB\u52A1\u62A5\u544A\uFF1A${title}`,
1137
+ statusEntry: (v) => `- \u72B6\u6001\uFF1A${v}`,
1138
+ budgetTruncNote: "\uFF08\u26A0\uFE0F \u9884\u7B97\u622A\u65AD\u540E\u6536\u5C3E\uFF09",
1139
+ costEntry: (v) => `- \u7ED3\u7B97\u5355\uFF1A${v}`,
1140
+ receiptEntry: (v) => `- \u672C\u8F6E receipt\uFF1A${v}`,
1141
+ errorEntry: (v) => `- \u5F02\u5E38\uFF1A${v}
1142
+ `,
1143
+ notCreated: "\uFF08\u672A\u521B\u5EFA\uFF09",
1144
+ taskH: "## \u4EFB\u52A1",
1145
+ finalH: "## \u6A21\u578B\u6536\u5C3E\u6C47\u62A5",
1146
+ noFinal: "\uFF08\u65E0\u8F93\u51FA\u2014\u2014run \u5728\u4EA7\u51FA\u6B63\u6587\u524D\u88AB\u622A\u65AD/\u5F02\u5E38\uFF09",
1147
+ acceptH: "## \u9A8C\u6536",
1148
+ acceptCmdLine: (cmd, code) => `\u547D\u4EE4\uFF1A\`${cmd}\` \u2192 exit ${code}`,
1149
+ acceptTimeoutSuffix: "\uFF08\u23F1\uFE0F \u9A8C\u6536\u8D85\u65F6\u88AB\u6740\uFF09",
1150
+ acceptTail: "\u8F93\u51FA\u5C3E\u90E8\uFF1A",
1151
+ noAccept: "\n\uFF08\u672A\u6267\u884C\u9A8C\u6536\u2014\u2014\u6539\u52A8\u4E0D\u5B8C\u6574\uFF0C\u8DD1\u4E86\u4E5F\u662F\u566A\u58F0\uFF09",
1152
+ deniedH: "## \u88AB\u62D2\u5DE5\u5177\u8C03\u7528\uFF08fail-closed \u8BB0\u5F55\uFF09",
1153
+ deniedNone: "\u65E0\u3002",
1154
+ diffH: "## Diff \u6458\u8981\uFF08worktree \u76F8\u5BF9\u57FA\u5EA7\uFF0C\u672A\u63D0\u4EA4\uFF0C\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09",
1155
+ diffNote: (branch, p) => `\u6539\u52A8\u5728\u72EC\u7ACB worktree\uFF08\u5206\u652F \`${branch}\`\uFF0C\u8DEF\u5F84 ${p}\uFF09\u3002\u5904\u7F6E\uFF1A\u68C0\u67E5\u6EE1\u610F \u2192 \u5728 worktree \u5185 commit \u5E76\u5408\u56DE\u4E3B\u4ED3\uFF1B\u4E22\u5F03 \u2192 \u5220\u9664\u8BE5 worktree \u76EE\u5F55 + \`git worktree prune\` + \`git branch -D <\u5206\u652F>\`\u3002`,
1156
+ diffNone: "\uFF08\u65E0\uFF09"
1157
+ };
1158
+ EN = {
1159
+ usage: `deepseeker-code batch \u2014 unattended batch runs (cost trust chain)
1160
+
1161
+ Usage: deepseeker-code batch <subcommand>
1162
+ add <ticket.md> [...] Enqueue tasks (multiple files at once)
1163
+ list Show the pending / running / done queue
1164
+ run [--wait-window HH:MM-HH:MM] Run the queue (serial, one report per task; --wait-window waits for the window)
1165
+ clean Clear done/ (reports and ticket archives you've read)
1166
+
1167
+ Ticket format (Markdown; frontmatter \`accept\` is required \u2014 no acceptance, no enqueue):
1168
+ ---
1169
+ title: Fix date parsing bug
1170
+ accept: npm test # required: acceptance command (exit 0 = pass)
1171
+ budget-yuan: 0.5 # optional: per-task cost cap (yuan)
1172
+ timeout-min: 30 # optional: agent-phase timeout (minutes, default 30)
1173
+ tools: run_command # optional: unlock risky tools (run_command/delete_path denied by default)
1174
+ ---
1175
+
1176
+ Task description body (Markdown)\u2026
1177
+
1178
+ Artifacts: <project>/.deepseeker-code/batch/done/<id>.report.md (status / receipt / acceptance output / diff summary);
1179
+ changes are harvested onto an isolated git worktree (temp branch), kept for manual merge or disposal.`,
1180
+ adminAddUsage: "Usage: deepseeker-code batch add <ticket.md> [...] (tickets without an acceptance command are rejected)\nDetails: deepseeker-code batch help",
1181
+ readFileFail: (e) => `Failed to read file: ${e}`,
1182
+ enqueued: (id, title) => `\u{1F4E5} Enqueued [${id}] ${title}`,
1183
+ addRejected: (name, err) => `\u{1F6AB} Rejected ${name}: ${err}`,
1184
+ nextStepRun: "\nRun the queue: deepseeker-code batch run",
1185
+ adminQueueEmpty: "Queue is empty. Enqueue: deepseeker-code batch add <ticket.md> (format: batch help)",
1186
+ listRunningHead: "\u25B6 Running:",
1187
+ listPendingHead: (n) => `\u23F3 Pending ${n}:`,
1188
+ listDoneHead: (n) => `\u2714 Done ${n}:`,
1189
+ cleaned: (n) => `\u{1F9F9} Cleared done/ (${n} task archives). pending/running untouched.`,
1190
+ reportMissing: "(report missing)",
1191
+ ticketRejected: (id, err) => `\u{1F6AB} [${id}] Ticket rejected: ${err}`,
1192
+ taskMeta: (budget, timeoutMin, accept) => ` Budget ${budget !== void 0 ? `\xA5${budget}` : "uncapped"} \xB7 timeout ${timeoutMin}min \xB7 accept \`${accept}\``,
1193
+ roundLine: (ts2, round) => ` [${ts2}] round ${round}`,
1194
+ wtCreateFail: (e) => `Failed to create worktree (the main workspace must be a git repo): ${e}`,
1195
+ agentErrorLine: (id, err) => `\u{1F4A5} [${id}] agent error: ${err}`,
1196
+ timeoutLine: (id, min) => `\u23F1\uFE0F [${id}] Timed out (${min}min); wrapped up truncated (acceptance skipped)`,
1197
+ acceptLine: (passed, id, code) => `${passed ? "\u2705" : "\u274C"} [${id}] acceptance exit ${code}`,
1198
+ acceptKilledSuffix: " (killed on timeout)",
1199
+ timeoutAbort: (min) => `Task timed out (${min} minutes)`,
1200
+ artifactLine: (p, branch) => ` Artifact: ${p} (branch ${branch}, kept for manual handling)`,
1201
+ inWindowNow: (hhmm) => `\u23F0 It's ${hhmm}, already inside the window \u2014 starting now.`,
1202
+ waitWindowWait: (h, m) => `\u23F0 --wait-window: starting in ${h}h ${m}m (at the window start). Waiting \u2014 Ctrl+C to cancel.`,
1203
+ waitWindowInvalid: (s) => `\u274C Invalid --wait-window: "${s}" (expected HH:MM-HH:MM, e.g. 23:30-06:30)`,
1204
+ runnerQueueEmpty: "Queue empty: no tasks in pending/. Enqueue: deepseeker-code batch add <ticket.md>",
1205
+ summaryDone: (total, passed, mins) => `\u{1F319} Batch finished: ${total} tasks \xB7 ${passed} passed \xB7 ${total - passed} failed (${mins}min)`,
1206
+ summaryCost: (sum, tasks) => `\u{1F4B0} Total cost: \xA5${sum} (${tasks} tasks priced)`,
1207
+ summaryCostFree: "\u{1F4B0} Total cost: all records unpriced, no money counted",
1208
+ failedHead: "\u26A0\uFE0F Failed tasks:",
1209
+ artifactsDir: "Reports and changes: .deepseeker-code/batch/done/ (worktrees kept for manual handling)",
1210
+ statusLabel: (s) => ({
1211
+ passed: "\u2705 acceptance passed",
1212
+ "accept-failed": "\u274C acceptance failed",
1213
+ timeout: "\u23F1\uFE0F timed out",
1214
+ error: "\u{1F4A5} agent error",
1215
+ rejected: "\u{1F6AB} ticket rejected"
1216
+ })[s],
1217
+ spawnFail: (e) => `Spawn failed: ${e}`,
1218
+ execFail: (e) => `Execution failed: ${e}`,
1219
+ tailOmitted: (n) => `(first ${n} lines omitted)`,
1220
+ noOutput: "(no output)",
1221
+ costFree: "(unpriced model, no money counted)",
1222
+ costSummary: (c) => {
1223
+ const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "(unpriced model, no money counted)";
1224
+ const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
1225
+ const mins = Math.round(c.durationMs / 6e3) / 10;
1226
+ return `${c.rounds} rounds \xB7 ${money} \xB7 cache hit ${hit}% \xB7 ${mins}min${c.partialPrice ? " (some records unpriced; money is a partial sum)" : ""}${c.subRuns > 0 ? ` \xB7 ${c.subRuns} sub-runs included` : ""}`;
1227
+ },
1228
+ noReceipt: "(no receipt \u2014 the run was truncated before finishing; cost per the usageLog aggregate above)",
1229
+ receiptLine: (r) => `${r.rounds} rounds \xB7 ${r.costYuan !== null ? `\xA5${r.costYuan.toFixed(4)}` : "unpriced"} \xB7 cache hit ${Math.round(r.cacheHitRate * 100)}% \xB7 ${(r.durationMs / 1e3).toFixed(0)}s`,
1230
+ rejectedTitle: "# Batch task rejected",
1231
+ reportTitle: (title) => `# Batch task report: ${title}`,
1232
+ statusEntry: (v) => `- Status: ${v}`,
1233
+ budgetTruncNote: " (\u26A0\uFE0F wrapped up after budget truncation)",
1234
+ costEntry: (v) => `- Receipt: ${v}`,
1235
+ receiptEntry: (v) => `- This run's receipt: ${v}`,
1236
+ errorEntry: (v) => `- Error: ${v}
1237
+ `,
1238
+ notCreated: "(not created)",
1239
+ taskH: "## Task",
1240
+ finalH: "## Model final report",
1241
+ noFinal: "(no output \u2014 the run was truncated/errored before producing any text)",
1242
+ acceptH: "## Acceptance",
1243
+ acceptCmdLine: (cmd, code) => `Command: \`${cmd}\` \u2192 exit ${code}`,
1244
+ acceptTimeoutSuffix: " (\u23F1\uFE0F acceptance timed out, killed)",
1245
+ acceptTail: "Output tail:",
1246
+ noAccept: "\n(Acceptance not run \u2014 changes incomplete, running it would be noise.)",
1247
+ deniedH: "## Denied tool calls (fail-closed log)",
1248
+ deniedNone: "None.",
1249
+ diffH: "## Diff summary (worktree vs base, uncommitted, awaiting manual handling)",
1250
+ diffNote: (branch, p) => `Changes live in an isolated worktree (branch \`${branch}\`, path ${p}). To keep: commit inside the worktree and merge back. To discard: delete the worktree directory + \`git worktree prune\` + \`git branch -D <branch>\`.`,
1251
+ diffNone: "(none)"
1252
+ };
1253
+ DICT = { zh: ZH, en: EN };
1254
+ batchT = (locale2) => DICT[locale2] ?? ZH;
1255
+ }
1256
+ });
1257
+
966
1258
  // src/batchAdmin.ts
967
1259
  var batchAdmin_exports = {};
968
1260
  __export(batchAdmin_exports, {
@@ -971,11 +1263,14 @@ __export(batchAdmin_exports, {
971
1263
  });
972
1264
  import fs6 from "fs/promises";
973
1265
  import path7 from "path";
974
- var emit, parseBatchArgv, USAGE, handleBatchAdmin, describeDone;
1266
+ var emit, parseBatchArgv, handleBatchAdmin, describeDone;
975
1267
  var init_batchAdmin = __esm({
976
1268
  "src/batchAdmin.ts"() {
977
1269
  "use strict";
978
1270
  init_queue();
1271
+ init_batchStrings();
1272
+ init_strings();
1273
+ init_prefs();
979
1274
  emit = (s = "") => {
980
1275
  process.stdout.write(s + "\n");
981
1276
  };
@@ -992,62 +1287,45 @@ var init_batchAdmin = __esm({
992
1287
  if (sub === "clean") return { sub: "clean" };
993
1288
  return { sub: "help" };
994
1289
  };
995
- USAGE = `deepseeker-code batch \u2014\u2014 \u6302\u673A\u6279\u5904\u7406\uFF08\u6210\u672C\u4FE1\u4EFB\u94FE\u7B2C\u56DB\u73AF\uFF09
996
-
997
- \u7528\u6CD5\uFF1Adeepseeker-code batch <\u5B50\u547D\u4EE4>
998
- add <\u5DE5\u5355.md> [...] \u6295\u9012\u4EFB\u52A1\uFF08\u53EF\u4E00\u6B21\u591A\u4E2A\u6587\u4EF6\uFF09
999
- list \u67E5\u770B pending / running / done \u4E09\u6001\u961F\u5217
1000
- run [--wait-window HH:MM-HH:MM] \u6267\u884C\u961F\u5217\uFF08\u4E32\u884C\uFF0C\u9010\u4EFB\u52A1\u51FA\u62A5\u544A\uFF1B--wait-window \u5230\u70B9\u518D\u5F00\u8DD1\uFF09
1001
- clean \u6E05\u7A7A done/\uFF08\u5DF2\u8BFB\u8FC7\u7684\u62A5\u544A\u4E0E\u5DE5\u5355\u5B58\u6863\uFF09
1002
-
1003
- \u5DE5\u5355\u683C\u5F0F\uFF08Markdown\uFF0Cfrontmatter \u7684 accept \u5FC5\u586B\u2014\u2014\u7F3A\u9A8C\u6536\u62D2\u7EDD\u5165\u961F\uFF09\uFF1A
1004
- ---
1005
- title: \u4FEE\u590D\u65E5\u671F\u89E3\u6790 bug
1006
- accept: npm test
1007
- budget-yuan: 0.5 # \u53EF\u9009\uFF1A\u672C\u4EFB\u52A1\u82B1\u8D39\u4E0A\u9650\uFF08\u5143\uFF09
1008
- timeout-min: 30 # \u53EF\u9009\uFF1Aagent \u9636\u6BB5\u8D85\u65F6\uFF08\u5206\u949F\uFF0C\u7F3A\u7701 30\uFF09
1009
- tools: run_command # \u53EF\u9009\uFF1A\u663E\u5F0F\u89E3\u9501\u9AD8\u5371\u5DE5\u5177\uFF08\u7F3A\u7701\u7981 run_command/delete_path\uFF09
1010
- ---
1011
- \u4EFB\u52A1\u63CF\u8FF0\u6B63\u6587\uFF08Markdown\uFF09\u2026
1012
-
1013
- \u4EA7\u7269\uFF1A<\u9879\u76EE>/.deepseeker-code/batch/done/<id>.report.md\uFF08\u72B6\u6001/\u7ED3\u7B97\u5355/\u9A8C\u6536\u8F93\u51FA/diff \u6458\u8981\uFF09\uFF1B
1014
- \u6539\u52A8\u6536\u5272\u5728\u72EC\u7ACB git worktree\uFF08\u4E34\u65F6\u5206\u652F\uFF09\u4E0A\uFF0C\u4FDD\u7559\u5F85\u4EBA\u5DE5 merge \u6216\u4E22\u5F03\u3002`;
1015
1290
  handleBatchAdmin = async (b) => {
1291
+ const saved = await readLocale();
1292
+ if (saved) setLocale(saved);
1293
+ const T = batchT(getLocale());
1016
1294
  if (b.sub === "help") {
1017
- emit(USAGE);
1295
+ emit(T.usage);
1018
1296
  return;
1019
1297
  }
1020
1298
  if (b.sub === "add") {
1021
1299
  if (b.files.length === 0) {
1022
- emit("\u7528\u6CD5\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md> [...]\uFF08\u7F3A\u9A8C\u6536\u547D\u4EE4\u7684\u5DE5\u5355\u4F1A\u88AB\u62D2\u7EDD\uFF09\n\u8BE6\u60C5\uFF1Adeepseeker-code batch help");
1300
+ emit(T.adminAddUsage);
1023
1301
  return;
1024
1302
  }
1025
1303
  for (const f of b.files) {
1026
1304
  const abs = path7.resolve(f);
1027
- const r = await enqueueTicketFile(abs).catch((e) => ({ ok: false, error: `\u8BFB\u6587\u4EF6\u5931\u8D25\uFF1A${e?.message ?? e}` }));
1028
- if (r.ok) emit(`\u{1F4E5} \u5DF2\u5165\u961F [${r.id}] ${r.title}`);
1029
- else emit(`\u{1F6AB} \u62D2\u7EDD ${path7.basename(abs)}\uFF1A${r.error}`);
1305
+ const r = await enqueueTicketFile(abs, getLocale()).catch((e) => ({ ok: false, error: T.readFileFail(e?.message ?? e) }));
1306
+ if (r.ok) emit(T.enqueued(r.id, r.title));
1307
+ else emit(T.addRejected(path7.basename(abs), r.error));
1030
1308
  }
1031
- emit("\n\u8DD1\u6279\uFF1Adeepseeker-code batch run");
1309
+ emit(T.nextStepRun);
1032
1310
  return;
1033
1311
  }
1034
1312
  if (b.sub === "list") {
1035
1313
  const q = await snapshotQueue();
1036
1314
  if (q.pending.length + q.running.length + q.done.length === 0) {
1037
- emit("\u961F\u5217\u7A7A\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>\uFF08\u683C\u5F0F\u89C1 batch help\uFF09");
1315
+ emit(T.adminQueueEmpty);
1038
1316
  return;
1039
1317
  }
1040
- if (q.running.length > 0) emit(`\u25B6 \u8FD0\u884C\u4E2D\uFF1A`);
1318
+ if (q.running.length > 0) emit(T.listRunningHead);
1041
1319
  for (const f of q.running) emit(` ${f.replace(/\.md$/, "")}`);
1042
- if (q.pending.length > 0) emit(`\u23F3 \u5F85\u8DD1 ${q.pending.length}\uFF1A`);
1320
+ if (q.pending.length > 0) emit(T.listPendingHead(q.pending.length));
1043
1321
  for (const f of q.pending) emit(` ${f.replace(/\.md$/, "")}`);
1044
- if (q.done.length > 0) emit(`\u2714 \u5DF2\u5B8C\u6210 ${q.done.length}\uFF1A`);
1322
+ if (q.done.length > 0) emit(T.listDoneHead(q.done.length));
1045
1323
  for (const id of q.done) emit(` ${id} ${await describeDone(id)}`);
1046
1324
  return;
1047
1325
  }
1048
1326
  if (b.sub === "clean") {
1049
1327
  const n = await cleanDone();
1050
- emit(`\u{1F9F9} \u5DF2\u6E05\u7A7A done/\uFF08${n} \u4E2A\u4EFB\u52A1\u5B58\u6863\uFF09\u3002pending/running \u4E0D\u53D7\u5F71\u54CD\u3002`);
1328
+ emit(T.cleaned(n));
1051
1329
  return;
1052
1330
  }
1053
1331
  };
@@ -1056,7 +1334,7 @@ var init_batchAdmin = __esm({
1056
1334
  try {
1057
1335
  text = await fs6.readFile(reportPath(id), "utf-8");
1058
1336
  } catch {
1059
- return "\uFF08\u62A5\u544A\u7F3A\u5931\uFF09";
1337
+ return batchT(getLocale()).reportMissing;
1060
1338
  }
1061
1339
  const fm = (key) => {
1062
1340
  const m = new RegExp(`^${key}:\\s*(.+)$`, "m").exec(text);
@@ -15390,7 +15668,7 @@ var init_useChatState = __esm({
15390
15668
  break;
15391
15669
  }
15392
15670
  case "vision.downgraded":
15393
- pushInfo(`\u{1F5BC} \u6A21\u578B ${String(obj.model ?? "")} \u4E0D\u652F\u6301\u56FE\u7247\uFF0C\u5DF2\u81EA\u52A8\u964D\u7EA7\u4E3A\u6587\u672C\u5904\u7406\uFF08\u5DF2\u8BB0\u4F4F\uFF0C\u540E\u7EED\u6D88\u606F\u76F4\u63A5\u6309\u6587\u672C\u53D1\u9001\uFF09`);
15671
+ pushInfo(S.visionDowngraded(String(obj.model ?? "")));
15394
15672
  break;
15395
15673
  default:
15396
15674
  break;
@@ -15535,7 +15813,7 @@ ${finalPlan}`, false, res.autoExecute);
15535
15813
  } else {
15536
15814
  const enterReason = takeEnterPlanRequest();
15537
15815
  if (enterReason != null) {
15538
- pushInfo(`\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F${enterReason ? `\uFF1A${enterReason}` : ""}\uFF0C\u5DF2\u5207\u6362\u2026`);
15816
+ pushInfo(S.planEnterSwitched(enterReason));
15539
15817
  planModeRef.current = true;
15540
15818
  const kept = await runPlanStage(sid, ENTER_PLAN_RESEARCH_PROMPT);
15541
15819
  if (kept) planModeRef.current = false;
@@ -15549,7 +15827,7 @@ ${finalPlan}`, false, res.autoExecute);
15549
15827
  const runBangCommand = useCallback(async (raw) => {
15550
15828
  const cmd = raw.replace(/^!/, "").trim();
15551
15829
  if (!cmd) {
15552
- pushInfo("\u7528\u6CD5\uFF1A!<shell \u547D\u4EE4>\uFF08\u5982 !git status\uFF09\uFF1B\u8F93\u51FA\u4F1A\u5E26\u5165\u4E0B\u8F6E\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u3002");
15830
+ pushInfo(S.bangUsage);
15553
15831
  return;
15554
15832
  }
15555
15833
  pushUser(raw);
@@ -15557,9 +15835,9 @@ ${finalPlan}`, false, res.autoExecute);
15557
15835
  sessionIdRef.current = await getOrCreateSessionId(initialSessionId ?? void 0);
15558
15836
  }
15559
15837
  const r = await runBashDirect(cmd, getActiveWorkspaceRoot());
15560
- const head = r.timedOut ? `\u23F1 \u8D85\u65F6\u4E2D\u6B62` : r.ok ? `\u2705 \u9000\u51FA\u7801 0` : r.exitCode != null ? `\u274C \u9000\u51FA\u7801 ${r.exitCode}` : `\u274C \u5DF2\u4E2D\u6B62`;
15838
+ const head = S.bangHead(r.timedOut, r.ok, r.exitCode ?? null);
15561
15839
  pushInfo(`${head} \xB7 ${Math.round(r.durationMs / 100) / 10}s
15562
- ${r.output.trim() || "(\u65E0\u8F93\u51FA)"}`);
15840
+ ${r.output.trim() || S.bangNoOutput}`);
15563
15841
  await recordBashEntry(sessionIdRef.current, formatBashEntry(cmd, r));
15564
15842
  }, [initialSessionId, pushUser, pushInfo]);
15565
15843
  const abortCurrent = useCallback(() => {
@@ -16854,7 +17132,7 @@ var init_TopPanel = __esm({
16854
17132
 
16855
17133
  // ../core/src/observability/inspect.ts
16856
17134
  import fs48 from "fs";
16857
- var CWD, fmt, readTrace, STOP_LABEL, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
17135
+ var OBS, obsT, CWD, fmt, readTrace, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
16858
17136
  var init_inspect = __esm({
16859
17137
  "../core/src/observability/inspect.ts"() {
16860
17138
  "use strict";
@@ -16870,6 +17148,104 @@ var init_inspect = __esm({
16870
17148
  init_truncate();
16871
17149
  init_recall();
16872
17150
  init_createModel();
17151
+ OBS = {
17152
+ zh: {
17153
+ usageNoData: "\u{1F4CA} Token \u7528\u91CF\uFF1A\u6682\u65E0\u6570\u636E\uFF08\u672C\u4F1A\u8BDD\u5C1A\u65E0 LLM \u54CD\u5E94\u843D\u76D8\uFF09\u3002",
17154
+ usageHead: (rounds) => `\u{1F4CA} Token \u7528\u91CF\uFF08\u672C\u4F1A\u8BDD\uFF0C${rounds} \u6B21 LLM \u54CD\u5E94\uFF09`,
17155
+ usageTokens: (prompt, completion, total) => ` \u8F93\u5165(prompt): ${prompt} | \u8F93\u51FA(completion): ${completion} | \u5408\u8BA1: ${total}`,
17156
+ usageCache: (hit, rate, miss) => ` \u7F13\u5B58\u547D\u4E2D: ${hit}\uFF08${rate}%\uFF09 | \u7F13\u5B58\u672A\u547D\u4E2D: ${miss}`,
17157
+ usageSplit: (main2, sub) => ` \u4E3B agent: ${main2} | \u5B50 agent(spawn/workflow): ${sub}`,
17158
+ usageSessionRuns: (runs, rounds, avg, aborted, pct) => ` \u258E\u672C\u4F1A\u8BDD ${runs} \u4E2A run\uFF1A${rounds} \u8F6E\uFF08\u5747 ${avg} \u8F6E/run\uFF09\xB7 \u4E2D\u6B62 ${aborted}/${runs}\uFF08${pct}%\uFF09`,
17159
+ usageNoTools: "\uFF08\u65E0\u5DE5\u5177\u8C03\u7528\uFF09",
17160
+ usageRunLine: (ts2, rounds, tok, stop, tools) => ` ${ts2} ${rounds}\u8F6E ${tok} tok ${stop} ${tools}`,
17161
+ stopLabel: { normal: "\u5B8C\u6210", aborted: "\u4E2D\u6B62", error: "\u9519\u8BEF", repeat: "\u7194\u65AD", limit: "\u8D85\u9650" },
17162
+ usageWeek: (runs, rounds, avg, abortPct, tokens, hitPct) => ` \u258E\u8FD1 7 \u5929\uFF08\u4F7F\u7528\u65E5\u5FD7 \xB7 \u5168\u9879\u76EE\uFF09\uFF1A${runs} runs \xB7 ${rounds} \u8F6E\uFF08\u5747 ${avg} \u8F6E/run\uFF09\xB7 \u4E2D\u6B62 ${abortPct}% \xB7 ${tokens} tok\uFF08\u7F13\u5B58\u547D\u4E2D ${hitPct}%\uFF09`,
17163
+ usageTopTools: (tools) => ` \u5E38\u7528\u5DE5\u5177: ${tools.join("\u3001") || "\uFF08\u65E0\uFF09"}`,
17164
+ usageWeekCost: (cost, saved, unpriced) => ` \u8FD1 7 \u5929\u82B1\u8D39 \xA5${cost}\uFF08\u7F13\u5B58\u547D\u4E2D\u5DF2\u7701 \xA5${saved}\uFF09${unpriced > 0 ? ` \xB7 ${unpriced} \u6761\u65E0\u4EF7\u8BB0\u5F55\u672A\u8BA1` : ""}`,
17165
+ usageTopSessions: (tops) => ` \u8D39\u7528 top \u4F1A\u8BDD: ${tops}`,
17166
+ usageTip: " \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",
17167
+ ctxHead: "\u{1F9E0} \u4E0A\u4E0B\u6587\u6CBB\u7406",
17168
+ ctxWindow: (window, threshold, ratio, keep) => ` \u7A97\u53E3\u4E0A\u9650: ${window} | \u538B\u7F29\u9608\u503C: ${threshold}\uFF08${ratio}\xD7\uFF09 | \u4FDD\u7559\u6700\u8FD1: ${keep} \u5BF9\u8BDD\u5355\u5143`,
17169
+ ctxLastPrompt: (p, fillRate) => ` \u6700\u8FD1 prompt: ${p ?? "\uFF08\u5C1A\u65E0\uFF09"}${fillRate ? `\uFF08\u5360\u7A97\u53E3 ${fillRate}%\uFF09` : ""}`,
17170
+ ctxArchived: (n) => ` \u5DF2\u5F52\u6863(\u538B\u7F29)\u6D88\u606F\u6761\u6570: ${n}`,
17171
+ ctxEntities: (n) => ` \u672C\u4F1A\u8BDD\u5DF2\u5F52\u6863\u5B9E\u4F53: ${n} \u4E2A\uFF08\u6458\u8981\u69FD\u7D22\u5F15\uFF09`,
17172
+ ctxRecall: (n) => ` \u53EC\u56DE\u547D\u4E2D: ${n} \u6B21\uFF08recall \u5DE5\u5177\uFF0C\u672C\u6B21\u8FDB\u7A0B\u5185\u8BA1\u6570\uFF09`,
17173
+ ctxSidecar: (n) => ` \u4FA7\u8F66\u5B58\u6863: ${n} \u6761\uFF08\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u539F\u6587\uFF0Crecall with_full \u53D6\u56DE\uFF09`,
17174
+ ctxTip: (pct) => ` \u8D85\u8FC7\u9608\u503C\u5373\u89E6\u53D1\u6EDA\u52A8\u538B\u7F29\uFF1BfillRate \u63A5\u8FD1 ${pct}% \u65F6\u5373\u5C06\u538B\u7F29\u3002`,
17175
+ permEmpty: "\u{1F512} \u6743\u9650\u89C4\u5219\uFF1A\u65E0\uFF08\u5168\u90E8\u8D70\u9ED8\u8BA4 safetyLevel\uFF1ASAFE \u514D\u5BA1 / MUTATION\xB7DANGER \u5BA1\u6279\uFF09\u3002",
17176
+ permHead: "\u{1F512} \u6743\u9650\u89C4\u5219\uFF08\u4F18\u5148\u7EA7 deny > ask > allow\uFF1B\u672A\u5339\u914D\u8D70\u9ED8\u8BA4 safetyLevel\uFF09",
17177
+ permNone: "(\u65E0)",
17178
+ permGroup: (icon, label, n) => ` ${icon} ${label}\uFF08${n}\uFF09:`,
17179
+ mcpEmpty: "\u{1F50C} MCP\uFF1A\u65E0\u5DF2\u8FDE\u63A5 server\uFF08\u5728 ~/.deepseeker-code/mcp.json \u914D\u7F6E\uFF09\u3002",
17180
+ mcpHead: (n, tools) => `\u{1F50C} MCP server\uFF08${n} \u4E2A\uFF0C\u5171 ${tools} \u4E2A\u5DE5\u5177\uFF09`,
17181
+ mcpLine: (name, tools) => ` \u2022 ${name}\uFF08${tools}\uFF09`,
17182
+ mcpToolCount: (n) => `${n} \u4E2A\u5DE5\u5177`,
17183
+ mcpToolCountFail: "\u5DE5\u5177\u6570\u83B7\u53D6\u5931\u8D25",
17184
+ hooksEmpty: "\u{1FA9D} Hooks\uFF1A\u65E0\uFF08\u5728 ~/.deepseeker-code/settings.json \u7684 hooks \u6BB5\u914D\u7F6E\uFF09\u3002",
17185
+ hooksHead: (n) => `\u{1FA9D} Hooks\uFF08\u5171 ${n} \u6761\uFF0C\u6309\u4E8B\u4EF6\u5206\u7EC4\uFF09`,
17186
+ hooksGroup: (evt, n) => ` \u2022 ${evt}\uFF08${n}\uFF09:`,
17187
+ dbgHead: "\u{1F41E} Debug \u5FEB\u7167",
17188
+ dbgSessionNone: "(\u672A\u521D\u59CB\u5316)",
17189
+ dbgModels: (main2, aux) => ` \u4E3B\u6A21\u578B: ${main2} | \u8F85\u52A9\u6A21\u578B: ${aux}`,
17190
+ dbgAuxNone: "(\u672A\u914D\u7F6E)",
17191
+ dbgOn: "\u5F00",
17192
+ dbgOff: "\u5173",
17193
+ dbgParallel: (on, conc, steps) => ` \u5E76\u884C\u5DE5\u5177: ${on} | workflow \u5E76\u53D1\u4E0A\u9650: ${conc} | workflow \u6700\u5927\u6B65\u6570: ${steps}`,
17194
+ dbgUndo: (on, search) => ` undo: ${on} | \u641C\u7D22\u540E\u7AEF: ${search}`,
17195
+ dbgSearchAuto: "(\u81EA\u52A8)",
17196
+ dbgPlatform: (node, platform) => ` Node: ${node} | \u5E73\u53F0: ${platform}`
17197
+ },
17198
+ en: {
17199
+ usageNoData: "\u{1F4CA} Token usage: no data yet (no LLM responses logged for this session).",
17200
+ usageHead: (rounds) => `\u{1F4CA} Token usage (this session, ${rounds} LLM responses)`,
17201
+ usageTokens: (prompt, completion, total) => ` Input (prompt): ${prompt} | Output (completion): ${completion} | Total: ${total}`,
17202
+ usageCache: (hit, rate, miss) => ` Cache hit: ${hit} (${rate}%) | Cache miss: ${miss}`,
17203
+ usageSplit: (main2, sub) => ` Main agent: ${main2} | Sub agents (spawn/workflow): ${sub}`,
17204
+ usageSessionRuns: (runs, rounds, avg, aborted, pct) => ` \u258EThis session: ${runs} runs, ${rounds} rounds (avg ${avg}/run) \xB7 aborted ${aborted}/${runs} (${pct}%)`,
17205
+ usageNoTools: "(no tool calls)",
17206
+ usageRunLine: (ts2, rounds, tok, stop, tools) => ` ${ts2} ${rounds} rounds ${tok} tok ${stop} ${tools}`,
17207
+ stopLabel: { normal: "done", aborted: "aborted", error: "error", repeat: "fuse", limit: "limit" },
17208
+ usageWeek: (runs, rounds, avg, abortPct, tokens, hitPct) => ` \u258ELast 7 days (usage log \xB7 all projects): ${runs} runs \xB7 ${rounds} rounds (avg ${avg}/run) \xB7 aborted ${abortPct}% \xB7 ${tokens} tok (cache hit ${hitPct}%)`,
17209
+ usageTopTools: (tools) => ` Top tools: ${tools.join(", ") || "(none)"}`,
17210
+ usageWeekCost: (cost, saved, unpriced) => ` 7-day spend \xA5${cost} (cache hits saved \xA5${saved})${unpriced > 0 ? ` \xB7 ${unpriced} unpriced records excluded` : ""}`,
17211
+ usageTopSessions: (tops) => ` Costliest sessions: ${tops}`,
17212
+ usageTip: " Tip: parallel workflows multiply sub-agent usage; higher cache hit rate means lower input cost.",
17213
+ ctxHead: "\u{1F9E0} Context governance",
17214
+ ctxWindow: (window, threshold, ratio, keep) => ` Window cap: ${window} | Compact threshold: ${threshold} (${ratio}\xD7) | Keep recent: ${keep} dialogue units`,
17215
+ ctxLastPrompt: (p, fillRate) => ` Last prompt: ${p ?? "(none yet)"}${fillRate ? ` (${fillRate}% of window)` : ""}`,
17216
+ ctxArchived: (n) => ` Archived (compacted) messages: ${n}`,
17217
+ ctxEntities: (n) => ` Archived entities this session: ${n} (summary-slot index)`,
17218
+ ctxRecall: (n) => ` Recall hits: ${n} (recall tool, in-process count)`,
17219
+ ctxSidecar: (n) => ` Sidecar archives: ${n} (full tool outputs, retrievable via recall with_full)`,
17220
+ ctxTip: (pct) => ` Rolling compaction triggers past the threshold; fillRate near ${pct}% means compaction is imminent.`,
17221
+ permEmpty: "\u{1F512} Permission rules: none (everything uses default safetyLevel: SAFE auto-approve / MUTATION\xB7DANGER gated).",
17222
+ permHead: "\u{1F512} Permission rules (priority deny > ask > allow; unmatched uses default safetyLevel)",
17223
+ permNone: "(none)",
17224
+ permGroup: (icon, label, n) => ` ${icon} ${label} (${n}):`,
17225
+ mcpEmpty: "\u{1F50C} MCP: no connected servers (configure in ~/.deepseeker-code/mcp.json).",
17226
+ mcpHead: (n, tools) => `\u{1F50C} MCP servers (${n}, ${tools} tools total)`,
17227
+ mcpLine: (name, tools) => ` \u2022 ${name} (${tools})`,
17228
+ mcpToolCount: (n) => `${n} tools`,
17229
+ mcpToolCountFail: "failed to count tools",
17230
+ hooksEmpty: "\u{1FA9D} Hooks: none (configure under the hooks key in ~/.deepseeker-code/settings.json).",
17231
+ hooksHead: (n) => `\u{1FA9D} Hooks (${n} total, grouped by event)`,
17232
+ hooksGroup: (evt, n) => ` \u2022 ${evt} (${n}):`,
17233
+ dbgHead: "\u{1F41E} Debug snapshot",
17234
+ dbgSessionNone: "(not initialized)",
17235
+ dbgModels: (main2, aux) => ` Main model: ${main2} | Aux model: ${aux}`,
17236
+ dbgAuxNone: "(not configured)",
17237
+ dbgOn: "on",
17238
+ dbgOff: "off",
17239
+ dbgParallel: (on, conc, steps) => ` Parallel tools: ${on} | Workflow concurrency cap: ${conc} | Workflow max steps: ${steps}`,
17240
+ dbgUndo: (on, search) => ` undo: ${on} | Search backend: ${search}`,
17241
+ dbgSearchAuto: "(auto)",
17242
+ dbgPlatform: (node, platform) => ` Node: ${node} | Platform: ${platform}`
17243
+ }
17244
+ };
17245
+ obsT = (locale2) => {
17246
+ const dict = OBS[locale2] ?? OBS.zh;
17247
+ return (key) => dict[key] ?? OBS.zh[key] ?? key;
17248
+ };
16873
17249
  CWD = process.cwd();
16874
17250
  fmt = (n) => typeof n === "number" && Number.isFinite(n) ? n.toLocaleString("en-US") : "-";
16875
17251
  readTrace = async (sessionId) => {
@@ -16890,13 +17266,13 @@ var init_inspect = __esm({
16890
17266
  return [];
16891
17267
  }
16892
17268
  };
16893
- STOP_LABEL = { normal: "\u5B8C\u6210", aborted: "\u4E2D\u6B62", error: "\u9519\u8BEF", repeat: "\u7194\u65AD", limit: "\u8D85\u9650" };
16894
17269
  fmtK = (n) => n >= 1e4 ? `${(n / 1e3).toFixed(1)}k` : fmt(n);
16895
- inspectUsage = async (sessionId) => {
17270
+ inspectUsage = async (sessionId, locale2 = "zh") => {
17271
+ const t = obsT(locale2);
16896
17272
  const trace = await readTrace(sessionId);
16897
- const responses = trace.filter((t) => t?.eventType === "llm.response" && t?.usage);
17273
+ const responses = trace.filter((x) => x?.eventType === "llm.response" && x?.usage);
16898
17274
  const sessionRuns = (await readUsageRecords()).filter((r) => r.sessionId === sessionId);
16899
- 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`;
17275
+ if (responses.length === 0 && sessionRuns.length === 0) return t("usageNoData");
16900
17276
  let prompt = 0, completion = 0, total = 0, cacheHit = 0, cacheMiss = 0;
16901
17277
  let mainTotal = 0, subTotal = 0;
16902
17278
  for (const r of responses) {
@@ -16912,20 +17288,21 @@ var init_inspect = __esm({
16912
17288
  }
16913
17289
  const cacheRate = prompt > 0 ? (cacheHit / prompt * 100).toFixed(1) : "0.0";
16914
17290
  const rounds = responses.length;
17291
+ const stopLabel = t("stopLabel");
16915
17292
  const lines = [
16916
- `\u{1F4CA} Token \u7528\u91CF\uFF08\u672C\u4F1A\u8BDD\uFF0C${rounds} \u6B21 LLM \u54CD\u5E94\uFF09`,
16917
- ` \u8F93\u5165(prompt): ${fmt(prompt)} | \u8F93\u51FA(completion): ${fmt(completion)} | \u5408\u8BA1: ${fmt(total)}`,
16918
- ` \u7F13\u5B58\u547D\u4E2D: ${fmt(cacheHit)}\uFF08${cacheRate}%\uFF09 | \u7F13\u5B58\u672A\u547D\u4E2D: ${fmt(cacheMiss)}`,
16919
- ` \u4E3B agent: ${fmt(mainTotal)} | \u5B50 agent(spawn/workflow): ${fmt(subTotal)}`
17293
+ t("usageHead")(rounds),
17294
+ t("usageTokens")(fmt(prompt), fmt(completion), fmt(total)),
17295
+ t("usageCache")(fmt(cacheHit), cacheRate, fmt(cacheMiss)),
17296
+ t("usageSplit")(fmt(mainTotal), fmt(subTotal))
16920
17297
  ];
16921
17298
  if (sessionRuns.length > 0) {
16922
17299
  const ss = summarizeUsage(sessionRuns);
16923
17300
  const abortedPct = ss.mainRuns > 0 ? Math.round(ss.stopReasons.aborted / ss.mainRuns * 100) : 0;
16924
17301
  const avgRounds = ss.mainRuns > 0 ? (ss.mainRounds / ss.mainRuns).toFixed(1) : "0";
16925
- 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`);
17302
+ lines.push(t("usageSessionRuns")(ss.mainRuns, ss.mainRounds, avgRounds, ss.stopReasons.aborted, abortedPct));
16926
17303
  for (const r of sessionRuns.filter((x) => x.depth === 0).slice(-6)) {
16927
- const tools = topTools(r.tools, 3).map(([n, c]) => `${n}\xD7${c}`).join(" ") || "\uFF08\u65E0\u5DE5\u5177\u8C03\u7528\uFF09";
16928
- 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}`);
17304
+ const tools = topTools(r.tools, 3).map(([n, c]) => `${n}\xD7${c}`).join(" ") || t("usageNoTools");
17305
+ lines.push(t("usageRunLine")(String(r.ts).slice(5, 16).replace("T", " "), r.rounds, fmtK(r.totalTokens ?? 0), stopLabel[r.stopReason] ?? r.stopReason, tools));
16929
17306
  }
16930
17307
  }
16931
17308
  const weekRecords = await readUsageRecords(7);
@@ -16934,10 +17311,10 @@ var init_inspect = __esm({
16934
17311
  const weekHit = week.promptTokens > 0 ? (week.cachedTokens / week.promptTokens * 100).toFixed(1) : "0.0";
16935
17312
  const weekAbort = week.mainRuns > 0 ? Math.round(week.stopReasons.aborted / week.mainRuns * 100) : 0;
16936
17313
  const weekAvg = week.mainRuns > 0 ? (week.mainRounds / week.mainRuns).toFixed(1) : "0";
16937
- const tools = topTools(week.tools, 4).map(([n, c]) => `${n}\xD7${c}`).join("\u3001");
17314
+ const tools = topTools(week.tools, 4).map(([n, c]) => `${n}\xD7${c}`);
16938
17315
  lines.push(
16939
- ` \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`,
16940
- ` \u5E38\u7528\u5DE5\u5177: ${tools || "\uFF08\u65E0\uFF09"}`
17316
+ t("usageWeek")(week.mainRuns, week.mainRounds, weekAvg, weekAbort, fmtK(week.totalTokens), weekHit),
17317
+ t("usageTopTools")(tools)
16941
17318
  );
16942
17319
  const bySession = /* @__PURE__ */ new Map();
16943
17320
  let weekCost = 0, weekSaved = 0, pricedRuns = 0;
@@ -16953,14 +17330,15 @@ var init_inspect = __esm({
16953
17330
  if (pricedRuns > 0) {
16954
17331
  const tops = [...bySession.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([sid, c]) => `${sid.slice(0, 8)} \xA5${c.toFixed(3)}`).join(" \xB7 ");
16955
17332
  const unpaid = week.runs - pricedRuns;
16956
- lines.push(` \u8FD1 7 \u5929\u82B1\u8D39 \xA5${weekCost.toFixed(3)}\uFF08\u7F13\u5B58\u547D\u4E2D\u5DF2\u7701 \xA5${weekSaved.toFixed(3)}\uFF09${unpaid > 0 ? ` \xB7 ${unpaid} \u6761\u65E0\u4EF7\u8BB0\u5F55\u672A\u8BA1` : ""}`);
16957
- if (tops) lines.push(` \u8D39\u7528 top \u4F1A\u8BDD: ${tops}`);
17333
+ lines.push(t("usageWeekCost")(weekCost.toFixed(3), weekSaved.toFixed(3), unpaid));
17334
+ if (tops) lines.push(t("usageTopSessions")(tops));
16958
17335
  }
16959
17336
  }
16960
- 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`);
17337
+ lines.push(t("usageTip"));
16961
17338
  return lines.join("\n");
16962
17339
  };
16963
- inspectContext = async (sessionId) => {
17340
+ inspectContext = async (sessionId, locale2 = "zh") => {
17341
+ const t = obsT(locale2);
16964
17342
  const trace = await readTrace(sessionId);
16965
17343
  let lastPrompt;
16966
17344
  for (let i = trace.length - 1; i >= 0; i--) {
@@ -16988,38 +17366,40 @@ var init_inspect = __esm({
16988
17366
  const threshold = Math.round(window * ratio);
16989
17367
  const fillRate = lastPrompt ? (lastPrompt / window * 100).toFixed(1) : null;
16990
17368
  return [
16991
- `\u{1F9E0} \u4E0A\u4E0B\u6587\u6CBB\u7406`,
16992
- ` \u7A97\u53E3\u4E0A\u9650: ${fmt(window)} | \u538B\u7F29\u9608\u503C: ${fmt(threshold)}\uFF08${ratio}\xD7\uFF09 | \u4FDD\u7559\u6700\u8FD1: ${appConfig.KEEP_RECENT_UNITS} \u5BF9\u8BDD\u5355\u5143`,
16993
- ` \u6700\u8FD1 prompt: ${lastPrompt ? fmt(lastPrompt) : "\uFF08\u5C1A\u65E0\uFF09"}${fillRate ? `\uFF08\u5360\u7A97\u53E3 ${fillRate}%\uFF09` : ""}`,
16994
- ` \u5DF2\u5F52\u6863(\u538B\u7F29)\u6D88\u606F\u6761\u6570: ${archived}`,
16995
- ` \u672C\u4F1A\u8BDD\u5DF2\u5F52\u6863\u5B9E\u4F53: ${archivedEntities} \u4E2A\uFF08\u6458\u8981\u69FD\u7D22\u5F15\uFF09`,
16996
- ` \u53EC\u56DE\u547D\u4E2D: ${getRecallHitCount(sessionId)} \u6B21\uFF08recall \u5DE5\u5177\uFF0C\u672C\u6B21\u8FDB\u7A0B\u5185\u8BA1\u6570\uFF09`,
16997
- ` \u4FA7\u8F66\u5B58\u6863: ${sidecarCount} \u6761\uFF08\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u539F\u6587\uFF0Crecall with_full \u53D6\u56DE\uFF09`,
16998
- ` \u8D85\u8FC7\u9608\u503C\u5373\u89E6\u53D1\u6EDA\u52A8\u538B\u7F29\uFF1BfillRate \u63A5\u8FD1 ${Math.round(ratio * 100)}% \u65F6\u5373\u5C06\u538B\u7F29\u3002`
17369
+ t("ctxHead"),
17370
+ t("ctxWindow")(fmt(window), fmt(threshold), ratio, appConfig.KEEP_RECENT_UNITS),
17371
+ t("ctxLastPrompt")(lastPrompt ? fmt(lastPrompt) : null, fillRate),
17372
+ t("ctxArchived")(archived),
17373
+ t("ctxEntities")(archivedEntities),
17374
+ t("ctxRecall")(getRecallHitCount(sessionId)),
17375
+ t("ctxSidecar")(sidecarCount),
17376
+ t("ctxTip")(Math.round(ratio * 100))
16999
17377
  ].join("\n");
17000
17378
  };
17001
- inspectPermissions = () => {
17379
+ inspectPermissions = (locale2 = "zh") => {
17380
+ const t = obsT(locale2);
17002
17381
  const r = listPermissionRules();
17003
17382
  const total = r.allow.length + r.deny.length + r.ask.length;
17004
- if (total === 0) return `\u{1F512} \u6743\u9650\u89C4\u5219\uFF1A\u65E0\uFF08\u5168\u90E8\u8D70\u9ED8\u8BA4 safetyLevel\uFF1ASAFE \u514D\u5BA1 / MUTATION\xB7DANGER \u5BA1\u6279\uFF09\u3002`;
17383
+ if (total === 0) return t("permEmpty");
17005
17384
  const render2 = (label, kind, icon) => {
17006
17385
  const list = r[kind];
17007
- if (list.length === 0) return ` ${icon} ${label}: (\u65E0)`;
17008
- return ` ${icon} ${label}\uFF08${list.length}\uFF09:
17009
- ` + list.map((x) => ` ${x.toolName}${x.raw && x.raw !== x.toolName ? ` \u2190 ${x.raw}` : ""}`).join("\n");
17386
+ if (list.length === 0) return ` ${icon} ${label}: ${t("permNone")}`;
17387
+ return t("permGroup")(icon, label, list.length) + "\n" + list.map((x) => ` ${x.toolName}${x.raw && x.raw !== x.toolName ? ` \u2190 ${x.raw}` : ""}`).join("\n");
17010
17388
  };
17011
- return [`\u{1F512} \u6743\u9650\u89C4\u5219\uFF08\u4F18\u5148\u7EA7 deny > ask > allow\uFF1B\u672A\u5339\u914D\u8D70\u9ED8\u8BA4 safetyLevel\uFF09`, render2("Deny", "deny", "\u{1F6AB}"), render2("Ask", "ask", "\u2753"), render2("Allow", "allow", "\u2705")].join("\n");
17389
+ return [t("permHead"), render2("Deny", "deny", "\u{1F6AB}"), render2("Ask", "ask", "\u2753"), render2("Allow", "allow", "\u2705")].join("\n");
17012
17390
  };
17013
- inspectMcp = async () => {
17391
+ inspectMcp = async (locale2 = "zh") => {
17392
+ const t = obsT(locale2);
17014
17393
  const clients2 = await listMcpClients();
17015
- if (clients2.length === 0) return `\u{1F50C} MCP\uFF1A\u65E0\u5DF2\u8FDE\u63A5 server\uFF08\u5728 ~/.deepseeker-code/mcp.json \u914D\u7F6E\uFF09\u3002`;
17016
- const lines = clients2.map((c) => ` \u2022 ${c.serverName}\uFF08${c.toolCount >= 0 ? `${c.toolCount} \u4E2A\u5DE5\u5177` : "\u5DE5\u5177\u6570\u83B7\u53D6\u5931\u8D25"}\uFF09`);
17394
+ if (clients2.length === 0) return t("mcpEmpty");
17395
+ const lines = clients2.map((c) => t("mcpLine")(c.serverName, c.toolCount >= 0 ? t("mcpToolCount")(c.toolCount) : t("mcpToolCountFail")));
17017
17396
  const totalTools = clients2.reduce((s, c) => s + (c.toolCount > 0 ? c.toolCount : 0), 0);
17018
- return [`\u{1F50C} MCP server\uFF08${clients2.length} \u4E2A\uFF0C\u5171 ${totalTools} \u4E2A\u5DE5\u5177\uFF09`, ...lines].join("\n");
17397
+ return [t("mcpHead")(clients2.length, totalTools), ...lines].join("\n");
17019
17398
  };
17020
- inspectHooks = () => {
17399
+ inspectHooks = (locale2 = "zh") => {
17400
+ const t = obsT(locale2);
17021
17401
  const hooks = listHooks();
17022
- if (hooks.length === 0) return `\u{1FA9D} Hooks\uFF1A\u65E0\uFF08\u5728 ~/.deepseeker-code/settings.json \u7684 hooks \u6BB5\u914D\u7F6E\uFF09\u3002`;
17402
+ if (hooks.length === 0) return t("hooksEmpty");
17023
17403
  const byEvent = /* @__PURE__ */ new Map();
17024
17404
  for (const h of hooks) {
17025
17405
  const arr = byEvent.get(h.event) ?? [];
@@ -17027,21 +17407,21 @@ var init_inspect = __esm({
17027
17407
  byEvent.set(h.event, arr);
17028
17408
  }
17029
17409
  const lines = [...byEvent.entries()].map(([evt, arr]) => {
17030
- return ` \u2022 ${evt}\uFF08${arr.length}\uFF09:
17031
- ` + arr.map((h) => ` matcher=${h.matcher} source=${h.source}${h.onError ? ` onError=${h.onError}` : ""}`).join("\n");
17410
+ return t("hooksGroup")(evt, arr.length) + "\n" + arr.map((h) => ` matcher=${h.matcher} source=${h.source}${h.onError ? ` onError=${h.onError}` : ""}`).join("\n");
17032
17411
  });
17033
- return [`\u{1FA9D} Hooks\uFF08\u5171 ${hooks.length} \u6761\uFF0C\u6309\u4E8B\u4EF6\u5206\u7EC4\uFF09`, ...lines].join("\n");
17412
+ return [t("hooksHead")(hooks.length), ...lines].join("\n");
17034
17413
  };
17035
- inspectDebug = (sessionId) => {
17414
+ inspectDebug = (sessionId, locale2 = "zh") => {
17415
+ const t = obsT(locale2);
17036
17416
  return [
17037
- `\u{1F41E} Debug \u5FEB\u7167`,
17038
- ` session: ${sessionId || "(\u672A\u521D\u59CB\u5316)"}`,
17417
+ t("dbgHead"),
17418
+ ` session: ${sessionId || t("dbgSessionNone")}`,
17039
17419
  ` cwd: ${CWD}`,
17040
- ` \u4E3B\u6A21\u578B: ${MODEL_NAME} | \u8F85\u52A9\u6A21\u578B: ${AUX_MODEL_NAME ?? "(\u672A\u914D\u7F6E)"}`,
17420
+ t("dbgModels")(MODEL_NAME, AUX_MODEL_NAME ?? t("dbgAuxNone")),
17041
17421
  ` dataDir: ${appConfig.dataDir}`,
17042
- ` \u5E76\u884C\u5DE5\u5177: ${appConfig.parallelSafeTools ? "\u5F00" : "\u5173"} | workflow \u5E76\u53D1\u4E0A\u9650: ${appConfig.workflowConcurrency} | workflow \u6700\u5927\u6B65\u6570: ${appConfig.workflowMaxSteps}`,
17043
- ` undo: ${appConfig.undoEnabled ? "\u5F00" : "\u5173"} | \u641C\u7D22\u540E\u7AEF: ${appConfig.searchProvider || "(\u81EA\u52A8)"}`,
17044
- ` Node: ${process.version} | \u5E73\u53F0: ${process.platform}`
17422
+ t("dbgParallel")(appConfig.parallelSafeTools ? t("dbgOn") : t("dbgOff"), appConfig.workflowConcurrency, appConfig.workflowMaxSteps),
17423
+ t("dbgUndo")(appConfig.undoEnabled ? t("dbgOn") : t("dbgOff"), appConfig.searchProvider || t("dbgSearchAuto")),
17424
+ t("dbgPlatform")(process.version, process.platform)
17045
17425
  ].join("\n");
17046
17426
  };
17047
17427
  }
@@ -17340,54 +17720,49 @@ var init_App = __esm({
17340
17720
  state.pushInfo(S.statusText(modelDisplay, state.getThinkingLevel(), state.getPlanMode(), getLocale(), CWD2));
17341
17721
  return true;
17342
17722
  case "/usage":
17343
- state.pushInfo(await inspectUsage(state.sessionIdRef.current ?? ""));
17723
+ state.pushInfo(await inspectUsage(state.sessionIdRef.current ?? "", getLocale()));
17344
17724
  return true;
17345
17725
  case "/context":
17346
- state.pushInfo(await inspectContext(state.sessionIdRef.current ?? ""));
17726
+ state.pushInfo(await inspectContext(state.sessionIdRef.current ?? "", getLocale()));
17347
17727
  return true;
17348
17728
  case "/permissions":
17349
- state.pushInfo(inspectPermissions());
17729
+ state.pushInfo(inspectPermissions(getLocale()));
17350
17730
  return true;
17351
17731
  case "/mcp":
17352
- state.pushInfo(await inspectMcp());
17732
+ state.pushInfo(await inspectMcp(getLocale()));
17353
17733
  return true;
17354
17734
  case "/hooks":
17355
- state.pushInfo(inspectHooks());
17735
+ state.pushInfo(inspectHooks(getLocale()));
17356
17736
  return true;
17357
17737
  case "/trust": {
17358
17738
  const trusted = await readTrustedDirs();
17359
17739
  if (!arg) {
17360
17740
  if (trusted.length === 0) {
17361
- state.pushInfo("\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF1A\uFF08\u65E0\uFF09\u3002\u9996\u6B21\u8FDB\u5165\u67D0\u76EE\u5F55\u65F6\u4F1A\u8BE2\u95EE\u662F\u5426\u4FE1\u4EFB\u3002");
17741
+ state.pushInfo(S.trustEmpty);
17362
17742
  return true;
17363
17743
  }
17364
17744
  const list = trusted.map((d, i) => ` [${i}] ${d}`).join("\n");
17365
- state.pushInfo(`\u5DF2\u4FE1\u4EFB\u76EE\u5F55\uFF08\u64A4\u9500\u7528 /trust <\u5E8F\u53F7 \u6216 \u8DEF\u5F84>\uFF09\uFF1A
17366
- ${list}
17367
-
17368
- \u64A4\u9500\u540E\u9700\u91CD\u542F CLI \u751F\u6548\uFF08\u9879\u76EE\u7EA7\u914D\u7F6E\u4EC5\u5728\u542F\u52A8\u65F6\u52A0\u8F7D\uFF09\u3002`);
17745
+ state.pushInfo(S.trustList(list));
17369
17746
  return true;
17370
17747
  }
17371
17748
  const idx = Number(arg);
17372
17749
  const target = Number.isInteger(idx) && idx >= 0 && idx < trusted.length ? trusted[idx] : arg;
17373
17750
  const removed = await untrustDir(target);
17374
- state.pushInfo(removed ? `\u5DF2\u64A4\u9500\u4FE1\u4EFB\uFF1A${target}
17375
- \uFF08\u91CD\u542F CLI \u540E\u751F\u6548\u2014\u2014\u9879\u76EE\u7EA7\u914D\u7F6E\u4EC5\u5728\u542F\u52A8\u65F6\u52A0\u8F7D\uFF09` : `\u672A\u627E\u5230\u8BE5\u4FE1\u4EFB\u76EE\u5F55\uFF1A${target}`);
17751
+ state.pushInfo(removed ? S.trustRemoved(target) : S.trustNotFound(target));
17376
17752
  return true;
17377
17753
  }
17378
17754
  case "/debug":
17379
- state.pushInfo(inspectDebug(state.sessionIdRef.current ?? ""));
17755
+ state.pushInfo(inspectDebug(state.sessionIdRef.current ?? "", getLocale()));
17380
17756
  return true;
17381
17757
  case "/archive": {
17382
17758
  const s = state.getArchivedSummary();
17383
- state.pushInfo(s ? `\u{1F4DC} \u6700\u8FD1\u4E00\u6B21\u538B\u7F29\u7684\u5F52\u6863\u6458\u8981\uFF1A
17384
- ${s}` : "\u672C\u4F1A\u8BDD\u542F\u52A8\u4EE5\u6765\u5C1A\u672A\u53D1\u751F\u8FC7\u538B\u7F29\uFF08\u6458\u8981\u4EC5\u4FDD\u7559\u8FD0\u884C\u65F6\u6700\u8FD1\u4E00\u6B21\uFF09\u3002");
17759
+ state.pushInfo(s ? S.archiveSummary(s) : S.archiveNone);
17385
17760
  return true;
17386
17761
  }
17387
17762
  case "/plan": {
17388
17763
  const on = !state.getPlanMode();
17389
17764
  state.setPlanMode(on);
17390
- state.pushInfo(`\u8BA1\u5212\u6A21\u5F0F\uFF1A${on ? "\u5F00\uFF08\u4E0B\u6B21\u63D0\u95EE\u5148\u53EA\u8BFB\u8C03\u7814\u5E76\u4EA7\u51FA\u65B9\u6848\uFF0C\u5BA1\u6279\u540E\u5B9E\u73B0\uFF09" : "\u5173"}`);
17765
+ state.pushInfo(S.planModeSet(on));
17391
17766
  return true;
17392
17767
  }
17393
17768
  case "/budget": {
@@ -17395,27 +17770,27 @@ ${s}` : "\u672C\u4F1A\u8BDD\u542F\u52A8\u4EE5\u6765\u5C1A\u672A\u53D1\u751F\u8FC
17395
17770
  if (!v) {
17396
17771
  const b = state.getBudgetYuan();
17397
17772
  const last = state.getLastRunCostYuan();
17398
- state.pushInfo(`\u{1F4B0} \u9884\u7B97\uFF1A${b != null ? `\xA5${b}/run` : "\u672A\u8BBE\u7F6E\uFF08/budget 0.5 \u8BBE\u7F6E\u6BCF\u4E2A run \u7684\u82B1\u8D39\u4E0A\u9650\uFF09"}${last != null ? ` \xB7 \u4E0A\u4E2A run \u5DF2\u82B1 \xA5${last.toFixed(3)}` : " \xB7 \u5C1A\u65E0\u5B8C\u6210 run"}`);
17773
+ state.pushInfo(S.budgetView(b, last));
17399
17774
  return true;
17400
17775
  }
17401
17776
  if (v === "off" || v === "0") {
17402
17777
  state.setBudgetYuan(void 0);
17403
- state.pushInfo("\u{1F4B0} \u9884\u7B97\u5DF2\u5173\u95ED\uFF08\u4E0D\u9650\u82B1\u8D39\uFF09\u3002");
17778
+ state.pushInfo(S.budgetOff);
17404
17779
  return true;
17405
17780
  }
17406
17781
  const n = Number(v);
17407
17782
  if (!Number.isFinite(n) || n <= 0) {
17408
- state.pushInfo("\u7528\u6CD5\uFF1A/budget 0.5\uFF08\u5143/run\uFF09\xB7 /budget off \xB7 /budget \u67E5\u770B");
17783
+ state.pushInfo(S.budgetUsage);
17409
17784
  return true;
17410
17785
  }
17411
17786
  state.setBudgetYuan(n);
17412
- state.pushInfo(`\u{1F4B0} \u9884\u7B97\u5DF2\u8BBE\u7F6E\uFF1A\u6BCF run \xA5${n}\uFF08\u8D85\u9650\u5148\u63D0\u9192\u6536\u655B\uFF0C\u4ECD\u8D85 120% \u5C06\u622A\u65AD\u672C run\uFF09\u3002`);
17787
+ state.pushInfo(S.budgetSet(n));
17413
17788
  return true;
17414
17789
  }
17415
17790
  case "/auto": {
17416
17791
  const on = !state.getAutoMode();
17417
17792
  state.setAutoMode(on);
17418
- state.pushInfo(`\u81EA\u52A8\u6A21\u5F0F\uFF1A${on ? "\u5F00\uFF08\u5DE5\u4F5C\u533A\u5185\u6587\u4EF6\u7F16\u8F91\u7531\u5206\u7C7B\u5668\u81EA\u52A8\u653E\u884C\uFF0C\u9AD8\u5371\u8F6C\u4EBA\u5DE5\uFF09" : "\u5173"}`);
17793
+ state.pushInfo(S.autoModeSet(on));
17419
17794
  return true;
17420
17795
  }
17421
17796
  case "/model":
@@ -17435,15 +17810,15 @@ ${s}` : "\u672C\u4F1A\u8BDD\u542F\u52A8\u4EE5\u6765\u5C1A\u672A\u53D1\u751F\u8FC
17435
17810
  case "/thinking": {
17436
17811
  const lvl = arg.toLowerCase();
17437
17812
  if (arg && lvl !== "off" && lvl !== "high" && lvl !== "max") {
17438
- state.pushInfo(`\u65E0\u6548\u7B49\u7EA7\uFF1A${arg}\uFF08\u53EF\u9009\uFF1Aoff \u5173\u95ED / high \u5E38\u89C4 / max \u6DF1\u5EA6\uFF09`);
17813
+ state.pushInfo(S.thinkingInvalid(arg));
17439
17814
  return true;
17440
17815
  }
17441
17816
  if (!arg) {
17442
- state.pushInfo(`\u5F53\u524D\u601D\u8003\u7B49\u7EA7\uFF1A${state.getThinkingLevel()}\uFF08off \u5173\u95ED / high \u5E38\u89C4 / max \u6DF1\u5EA6\uFF09`);
17817
+ state.pushInfo(S.thinkingCurrent(state.getThinkingLevel()));
17443
17818
  return true;
17444
17819
  }
17445
17820
  state.setThinkingLevel(lvl);
17446
- state.pushInfo(`\u601D\u8003\u7B49\u7EA7\uFF1A${lvl}${lvl === "off" ? "\uFF08\u5173\u95ED\uFF0C\u7B80\u5355\u4EFB\u52A1\u66F4\u5FEB\u66F4\u7701\uFF09" : lvl === "max" ? "\uFF08\u6DF1\u5EA6\uFF0C\u590D\u6742\u4EFB\u52A1\uFF09" : "\uFF08\u5E38\u89C4\uFF09"}`);
17821
+ state.pushInfo(S.thinkingSet(lvl));
17447
17822
  return true;
17448
17823
  }
17449
17824
  case "/lang": {
@@ -17497,7 +17872,7 @@ ${listText}`);
17497
17872
  };
17498
17873
  const ensureEngine = async () => {
17499
17874
  if (engineReadyFlag) return;
17500
- state.pushInfo("\u23F3 \u5F15\u64CE\u52A0\u8F7D\u4E2D\uFF08MCP/skills/commands\uFF09\u2026");
17875
+ state.pushInfo(S.engineLoading);
17501
17876
  await engineReady;
17502
17877
  };
17503
17878
  const executeSlash = async (fullText) => {
@@ -17513,7 +17888,7 @@ ${listText}`);
17513
17888
  await state.submit(text);
17514
17889
  return;
17515
17890
  }
17516
- state.pushInfo(`\u672A\u77E5\u547D\u4EE4\uFF1A/${name || "(\u7A7A)"}\uFF08\u8F93\u5165 / \u67E5\u770B\u53EF\u7528\u547D\u4EE4\uFF09`);
17891
+ state.pushInfo(S.unknownCmd(name));
17517
17892
  };
17518
17893
  const onSubmit = async () => {
17519
17894
  const v = inputRef.current.trim();
@@ -17793,7 +18168,7 @@ __export(batchRunner_exports, {
17793
18168
  });
17794
18169
  import fs49 from "fs";
17795
18170
  import { spawn as spawn6 } from "child_process";
17796
- var BATCH_EDIT_TOOLS, BATCH_DEFAULT_DENYLIST, ACCEPT_TIMEOUT_MIN, ACCEPT_TAIL_LINES, emit2, ts, STATUS_LABEL, runAcceptCommand, collectSessionCost, fmtCost, fmtReceiptLine, buildReport, runOneTask, parseWaitWindow, nowMinutes, sleep, waitUntilWindow, runBatchQueue;
18171
+ var BATCH_EDIT_TOOLS, BATCH_DEFAULT_DENYLIST, ACCEPT_TIMEOUT_MIN, ACCEPT_TAIL_LINES, emit2, ts, runAcceptCommand, collectSessionCost, buildReport, runOneTask, parseWaitWindow, nowMinutes, sleep, waitUntilWindow, runBatchQueue;
17797
18172
  var init_batchRunner = __esm({
17798
18173
  "src/batchRunner.ts"() {
17799
18174
  "use strict";
@@ -17807,6 +18182,8 @@ var init_batchRunner = __esm({
17807
18182
  init_usageLog();
17808
18183
  init_ticket();
17809
18184
  init_queue();
18185
+ init_batchStrings();
18186
+ init_strings();
17810
18187
  BATCH_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit_file", "create_file", "write_file", "move_file", "notebook_edit"]);
17811
18188
  BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
17812
18189
  ACCEPT_TIMEOUT_MIN = 10;
@@ -17815,14 +18192,8 @@ var init_batchRunner = __esm({
17815
18192
  process.stdout.write(s + "\n");
17816
18193
  };
17817
18194
  ts = () => (/* @__PURE__ */ new Date()).toLocaleTimeString("zh-CN", { hour12: false });
17818
- STATUS_LABEL = {
17819
- passed: "\u2705 \u901A\u8FC7\u9A8C\u6536",
17820
- "accept-failed": "\u274C \u9A8C\u6536\u672A\u8FC7",
17821
- timeout: "\u23F1\uFE0F \u8D85\u65F6\u622A\u65AD",
17822
- error: "\u{1F4A5} agent \u5F02\u5E38",
17823
- rejected: "\u{1F6AB} \u5DE5\u5355\u88AB\u62D2"
17824
- };
17825
18195
  runAcceptCommand = async (cmd, cwd) => {
18196
+ const T = batchT(getLocale());
17826
18197
  const isWin = process.platform === "win32";
17827
18198
  return new Promise((resolve) => {
17828
18199
  let child;
@@ -17835,7 +18206,7 @@ var init_batchRunner = __esm({
17835
18206
  // 剔除 agent 自身凭证(同 run_command 红线)
17836
18207
  });
17837
18208
  } catch (e) {
17838
- resolve({ code: null, output: `\u542F\u52A8\u5931\u8D25\uFF1A${e?.message ?? e}`, timedOut: false });
18209
+ resolve({ code: null, output: T.spawnFail(e?.message ?? e), timedOut: false });
17839
18210
  return;
17840
18211
  }
17841
18212
  const chunks = [];
@@ -17851,15 +18222,15 @@ var init_batchRunner = __esm({
17851
18222
  }, ACCEPT_TIMEOUT_MIN * 6e4);
17852
18223
  child.on("error", (e) => {
17853
18224
  clearTimeout(killer);
17854
- resolve({ code: null, output: `\u6267\u884C\u5931\u8D25\uFF1A${e?.message ?? e}`, timedOut });
18225
+ resolve({ code: null, output: T.execFail(e?.message ?? e), timedOut });
17855
18226
  });
17856
18227
  child.on("close", (code) => {
17857
18228
  clearTimeout(killer);
17858
18229
  const text = decodeProcessOutput(Buffer.concat(chunks)).trim();
17859
18230
  const lines = text.split("\n");
17860
- const tail = lines.length > ACCEPT_TAIL_LINES ? `\uFF08\u524D ${lines.length - ACCEPT_TAIL_LINES} \u884C\u5DF2\u7701\u7565\uFF09
18231
+ const tail = lines.length > ACCEPT_TAIL_LINES ? `${T.tailOmitted(lines.length - ACCEPT_TAIL_LINES)}
17861
18232
  ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
17862
- resolve({ code, output: tail || "\uFF08\u65E0\u8F93\u51FA\uFF09", timedOut });
18233
+ resolve({ code, output: tail || T.noOutput, timedOut });
17863
18234
  });
17864
18235
  });
17865
18236
  };
@@ -17880,19 +18251,10 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
17880
18251
  if (c.costYuan === null) c.partialPrice = false;
17881
18252
  return c;
17882
18253
  };
17883
- fmtCost = (c) => {
17884
- const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "\uFF08\u65E0\u4EF7\u6A21\u578B\uFF0C\u4E0D\u8BA1\u94B1\uFF09";
17885
- const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
17886
- const mins = Math.round(c.durationMs / 6e3) / 10;
17887
- return `${c.rounds} \u8F6E \xB7 ${money} \xB7 \u7F13\u5B58\u547D\u4E2D ${hit}% \xB7 ${mins}min${c.partialPrice ? "\uFF08\u90E8\u5206\u8BB0\u5F55\u65E0\u4EF7\uFF0C\u94B1\u4E3A\u90E8\u5206\u548C\uFF09" : ""}${c.subRuns > 0 ? ` \xB7 \u5B50 run ${c.subRuns} \u4E2A\u5DF2\u8BA1\u5165` : ""}`;
17888
- };
17889
- fmtReceiptLine = (receipt) => {
17890
- if (!receipt) return "\uFF08\u65E0\u7ED3\u7B97\u5355\u2014\u2014run \u672A\u5230\u6536\u5C3E\u5373\u88AB\u622A\u65AD\uFF0C\u82B1\u8D39\u4EE5\u4E0A\u65B9 usageLog \u805A\u5408\u4E3A\u51C6\uFF09";
17891
- return `${receipt.rounds} \u8F6E \xB7 ${receipt.costYuan !== null ? `\xA5${receipt.costYuan.toFixed(4)}` : "\u65E0\u4EF7"} \xB7 \u7F13\u5B58\u547D\u4E2D ${Math.round(receipt.cacheHitRate * 100)}% \xB7 ${(receipt.durationMs / 1e3).toFixed(0)}s`;
17892
- };
17893
18254
  buildReport = (args) => {
17894
18255
  const { id, ticket, status, budgetTruncated, cost, receipt, finalText, accept, deniedTools, diff, wt } = args;
17895
- const budgetNote = budgetTruncated ? "\uFF08\u26A0\uFE0F \u9884\u7B97\u622A\u65AD\u540E\u6536\u5C3E\uFF09" : "";
18256
+ const T = batchT(getLocale());
18257
+ const budgetNote = budgetTruncated ? T.budgetTruncNote : "";
17896
18258
  return `---
17897
18259
  id: ${id}
17898
18260
  title: ${ticket.title}
@@ -17901,43 +18263,42 @@ cost-yuan: ${cost.costYuan !== null ? cost.costYuan.toFixed(4) : "null"}
17901
18263
  rounds: ${cost.rounds}
17902
18264
  budget-truncated: ${budgetTruncated}
17903
18265
  session: batch-${id}
17904
- worktree: ${wt?.path ?? "\uFF08\u672A\u521B\u5EFA\uFF09"}
17905
- branch: ${wt?.branch ?? "\uFF08\u672A\u521B\u5EFA\uFF09"}
18266
+ worktree: ${wt?.path ?? T.notCreated}
18267
+ branch: ${wt?.branch ?? T.notCreated}
17906
18268
  ---
17907
18269
 
17908
- # \u6302\u673A\u4EFB\u52A1\u62A5\u544A\uFF1A${ticket.title}
18270
+ ${T.reportTitle(ticket.title)}
17909
18271
 
17910
- - \u72B6\u6001\uFF1A${STATUS_LABEL[status]}${budgetNote}
17911
- - \u7ED3\u7B97\u5355\uFF1A${fmtCost(cost)}
17912
- - \u672C\u8F6E receipt\uFF1A${fmtReceiptLine(receipt)}
17913
- ${args.errorMessage ? `- \u5F02\u5E38\uFF1A${args.errorMessage}
17914
- ` : ""}
17915
- ## \u4EFB\u52A1
18272
+ ${T.statusEntry(`${T.statusLabel(status)}${budgetNote}`)}
18273
+ ${T.costEntry(T.costSummary(cost))}
18274
+ ${T.receiptEntry(receipt ? T.receiptLine(receipt) : T.noReceipt)}
18275
+ ${args.errorMessage ? T.errorEntry(args.errorMessage) : ""}
18276
+ ${T.taskH}
17916
18277
 
17917
18278
  ${ticket.body}
17918
18279
 
17919
- ## \u6A21\u578B\u6536\u5C3E\u6C47\u62A5
18280
+ ${T.finalH}
17920
18281
 
17921
- ${finalText.trim() || "\uFF08\u65E0\u8F93\u51FA\u2014\u2014run \u5728\u4EA7\u51FA\u6B63\u6587\u524D\u88AB\u622A\u65AD/\u5F02\u5E38\uFF09"}
18282
+ ${finalText.trim() || T.noFinal}
17922
18283
 
17923
- ## \u9A8C\u6536
18284
+ ${T.acceptH}
17924
18285
  ${accept ? `
17925
- \u547D\u4EE4\uFF1A\`${accept.cmd}\` \u2192 exit ${accept.code}${accept.timedOut ? "\uFF08\u23F1\uFE0F \u9A8C\u6536\u8D85\u65F6\u88AB\u6740\uFF09" : ""}
18286
+ ${T.acceptCmdLine(accept.cmd, accept.code)}${accept.timedOut ? T.acceptTimeoutSuffix : ""}
17926
18287
 
17927
- \u8F93\u51FA\u5C3E\u90E8\uFF1A
18288
+ ${T.acceptTail}
17928
18289
 
17929
18290
  \`\`\`
17930
18291
  ${accept.output}
17931
18292
  \`\`\`
17932
- ` : "\n\uFF08\u672A\u6267\u884C\u9A8C\u6536\u2014\u2014\u6539\u52A8\u4E0D\u5B8C\u6574\uFF0C\u8DD1\u4E86\u4E5F\u662F\u566A\u58F0\uFF09"}
18293
+ ` : T.noAccept}
17933
18294
 
17934
- ## \u88AB\u62D2\u5DE5\u5177\u8C03\u7528\uFF08fail-closed \u8BB0\u5F55\uFF09
18295
+ ${T.deniedH}
17935
18296
 
17936
- ${deniedTools.length === 0 ? "\u65E0\u3002" : deniedTools.map((d) => `- **${d.tool}** \u2014 ${d.detail}`).join("\n")}
18297
+ ${deniedTools.length === 0 ? T.deniedNone : deniedTools.map((d) => `- **${d.tool}** \u2014 ${d.detail}`).join("\n")}
17937
18298
 
17938
- ## Diff \u6458\u8981\uFF08worktree \u76F8\u5BF9\u57FA\u5EA7\uFF0C\u672A\u63D0\u4EA4\uFF0C\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09
18299
+ ${T.diffH}
17939
18300
 
17940
- \u6539\u52A8\u5728\u72EC\u7ACB worktree\uFF08\u5206\u652F \`${wt?.branch ?? "?"}\`\uFF0C\u8DEF\u5F84 ${wt?.path ?? "?"}\uFF09\u3002\u5904\u7F6E\uFF1A\u68C0\u67E5\u6EE1\u610F \u2192 \u5728 worktree \u5185 commit \u5E76\u5408\u56DE\u4E3B\u4ED3\uFF1B\u4E22\u5F03 \u2192 \u5220\u9664\u8BE5 worktree \u76EE\u5F55 + \`git worktree prune\` + \`git branch -D <\u5206\u652F>\`\u3002
18301
+ ${T.diffNote(wt?.branch ?? "?", wt?.path ?? "?")}
17941
18302
 
17942
18303
  \`\`\`\`\`diff
17943
18304
  ${diff}
@@ -17945,8 +18306,9 @@ ${diff}
17945
18306
  `;
17946
18307
  };
17947
18308
  runOneTask = async (task) => {
18309
+ const T = batchT(getLocale());
17948
18310
  const source = await fs49.promises.readFile(task.path, "utf-8");
17949
- const parsed = parseBatchTicket(source);
18311
+ const parsed = parseBatchTicket(source, getLocale());
17950
18312
  if (!parsed.ok) {
17951
18313
  const cost2 = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: 0, partialPrice: false, durationMs: 0 };
17952
18314
  await finishTicket(task.id, `---
@@ -17955,17 +18317,17 @@ title: ${task.id}
17955
18317
  status: rejected
17956
18318
  ---
17957
18319
 
17958
- # \u6302\u673A\u4EFB\u52A1\u88AB\u62D2
18320
+ ${T.rejectedTitle}
17959
18321
 
17960
18322
  ${parsed.error}`);
17961
- emit2(`\u{1F6AB} [${task.id}] \u5DE5\u5355\u88AB\u62D2\uFF1A${parsed.error}`);
18323
+ emit2(T.ticketRejected(task.id, parsed.error));
17962
18324
  return { status: "rejected", cost: cost2, budgetTruncated: false };
17963
18325
  }
17964
18326
  const t = parsed.ticket;
17965
18327
  const sessionId = `batch-${task.id}`;
17966
18328
  const timeoutMin = t.timeoutMin ?? 30;
17967
18329
  emit2(`\u25B6 [${task.id}] ${t.title}`);
17968
- emit2(` \u9884\u7B97 ${t.budgetYuan !== void 0 ? `\xA5${t.budgetYuan}` : "\u4E0D\u9650"} \xB7 \u8D85\u65F6 ${timeoutMin}min \xB7 \u9A8C\u6536 \`${t.accept}\``);
18330
+ emit2(T.taskMeta(t.budgetYuan, timeoutMin, t.accept));
17969
18331
  const unlocked = new Set(t.tools ?? []);
17970
18332
  const denySchemas = new Set([...BATCH_DEFAULT_DENYLIST].filter((n) => !unlocked.has(n)));
17971
18333
  const toolSchemas = agentTools.filter((x) => !denySchemas.has(x?.function?.name));
@@ -17975,7 +18337,7 @@ ${parsed.error}`);
17975
18337
  try {
17976
18338
  wt = await createWorktree(sessionId, task.id);
17977
18339
  } catch (e) {
17978
- const msg = `\u521B\u5EFA worktree \u5931\u8D25\uFF08\u9700\u8981\u4E3B\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\uFF09\uFF1A${e?.message ?? e}`;
18340
+ const msg = T.wtCreateFail(e?.message ?? e);
17979
18341
  const cost2 = await collectSessionCost(sessionId);
17980
18342
  await finishTicket(task.id, buildReport({
17981
18343
  id: task.id,
@@ -17987,7 +18349,7 @@ ${parsed.error}`);
17987
18349
  finalText: "",
17988
18350
  accept: null,
17989
18351
  deniedTools: [],
17990
- diff: "\uFF08\u65E0\uFF09",
18352
+ diff: T.diffNone,
17991
18353
  wt: null,
17992
18354
  errorMessage: msg
17993
18355
  }));
@@ -17996,7 +18358,7 @@ ${parsed.error}`);
17996
18358
  }
17997
18359
  setSessionWorktree(sessionId, wt);
17998
18360
  const ac = new AbortController();
17999
- const timer = setTimeout(() => ac.abort(new Error(`\u4EFB\u52A1\u8D85\u65F6\uFF08${timeoutMin} \u5206\u949F\uFF09`)), timeoutMin * 6e4);
18361
+ const timer = setTimeout(() => ac.abort(new Error(T.timeoutAbort(timeoutMin))), timeoutMin * 6e4);
18000
18362
  let receipt;
18001
18363
  let finalText = "";
18002
18364
  let agentError;
@@ -18009,7 +18371,7 @@ ${parsed.error}`);
18009
18371
  if (evt?.type === "text.delta") finalText += evt.text;
18010
18372
  else if (evt?.type === "text.reset") finalText = "";
18011
18373
  else if (evt?.type === "final") finalText = evt.text || finalText;
18012
- else if (evt?.type === "round.start") emit2(` [${ts()}] \u8F6E ${evt.round}`);
18374
+ else if (evt?.type === "round.start") emit2(T.roundLine(ts(), evt.round));
18013
18375
  },
18014
18376
  ac.signal,
18015
18377
  {
@@ -18037,15 +18399,15 @@ ${parsed.error}`);
18037
18399
  let accept = null;
18038
18400
  if (agentError) {
18039
18401
  status = "error";
18040
- emit2(`\u{1F4A5} [${task.id}] agent \u5F02\u5E38\uFF1A${agentError}`);
18402
+ emit2(T.agentErrorLine(task.id, agentError));
18041
18403
  } else if (timedOut) {
18042
18404
  status = "timeout";
18043
- emit2(`\u23F1\uFE0F [${task.id}] \u8D85\u65F6\uFF08${timeoutMin}min\uFF09\uFF0C\u622A\u65AD\u6536\u5C3E\uFF08\u9A8C\u6536\u4E0D\u8DD1\uFF09`);
18405
+ emit2(T.timeoutLine(task.id, timeoutMin));
18044
18406
  } else {
18045
18407
  const r = await runAcceptCommand(t.accept, wt.path);
18046
18408
  accept = { cmd: t.accept, code: r.code, output: r.output, timedOut: r.timedOut };
18047
18409
  status = r.code === 0 && !r.timedOut ? "passed" : "accept-failed";
18048
- emit2(`${status === "passed" ? "\u2705" : "\u274C"} [${task.id}] \u9A8C\u6536 exit ${r.code}${r.timedOut ? "\uFF08\u8D85\u65F6\u88AB\u6740\uFF09" : ""}`);
18410
+ emit2(T.acceptLine(status === "passed", task.id, r.code) + (r.timedOut ? T.acceptKilledSuffix : ""));
18049
18411
  }
18050
18412
  await killBackgroundTasksUnder(wt.path).catch(() => {
18051
18413
  });
@@ -18065,8 +18427,8 @@ ${parsed.error}`);
18065
18427
  wt,
18066
18428
  errorMessage: agentError
18067
18429
  }));
18068
- emit2(` ${STATUS_LABEL[status]} \xB7 ${fmtCost(cost)}`);
18069
- emit2(` \u4EA7\u7269\uFF1A${wt.path}\uFF08\u5206\u652F ${wt.branch}\uFF0C\u4FDD\u7559\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09`);
18430
+ emit2(` ${T.statusLabel(status)} \xB7 ${T.costSummary(cost)}`);
18431
+ emit2(T.artifactLine(wt.path, wt.branch));
18070
18432
  return { status, cost, budgetTruncated };
18071
18433
  };
18072
18434
  parseWaitWindow = (s) => {
@@ -18084,14 +18446,15 @@ ${parsed.error}`);
18084
18446
  };
18085
18447
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
18086
18448
  waitUntilWindow = async (w) => {
18449
+ const T = batchT(getLocale());
18087
18450
  const now = nowMinutes();
18088
18451
  let waitMin = 0;
18089
18452
  if (now >= w.startMin && now < w.endMin) {
18090
- emit2(`\u23F0 \u5F53\u524D ${Math.floor(now / 60)}:${String(now % 60).padStart(2, "0")} \u5DF2\u5728\u7A97\u53E3\u5185\uFF0C\u7ACB\u5373\u5F00\u8DD1\u3002`);
18453
+ emit2(T.inWindowNow(`${Math.floor(now / 60)}:${String(now % 60).padStart(2, "0")}`));
18091
18454
  return;
18092
18455
  }
18093
18456
  waitMin = now < w.startMin ? w.startMin - now : 24 * 60 - now + w.startMin;
18094
- emit2(`\u23F0 --wait-window\uFF1A${Math.floor(waitMin / 60)} \u5C0F\u65F6 ${waitMin % 60} \u5206\u540E\uFF08\u7A97\u53E3\u8D77\u70B9\uFF09\u5F00\u8DD1\u3002\u6302\u673A\u7B49\u5F85\u4E2D\uFF0CCtrl+C \u53EF\u53D6\u6D88\u3002`);
18457
+ emit2(T.waitWindowWait(Math.floor(waitMin / 60), waitMin % 60));
18095
18458
  while (waitMin > 0) {
18096
18459
  const chunkMin = Math.min(waitMin, 30);
18097
18460
  await sleep(chunkMin * 6e4);
@@ -18099,14 +18462,15 @@ ${parsed.error}`);
18099
18462
  }
18100
18463
  };
18101
18464
  runBatchQueue = async (opts) => {
18465
+ const T = batchT(getLocale());
18102
18466
  const window = parseWaitWindow(opts?.waitWindow);
18103
18467
  if (opts?.waitWindow && !window) {
18104
- emit2(`\u274C --wait-window \u683C\u5F0F\u975E\u6CD5\uFF1A"${opts.waitWindow}"\uFF08\u5E94\u4E3A HH:MM-HH:MM\uFF0C\u5982 23:30-06:30\uFF09`);
18468
+ emit2(T.waitWindowInvalid(opts.waitWindow));
18105
18469
  process.exitCode = 2;
18106
18470
  return;
18107
18471
  }
18108
18472
  if (window) await waitUntilWindow(window);
18109
- const lock = await acquireRunLock();
18473
+ const lock = await acquireRunLock(getLocale());
18110
18474
  if (!lock.ok) {
18111
18475
  emit2(`\u{1F6AB} ${lock.error}`);
18112
18476
  process.exitCode = 1;
@@ -18125,7 +18489,7 @@ ${parsed.error}`);
18125
18489
  total++;
18126
18490
  const r = await runOneTask(task);
18127
18491
  if (r.status === "passed") passed++;
18128
- else failedIds.push(`[${task.id}] ${STATUS_LABEL[r.status]}`);
18492
+ else failedIds.push(`[${task.id}] ${T.statusLabel(r.status)}`);
18129
18493
  if (r.cost.costYuan !== null) {
18130
18494
  costSum += r.cost.costYuan;
18131
18495
  costTasks++;
@@ -18135,16 +18499,16 @@ ${parsed.error}`);
18135
18499
  await releaseRunLock();
18136
18500
  }
18137
18501
  if (total === 0) {
18138
- emit2("\u961F\u5217\u7A7A\uFF1Apending/ \u65E0\u4EFB\u52A1\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>");
18502
+ emit2(T.runnerQueueEmpty);
18139
18503
  return;
18140
18504
  }
18141
18505
  emit2("");
18142
18506
  emit2("\u2501".repeat(56));
18143
- emit2(`\u{1F319} \u6302\u673A\u7ED3\u675F\uFF1A${total} \u4EFB\u52A1 \xB7 ${passed} \u8FC7 \xB7 ${total - passed} \u672A\u8FC7\uFF08\u8017\u65F6 ${Math.round((Date.now() - startedAt) / 6e3) / 10}min\uFF09`);
18144
- emit2(`\u{1F4B0} \u603B\u82B1\u8D39\uFF1A${costTasks > 0 ? `\xA5${costSum.toFixed(4)}\uFF08${costTasks} \u4E2A\u4EFB\u52A1\u8BA1\u4EF7\uFF09` : "\u5168\u90E8\u65E0\u4EF7\u6A21\u578B\uFF0C\u672A\u8BA1\u94B1"}`);
18145
- if (failedIds.length > 0) emit2(`\u26A0\uFE0F \u672A\u8FC7\u6E05\u5355\uFF1A
18507
+ emit2(T.summaryDone(total, passed, `${Math.round((Date.now() - startedAt) / 6e3) / 10}`));
18508
+ emit2(costTasks > 0 ? T.summaryCost(costSum.toFixed(4), costTasks) : T.summaryCostFree);
18509
+ if (failedIds.length > 0) emit2(`${T.failedHead}
18146
18510
  ${failedIds.join("\n ")}`);
18147
- emit2("\u62A5\u544A\u4E0E\u6539\u52A8\uFF1A.deepseeker-code/batch/done/\uFF08worktree \u5747\u4FDD\u7559\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09");
18511
+ emit2(T.artifactsDir);
18148
18512
  };
18149
18513
  }
18150
18514
  });