deepseeker-code 1.0.55 → 1.0.57

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 +631 -206
  2. package/package.json +4 -3
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,35 @@ 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") };
916
+ const setup = (fm["setup"] ?? "").trim() || void 0;
839
917
  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)";
842
- return { ok: true, ticket: { title, accept, budgetYuan, timeoutMin, tools: tools && tools.length > 0 ? tools : void 0, body } };
918
+ if (!body) return { ok: false, error: ticketMsg(locale2, "emptyBody") };
919
+ const title = (fm["title"] ?? "").trim() || body.split(/\r?\n/).find((l) => l.trim())?.slice(0, 40) || ticketMsg(locale2, "untitled");
920
+ return { ok: true, ticket: { title, accept, setup, budgetYuan, timeoutMin, tools: tools && tools.length > 0 ? tools : void 0, body } };
843
921
  };
844
- readBatchTicket = async (filePath) => {
922
+ readBatchTicket = async (filePath, locale2 = "zh") => {
845
923
  let source;
846
924
  try {
847
925
  source = await fs4.readFile(filePath, "utf-8");
848
926
  } catch (e) {
849
- return { ok: false, error: `\u8BFB\u5DE5\u5355\u5931\u8D25\uFF1A${e?.message ?? e}` };
927
+ return { ok: false, error: ticketMsg(locale2, "readFail")(e?.message ?? e) };
850
928
  }
851
- return parseBatchTicket(source);
929
+ return parseBatchTicket(source, locale2);
852
930
  };
853
931
  }
854
932
  });
@@ -856,11 +934,21 @@ var init_ticket = __esm({
856
934
  // ../core/src/batch/queue.ts
857
935
  import fs5 from "fs/promises";
858
936
  import path6 from "path";
859
- var batchQueueRoot, stateDir, lockPath, listState, snapshotQueue, reportPath, enqueueTicketFile, takeNextTicket, requeueStaleRunning, finishTicket, cleanDone, pidAlive, acquireRunLock, releaseRunLock;
937
+ var LOCK_MSG, batchQueueRoot, stateDir, lockPath, listState, snapshotQueue, reportPath, enqueueTicketFile, takeNextTicket, requeueStaleRunning, finishTicket, cleanDone, pidAlive, acquireRunLock, releaseRunLock;
860
938
  var init_queue = __esm({
861
939
  "../core/src/batch/queue.ts"() {
862
940
  "use strict";
863
941
  init_ticket();
942
+ LOCK_MSG = {
943
+ zh: {
944
+ createFail: (e) => `\u9501\u521B\u5EFA\u5931\u8D25\uFF1A${e}`,
945
+ 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`
946
+ },
947
+ en: {
948
+ createFail: (e) => `Failed to create the lock file: ${e}`,
949
+ held: (pid) => `Another batch runner is already running (pid ${pid}). Batch mode is serial \u2014 don't run two runners at once.`
950
+ }
951
+ };
864
952
  batchQueueRoot = () => process.env.DEEPSEEKER_CODE_BATCH_DIR || path6.join(process.cwd(), ".deepseeker-code", "batch");
865
953
  stateDir = (s) => path6.join(batchQueueRoot(), s);
866
954
  lockPath = () => path6.join(batchQueueRoot(), ".lock");
@@ -877,8 +965,8 @@ var init_queue = __esm({
877
965
  done: (await listState("done")).map((f) => f.replace(/\.md$/, ""))
878
966
  });
879
967
  reportPath = (id) => path6.join(stateDir("done"), `${id}.report.md`);
880
- enqueueTicketFile = async (srcPath) => {
881
- const parsed = await readBatchTicket(srcPath);
968
+ enqueueTicketFile = async (srcPath, locale2 = "zh") => {
969
+ const parsed = await readBatchTicket(srcPath, locale2);
882
970
  if (!parsed.ok) return parsed;
883
971
  const source = await fs5.readFile(srcPath, "utf-8");
884
972
  const id = generateBatchId();
@@ -935,18 +1023,18 @@ var init_queue = __esm({
935
1023
  return e?.code === "EPERM";
936
1024
  }
937
1025
  };
938
- acquireRunLock = async () => {
1026
+ acquireRunLock = async (locale2 = "zh") => {
939
1027
  await fs5.mkdir(batchQueueRoot(), { recursive: true });
940
1028
  const payload = `${process.pid} ${(/* @__PURE__ */ new Date()).toISOString()}`;
941
1029
  try {
942
1030
  await fs5.writeFile(lockPath(), payload, { flag: "wx" });
943
1031
  return { ok: true };
944
1032
  } catch (e) {
945
- if (e?.code !== "EEXIST") return { ok: false, error: `\u9501\u521B\u5EFA\u5931\u8D25\uFF1A${e?.message ?? e}` };
1033
+ if (e?.code !== "EEXIST") return { ok: false, error: LOCK_MSG[locale2].createFail(e?.message ?? e) };
946
1034
  const raw = await fs5.readFile(lockPath(), "utf-8").catch(() => "");
947
1035
  const ownerPid = parseInt(raw.split(" ")[0], 10);
948
1036
  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` };
1037
+ return { ok: false, error: LOCK_MSG[locale2].held(ownerPid) };
950
1038
  }
951
1039
  await fs5.writeFile(lockPath(), payload).catch(() => {
952
1040
  });
@@ -963,6 +1051,219 @@ var init_queue = __esm({
963
1051
  }
964
1052
  });
965
1053
 
1054
+ // src/batchStrings.ts
1055
+ var ZH, EN, DICT, batchT;
1056
+ var init_batchStrings = __esm({
1057
+ "src/batchStrings.ts"() {
1058
+ "use strict";
1059
+ ZH = {
1060
+ // —— batchAdmin(add / list / clean / help) ——
1061
+ usage: `deepseeker-code batch \u2014\u2014 \u6302\u673A\u6279\u5904\u7406\uFF08\u6210\u672C\u4FE1\u4EFB\u94FE\u7B2C\u56DB\u73AF\uFF09
1062
+
1063
+ \u7528\u6CD5\uFF1Adeepseeker-code batch <\u5B50\u547D\u4EE4>
1064
+ add <\u5DE5\u5355.md> [...] \u6295\u9012\u4EFB\u52A1\uFF08\u53EF\u4E00\u6B21\u591A\u4E2A\u6587\u4EF6\uFF09
1065
+ list \u67E5\u770B pending / running / done \u4E09\u6001\u961F\u5217
1066
+ 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
1067
+ clean \u6E05\u7A7A done/\uFF08\u5DF2\u8BFB\u8FC7\u7684\u62A5\u544A\u4E0E\u5DE5\u5355\u5B58\u6863\uFF09
1068
+
1069
+ \u5DE5\u5355\u683C\u5F0F\uFF08Markdown\uFF0Cfrontmatter \u7684 accept \u5FC5\u586B\u2014\u2014\u7F3A\u9A8C\u6536\u62D2\u7EDD\u5165\u961F\uFF09\uFF1A
1070
+ ---
1071
+ title: \u4FEE\u590D\u65E5\u671F\u89E3\u6790 bug
1072
+ accept: npm test
1073
+ setup: pnpm install # \u53EF\u9009\uFF1Aagent \u524D\u5728 worktree \u5185\u5907\u73AF\u5883\uFF08\u5E72\u51C0\u68C0\u51FA\u65E0 node_modules\uFF0C\u9A8C\u6536\u4F9D\u8D56\u73AF\u5883\u65F6\u5148\u88C5\uFF09
1074
+ budget-yuan: 0.5 # \u53EF\u9009\uFF1A\u672C\u4EFB\u52A1\u82B1\u8D39\u4E0A\u9650\uFF08\u5143\uFF09
1075
+ timeout-min: 30 # \u53EF\u9009\uFF1Aagent \u9636\u6BB5\u8D85\u65F6\uFF08\u5206\u949F\uFF0C\u7F3A\u7701 30\uFF09
1076
+ tools: run_command # \u53EF\u9009\uFF1A\u663E\u5F0F\u89E3\u9501\u9AD8\u5371\u5DE5\u5177\uFF08\u7F3A\u7701\u7981 run_command/delete_path\uFF09
1077
+ ---
1078
+ \u4EFB\u52A1\u63CF\u8FF0\u6B63\u6587\uFF08Markdown\uFF09\u2026
1079
+
1080
+ \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
1081
+ \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`,
1082
+ 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",
1083
+ readFileFail: (e) => `\u8BFB\u6587\u4EF6\u5931\u8D25\uFF1A${e}`,
1084
+ enqueued: (id, title) => `\u{1F4E5} \u5DF2\u5165\u961F [${id}] ${title}`,
1085
+ addRejected: (name, err) => `\u{1F6AB} \u62D2\u7EDD ${name}\uFF1A${err}`,
1086
+ nextStepRun: "\n\u8DD1\u6279\uFF1Adeepseeker-code batch run",
1087
+ adminQueueEmpty: "\u961F\u5217\u7A7A\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>\uFF08\u683C\u5F0F\u89C1 batch help\uFF09",
1088
+ listRunningHead: "\u25B6 \u8FD0\u884C\u4E2D\uFF1A",
1089
+ listPendingHead: (n) => `\u23F3 \u5F85\u8DD1 ${n}\uFF1A`,
1090
+ listDoneHead: (n) => `\u2714 \u5DF2\u5B8C\u6210 ${n}\uFF1A`,
1091
+ cleaned: (n) => `\u{1F9F9} \u5DF2\u6E05\u7A7A done/\uFF08${n} \u4E2A\u4EFB\u52A1\u5B58\u6863\uFF09\u3002pending/running \u4E0D\u53D7\u5F71\u54CD\u3002`,
1092
+ reportMissing: "\uFF08\u62A5\u544A\u7F3A\u5931\uFF09",
1093
+ // —— batchRunner 控制台进度 ——
1094
+ ticketRejected: (id, err) => `\u{1F6AB} [${id}] \u5DE5\u5355\u88AB\u62D2\uFF1A${err}`,
1095
+ taskMeta: (budget, timeoutMin, accept) => ` \u9884\u7B97 ${budget !== void 0 ? `\xA5${budget}` : "\u4E0D\u9650"} \xB7 \u8D85\u65F6 ${timeoutMin}min \xB7 \u9A8C\u6536 \`${accept}\``,
1096
+ setupLine: (cmd) => `\u{1F6E0}\uFE0F setup\uFF1A\`${cmd}\`\uFF08worktree \u5185\u5907\u73AF\u5883\uFF09`,
1097
+ setupFail: (cmd, code) => `\u73AF\u5883\u51C6\u5907\u5931\u8D25\uFF08setup \`${cmd}\` exit ${code ?? "null"}\uFF09\u2014\u2014\u8DF3\u8FC7 agent \u9636\u6BB5\uFF0C\u4EFB\u52A1\u8BB0\u5931\u8D25`,
1098
+ roundLine: (ts2, round) => ` [${ts2}] \u8F6E ${round}`,
1099
+ wtCreateFail: (e) => `\u521B\u5EFA worktree \u5931\u8D25\uFF08\u9700\u8981\u4E3B\u5DE5\u4F5C\u533A\u662F git \u4ED3\u5E93\uFF09\uFF1A${e}`,
1100
+ agentErrorLine: (id, err) => `\u{1F4A5} [${id}] agent \u5F02\u5E38\uFF1A${err}`,
1101
+ timeoutLine: (id, min) => `\u23F1\uFE0F [${id}] \u8D85\u65F6\uFF08${min}min\uFF09\uFF0C\u622A\u65AD\u6536\u5C3E\uFF08\u9A8C\u6536\u4E0D\u8DD1\uFF09`,
1102
+ acceptLine: (passed, id, code) => `${passed ? "\u2705" : "\u274C"} [${id}] \u9A8C\u6536 exit ${code}`,
1103
+ acceptKilledSuffix: "\uFF08\u8D85\u65F6\u88AB\u6740\uFF09",
1104
+ timeoutAbort: (min) => `\u4EFB\u52A1\u8D85\u65F6\uFF08${min} \u5206\u949F\uFF09`,
1105
+ artifactLine: (p, branch) => ` \u4EA7\u7269\uFF1A${p}\uFF08\u5206\u652F ${branch}\uFF0C\u4FDD\u7559\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09`,
1106
+ inWindowNow: (hhmm) => `\u23F0 \u5F53\u524D ${hhmm} \u5DF2\u5728\u7A97\u53E3\u5185\uFF0C\u7ACB\u5373\u5F00\u8DD1\u3002`,
1107
+ 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`,
1108
+ 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`,
1109
+ runnerQueueEmpty: "\u961F\u5217\u7A7A\uFF1Apending/ \u65E0\u4EFB\u52A1\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>",
1110
+ 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`,
1111
+ summaryCost: (sum, tasks) => `\u{1F4B0} \u603B\u82B1\u8D39\uFF1A\xA5${sum}\uFF08${tasks} \u4E2A\u4EFB\u52A1\u8BA1\u4EF7\uFF09`,
1112
+ summaryCostFree: "\u{1F4B0} \u603B\u82B1\u8D39\uFF1A\u5168\u90E8\u65E0\u4EF7\u6A21\u578B\uFF0C\u672A\u8BA1\u94B1",
1113
+ failedHead: "\u26A0\uFE0F \u672A\u8FC7\u6E05\u5355\uFF1A",
1114
+ artifactsDir: "\u62A5\u544A\u4E0E\u6539\u52A8\uFF1A.deepseeker-code/batch/done/\uFF08worktree \u5747\u4FDD\u7559\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09",
1115
+ // —— 状态标签(控制台与报告共用) ——
1116
+ statusLabel: (s) => ({
1117
+ passed: "\u2705 \u901A\u8FC7\u9A8C\u6536",
1118
+ "accept-failed": "\u274C \u9A8C\u6536\u672A\u8FC7",
1119
+ timeout: "\u23F1\uFE0F \u8D85\u65F6\u622A\u65AD",
1120
+ error: "\u{1F4A5} agent \u5F02\u5E38",
1121
+ "setup-failed": "\u{1F6E0}\uFE0F \u73AF\u5883\u51C6\u5907\u5931\u8D25",
1122
+ rejected: "\u{1F6AB} \u5DE5\u5355\u88AB\u62D2"
1123
+ })[s],
1124
+ // —— 验收命令执行(输出嵌入报告) ——
1125
+ spawnFail: (e) => `\u542F\u52A8\u5931\u8D25\uFF1A${e}`,
1126
+ execFail: (e) => `\u6267\u884C\u5931\u8D25\uFF1A${e}`,
1127
+ tailOmitted: (n) => `\uFF08\u524D ${n} \u884C\u5DF2\u7701\u7565\uFF09`,
1128
+ noOutput: "\uFF08\u65E0\u8F93\u51FA\uFF09",
1129
+ // —— 结算单聚合与 receipt 行(报告内嵌) ——
1130
+ costFree: "\uFF08\u65E0\u4EF7\u6A21\u578B\uFF0C\u4E0D\u8BA1\u94B1\uFF09",
1131
+ costSummary: (c) => {
1132
+ const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "\uFF08\u65E0\u4EF7\u6A21\u578B\uFF0C\u4E0D\u8BA1\u94B1\uFF09";
1133
+ const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
1134
+ const mins = Math.round(c.durationMs / 6e3) / 10;
1135
+ 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` : ""}`;
1136
+ },
1137
+ 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",
1138
+ 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`,
1139
+ // —— 报告骨架 ——
1140
+ rejectedTitle: "# \u6302\u673A\u4EFB\u52A1\u88AB\u62D2",
1141
+ reportTitle: (title) => `# \u6302\u673A\u4EFB\u52A1\u62A5\u544A\uFF1A${title}`,
1142
+ statusEntry: (v) => `- \u72B6\u6001\uFF1A${v}`,
1143
+ budgetTruncNote: "\uFF08\u26A0\uFE0F \u9884\u7B97\u622A\u65AD\u540E\u6536\u5C3E\uFF09",
1144
+ costEntry: (v) => `- \u7ED3\u7B97\u5355\uFF1A${v}`,
1145
+ receiptEntry: (v) => `- \u672C\u8F6E receipt\uFF1A${v}`,
1146
+ errorEntry: (v) => `- \u5F02\u5E38\uFF1A${v}
1147
+ `,
1148
+ notCreated: "\uFF08\u672A\u521B\u5EFA\uFF09",
1149
+ taskH: "## \u4EFB\u52A1",
1150
+ finalH: "## \u6A21\u578B\u6536\u5C3E\u6C47\u62A5",
1151
+ noFinal: "\uFF08\u65E0\u8F93\u51FA\u2014\u2014run \u5728\u4EA7\u51FA\u6B63\u6587\u524D\u88AB\u622A\u65AD/\u5F02\u5E38\uFF09",
1152
+ acceptH: "## \u9A8C\u6536",
1153
+ acceptCmdLine: (cmd, code) => `\u547D\u4EE4\uFF1A\`${cmd}\` \u2192 exit ${code}`,
1154
+ acceptTimeoutSuffix: "\uFF08\u23F1\uFE0F \u9A8C\u6536\u8D85\u65F6\u88AB\u6740\uFF09",
1155
+ acceptTail: "\u8F93\u51FA\u5C3E\u90E8\uFF1A",
1156
+ noAccept: "\n\uFF08\u672A\u6267\u884C\u9A8C\u6536\u2014\u2014\u6539\u52A8\u4E0D\u5B8C\u6574\uFF0C\u8DD1\u4E86\u4E5F\u662F\u566A\u58F0\uFF09",
1157
+ deniedH: "## \u88AB\u62D2\u5DE5\u5177\u8C03\u7528\uFF08fail-closed \u8BB0\u5F55\uFF09",
1158
+ deniedNone: "\u65E0\u3002",
1159
+ diffH: "## Diff \u6458\u8981\uFF08worktree \u76F8\u5BF9\u57FA\u5EA7\uFF0C\u672A\u63D0\u4EA4\uFF0C\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09",
1160
+ 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`,
1161
+ diffNone: "\uFF08\u65E0\uFF09"
1162
+ };
1163
+ EN = {
1164
+ usage: `deepseeker-code batch \u2014 unattended batch runs (cost trust chain)
1165
+
1166
+ Usage: deepseeker-code batch <subcommand>
1167
+ add <ticket.md> [...] Enqueue tasks (multiple files at once)
1168
+ list Show the pending / running / done queue
1169
+ run [--wait-window HH:MM-HH:MM] Run the queue (serial, one report per task; --wait-window waits for the window)
1170
+ clean Clear done/ (reports and ticket archives you've read)
1171
+
1172
+ Ticket format (Markdown; frontmatter \`accept\` is required \u2014 no acceptance, no enqueue):
1173
+ ---
1174
+ title: Fix date parsing bug
1175
+ accept: npm test # required: acceptance command (exit 0 = pass)
1176
+ setup: pnpm install # optional: prep env inside the worktree before the agent (fresh checkout has no node_modules)
1177
+ budget-yuan: 0.5 # optional: per-task cost cap (yuan)
1178
+ timeout-min: 30 # optional: agent-phase timeout (minutes, default 30)
1179
+ tools: run_command # optional: unlock risky tools (run_command/delete_path denied by default)
1180
+ ---
1181
+
1182
+ Task description body (Markdown)\u2026
1183
+
1184
+ Artifacts: <project>/.deepseeker-code/batch/done/<id>.report.md (status / receipt / acceptance output / diff summary);
1185
+ changes are harvested onto an isolated git worktree (temp branch), kept for manual merge or disposal.`,
1186
+ adminAddUsage: "Usage: deepseeker-code batch add <ticket.md> [...] (tickets without an acceptance command are rejected)\nDetails: deepseeker-code batch help",
1187
+ readFileFail: (e) => `Failed to read file: ${e}`,
1188
+ enqueued: (id, title) => `\u{1F4E5} Enqueued [${id}] ${title}`,
1189
+ addRejected: (name, err) => `\u{1F6AB} Rejected ${name}: ${err}`,
1190
+ nextStepRun: "\nRun the queue: deepseeker-code batch run",
1191
+ adminQueueEmpty: "Queue is empty. Enqueue: deepseeker-code batch add <ticket.md> (format: batch help)",
1192
+ listRunningHead: "\u25B6 Running:",
1193
+ listPendingHead: (n) => `\u23F3 Pending ${n}:`,
1194
+ listDoneHead: (n) => `\u2714 Done ${n}:`,
1195
+ cleaned: (n) => `\u{1F9F9} Cleared done/ (${n} task archives). pending/running untouched.`,
1196
+ reportMissing: "(report missing)",
1197
+ ticketRejected: (id, err) => `\u{1F6AB} [${id}] Ticket rejected: ${err}`,
1198
+ taskMeta: (budget, timeoutMin, accept) => ` Budget ${budget !== void 0 ? `\xA5${budget}` : "uncapped"} \xB7 timeout ${timeoutMin}min \xB7 accept \`${accept}\``,
1199
+ setupLine: (cmd) => `\u{1F6E0}\uFE0F setup: \`${cmd}\` (preparing the environment inside the worktree)`,
1200
+ setupFail: (cmd, code) => `Setup failed (\`${cmd}\` exit ${code ?? "null"}) \u2014 agent phase skipped, task marked failed`,
1201
+ roundLine: (ts2, round) => ` [${ts2}] round ${round}`,
1202
+ wtCreateFail: (e) => `Failed to create worktree (the main workspace must be a git repo): ${e}`,
1203
+ agentErrorLine: (id, err) => `\u{1F4A5} [${id}] agent error: ${err}`,
1204
+ timeoutLine: (id, min) => `\u23F1\uFE0F [${id}] Timed out (${min}min); wrapped up truncated (acceptance skipped)`,
1205
+ acceptLine: (passed, id, code) => `${passed ? "\u2705" : "\u274C"} [${id}] acceptance exit ${code}`,
1206
+ acceptKilledSuffix: " (killed on timeout)",
1207
+ timeoutAbort: (min) => `Task timed out (${min} minutes)`,
1208
+ artifactLine: (p, branch) => ` Artifact: ${p} (branch ${branch}, kept for manual handling)`,
1209
+ inWindowNow: (hhmm) => `\u23F0 It's ${hhmm}, already inside the window \u2014 starting now.`,
1210
+ waitWindowWait: (h, m) => `\u23F0 --wait-window: starting in ${h}h ${m}m (at the window start). Waiting \u2014 Ctrl+C to cancel.`,
1211
+ waitWindowInvalid: (s) => `\u274C Invalid --wait-window: "${s}" (expected HH:MM-HH:MM, e.g. 23:30-06:30)`,
1212
+ runnerQueueEmpty: "Queue empty: no tasks in pending/. Enqueue: deepseeker-code batch add <ticket.md>",
1213
+ summaryDone: (total, passed, mins) => `\u{1F319} Batch finished: ${total} tasks \xB7 ${passed} passed \xB7 ${total - passed} failed (${mins}min)`,
1214
+ summaryCost: (sum, tasks) => `\u{1F4B0} Total cost: \xA5${sum} (${tasks} tasks priced)`,
1215
+ summaryCostFree: "\u{1F4B0} Total cost: all records unpriced, no money counted",
1216
+ failedHead: "\u26A0\uFE0F Failed tasks:",
1217
+ artifactsDir: "Reports and changes: .deepseeker-code/batch/done/ (worktrees kept for manual handling)",
1218
+ statusLabel: (s) => ({
1219
+ passed: "\u2705 acceptance passed",
1220
+ "accept-failed": "\u274C acceptance failed",
1221
+ timeout: "\u23F1\uFE0F timed out",
1222
+ error: "\u{1F4A5} agent error",
1223
+ "setup-failed": "\u{1F6E0}\uFE0F setup failed",
1224
+ rejected: "\u{1F6AB} ticket rejected"
1225
+ })[s],
1226
+ spawnFail: (e) => `Spawn failed: ${e}`,
1227
+ execFail: (e) => `Execution failed: ${e}`,
1228
+ tailOmitted: (n) => `(first ${n} lines omitted)`,
1229
+ noOutput: "(no output)",
1230
+ costFree: "(unpriced model, no money counted)",
1231
+ costSummary: (c) => {
1232
+ const money = c.costYuan !== null ? `\xA5${c.costYuan.toFixed(4)}` : "(unpriced model, no money counted)";
1233
+ const hit = c.promptTokens > 0 ? Math.round(c.cachedTokens / c.promptTokens * 100) : 0;
1234
+ const mins = Math.round(c.durationMs / 6e3) / 10;
1235
+ 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` : ""}`;
1236
+ },
1237
+ noReceipt: "(no receipt \u2014 the run was truncated before finishing; cost per the usageLog aggregate above)",
1238
+ 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`,
1239
+ rejectedTitle: "# Batch task rejected",
1240
+ reportTitle: (title) => `# Batch task report: ${title}`,
1241
+ statusEntry: (v) => `- Status: ${v}`,
1242
+ budgetTruncNote: " (\u26A0\uFE0F wrapped up after budget truncation)",
1243
+ costEntry: (v) => `- Receipt: ${v}`,
1244
+ receiptEntry: (v) => `- This run's receipt: ${v}`,
1245
+ errorEntry: (v) => `- Error: ${v}
1246
+ `,
1247
+ notCreated: "(not created)",
1248
+ taskH: "## Task",
1249
+ finalH: "## Model final report",
1250
+ noFinal: "(no output \u2014 the run was truncated/errored before producing any text)",
1251
+ acceptH: "## Acceptance",
1252
+ acceptCmdLine: (cmd, code) => `Command: \`${cmd}\` \u2192 exit ${code}`,
1253
+ acceptTimeoutSuffix: " (\u23F1\uFE0F acceptance timed out, killed)",
1254
+ acceptTail: "Output tail:",
1255
+ noAccept: "\n(Acceptance not run \u2014 changes incomplete, running it would be noise.)",
1256
+ deniedH: "## Denied tool calls (fail-closed log)",
1257
+ deniedNone: "None.",
1258
+ diffH: "## Diff summary (worktree vs base, uncommitted, awaiting manual handling)",
1259
+ 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>\`.`,
1260
+ diffNone: "(none)"
1261
+ };
1262
+ DICT = { zh: ZH, en: EN };
1263
+ batchT = (locale2) => DICT[locale2] ?? ZH;
1264
+ }
1265
+ });
1266
+
966
1267
  // src/batchAdmin.ts
967
1268
  var batchAdmin_exports = {};
968
1269
  __export(batchAdmin_exports, {
@@ -971,11 +1272,14 @@ __export(batchAdmin_exports, {
971
1272
  });
972
1273
  import fs6 from "fs/promises";
973
1274
  import path7 from "path";
974
- var emit, parseBatchArgv, USAGE, handleBatchAdmin, describeDone;
1275
+ var emit, parseBatchArgv, handleBatchAdmin, describeDone;
975
1276
  var init_batchAdmin = __esm({
976
1277
  "src/batchAdmin.ts"() {
977
1278
  "use strict";
978
1279
  init_queue();
1280
+ init_batchStrings();
1281
+ init_strings();
1282
+ init_prefs();
979
1283
  emit = (s = "") => {
980
1284
  process.stdout.write(s + "\n");
981
1285
  };
@@ -992,62 +1296,45 @@ var init_batchAdmin = __esm({
992
1296
  if (sub === "clean") return { sub: "clean" };
993
1297
  return { sub: "help" };
994
1298
  };
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
1299
  handleBatchAdmin = async (b) => {
1300
+ const saved = await readLocale();
1301
+ if (saved) setLocale(saved);
1302
+ const T = batchT(getLocale());
1016
1303
  if (b.sub === "help") {
1017
- emit(USAGE);
1304
+ emit(T.usage);
1018
1305
  return;
1019
1306
  }
1020
1307
  if (b.sub === "add") {
1021
1308
  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");
1309
+ emit(T.adminAddUsage);
1023
1310
  return;
1024
1311
  }
1025
1312
  for (const f of b.files) {
1026
1313
  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}`);
1314
+ const r = await enqueueTicketFile(abs, getLocale()).catch((e) => ({ ok: false, error: T.readFileFail(e?.message ?? e) }));
1315
+ if (r.ok) emit(T.enqueued(r.id, r.title));
1316
+ else emit(T.addRejected(path7.basename(abs), r.error));
1030
1317
  }
1031
- emit("\n\u8DD1\u6279\uFF1Adeepseeker-code batch run");
1318
+ emit(T.nextStepRun);
1032
1319
  return;
1033
1320
  }
1034
1321
  if (b.sub === "list") {
1035
1322
  const q = await snapshotQueue();
1036
1323
  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");
1324
+ emit(T.adminQueueEmpty);
1038
1325
  return;
1039
1326
  }
1040
- if (q.running.length > 0) emit(`\u25B6 \u8FD0\u884C\u4E2D\uFF1A`);
1327
+ if (q.running.length > 0) emit(T.listRunningHead);
1041
1328
  for (const f of q.running) emit(` ${f.replace(/\.md$/, "")}`);
1042
- if (q.pending.length > 0) emit(`\u23F3 \u5F85\u8DD1 ${q.pending.length}\uFF1A`);
1329
+ if (q.pending.length > 0) emit(T.listPendingHead(q.pending.length));
1043
1330
  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`);
1331
+ if (q.done.length > 0) emit(T.listDoneHead(q.done.length));
1045
1332
  for (const id of q.done) emit(` ${id} ${await describeDone(id)}`);
1046
1333
  return;
1047
1334
  }
1048
1335
  if (b.sub === "clean") {
1049
1336
  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`);
1337
+ emit(T.cleaned(n));
1051
1338
  return;
1052
1339
  }
1053
1340
  };
@@ -1056,7 +1343,7 @@ var init_batchAdmin = __esm({
1056
1343
  try {
1057
1344
  text = await fs6.readFile(reportPath(id), "utf-8");
1058
1345
  } catch {
1059
- return "\uFF08\u62A5\u544A\u7F3A\u5931\uFF09";
1346
+ return batchT(getLocale()).reportMissing;
1060
1347
  }
1061
1348
  const fm = (key) => {
1062
1349
  const m = new RegExp(`^${key}:\\s*(.+)$`, "m").exec(text);
@@ -12537,13 +12824,29 @@ var init_usageLog = __esm({
12537
12824
  });
12538
12825
 
12539
12826
  // ../core/src/config/prices.ts
12540
- var MODEL_PRICES, envPrice, ENV_OVERRIDE, priceFor, calcCostYuan;
12827
+ var MODEL_PRICES, PRICES_VERIFIED_AT, PRICES_STALE_DAYS, pricesAgeDays, isBeijingPeak, envPrice, ENV_OVERRIDE, priceFor, calcCostYuan;
12541
12828
  var init_prices = __esm({
12542
12829
  "../core/src/config/prices.ts"() {
12543
12830
  "use strict";
12544
12831
  MODEL_PRICES = {
12545
- "deepseek-flash": { input: 1, output: 2, cacheHit: 0.02 },
12546
- "deepseek-v4-pro": { input: 3, output: 6, cacheHit: 0.025 }
12832
+ "deepseek-flash": {
12833
+ idle: { input: 1, output: 4, cacheHit: 0.02 },
12834
+ peak: { input: 2, output: 8, cacheHit: 0.04 }
12835
+ },
12836
+ "deepseek-v4-pro": {
12837
+ idle: { input: 4.5, output: 13.5, cacheHit: 0.15 },
12838
+ peak: { input: 9, output: 27, cacheHit: 0.3 }
12839
+ }
12840
+ };
12841
+ PRICES_VERIFIED_AT = "2026-10-16";
12842
+ PRICES_STALE_DAYS = 30;
12843
+ pricesAgeDays = () => Math.max(0, Math.floor((Date.now() - Date.parse(`${PRICES_VERIFIED_AT}T00:00:00Z`)) / 864e5));
12844
+ isBeijingPeak = (ts2 = /* @__PURE__ */ new Date()) => {
12845
+ const bj = new Date(ts2.getTime() + 8 * 36e5);
12846
+ const day = bj.getUTCDay();
12847
+ if (day === 0 || day === 6) return false;
12848
+ const h = bj.getUTCHours();
12849
+ return h >= 9 && h < 12 || h >= 14 && h < 18;
12547
12850
  };
12548
12851
  envPrice = (name) => {
12549
12852
  const raw = process.env[name];
@@ -12558,8 +12861,9 @@ var init_prices = __esm({
12558
12861
  if (input === void 0 && output === void 0 && cacheHit === void 0) return null;
12559
12862
  return { input, output, cacheHit };
12560
12863
  })();
12561
- priceFor = (model) => {
12562
- const base = MODEL_PRICES[model];
12864
+ priceFor = (model, ts2) => {
12865
+ const tier = MODEL_PRICES[model];
12866
+ const base = tier ? isBeijingPeak(ts2 ?? /* @__PURE__ */ new Date()) ? tier.peak : tier.idle : void 0;
12563
12867
  if (!ENV_OVERRIDE) return base ?? null;
12564
12868
  const input = ENV_OVERRIDE.input ?? base?.input;
12565
12869
  const output = ENV_OVERRIDE.output ?? base?.output;
@@ -12567,8 +12871,8 @@ var init_prices = __esm({
12567
12871
  if (input === void 0 || output === void 0 || cacheHit === void 0) return null;
12568
12872
  return { input, output, cacheHit };
12569
12873
  };
12570
- calcCostYuan = (model, u) => {
12571
- const p = priceFor(model);
12874
+ calcCostYuan = (model, u, ts2) => {
12875
+ const p = priceFor(model, ts2);
12572
12876
  if (!p) return null;
12573
12877
  const cached2 = Math.max(0, u.cachedTokens ?? 0);
12574
12878
  const miss = Math.max(0, (u.promptTokens ?? 0) - cached2);
@@ -15390,7 +15694,7 @@ var init_useChatState = __esm({
15390
15694
  break;
15391
15695
  }
15392
15696
  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`);
15697
+ pushInfo(S.visionDowngraded(String(obj.model ?? "")));
15394
15698
  break;
15395
15699
  default:
15396
15700
  break;
@@ -15535,7 +15839,7 @@ ${finalPlan}`, false, res.autoExecute);
15535
15839
  } else {
15536
15840
  const enterReason = takeEnterPlanRequest();
15537
15841
  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`);
15842
+ pushInfo(S.planEnterSwitched(enterReason));
15539
15843
  planModeRef.current = true;
15540
15844
  const kept = await runPlanStage(sid, ENTER_PLAN_RESEARCH_PROMPT);
15541
15845
  if (kept) planModeRef.current = false;
@@ -15549,7 +15853,7 @@ ${finalPlan}`, false, res.autoExecute);
15549
15853
  const runBangCommand = useCallback(async (raw) => {
15550
15854
  const cmd = raw.replace(/^!/, "").trim();
15551
15855
  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");
15856
+ pushInfo(S.bangUsage);
15553
15857
  return;
15554
15858
  }
15555
15859
  pushUser(raw);
@@ -15557,9 +15861,9 @@ ${finalPlan}`, false, res.autoExecute);
15557
15861
  sessionIdRef.current = await getOrCreateSessionId(initialSessionId ?? void 0);
15558
15862
  }
15559
15863
  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`;
15864
+ const head = S.bangHead(r.timedOut, r.ok, r.exitCode ?? null);
15561
15865
  pushInfo(`${head} \xB7 ${Math.round(r.durationMs / 100) / 10}s
15562
- ${r.output.trim() || "(\u65E0\u8F93\u51FA)"}`);
15866
+ ${r.output.trim() || S.bangNoOutput}`);
15563
15867
  await recordBashEntry(sessionIdRef.current, formatBashEntry(cmd, r));
15564
15868
  }, [initialSessionId, pushUser, pushInfo]);
15565
15869
  const abortCurrent = useCallback(() => {
@@ -16854,7 +17158,7 @@ var init_TopPanel = __esm({
16854
17158
 
16855
17159
  // ../core/src/observability/inspect.ts
16856
17160
  import fs48 from "fs";
16857
- var CWD, fmt, readTrace, STOP_LABEL, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
17161
+ var OBS, obsT, CWD, fmt, readTrace, fmtK, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
16858
17162
  var init_inspect = __esm({
16859
17163
  "../core/src/observability/inspect.ts"() {
16860
17164
  "use strict";
@@ -16870,6 +17174,106 @@ var init_inspect = __esm({
16870
17174
  init_truncate();
16871
17175
  init_recall();
16872
17176
  init_createModel();
17177
+ OBS = {
17178
+ zh: {
17179
+ usageNoData: "\u{1F4CA} Token \u7528\u91CF\uFF1A\u6682\u65E0\u6570\u636E\uFF08\u672C\u4F1A\u8BDD\u5C1A\u65E0 LLM \u54CD\u5E94\u843D\u76D8\uFF09\u3002",
17180
+ usageHead: (rounds) => `\u{1F4CA} Token \u7528\u91CF\uFF08\u672C\u4F1A\u8BDD\uFF0C${rounds} \u6B21 LLM \u54CD\u5E94\uFF09`,
17181
+ usageTokens: (prompt, completion, total) => ` \u8F93\u5165(prompt): ${prompt} | \u8F93\u51FA(completion): ${completion} | \u5408\u8BA1: ${total}`,
17182
+ usageCache: (hit, rate, miss) => ` \u7F13\u5B58\u547D\u4E2D: ${hit}\uFF08${rate}%\uFF09 | \u7F13\u5B58\u672A\u547D\u4E2D: ${miss}`,
17183
+ usageSplit: (main2, sub) => ` \u4E3B agent: ${main2} | \u5B50 agent(spawn/workflow): ${sub}`,
17184
+ 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`,
17185
+ usageNoTools: "\uFF08\u65E0\u5DE5\u5177\u8C03\u7528\uFF09",
17186
+ usageRunLine: (ts2, rounds, tok, stop, tools) => ` ${ts2} ${rounds}\u8F6E ${tok} tok ${stop} ${tools}`,
17187
+ stopLabel: { normal: "\u5B8C\u6210", aborted: "\u4E2D\u6B62", error: "\u9519\u8BEF", repeat: "\u7194\u65AD", limit: "\u8D85\u9650" },
17188
+ 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`,
17189
+ usageTopTools: (tools) => ` \u5E38\u7528\u5DE5\u5177: ${tools.join("\u3001") || "\uFF08\u65E0\uFF09"}`,
17190
+ 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` : ""}`,
17191
+ usageTopSessions: (tops) => ` \u8D39\u7528 top \u4F1A\u8BDD: ${tops}`,
17192
+ usagePriceStale: (days, at) => ` \u26A0\uFE0F \u4EF7\u683C\u8868 ${at} \u6838\u5B9E\u540E\u5DF2 ${days} \u5929\u672A\u590D\u6838\u2014\u2014\u5B98\u7F51\u82E5\u5DF2\u8C03\u4EF7\uFF0C\u4E0A\u65B9\u82B1\u8D39\u4F1A\u5931\u771F\uFF08\u53EF\u8BBE DEEP_SEEK_PRICE_* \u4E34\u65F6\u6821\u51C6\uFF09\u3002`,
17193
+ 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",
17194
+ ctxHead: "\u{1F9E0} \u4E0A\u4E0B\u6587\u6CBB\u7406",
17195
+ 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`,
17196
+ ctxLastPrompt: (p, fillRate) => ` \u6700\u8FD1 prompt: ${p ?? "\uFF08\u5C1A\u65E0\uFF09"}${fillRate ? `\uFF08\u5360\u7A97\u53E3 ${fillRate}%\uFF09` : ""}`,
17197
+ ctxArchived: (n) => ` \u5DF2\u5F52\u6863(\u538B\u7F29)\u6D88\u606F\u6761\u6570: ${n}`,
17198
+ ctxEntities: (n) => ` \u672C\u4F1A\u8BDD\u5DF2\u5F52\u6863\u5B9E\u4F53: ${n} \u4E2A\uFF08\u6458\u8981\u69FD\u7D22\u5F15\uFF09`,
17199
+ ctxRecall: (n) => ` \u53EC\u56DE\u547D\u4E2D: ${n} \u6B21\uFF08recall \u5DE5\u5177\uFF0C\u672C\u6B21\u8FDB\u7A0B\u5185\u8BA1\u6570\uFF09`,
17200
+ ctxSidecar: (n) => ` \u4FA7\u8F66\u5B58\u6863: ${n} \u6761\uFF08\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u539F\u6587\uFF0Crecall with_full \u53D6\u56DE\uFF09`,
17201
+ ctxTip: (pct) => ` \u8D85\u8FC7\u9608\u503C\u5373\u89E6\u53D1\u6EDA\u52A8\u538B\u7F29\uFF1BfillRate \u63A5\u8FD1 ${pct}% \u65F6\u5373\u5C06\u538B\u7F29\u3002`,
17202
+ 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",
17203
+ permHead: "\u{1F512} \u6743\u9650\u89C4\u5219\uFF08\u4F18\u5148\u7EA7 deny > ask > allow\uFF1B\u672A\u5339\u914D\u8D70\u9ED8\u8BA4 safetyLevel\uFF09",
17204
+ permNone: "(\u65E0)",
17205
+ permGroup: (icon, label, n) => ` ${icon} ${label}\uFF08${n}\uFF09:`,
17206
+ mcpEmpty: "\u{1F50C} MCP\uFF1A\u65E0\u5DF2\u8FDE\u63A5 server\uFF08\u5728 ~/.deepseeker-code/mcp.json \u914D\u7F6E\uFF09\u3002",
17207
+ mcpHead: (n, tools) => `\u{1F50C} MCP server\uFF08${n} \u4E2A\uFF0C\u5171 ${tools} \u4E2A\u5DE5\u5177\uFF09`,
17208
+ mcpLine: (name, tools) => ` \u2022 ${name}\uFF08${tools}\uFF09`,
17209
+ mcpToolCount: (n) => `${n} \u4E2A\u5DE5\u5177`,
17210
+ mcpToolCountFail: "\u5DE5\u5177\u6570\u83B7\u53D6\u5931\u8D25",
17211
+ hooksEmpty: "\u{1FA9D} Hooks\uFF1A\u65E0\uFF08\u5728 ~/.deepseeker-code/settings.json \u7684 hooks \u6BB5\u914D\u7F6E\uFF09\u3002",
17212
+ hooksHead: (n) => `\u{1FA9D} Hooks\uFF08\u5171 ${n} \u6761\uFF0C\u6309\u4E8B\u4EF6\u5206\u7EC4\uFF09`,
17213
+ hooksGroup: (evt, n) => ` \u2022 ${evt}\uFF08${n}\uFF09:`,
17214
+ dbgHead: "\u{1F41E} Debug \u5FEB\u7167",
17215
+ dbgSessionNone: "(\u672A\u521D\u59CB\u5316)",
17216
+ dbgModels: (main2, aux) => ` \u4E3B\u6A21\u578B: ${main2} | \u8F85\u52A9\u6A21\u578B: ${aux}`,
17217
+ dbgAuxNone: "(\u672A\u914D\u7F6E)",
17218
+ dbgOn: "\u5F00",
17219
+ dbgOff: "\u5173",
17220
+ dbgParallel: (on, conc, steps) => ` \u5E76\u884C\u5DE5\u5177: ${on} | workflow \u5E76\u53D1\u4E0A\u9650: ${conc} | workflow \u6700\u5927\u6B65\u6570: ${steps}`,
17221
+ dbgUndo: (on, search) => ` undo: ${on} | \u641C\u7D22\u540E\u7AEF: ${search}`,
17222
+ dbgSearchAuto: "(\u81EA\u52A8)",
17223
+ dbgPlatform: (node, platform) => ` Node: ${node} | \u5E73\u53F0: ${platform}`
17224
+ },
17225
+ en: {
17226
+ usageNoData: "\u{1F4CA} Token usage: no data yet (no LLM responses logged for this session).",
17227
+ usageHead: (rounds) => `\u{1F4CA} Token usage (this session, ${rounds} LLM responses)`,
17228
+ usageTokens: (prompt, completion, total) => ` Input (prompt): ${prompt} | Output (completion): ${completion} | Total: ${total}`,
17229
+ usageCache: (hit, rate, miss) => ` Cache hit: ${hit} (${rate}%) | Cache miss: ${miss}`,
17230
+ usageSplit: (main2, sub) => ` Main agent: ${main2} | Sub agents (spawn/workflow): ${sub}`,
17231
+ usageSessionRuns: (runs, rounds, avg, aborted, pct) => ` \u258EThis session: ${runs} runs, ${rounds} rounds (avg ${avg}/run) \xB7 aborted ${aborted}/${runs} (${pct}%)`,
17232
+ usageNoTools: "(no tool calls)",
17233
+ usageRunLine: (ts2, rounds, tok, stop, tools) => ` ${ts2} ${rounds} rounds ${tok} tok ${stop} ${tools}`,
17234
+ stopLabel: { normal: "done", aborted: "aborted", error: "error", repeat: "fuse", limit: "limit" },
17235
+ 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}%)`,
17236
+ usageTopTools: (tools) => ` Top tools: ${tools.join(", ") || "(none)"}`,
17237
+ usageWeekCost: (cost, saved, unpriced) => ` 7-day spend \xA5${cost} (cache hits saved \xA5${saved})${unpriced > 0 ? ` \xB7 ${unpriced} unpriced records excluded` : ""}`,
17238
+ usageTopSessions: (tops) => ` Costliest sessions: ${tops}`,
17239
+ usagePriceStale: (days, at) => ` \u26A0\uFE0F Price table verified on ${at}, ${days} days ago \u2014 if official pricing changed, costs above may drift (set DEEP_SEEK_PRICE_* to correct).`,
17240
+ usageTip: " Tip: parallel workflows multiply sub-agent usage; higher cache hit rate means lower input cost.",
17241
+ ctxHead: "\u{1F9E0} Context governance",
17242
+ ctxWindow: (window, threshold, ratio, keep) => ` Window cap: ${window} | Compact threshold: ${threshold} (${ratio}\xD7) | Keep recent: ${keep} dialogue units`,
17243
+ ctxLastPrompt: (p, fillRate) => ` Last prompt: ${p ?? "(none yet)"}${fillRate ? ` (${fillRate}% of window)` : ""}`,
17244
+ ctxArchived: (n) => ` Archived (compacted) messages: ${n}`,
17245
+ ctxEntities: (n) => ` Archived entities this session: ${n} (summary-slot index)`,
17246
+ ctxRecall: (n) => ` Recall hits: ${n} (recall tool, in-process count)`,
17247
+ ctxSidecar: (n) => ` Sidecar archives: ${n} (full tool outputs, retrievable via recall with_full)`,
17248
+ ctxTip: (pct) => ` Rolling compaction triggers past the threshold; fillRate near ${pct}% means compaction is imminent.`,
17249
+ permEmpty: "\u{1F512} Permission rules: none (everything uses default safetyLevel: SAFE auto-approve / MUTATION\xB7DANGER gated).",
17250
+ permHead: "\u{1F512} Permission rules (priority deny > ask > allow; unmatched uses default safetyLevel)",
17251
+ permNone: "(none)",
17252
+ permGroup: (icon, label, n) => ` ${icon} ${label} (${n}):`,
17253
+ mcpEmpty: "\u{1F50C} MCP: no connected servers (configure in ~/.deepseeker-code/mcp.json).",
17254
+ mcpHead: (n, tools) => `\u{1F50C} MCP servers (${n}, ${tools} tools total)`,
17255
+ mcpLine: (name, tools) => ` \u2022 ${name} (${tools})`,
17256
+ mcpToolCount: (n) => `${n} tools`,
17257
+ mcpToolCountFail: "failed to count tools",
17258
+ hooksEmpty: "\u{1FA9D} Hooks: none (configure under the hooks key in ~/.deepseeker-code/settings.json).",
17259
+ hooksHead: (n) => `\u{1FA9D} Hooks (${n} total, grouped by event)`,
17260
+ hooksGroup: (evt, n) => ` \u2022 ${evt} (${n}):`,
17261
+ dbgHead: "\u{1F41E} Debug snapshot",
17262
+ dbgSessionNone: "(not initialized)",
17263
+ dbgModels: (main2, aux) => ` Main model: ${main2} | Aux model: ${aux}`,
17264
+ dbgAuxNone: "(not configured)",
17265
+ dbgOn: "on",
17266
+ dbgOff: "off",
17267
+ dbgParallel: (on, conc, steps) => ` Parallel tools: ${on} | Workflow concurrency cap: ${conc} | Workflow max steps: ${steps}`,
17268
+ dbgUndo: (on, search) => ` undo: ${on} | Search backend: ${search}`,
17269
+ dbgSearchAuto: "(auto)",
17270
+ dbgPlatform: (node, platform) => ` Node: ${node} | Platform: ${platform}`
17271
+ }
17272
+ };
17273
+ obsT = (locale2) => {
17274
+ const dict = OBS[locale2] ?? OBS.zh;
17275
+ return (key) => dict[key] ?? OBS.zh[key] ?? key;
17276
+ };
16873
17277
  CWD = process.cwd();
16874
17278
  fmt = (n) => typeof n === "number" && Number.isFinite(n) ? n.toLocaleString("en-US") : "-";
16875
17279
  readTrace = async (sessionId) => {
@@ -16890,13 +17294,13 @@ var init_inspect = __esm({
16890
17294
  return [];
16891
17295
  }
16892
17296
  };
16893
- STOP_LABEL = { normal: "\u5B8C\u6210", aborted: "\u4E2D\u6B62", error: "\u9519\u8BEF", repeat: "\u7194\u65AD", limit: "\u8D85\u9650" };
16894
17297
  fmtK = (n) => n >= 1e4 ? `${(n / 1e3).toFixed(1)}k` : fmt(n);
16895
- inspectUsage = async (sessionId) => {
17298
+ inspectUsage = async (sessionId, locale2 = "zh") => {
17299
+ const t = obsT(locale2);
16896
17300
  const trace = await readTrace(sessionId);
16897
- const responses = trace.filter((t) => t?.eventType === "llm.response" && t?.usage);
17301
+ const responses = trace.filter((x) => x?.eventType === "llm.response" && x?.usage);
16898
17302
  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`;
17303
+ if (responses.length === 0 && sessionRuns.length === 0) return t("usageNoData");
16900
17304
  let prompt = 0, completion = 0, total = 0, cacheHit = 0, cacheMiss = 0;
16901
17305
  let mainTotal = 0, subTotal = 0;
16902
17306
  for (const r of responses) {
@@ -16912,20 +17316,21 @@ var init_inspect = __esm({
16912
17316
  }
16913
17317
  const cacheRate = prompt > 0 ? (cacheHit / prompt * 100).toFixed(1) : "0.0";
16914
17318
  const rounds = responses.length;
17319
+ const stopLabel = t("stopLabel");
16915
17320
  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)}`
17321
+ t("usageHead")(rounds),
17322
+ t("usageTokens")(fmt(prompt), fmt(completion), fmt(total)),
17323
+ t("usageCache")(fmt(cacheHit), cacheRate, fmt(cacheMiss)),
17324
+ t("usageSplit")(fmt(mainTotal), fmt(subTotal))
16920
17325
  ];
16921
17326
  if (sessionRuns.length > 0) {
16922
17327
  const ss = summarizeUsage(sessionRuns);
16923
17328
  const abortedPct = ss.mainRuns > 0 ? Math.round(ss.stopReasons.aborted / ss.mainRuns * 100) : 0;
16924
17329
  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`);
17330
+ lines.push(t("usageSessionRuns")(ss.mainRuns, ss.mainRounds, avgRounds, ss.stopReasons.aborted, abortedPct));
16926
17331
  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}`);
17332
+ const tools = topTools(r.tools, 3).map(([n, c]) => `${n}\xD7${c}`).join(" ") || t("usageNoTools");
17333
+ 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
17334
  }
16930
17335
  }
16931
17336
  const weekRecords = await readUsageRecords(7);
@@ -16934,33 +17339,37 @@ var init_inspect = __esm({
16934
17339
  const weekHit = week.promptTokens > 0 ? (week.cachedTokens / week.promptTokens * 100).toFixed(1) : "0.0";
16935
17340
  const weekAbort = week.mainRuns > 0 ? Math.round(week.stopReasons.aborted / week.mainRuns * 100) : 0;
16936
17341
  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");
17342
+ const tools = topTools(week.tools, 4).map(([n, c]) => `${n}\xD7${c}`);
16938
17343
  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"}`
17344
+ t("usageWeek")(week.mainRuns, week.mainRounds, weekAvg, weekAbort, fmtK(week.totalTokens), weekHit),
17345
+ t("usageTopTools")(tools)
16941
17346
  );
16942
17347
  const bySession = /* @__PURE__ */ new Map();
16943
17348
  let weekCost = 0, weekSaved = 0, pricedRuns = 0;
16944
17349
  for (const r of weekRecords) {
16945
- const c = r.costYuan ?? calcCostYuan(r.model, r);
17350
+ const at = Number.isNaN(Date.parse(String(r.ts))) ? void 0 : new Date(r.ts);
17351
+ const c = r.costYuan ?? calcCostYuan(r.model, r, at);
16946
17352
  if (c == null) continue;
16947
17353
  pricedRuns++;
16948
17354
  weekCost += c;
16949
17355
  bySession.set(r.sessionId, (bySession.get(r.sessionId) ?? 0) + c);
16950
- const p = priceFor(r.model);
17356
+ const p = priceFor(r.model, at);
16951
17357
  if (p) weekSaved += (r.cachedTokens ?? 0) * (p.input - p.cacheHit) / 1e6;
16952
17358
  }
16953
17359
  if (pricedRuns > 0) {
16954
17360
  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
17361
  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}`);
17362
+ lines.push(t("usageWeekCost")(weekCost.toFixed(3), weekSaved.toFixed(3), unpaid));
17363
+ if (tops) lines.push(t("usageTopSessions")(tops));
16958
17364
  }
16959
17365
  }
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`);
17366
+ const staleDays = pricesAgeDays();
17367
+ if (staleDays >= PRICES_STALE_DAYS) lines.push(t("usagePriceStale")(staleDays, PRICES_VERIFIED_AT));
17368
+ lines.push(t("usageTip"));
16961
17369
  return lines.join("\n");
16962
17370
  };
16963
- inspectContext = async (sessionId) => {
17371
+ inspectContext = async (sessionId, locale2 = "zh") => {
17372
+ const t = obsT(locale2);
16964
17373
  const trace = await readTrace(sessionId);
16965
17374
  let lastPrompt;
16966
17375
  for (let i = trace.length - 1; i >= 0; i--) {
@@ -16988,38 +17397,40 @@ var init_inspect = __esm({
16988
17397
  const threshold = Math.round(window * ratio);
16989
17398
  const fillRate = lastPrompt ? (lastPrompt / window * 100).toFixed(1) : null;
16990
17399
  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`
17400
+ t("ctxHead"),
17401
+ t("ctxWindow")(fmt(window), fmt(threshold), ratio, appConfig.KEEP_RECENT_UNITS),
17402
+ t("ctxLastPrompt")(lastPrompt ? fmt(lastPrompt) : null, fillRate),
17403
+ t("ctxArchived")(archived),
17404
+ t("ctxEntities")(archivedEntities),
17405
+ t("ctxRecall")(getRecallHitCount(sessionId)),
17406
+ t("ctxSidecar")(sidecarCount),
17407
+ t("ctxTip")(Math.round(ratio * 100))
16999
17408
  ].join("\n");
17000
17409
  };
17001
- inspectPermissions = () => {
17410
+ inspectPermissions = (locale2 = "zh") => {
17411
+ const t = obsT(locale2);
17002
17412
  const r = listPermissionRules();
17003
17413
  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`;
17414
+ if (total === 0) return t("permEmpty");
17005
17415
  const render2 = (label, kind, icon) => {
17006
17416
  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");
17417
+ if (list.length === 0) return ` ${icon} ${label}: ${t("permNone")}`;
17418
+ 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
17419
  };
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");
17420
+ return [t("permHead"), render2("Deny", "deny", "\u{1F6AB}"), render2("Ask", "ask", "\u2753"), render2("Allow", "allow", "\u2705")].join("\n");
17012
17421
  };
17013
- inspectMcp = async () => {
17422
+ inspectMcp = async (locale2 = "zh") => {
17423
+ const t = obsT(locale2);
17014
17424
  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`);
17425
+ if (clients2.length === 0) return t("mcpEmpty");
17426
+ const lines = clients2.map((c) => t("mcpLine")(c.serverName, c.toolCount >= 0 ? t("mcpToolCount")(c.toolCount) : t("mcpToolCountFail")));
17017
17427
  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");
17428
+ return [t("mcpHead")(clients2.length, totalTools), ...lines].join("\n");
17019
17429
  };
17020
- inspectHooks = () => {
17430
+ inspectHooks = (locale2 = "zh") => {
17431
+ const t = obsT(locale2);
17021
17432
  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`;
17433
+ if (hooks.length === 0) return t("hooksEmpty");
17023
17434
  const byEvent = /* @__PURE__ */ new Map();
17024
17435
  for (const h of hooks) {
17025
17436
  const arr = byEvent.get(h.event) ?? [];
@@ -17027,21 +17438,21 @@ var init_inspect = __esm({
17027
17438
  byEvent.set(h.event, arr);
17028
17439
  }
17029
17440
  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");
17441
+ return t("hooksGroup")(evt, arr.length) + "\n" + arr.map((h) => ` matcher=${h.matcher} source=${h.source}${h.onError ? ` onError=${h.onError}` : ""}`).join("\n");
17032
17442
  });
17033
- return [`\u{1FA9D} Hooks\uFF08\u5171 ${hooks.length} \u6761\uFF0C\u6309\u4E8B\u4EF6\u5206\u7EC4\uFF09`, ...lines].join("\n");
17443
+ return [t("hooksHead")(hooks.length), ...lines].join("\n");
17034
17444
  };
17035
- inspectDebug = (sessionId) => {
17445
+ inspectDebug = (sessionId, locale2 = "zh") => {
17446
+ const t = obsT(locale2);
17036
17447
  return [
17037
- `\u{1F41E} Debug \u5FEB\u7167`,
17038
- ` session: ${sessionId || "(\u672A\u521D\u59CB\u5316)"}`,
17448
+ t("dbgHead"),
17449
+ ` session: ${sessionId || t("dbgSessionNone")}`,
17039
17450
  ` cwd: ${CWD}`,
17040
- ` \u4E3B\u6A21\u578B: ${MODEL_NAME} | \u8F85\u52A9\u6A21\u578B: ${AUX_MODEL_NAME ?? "(\u672A\u914D\u7F6E)"}`,
17451
+ t("dbgModels")(MODEL_NAME, AUX_MODEL_NAME ?? t("dbgAuxNone")),
17041
17452
  ` 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}`
17453
+ t("dbgParallel")(appConfig.parallelSafeTools ? t("dbgOn") : t("dbgOff"), appConfig.workflowConcurrency, appConfig.workflowMaxSteps),
17454
+ t("dbgUndo")(appConfig.undoEnabled ? t("dbgOn") : t("dbgOff"), appConfig.searchProvider || t("dbgSearchAuto")),
17455
+ t("dbgPlatform")(process.version, process.platform)
17045
17456
  ].join("\n");
17046
17457
  };
17047
17458
  }
@@ -17340,54 +17751,49 @@ var init_App = __esm({
17340
17751
  state.pushInfo(S.statusText(modelDisplay, state.getThinkingLevel(), state.getPlanMode(), getLocale(), CWD2));
17341
17752
  return true;
17342
17753
  case "/usage":
17343
- state.pushInfo(await inspectUsage(state.sessionIdRef.current ?? ""));
17754
+ state.pushInfo(await inspectUsage(state.sessionIdRef.current ?? "", getLocale()));
17344
17755
  return true;
17345
17756
  case "/context":
17346
- state.pushInfo(await inspectContext(state.sessionIdRef.current ?? ""));
17757
+ state.pushInfo(await inspectContext(state.sessionIdRef.current ?? "", getLocale()));
17347
17758
  return true;
17348
17759
  case "/permissions":
17349
- state.pushInfo(inspectPermissions());
17760
+ state.pushInfo(inspectPermissions(getLocale()));
17350
17761
  return true;
17351
17762
  case "/mcp":
17352
- state.pushInfo(await inspectMcp());
17763
+ state.pushInfo(await inspectMcp(getLocale()));
17353
17764
  return true;
17354
17765
  case "/hooks":
17355
- state.pushInfo(inspectHooks());
17766
+ state.pushInfo(inspectHooks(getLocale()));
17356
17767
  return true;
17357
17768
  case "/trust": {
17358
17769
  const trusted = await readTrustedDirs();
17359
17770
  if (!arg) {
17360
17771
  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");
17772
+ state.pushInfo(S.trustEmpty);
17362
17773
  return true;
17363
17774
  }
17364
17775
  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`);
17776
+ state.pushInfo(S.trustList(list));
17369
17777
  return true;
17370
17778
  }
17371
17779
  const idx = Number(arg);
17372
17780
  const target = Number.isInteger(idx) && idx >= 0 && idx < trusted.length ? trusted[idx] : arg;
17373
17781
  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}`);
17782
+ state.pushInfo(removed ? S.trustRemoved(target) : S.trustNotFound(target));
17376
17783
  return true;
17377
17784
  }
17378
17785
  case "/debug":
17379
- state.pushInfo(inspectDebug(state.sessionIdRef.current ?? ""));
17786
+ state.pushInfo(inspectDebug(state.sessionIdRef.current ?? "", getLocale()));
17380
17787
  return true;
17381
17788
  case "/archive": {
17382
17789
  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");
17790
+ state.pushInfo(s ? S.archiveSummary(s) : S.archiveNone);
17385
17791
  return true;
17386
17792
  }
17387
17793
  case "/plan": {
17388
17794
  const on = !state.getPlanMode();
17389
17795
  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"}`);
17796
+ state.pushInfo(S.planModeSet(on));
17391
17797
  return true;
17392
17798
  }
17393
17799
  case "/budget": {
@@ -17395,27 +17801,27 @@ ${s}` : "\u672C\u4F1A\u8BDD\u542F\u52A8\u4EE5\u6765\u5C1A\u672A\u53D1\u751F\u8FC
17395
17801
  if (!v) {
17396
17802
  const b = state.getBudgetYuan();
17397
17803
  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"}`);
17804
+ state.pushInfo(S.budgetView(b, last));
17399
17805
  return true;
17400
17806
  }
17401
17807
  if (v === "off" || v === "0") {
17402
17808
  state.setBudgetYuan(void 0);
17403
- state.pushInfo("\u{1F4B0} \u9884\u7B97\u5DF2\u5173\u95ED\uFF08\u4E0D\u9650\u82B1\u8D39\uFF09\u3002");
17809
+ state.pushInfo(S.budgetOff);
17404
17810
  return true;
17405
17811
  }
17406
17812
  const n = Number(v);
17407
17813
  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");
17814
+ state.pushInfo(S.budgetUsage);
17409
17815
  return true;
17410
17816
  }
17411
17817
  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`);
17818
+ state.pushInfo(S.budgetSet(n));
17413
17819
  return true;
17414
17820
  }
17415
17821
  case "/auto": {
17416
17822
  const on = !state.getAutoMode();
17417
17823
  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"}`);
17824
+ state.pushInfo(S.autoModeSet(on));
17419
17825
  return true;
17420
17826
  }
17421
17827
  case "/model":
@@ -17435,15 +17841,15 @@ ${s}` : "\u672C\u4F1A\u8BDD\u542F\u52A8\u4EE5\u6765\u5C1A\u672A\u53D1\u751F\u8FC
17435
17841
  case "/thinking": {
17436
17842
  const lvl = arg.toLowerCase();
17437
17843
  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`);
17844
+ state.pushInfo(S.thinkingInvalid(arg));
17439
17845
  return true;
17440
17846
  }
17441
17847
  if (!arg) {
17442
- state.pushInfo(`\u5F53\u524D\u601D\u8003\u7B49\u7EA7\uFF1A${state.getThinkingLevel()}\uFF08off \u5173\u95ED / high \u5E38\u89C4 / max \u6DF1\u5EA6\uFF09`);
17848
+ state.pushInfo(S.thinkingCurrent(state.getThinkingLevel()));
17443
17849
  return true;
17444
17850
  }
17445
17851
  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"}`);
17852
+ state.pushInfo(S.thinkingSet(lvl));
17447
17853
  return true;
17448
17854
  }
17449
17855
  case "/lang": {
@@ -17497,7 +17903,7 @@ ${listText}`);
17497
17903
  };
17498
17904
  const ensureEngine = async () => {
17499
17905
  if (engineReadyFlag) return;
17500
- state.pushInfo("\u23F3 \u5F15\u64CE\u52A0\u8F7D\u4E2D\uFF08MCP/skills/commands\uFF09\u2026");
17906
+ state.pushInfo(S.engineLoading);
17501
17907
  await engineReady;
17502
17908
  };
17503
17909
  const executeSlash = async (fullText) => {
@@ -17513,7 +17919,7 @@ ${listText}`);
17513
17919
  await state.submit(text);
17514
17920
  return;
17515
17921
  }
17516
- state.pushInfo(`\u672A\u77E5\u547D\u4EE4\uFF1A/${name || "(\u7A7A)"}\uFF08\u8F93\u5165 / \u67E5\u770B\u53EF\u7528\u547D\u4EE4\uFF09`);
17922
+ state.pushInfo(S.unknownCmd(name));
17517
17923
  };
17518
17924
  const onSubmit = async () => {
17519
17925
  const v = inputRef.current.trim();
@@ -17793,7 +18199,7 @@ __export(batchRunner_exports, {
17793
18199
  });
17794
18200
  import fs49 from "fs";
17795
18201
  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;
18202
+ 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
18203
  var init_batchRunner = __esm({
17798
18204
  "src/batchRunner.ts"() {
17799
18205
  "use strict";
@@ -17807,6 +18213,8 @@ var init_batchRunner = __esm({
17807
18213
  init_usageLog();
17808
18214
  init_ticket();
17809
18215
  init_queue();
18216
+ init_batchStrings();
18217
+ init_strings();
17810
18218
  BATCH_EDIT_TOOLS = /* @__PURE__ */ new Set(["edit_file", "create_file", "write_file", "move_file", "notebook_edit"]);
17811
18219
  BATCH_DEFAULT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
17812
18220
  ACCEPT_TIMEOUT_MIN = 10;
@@ -17815,14 +18223,8 @@ var init_batchRunner = __esm({
17815
18223
  process.stdout.write(s + "\n");
17816
18224
  };
17817
18225
  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
18226
  runAcceptCommand = async (cmd, cwd) => {
18227
+ const T = batchT(getLocale());
17826
18228
  const isWin = process.platform === "win32";
17827
18229
  return new Promise((resolve) => {
17828
18230
  let child;
@@ -17835,7 +18237,7 @@ var init_batchRunner = __esm({
17835
18237
  // 剔除 agent 自身凭证(同 run_command 红线)
17836
18238
  });
17837
18239
  } catch (e) {
17838
- resolve({ code: null, output: `\u542F\u52A8\u5931\u8D25\uFF1A${e?.message ?? e}`, timedOut: false });
18240
+ resolve({ code: null, output: T.spawnFail(e?.message ?? e), timedOut: false });
17839
18241
  return;
17840
18242
  }
17841
18243
  const chunks = [];
@@ -17851,15 +18253,15 @@ var init_batchRunner = __esm({
17851
18253
  }, ACCEPT_TIMEOUT_MIN * 6e4);
17852
18254
  child.on("error", (e) => {
17853
18255
  clearTimeout(killer);
17854
- resolve({ code: null, output: `\u6267\u884C\u5931\u8D25\uFF1A${e?.message ?? e}`, timedOut });
18256
+ resolve({ code: null, output: T.execFail(e?.message ?? e), timedOut });
17855
18257
  });
17856
18258
  child.on("close", (code) => {
17857
18259
  clearTimeout(killer);
17858
18260
  const text = decodeProcessOutput(Buffer.concat(chunks)).trim();
17859
18261
  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
18262
+ const tail = lines.length > ACCEPT_TAIL_LINES ? `${T.tailOmitted(lines.length - ACCEPT_TAIL_LINES)}
17861
18263
  ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
17862
- resolve({ code, output: tail || "\uFF08\u65E0\u8F93\u51FA\uFF09", timedOut });
18264
+ resolve({ code, output: tail || T.noOutput, timedOut });
17863
18265
  });
17864
18266
  });
17865
18267
  };
@@ -17880,19 +18282,10 @@ ${lines.slice(-ACCEPT_TAIL_LINES).join("\n")}` : text;
17880
18282
  if (c.costYuan === null) c.partialPrice = false;
17881
18283
  return c;
17882
18284
  };
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
18285
  buildReport = (args) => {
17894
18286
  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" : "";
18287
+ const T = batchT(getLocale());
18288
+ const budgetNote = budgetTruncated ? T.budgetTruncNote : "";
17896
18289
  return `---
17897
18290
  id: ${id}
17898
18291
  title: ${ticket.title}
@@ -17901,43 +18294,42 @@ cost-yuan: ${cost.costYuan !== null ? cost.costYuan.toFixed(4) : "null"}
17901
18294
  rounds: ${cost.rounds}
17902
18295
  budget-truncated: ${budgetTruncated}
17903
18296
  session: batch-${id}
17904
- worktree: ${wt?.path ?? "\uFF08\u672A\u521B\u5EFA\uFF09"}
17905
- branch: ${wt?.branch ?? "\uFF08\u672A\u521B\u5EFA\uFF09"}
18297
+ worktree: ${wt?.path ?? T.notCreated}
18298
+ branch: ${wt?.branch ?? T.notCreated}
17906
18299
  ---
17907
18300
 
17908
- # \u6302\u673A\u4EFB\u52A1\u62A5\u544A\uFF1A${ticket.title}
18301
+ ${T.reportTitle(ticket.title)}
17909
18302
 
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
18303
+ ${T.statusEntry(`${T.statusLabel(status)}${budgetNote}`)}
18304
+ ${T.costEntry(T.costSummary(cost))}
18305
+ ${T.receiptEntry(receipt ? T.receiptLine(receipt) : T.noReceipt)}
18306
+ ${args.errorMessage ? T.errorEntry(args.errorMessage) : ""}
18307
+ ${T.taskH}
17916
18308
 
17917
18309
  ${ticket.body}
17918
18310
 
17919
- ## \u6A21\u578B\u6536\u5C3E\u6C47\u62A5
18311
+ ${T.finalH}
17920
18312
 
17921
- ${finalText.trim() || "\uFF08\u65E0\u8F93\u51FA\u2014\u2014run \u5728\u4EA7\u51FA\u6B63\u6587\u524D\u88AB\u622A\u65AD/\u5F02\u5E38\uFF09"}
18313
+ ${finalText.trim() || T.noFinal}
17922
18314
 
17923
- ## \u9A8C\u6536
18315
+ ${T.acceptH}
17924
18316
  ${accept ? `
17925
- \u547D\u4EE4\uFF1A\`${accept.cmd}\` \u2192 exit ${accept.code}${accept.timedOut ? "\uFF08\u23F1\uFE0F \u9A8C\u6536\u8D85\u65F6\u88AB\u6740\uFF09" : ""}
18317
+ ${T.acceptCmdLine(accept.cmd, accept.code)}${accept.timedOut ? T.acceptTimeoutSuffix : ""}
17926
18318
 
17927
- \u8F93\u51FA\u5C3E\u90E8\uFF1A
18319
+ ${T.acceptTail}
17928
18320
 
17929
18321
  \`\`\`
17930
18322
  ${accept.output}
17931
18323
  \`\`\`
17932
- ` : "\n\uFF08\u672A\u6267\u884C\u9A8C\u6536\u2014\u2014\u6539\u52A8\u4E0D\u5B8C\u6574\uFF0C\u8DD1\u4E86\u4E5F\u662F\u566A\u58F0\uFF09"}
18324
+ ` : T.noAccept}
17933
18325
 
17934
- ## \u88AB\u62D2\u5DE5\u5177\u8C03\u7528\uFF08fail-closed \u8BB0\u5F55\uFF09
18326
+ ${T.deniedH}
17935
18327
 
17936
- ${deniedTools.length === 0 ? "\u65E0\u3002" : deniedTools.map((d) => `- **${d.tool}** \u2014 ${d.detail}`).join("\n")}
18328
+ ${deniedTools.length === 0 ? T.deniedNone : deniedTools.map((d) => `- **${d.tool}** \u2014 ${d.detail}`).join("\n")}
17937
18329
 
17938
- ## Diff \u6458\u8981\uFF08worktree \u76F8\u5BF9\u57FA\u5EA7\uFF0C\u672A\u63D0\u4EA4\uFF0C\u5F85\u4EBA\u5DE5\u5904\u7F6E\uFF09
18330
+ ${T.diffH}
17939
18331
 
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
18332
+ ${T.diffNote(wt?.branch ?? "?", wt?.path ?? "?")}
17941
18333
 
17942
18334
  \`\`\`\`\`diff
17943
18335
  ${diff}
@@ -17945,8 +18337,9 @@ ${diff}
17945
18337
  `;
17946
18338
  };
17947
18339
  runOneTask = async (task) => {
18340
+ const T = batchT(getLocale());
17948
18341
  const source = await fs49.promises.readFile(task.path, "utf-8");
17949
- const parsed = parseBatchTicket(source);
18342
+ const parsed = parseBatchTicket(source, getLocale());
17950
18343
  if (!parsed.ok) {
17951
18344
  const cost2 = { rounds: 0, mainRounds: 0, subRuns: 0, promptTokens: 0, completionTokens: 0, cachedTokens: 0, costYuan: 0, partialPrice: false, durationMs: 0 };
17952
18345
  await finishTicket(task.id, `---
@@ -17955,17 +18348,17 @@ title: ${task.id}
17955
18348
  status: rejected
17956
18349
  ---
17957
18350
 
17958
- # \u6302\u673A\u4EFB\u52A1\u88AB\u62D2
18351
+ ${T.rejectedTitle}
17959
18352
 
17960
18353
  ${parsed.error}`);
17961
- emit2(`\u{1F6AB} [${task.id}] \u5DE5\u5355\u88AB\u62D2\uFF1A${parsed.error}`);
18354
+ emit2(T.ticketRejected(task.id, parsed.error));
17962
18355
  return { status: "rejected", cost: cost2, budgetTruncated: false };
17963
18356
  }
17964
18357
  const t = parsed.ticket;
17965
18358
  const sessionId = `batch-${task.id}`;
17966
18359
  const timeoutMin = t.timeoutMin ?? 30;
17967
18360
  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}\``);
18361
+ emit2(T.taskMeta(t.budgetYuan, timeoutMin, t.accept));
17969
18362
  const unlocked = new Set(t.tools ?? []);
17970
18363
  const denySchemas = new Set([...BATCH_DEFAULT_DENYLIST].filter((n) => !unlocked.has(n)));
17971
18364
  const toolSchemas = agentTools.filter((x) => !denySchemas.has(x?.function?.name));
@@ -17975,7 +18368,7 @@ ${parsed.error}`);
17975
18368
  try {
17976
18369
  wt = await createWorktree(sessionId, task.id);
17977
18370
  } 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}`;
18371
+ const msg = T.wtCreateFail(e?.message ?? e);
17979
18372
  const cost2 = await collectSessionCost(sessionId);
17980
18373
  await finishTicket(task.id, buildReport({
17981
18374
  id: task.id,
@@ -17987,7 +18380,7 @@ ${parsed.error}`);
17987
18380
  finalText: "",
17988
18381
  accept: null,
17989
18382
  deniedTools: [],
17990
- diff: "\uFF08\u65E0\uFF09",
18383
+ diff: T.diffNone,
17991
18384
  wt: null,
17992
18385
  errorMessage: msg
17993
18386
  }));
@@ -17995,8 +18388,38 @@ ${parsed.error}`);
17995
18388
  return { status: "error", cost: cost2, budgetTruncated: false };
17996
18389
  }
17997
18390
  setSessionWorktree(sessionId, wt);
18391
+ if (t.setup) {
18392
+ emit2(T.setupLine(t.setup));
18393
+ const s = await runAcceptCommand(t.setup, wt.path);
18394
+ if (s.code !== 0 || s.timedOut) {
18395
+ const msg = `${T.setupFail(t.setup, s.code)}${s.timedOut ? T.acceptTimeoutSuffix : ""}
18396
+
18397
+ \`\`\`
18398
+ ${s.output}
18399
+ \`\`\``;
18400
+ await killBackgroundTasksUnder(wt.path).catch(() => {
18401
+ });
18402
+ const cost2 = await collectSessionCost(sessionId);
18403
+ await finishTicket(task.id, buildReport({
18404
+ id: task.id,
18405
+ ticket: t,
18406
+ status: "setup-failed",
18407
+ budgetTruncated: false,
18408
+ cost: cost2,
18409
+ receipt: void 0,
18410
+ finalText: "",
18411
+ accept: null,
18412
+ deniedTools: [],
18413
+ diff: T.diffNone,
18414
+ wt,
18415
+ errorMessage: msg
18416
+ }));
18417
+ emit2(`\u{1F6E0}\uFE0F [${task.id}] ${T.setupFail(t.setup, s.code)}`);
18418
+ return { status: "setup-failed", cost: cost2, budgetTruncated: false };
18419
+ }
18420
+ }
17998
18421
  const ac = new AbortController();
17999
- const timer = setTimeout(() => ac.abort(new Error(`\u4EFB\u52A1\u8D85\u65F6\uFF08${timeoutMin} \u5206\u949F\uFF09`)), timeoutMin * 6e4);
18422
+ const timer = setTimeout(() => ac.abort(new Error(T.timeoutAbort(timeoutMin))), timeoutMin * 6e4);
18000
18423
  let receipt;
18001
18424
  let finalText = "";
18002
18425
  let agentError;
@@ -18009,7 +18432,7 @@ ${parsed.error}`);
18009
18432
  if (evt?.type === "text.delta") finalText += evt.text;
18010
18433
  else if (evt?.type === "text.reset") finalText = "";
18011
18434
  else if (evt?.type === "final") finalText = evt.text || finalText;
18012
- else if (evt?.type === "round.start") emit2(` [${ts()}] \u8F6E ${evt.round}`);
18435
+ else if (evt?.type === "round.start") emit2(T.roundLine(ts(), evt.round));
18013
18436
  },
18014
18437
  ac.signal,
18015
18438
  {
@@ -18037,15 +18460,15 @@ ${parsed.error}`);
18037
18460
  let accept = null;
18038
18461
  if (agentError) {
18039
18462
  status = "error";
18040
- emit2(`\u{1F4A5} [${task.id}] agent \u5F02\u5E38\uFF1A${agentError}`);
18463
+ emit2(T.agentErrorLine(task.id, agentError));
18041
18464
  } else if (timedOut) {
18042
18465
  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`);
18466
+ emit2(T.timeoutLine(task.id, timeoutMin));
18044
18467
  } else {
18045
18468
  const r = await runAcceptCommand(t.accept, wt.path);
18046
18469
  accept = { cmd: t.accept, code: r.code, output: r.output, timedOut: r.timedOut };
18047
18470
  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" : ""}`);
18471
+ emit2(T.acceptLine(status === "passed", task.id, r.code) + (r.timedOut ? T.acceptKilledSuffix : ""));
18049
18472
  }
18050
18473
  await killBackgroundTasksUnder(wt.path).catch(() => {
18051
18474
  });
@@ -18065,8 +18488,8 @@ ${parsed.error}`);
18065
18488
  wt,
18066
18489
  errorMessage: agentError
18067
18490
  }));
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`);
18491
+ emit2(` ${T.statusLabel(status)} \xB7 ${T.costSummary(cost)}`);
18492
+ emit2(T.artifactLine(wt.path, wt.branch));
18070
18493
  return { status, cost, budgetTruncated };
18071
18494
  };
18072
18495
  parseWaitWindow = (s) => {
@@ -18084,14 +18507,15 @@ ${parsed.error}`);
18084
18507
  };
18085
18508
  sleep = (ms) => new Promise((r) => setTimeout(r, ms));
18086
18509
  waitUntilWindow = async (w) => {
18510
+ const T = batchT(getLocale());
18087
18511
  const now = nowMinutes();
18088
18512
  let waitMin = 0;
18089
18513
  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`);
18514
+ emit2(T.inWindowNow(`${Math.floor(now / 60)}:${String(now % 60).padStart(2, "0")}`));
18091
18515
  return;
18092
18516
  }
18093
18517
  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`);
18518
+ emit2(T.waitWindowWait(Math.floor(waitMin / 60), waitMin % 60));
18095
18519
  while (waitMin > 0) {
18096
18520
  const chunkMin = Math.min(waitMin, 30);
18097
18521
  await sleep(chunkMin * 6e4);
@@ -18099,14 +18523,15 @@ ${parsed.error}`);
18099
18523
  }
18100
18524
  };
18101
18525
  runBatchQueue = async (opts) => {
18526
+ const T = batchT(getLocale());
18102
18527
  const window = parseWaitWindow(opts?.waitWindow);
18103
18528
  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`);
18529
+ emit2(T.waitWindowInvalid(opts.waitWindow));
18105
18530
  process.exitCode = 2;
18106
18531
  return;
18107
18532
  }
18108
18533
  if (window) await waitUntilWindow(window);
18109
- const lock = await acquireRunLock();
18534
+ const lock = await acquireRunLock(getLocale());
18110
18535
  if (!lock.ok) {
18111
18536
  emit2(`\u{1F6AB} ${lock.error}`);
18112
18537
  process.exitCode = 1;
@@ -18125,7 +18550,7 @@ ${parsed.error}`);
18125
18550
  total++;
18126
18551
  const r = await runOneTask(task);
18127
18552
  if (r.status === "passed") passed++;
18128
- else failedIds.push(`[${task.id}] ${STATUS_LABEL[r.status]}`);
18553
+ else failedIds.push(`[${task.id}] ${T.statusLabel(r.status)}`);
18129
18554
  if (r.cost.costYuan !== null) {
18130
18555
  costSum += r.cost.costYuan;
18131
18556
  costTasks++;
@@ -18135,16 +18560,16 @@ ${parsed.error}`);
18135
18560
  await releaseRunLock();
18136
18561
  }
18137
18562
  if (total === 0) {
18138
- emit2("\u961F\u5217\u7A7A\uFF1Apending/ \u65E0\u4EFB\u52A1\u3002\u6295\u9012\uFF1Adeepseeker-code batch add <\u5DE5\u5355.md>");
18563
+ emit2(T.runnerQueueEmpty);
18139
18564
  return;
18140
18565
  }
18141
18566
  emit2("");
18142
18567
  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
18568
+ emit2(T.summaryDone(total, passed, `${Math.round((Date.now() - startedAt) / 6e3) / 10}`));
18569
+ emit2(costTasks > 0 ? T.summaryCost(costSum.toFixed(4), costTasks) : T.summaryCostFree);
18570
+ if (failedIds.length > 0) emit2(`${T.failedHead}
18146
18571
  ${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");
18572
+ emit2(T.artifactsDir);
18148
18573
  };
18149
18574
  }
18150
18575
  });