deepseeker-code 1.0.42 → 1.0.45

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 (3) hide show
  1. package/README.md +4 -0
  2. package/dist/cli.mjs +263 -101
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -70,12 +70,16 @@ deepseeker-code --plan
70
70
  | `/lang <zh\|en>` | 切换语言 |
71
71
  | `/output-style <名称>` | 切换输出风格(人格) |
72
72
  | `/sessions` | 列出并续接历史会话 |
73
+ | `/fork` | 从当前会话某轮回复处分叉出新会话 |
73
74
  | `/clear` | 新会话 |
74
75
  | `/status` / `/usage` / `/context` | 查看状态 / 用量 / 上下文 |
75
76
  | `/permissions` / `/mcp` / `/hooks` | 查看已加载的权限规则 / MCP / Hooks |
77
+ | `/trust` | 管理已信任目录(列出 / 撤销) |
76
78
  | `/debug` | 调试信息 |
77
79
  | `/exit` / `/quit` | 退出 |
78
80
 
81
+ > 菜单超出屏高时随选中项滚动(底栏显示 `n/m 条`);`commands/` 目录下的自定义命令引擎就绪后自动并入菜单。启动时界面先行渲染,引擎(MCP/skills/命令)后台加载,首次提交前自动等待就绪。
82
+
79
83
  **快捷键**:`Ctrl+C` 退出 · `Esc` 中止/清输入 · `Ctrl+G` 中止当前轮 · `Ctrl+T` 展开/收起思考 · 模态/菜单 `↑↓ Enter`。
80
84
 
81
85
  ---
package/dist/cli.mjs CHANGED
@@ -569,7 +569,7 @@ import { randomUUID } from "node:crypto";
569
569
  import fs from "fs/promises";
570
570
  import { execFile } from "node:child_process";
571
571
  import { promisify } from "node:util";
572
- var getFileName, createUUID, SAFE_SESSION_ID_RE, isSafeSessionId, assertSafeSessionId, readJSONFile, atomicWriteJSON, createSemaphore, injectMarkedBlock, decodeProcessOutput, execFileSmart;
572
+ var getFileName, createUUID, SAFE_SESSION_ID_RE, isSafeSessionId, assertSafeSessionId, detectTextLocale, readJSONFile, atomicWriteJSON, createSemaphore, injectMarkedBlock, decodeProcessOutput, execFileSmart;
573
573
  var init_common = __esm({
574
574
  "../core/src/common/index.ts"() {
575
575
  "use strict";
@@ -592,6 +592,15 @@ var init_common = __esm({
592
592
  throw new Error(`\u{1F6D1} [SECURITY] \u975E\u6CD5 ${label}: ${JSON.stringify(sessionId.slice(0, 48))}\uFF08\u4EC5\u5141\u8BB8\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF/\u8FDE\u5B57\u7B26\uFF09`);
593
593
  }
594
594
  };
595
+ detectTextLocale = (text) => {
596
+ const stripped = (text || "").replace(/```[\s\S]*?```/g, " ").replace(/`[^`]*`/g, " ").replace(/\s+/g, " ");
597
+ const cjk = (stripped.match(/[㐀-䶿一-鿿]/g) || []).length;
598
+ const latin = (stripped.match(/[A-Za-z]/g) || []).length;
599
+ if (cjk === 0 && latin === 0) return null;
600
+ if (cjk === 0) return "en";
601
+ if (latin === 0) return "zh";
602
+ return cjk / (cjk + latin) >= 0.2 ? "zh" : "en";
603
+ };
595
604
  readJSONFile = async (file, fallback = null) => {
596
605
  try {
597
606
  const raw = await fs.readFile(file, "utf-8");
@@ -2805,12 +2814,13 @@ task_id: ${taskId}
2805
2814
  type: "function",
2806
2815
  function: {
2807
2816
  name: "get_background_output",
2808
- description: "\u67E5\u770B\u6307\u5B9A\u540E\u53F0\u4EFB\u52A1\u7684\u6700\u8FD1\u8F93\u51FA\u65E5\u5FD7\u4E0E\u8FD0\u884C\u72B6\u6001\uFF08running/exited/killed + \u9000\u51FA\u7801\uFF09\u3002\u7EAF\u8BFB\uFF0C\u514D\u5BA1\u6279\u3002\u7528\u4E8E\u786E\u8BA4 dev server \u662F\u5426\u542F\u52A8\u6210\u529F\u3001\u67E5\u770B\u62A5\u9519\u7B49\u3002",
2817
+ description: "\u67E5\u770B\u6307\u5B9A\u540E\u53F0\u4EFB\u52A1\u7684\u6700\u8FD1\u8F93\u51FA\u65E5\u5FD7\u4E0E\u8FD0\u884C\u72B6\u6001\uFF08running/exited/killed + \u9000\u51FA\u7801\uFF09\u3002\u7EAF\u8BFB\uFF0C\u514D\u5BA1\u6279\u3002\u7528\u4E8E\u786E\u8BA4 dev server \u662F\u5426\u542F\u52A8\u6210\u529F\u3001\u67E5\u770B\u62A5\u9519\u7B49\u3002\u7B49\u957F\u4EFB\u52A1\uFF08\u6784\u5EFA/\u6D4B\u8BD5\uFF09\u8DD1\u5B8C\uFF1A\u5E26 wait_seconds \u963B\u585E\u7B49\u5F85\u4EFB\u52A1\u9000\u51FA\u540E\u518D\u8FD4\u56DE\uFF08\u4ECD\u5728\u8DD1\u5219\u7B49\u5230\u8D85\u65F6\u8FD4\u56DE\u5F53\u524D\u72B6\u6001\uFF09\u2014\u2014\u7528\u5B83\u7B49\uFF0C\u4E0D\u8981\u4E0D\u5E26 wait \u53CD\u590D\u8F6E\u8BE2\u67E5\u8BE2\u3002",
2809
2818
  parameters: {
2810
2819
  type: "object",
2811
2820
  properties: {
2812
2821
  task_id: { type: "string", description: "run_in_background \u8FD4\u56DE\u7684 task_id" },
2813
- tail_lines: { type: "number", description: "\u53EA\u8FD4\u56DE\u6700\u540E N \u884C\u65E5\u5FD7\uFF08\u9ED8\u8BA4 50\uFF0C\u907F\u514D\u4E00\u6B21\u704C\u5165\u8FC7\u591A\uFF09" }
2822
+ tail_lines: { type: "number", description: "\u53EA\u8FD4\u56DE\u6700\u540E N \u884C\u65E5\u5FD7\uFF08\u9ED8\u8BA4 50\uFF0C\u907F\u514D\u4E00\u6B21\u704C\u5165\u8FC7\u591A\uFF09" },
2823
+ wait_seconds: { type: "number", description: "\u963B\u585E\u7B49\u5F85\u4EFB\u52A1\u9000\u51FA\uFF08\u6216\u5230\u8FBE\u6B64\u65F6\u957F\uFF09\u540E\u518D\u8FD4\u56DE\uFF0C\u4E0A\u9650 120 \u79D2\uFF1B\u4EFB\u52A1\u5DF2\u5728\u8DD1\u4E14\u9700\u8981\u7B49\u7ED3\u679C\u65F6\u4F7F\u7528\uFF0C\u4E0D\u9700\u8981\u7B49\u5C31\u7701\u7565" }
2814
2824
  },
2815
2825
  required: ["task_id"]
2816
2826
  },
@@ -2821,6 +2831,13 @@ task_id: ${taskId}
2821
2831
  if (!task || task.sessionId !== ctx?.sessionId) {
2822
2832
  return `\u274C [\u67E5\u8BE2\u5931\u8D25]\uFF1A\u672A\u627E\u5230 task_id=${args.task_id}\uFF08\u53EF\u80FD\u5DF2\u968F\u670D\u52A1\u91CD\u542F\u4E22\u5931\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF09\u3002`;
2823
2833
  }
2834
+ const waitMs = Math.min(Math.max(0, args.wait_seconds ?? 0), 120) * 1e3;
2835
+ if (waitMs > 0 && task.status === "running") {
2836
+ const deadline = Date.now() + waitMs;
2837
+ while (task.status === "running" && Date.now() < deadline && !ctx?.abortSignal?.aborted) {
2838
+ await new Promise((r) => setTimeout(r, 500));
2839
+ }
2840
+ }
2824
2841
  const tail = Math.max(1, args.tail_lines ?? 50);
2825
2842
  const allLines = task.outputBuffer.split("\n");
2826
2843
  const tailText = allLines.slice(-tail).join("\n").trim();
@@ -3883,26 +3900,31 @@ var init_repeatBreaker = __esm({
3883
3900
  const recentSignatures = [];
3884
3901
  const recentPollTargets = [];
3885
3902
  const POLL_TARGET_KEYS = { get_background_output: ["task_id"] };
3903
+ const parseArgs2 = (tc) => {
3904
+ try {
3905
+ return JSON.parse(tc?.function?.arguments || "{}") ?? {};
3906
+ } catch {
3907
+ return {};
3908
+ }
3909
+ };
3886
3910
  const pollTarget = (tc) => {
3887
3911
  const name = tc?.function?.name;
3888
3912
  const keys = name ? POLL_TARGET_KEYS[name] : void 0;
3889
3913
  if (!keys) return null;
3890
- let args = {};
3891
- try {
3892
- args = JSON.parse(tc?.function?.arguments || "{}");
3893
- } catch {
3894
- }
3914
+ const args = parseArgs2(tc);
3895
3915
  const picked = {};
3896
3916
  for (const k of keys) if (k in args) picked[k] = args[k];
3897
3917
  return `${name}:${JSON.stringify(picked)}`;
3898
3918
  };
3919
+ const isWaitPoll = (tc) => tc?.function?.name === "get_background_output" && Number(parseArgs2(tc).wait_seconds ?? 0) > 0;
3899
3920
  const check = (toolCalls, round, lastContent) => {
3900
- const sig = toolCalls.map((t) => `${t.function.name}:${t.function.arguments}`).join("|");
3921
+ const effCalls = toolCalls.filter((t) => !isWaitPoll(t));
3922
+ const sig = effCalls.map((t) => `${t.function.name}:${t.function.arguments}`).join("|");
3901
3923
  const tooName = toolCalls.map((t) => `${t.function.name}`).join("|");
3902
3924
  recentSignatures.push(sig);
3903
3925
  if (recentSignatures.length > 6) recentSignatures.shift();
3904
3926
  const last3 = recentSignatures.slice(-3);
3905
- if (last3.length === 3 && last3.every((s) => s === last3[0])) {
3927
+ if (sig && last3.length === 3 && last3.every((s) => s === sig)) {
3906
3928
  const text = (lastContent || "") + "\n\uFF08\u68C0\u6D4B\u5230\u91CD\u590D\u5DE5\u5177\u8C03\u7528\uFF0C\u5DF2\u505C\u6B62\uFF09";
3907
3929
  events({
3908
3930
  sessionId,
@@ -3921,31 +3943,28 @@ var init_repeatBreaker = __esm({
3921
3943
  });
3922
3944
  return { tripped: true, kind: "full", text, toolName: tooName };
3923
3945
  }
3924
- const pollTargets = toolCalls.map(pollTarget).filter(Boolean);
3925
- if (pollTargets.length) {
3926
- const pollSig = pollTargets.join("|");
3927
- recentPollTargets.push(pollSig);
3928
- if (recentPollTargets.length > 8) recentPollTargets.shift();
3929
- const last4 = recentPollTargets.slice(-4);
3930
- if (last4.length === 4 && last4.every((s) => s === last4[0])) {
3931
- const text = (lastContent || "") + "\n\uFF08\u68C0\u6D4B\u5230\u540E\u53F0\u4EFB\u52A1\u8F6E\u8BE2\u6B7B\u5FAA\u73AF\uFF08\u53CD\u590D\u67E5\u8BE2\u540C\u4E00\u4EFB\u52A1\uFF09\uFF0C\u5DF2\u505C\u6B62\uFF09";
3932
- events({
3933
- sessionId,
3934
- eventType: "tool.repeat_break",
3935
- metadata: {
3936
- depth,
3937
- decisionSource: llmDecisionSource,
3938
- durationMs: performance.now() - startTime,
3939
- round,
3940
- toolName: tooName,
3941
- toolSource: "builtin",
3942
- ok: false,
3943
- attempt: round
3944
- },
3945
- payload: { output: text }
3946
- });
3947
- return { tripped: true, kind: "names", text, toolName: tooName };
3948
- }
3946
+ const pollSig = effCalls.map(pollTarget).filter(Boolean).join("|");
3947
+ recentPollTargets.push(pollSig);
3948
+ if (recentPollTargets.length > 8) recentPollTargets.shift();
3949
+ const last4 = recentPollTargets.slice(-4);
3950
+ if (pollSig && last4.length === 4 && last4.every((s) => s === pollSig)) {
3951
+ const text = (lastContent || "") + "\n\uFF08\u68C0\u6D4B\u5230\u540E\u53F0\u4EFB\u52A1\u8F6E\u8BE2\u6B7B\u5FAA\u73AF\uFF08\u53CD\u590D\u67E5\u8BE2\u540C\u4E00\u4EFB\u52A1\uFF09\uFF0C\u5DF2\u505C\u6B62\uFF09";
3952
+ events({
3953
+ sessionId,
3954
+ eventType: "tool.repeat_break",
3955
+ metadata: {
3956
+ depth,
3957
+ decisionSource: llmDecisionSource,
3958
+ durationMs: performance.now() - startTime,
3959
+ round,
3960
+ toolName: tooName,
3961
+ toolSource: "builtin",
3962
+ ok: false,
3963
+ attempt: round
3964
+ },
3965
+ payload: { output: text }
3966
+ });
3967
+ return { tripped: true, kind: "names", text, toolName: tooName };
3949
3968
  }
3950
3969
  events({
3951
3970
  sessionId,
@@ -4363,10 +4382,23 @@ var init_systemInjections = __esm({
4363
4382
  function: { name: t.function.name, description: t.function.description, parameters: t.function.parameters }
4364
4383
  }));
4365
4384
  ensureSummarySlot(message);
4366
- if (locale2) {
4367
- const hint = locale2 === "zh" ? "\u8BF7\u59CB\u7EC8\u7528\u4E2D\u6587\u56DE\u590D\u7528\u6237\u3002" : "Always reply to the user in English.";
4368
- injectMarkedBlock(message, "\u27E6DSC:LOCALE\u27E7", hint);
4369
- }
4385
+ const LOCALE_HINTS = {
4386
+ zh: "\u59CB\u7EC8\u7528\u4E2D\u6587\u8F93\u51FA\u6240\u6709\u9762\u5411\u7528\u6237\u7684\u5185\u5BB9\uFF1A\u4F60\u7684\u56DE\u590D\u3001\u4E2D\u95F4\u53D9\u8FF0\u3001todo \u4EFB\u52A1\u9879\u6807\u9898\u3001\u901A\u8FC7 exit_plan_mode \u63D0\u4EA4\u7684\u65B9\u6848\u6587\u672C\u3001\u4EE3\u7801\u6CE8\u91CA\u4E0E\u63D0\u4EA4\u4FE1\u606F\u3002\u4EE3\u7801\u6807\u8BC6\u7B26\u4FDD\u6301\u539F\u6837\uFF1B\u5DE5\u5177\u7ED3\u679C\u4E0E\u7CFB\u7EDF\u6307\u4EE4\u53EF\u80FD\u5939\u6742\u5176\u4ED6\u8BED\u8A00\u2014\u2014\u5FC5\u8981\u65F6\u7167\u5F15\u539F\u6587\uFF0C\u4F46\u4F60\u81EA\u5DF1\u7684\u53D9\u8FF0\u4E0E\u89E3\u91CA\u4E00\u5F8B\u7528\u4E2D\u6587\u3002",
4387
+ en: "Always produce ALL user-facing output in English: your replies, interim narration, todo item titles, the plan text you submit via exit_plan_mode, code comments, and commit messages. Keep code identifiers as-is; tool results and system instructions may arrive in Chinese \u2014 quote them verbatim where needed, but your own narration and explanations must stay in English."
4388
+ };
4389
+ const lastUserText = (() => {
4390
+ for (let i = message.length - 1; i >= 0; i--) {
4391
+ const m = message[i];
4392
+ if (m?.role !== "user") continue;
4393
+ const c = m.content;
4394
+ if (typeof c === "string") return c;
4395
+ if (Array.isArray(c)) return c.filter((p) => typeof p?.text === "string").map((p) => p.text).join(" ");
4396
+ return "";
4397
+ }
4398
+ return "";
4399
+ })();
4400
+ const effLocale = detectTextLocale(lastUserText) ?? locale2;
4401
+ if (effLocale) injectMarkedBlock(message, "\u27E6DSC:LOCALE\u27E7", LOCALE_HINTS[effLocale]);
4370
4402
  injectOutputStyle(message, outputStyle);
4371
4403
  injectSkillCatalog(message);
4372
4404
  injectAgentCatalog(message);
@@ -4389,13 +4421,14 @@ var init_agent = __esm({
4389
4421
  type: "function",
4390
4422
  function: {
4391
4423
  name: "spawn_agent",
4392
- description: "\u521B\u5EFA\u4E00\u4E2A\u62E5\u6709\u72EC\u7ACB\u6267\u884C\u73AF\u5883\u7684\u5B50 agent \u5904\u7406\u9694\u79BB\u7684\u7279\u5B9A\u5B50\u4EFB\u52A1\uFF0C\u5B50\u4EFB\u52A1\u5B8C\u6210\u540E\u81EA\u52A8\u6C47\u603B\u6539\u52A8\u70B9\u4E0E\u7ED3\u8BBA\u8FD4\u56DE\u3002\u53EF\u901A\u8FC7 name \u6307\u5B9A\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF09\u4EE5\u5957\u7528\u5176\u4E13\u957F\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF1B\u4E0D\u4F20 name \u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002",
4424
+ description: "\u521B\u5EFA\u4E00\u4E2A\u62E5\u6709\u72EC\u7ACB\u6267\u884C\u73AF\u5883\u7684\u5B50 agent \u5904\u7406\u9694\u79BB\u7684\u7279\u5B9A\u5B50\u4EFB\u52A1\uFF0C\u5B50\u4EFB\u52A1\u5B8C\u6210\u540E\u81EA\u52A8\u6C47\u603B\u6539\u52A8\u70B9\u4E0E\u7ED3\u8BBA\u8FD4\u56DE\u3002\u53EF\u901A\u8FC7 name \u6307\u5B9A\u58F0\u660E\u5F0F\u5B50 Agent\uFF08\u89C1\u7CFB\u7EDF\u63D0\u793A\u8BCD\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\uFF09\u4EE5\u5957\u7528\u5176\u4E13\u957F\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF1B\u4E0D\u4F20 name \u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002\u652F\u6301\u7EED\u8DD1\uFF1A\u4F20 resume_session_id\uFF08\u6B64\u524D\u8FD4\u56DE\u7684 agent_id\uFF09\u590D\u7528\u8BE5\u5B50 Agent \u7684\u5B8C\u6574\u5386\u53F2\u8BB0\u5FC6\uFF0C\u9002\u5408\u8FFD\u95EE\u3001\u7EE7\u7EED\u672A\u5B8C\u6210\u7684\u4EFB\u52A1\u3002",
4393
4425
  parameters: {
4394
4426
  type: "object",
4395
4427
  properties: {
4396
- task: { type: "string", description: "\u4EA4\u7ED9\u5B50 agent \u7684\u5177\u4F53\u5FAE\u89C2\u4EFB\u52A1\u63CF\u8FF0\uFF08\u5982 '\u7F16\u5199 Button \u7EC4\u4EF6\u7684\u5355\u5143\u6D4B\u8BD5'\uFF09" },
4428
+ task: { type: "string", description: "\u4EA4\u7ED9\u5B50 agent \u7684\u5177\u4F53\u5FAE\u89C2\u4EFB\u52A1\u63CF\u8FF0\uFF08\u5982 '\u7F16\u5199 Button \u7EC4\u4EF6\u7684\u5355\u5143\u6D4B\u8BD5'\uFF09\uFF1B\u7EED\u8DD1\u65F6\u4E3A\u65B0\u6307\u4EE4\uFF08\u5982 '\u7EE7\u7EED\u5B8C\u6210\u5269\u4F59\u7528\u4F8B'\uFF09" },
4397
4429
  name: { type: "string", description: "\u58F0\u660E\u5F0F\u5B50 Agent \u540D\u79F0\uFF08\u987B\u4E0E\u7CFB\u7EDF\u63D0\u793A\u8BCD\u4E2D\u3010\u53EF\u7528\u5B50 Agent \u76EE\u5F55\u3011\u4E00\u81F4\uFF09\u3002\u63D0\u4F9B\u65F6\u6309\u5176\u58F0\u660E\u52A0\u8F7D\u7CFB\u7EDF\u8BCD/\u5DE5\u5177\u767D\u540D\u5355/model\uFF1B\u4E0D\u63D0\u4F9B\u5219\u8D70\u9ED8\u8BA4\u901A\u7528\u5B50 agent\u3002" },
4398
- role: { type: "string", description: "\u5B50 agent \u7684\u89D2\u8272/\u4E13\u957F\uFF08\u53EF\u9009\uFF0C\u5982 '\u6D4B\u8BD5\u4E13\u5BB6'\u3001'\u91CD\u6784\u5148\u950B'\uFF09\u3002\u58F0\u660E\u5F0F agent \u81EA\u5E26 role \u65F6\u4F5C\u8865\u5145\u3002" }
4430
+ role: { type: "string", description: "\u5B50 agent \u7684\u89D2\u8272/\u4E13\u957F\uFF08\u53EF\u9009\uFF0C\u5982 '\u6D4B\u8BD5\u4E13\u5BB6'\u3001'\u91CD\u6784\u5148\u950B'\uFF09\u3002\u58F0\u660E\u5F0F agent \u81EA\u5E26 role \u65F6\u4F5C\u8865\u5145\u3002" },
4431
+ resume_session_id: { type: "string", description: "\u7EED\u8DD1\u65E2\u6709\u5B50 Agent\uFF1A\u4F20\u6B64\u524D spawn_agent \u8FD4\u56DE\u7684 agent_id\uFF0C\u5176\u78C1\u76D8\u4E0A\u7684\u5386\u53F2\u4E0E\u8BB0\u5FC6\u5C06\u5B8C\u6574\u6062\u590D\u3002\u6CE8\u610F\uFF1A\u5B50 Agent \u72B6\u6001\u4E0D\u968F\u4E3B\u4F1A\u8BDD fork \u56DE\u6EDA\u2014\u2014fork \u53EA\u5206\u53C9\u5BF9\u8BDD\uFF0C\u7EED\u8DD1\u62FF\u5230\u7684\u662F\u5B50\u4F1A\u8BDD\u78C1\u76D8\u6700\u65B0\u72B6\u6001\u3002\u7F3A\u7701\u5219\u65B0\u5EFA\u5B50 Agent\u3002" }
4399
4432
  },
4400
4433
  required: ["task"]
4401
4434
  },
@@ -4403,10 +4436,17 @@ var init_agent = __esm({
4403
4436
  isSync: true,
4404
4437
  async execute(args, ctx) {
4405
4438
  if (!ctx) return "\u274C [\u6D3E\u751F\u5931\u8D25]\uFF1Aspawn_agent \u7F3A\u5C11\u5FC5\u987B\u7684\u667A\u80FD\u4F53\u8FD0\u884C\u4E0A\u4E0B\u6587\u3002";
4406
- const res = await runSubagent(args, ctx, getGlobalTools);
4439
+ const res = await runSubagent(
4440
+ { task: args.task, name: args.name, role: args.role, resumeSessionId: args.resume_session_id },
4441
+ ctx,
4442
+ getGlobalTools
4443
+ );
4407
4444
  if (!res.ok) return res.output;
4408
4445
  return [
4409
- `\u{1F389} [\u5B50 Agent \u6267\u884C\u5B8C\u6BD5] \u6D3E\u751F\u5B50\u4EFB\u52A1\u6C47\u62A5\u5982\u4E0B\uFF1A
4446
+ `\u{1F389} [\u5B50 Agent \u6267\u884C\u5B8C\u6BD5${args.resume_session_id ? "\xB7\u7EED\u8DD1" : ""}] \u6D3E\u751F\u5B50\u4EFB\u52A1\u6C47\u62A5\u5982\u4E0B\uFF1A
4447
+ `,
4448
+ `\u{1F194} agent_id: ${res.sessionId}
4449
+ \uFF08\u9700\u8981\u7EE7\u7EED\u8BE5\u5B50 Agent \u7684\u4EFB\u52A1\u6216\u8FFD\u95EE\u65F6\uFF0C\u8C03\u7528 spawn_agent \u4F20 resume_session_id=${res.sessionId}\uFF0C\u5176\u5386\u53F2\u8BB0\u5FC6\u5B8C\u6574\u4FDD\u7559\uFF09
4410
4450
  `,
4411
4451
  `==================================================
4412
4452
  `,
@@ -9978,6 +10018,7 @@ var init_agentNudges = __esm({
9978
10018
  createNudgeScheduler = (opts = {}) => {
9979
10019
  const firstPrompt = opts.firstPrompt ?? "";
9980
10020
  const planMode = !!opts.planMode;
10021
+ const noEarlyFinal = !!opts.noEarlyFinal;
9981
10022
  let phantomRetries = 0;
9982
10023
  let phantomPending = null;
9983
10024
  let earlyFinalNudges = 0;
@@ -10040,7 +10081,7 @@ ${TOOL_DIGEST_TEXT}` };
10040
10081
  ${PHANTOM_TEXT}` };
10041
10082
  return true;
10042
10083
  }
10043
- if (text !== "" && round <= EARLY_FINAL_TURN_THRESHOLD && earlyFinalNudges < EARLY_FINAL_MAX && !looksComplete(finalText)) {
10084
+ if (!noEarlyFinal && text !== "" && round <= EARLY_FINAL_TURN_THRESHOLD && earlyFinalNudges < EARLY_FINAL_MAX && !looksComplete(finalText)) {
10044
10085
  earlyFinalNudges++;
10045
10086
  earlyFinalPending = { role: "system", content: `${EARLY_FINAL_FENCE}
10046
10087
  ${EARLY_FINAL_TEXT(round)}` };
@@ -10125,7 +10166,7 @@ async function* runAgent(message, options) {
10125
10166
  const m = message.find((x) => x?.role === "user");
10126
10167
  return typeof m?.content === "string" ? m.content : "";
10127
10168
  })();
10128
- const nudges = createNudgeScheduler({ firstPrompt, planMode: !!options.planMode });
10169
+ const nudges = createNudgeScheduler({ firstPrompt, planMode: !!options.planMode, noEarlyFinal: !!options.noEarlyFinal });
10129
10170
  const userDecisionSource = depth > 0 ? "spawn_agent" : "user";
10130
10171
  const llmDecisionSource = depth > 0 ? "llm_spawn_agent" : "llm";
10131
10172
  const startTime = performance.now();
@@ -10254,7 +10295,6 @@ async function* runAgent(message, options) {
10254
10295
  if (assistantMessage.content) {
10255
10296
  lastContent = assistantMessage.content;
10256
10297
  }
10257
- const tcCount = assistantMessage.tool_calls?.length ?? 0;
10258
10298
  if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
10259
10299
  await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
10260
10300
  const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
@@ -10499,8 +10539,61 @@ var init_content = __esm({
10499
10539
  }
10500
10540
  });
10501
10541
 
10542
+ // ../core/src/session/lineage.ts
10543
+ import fs27 from "node:fs";
10544
+ var parentOfSubsession, stringAncestorsOf, readForkedFromSync, lineageClosureOf, ownerCanApprove;
10545
+ var init_lineage = __esm({
10546
+ "../core/src/session/lineage.ts"() {
10547
+ "use strict";
10548
+ init_store();
10549
+ parentOfSubsession = (sessionId) => {
10550
+ const i = sessionId.lastIndexOf("__sub__");
10551
+ return i > 0 ? sessionId.slice(0, i) : void 0;
10552
+ };
10553
+ stringAncestorsOf = (sessionId) => {
10554
+ const out = [sessionId];
10555
+ let cur = sessionId;
10556
+ for (; ; ) {
10557
+ const p = parentOfSubsession(cur);
10558
+ if (p === void 0) break;
10559
+ out.push(p);
10560
+ cur = p;
10561
+ }
10562
+ return out;
10563
+ };
10564
+ readForkedFromSync = (sessionId) => {
10565
+ try {
10566
+ const raw = fs27.readFileSync(getStatePath(sessionId), "utf-8");
10567
+ const v = JSON.parse(raw)?.forkedFrom;
10568
+ return typeof v === "string" && v.length > 0 ? v : void 0;
10569
+ } catch {
10570
+ return void 0;
10571
+ }
10572
+ };
10573
+ lineageClosureOf = (sessionId) => {
10574
+ const closure = /* @__PURE__ */ new Set();
10575
+ const expand = (id) => {
10576
+ if (closure.has(id)) return;
10577
+ const ancestors = stringAncestorsOf(id);
10578
+ for (const a of ancestors) closure.add(a);
10579
+ const root = ancestors[ancestors.length - 1];
10580
+ const from = readForkedFromSync(root);
10581
+ if (from && !closure.has(from)) expand(from);
10582
+ };
10583
+ expand(sessionId);
10584
+ return closure;
10585
+ };
10586
+ ownerCanApprove = (entrySessionId, approverSessionId) => {
10587
+ if (entrySessionId === approverSessionId) return true;
10588
+ if (!entrySessionId.includes("__sub__")) return false;
10589
+ const closure = lineageClosureOf(approverSessionId);
10590
+ return stringAncestorsOf(entrySessionId).some((a, i) => i > 0 && closure.has(a));
10591
+ };
10592
+ }
10593
+ });
10594
+
10502
10595
  // ../core/src/agent/subagent.ts
10503
- var SUBAGENT_DENYLIST, runSubagent;
10596
+ var SUBAGENT_DENYLIST, activeSubagents, RESUME_NOTE, runSubagent;
10504
10597
  var init_subagent = __esm({
10505
10598
  "../core/src/agent/subagent.ts"() {
10506
10599
  "use strict";
@@ -10511,9 +10604,18 @@ var init_subagent = __esm({
10511
10604
  init_type();
10512
10605
  init_registry4();
10513
10606
  init_hooks();
10607
+ init_lineage();
10514
10608
  SUBAGENT_DENYLIST = /* @__PURE__ */ new Set(["run_command", "delete_path"]);
10609
+ activeSubagents = /* @__PURE__ */ new Set();
10610
+ RESUME_NOTE = [
10611
+ `
10612
+ # \u3010\u7EED\u8DD1\u8BF4\u660E\u3011`,
10613
+ `\u4F60\u6B64\u524D\u7684\u5DE5\u4F5C\u5386\u53F2\u5DF2\u5B8C\u6574\u6062\u590D\u5230\u672C\u6B21\u4E0A\u4E0B\u6587\uFF08\u78C1\u76D8\u6700\u65B0\u72B6\u6001\uFF0C\u53EF\u80FD\u6BD4\u4F60\u4E0A\u6B21\u8BB0\u5FC6\u66F4\u65B0\uFF09\u3002`,
10614
+ `\u8BF7\u5148\u5BF9\u7167\u5386\u53F2\u7B80\u8981\u56DE\u987E\u5DF2\u5B8C\u6210 / \u672A\u5B8C\u6210\u90E8\u5206\uFF0C\u518D\u7EE7\u7EED\u6267\u884C\u65B0\u4EFB\u52A1\uFF1B\u6700\u7EC8\u6C47\u62A5\u4E5F\u8BF7\u5148\u6982\u8FF0\u6B64\u524D\u8FDB\u5C55\u3002`
10615
+ ].join("\n");
10515
10616
  runSubagent = async (spec, ctx, getGlobalTools) => {
10516
- const { task, name, role } = spec;
10617
+ const { task, name, role, resumeSessionId } = spec;
10618
+ const resuming = typeof resumeSessionId === "string" && resumeSessionId.length > 0;
10517
10619
  if (ctx.depth >= MAX_AGENT_DEPTH) {
10518
10620
  return {
10519
10621
  ok: false,
@@ -10521,7 +10623,39 @@ var init_subagent = __esm({
10521
10623
  sessionId: ""
10522
10624
  };
10523
10625
  }
10524
- const subSessionId = `${ctx.sessionId}__sub__${createUUID()}`;
10626
+ if (resuming) {
10627
+ const target = resumeSessionId;
10628
+ if (!target.includes("__sub__")) {
10629
+ return {
10630
+ ok: false,
10631
+ output: `\u274C [\u7EED\u8DD1\u5931\u8D25]\uFF1A${target} \u4E0D\u662F\u5B50 Agent \u4F1A\u8BDD ID\uFF08\u5E94\u5F62\u5982 <\u4F1A\u8BDD>__sub__<uuid>\uFF0C\u6765\u81EA\u6B64\u524D spawn_agent \u8FD4\u56DE\u7684 agent_id\uFF09\u3002`,
10632
+ sessionId: ""
10633
+ };
10634
+ }
10635
+ if (!ownerCanApprove(target, ctx.sessionId)) {
10636
+ return {
10637
+ ok: false,
10638
+ output: `\u274C [\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50\u4F1A\u8BDD ${target} \u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF08\u53CA\u5176 fork \u8840\u7F18\uFF09\uFF0C\u7981\u6B62\u8DE8\u4F1A\u8BDD\u590D\u6D3B\u3002\u8BF7\u6838\u5BF9 agent_id \u6216\u6539\u7528\u65B0\u5EFA\u6D3E\u751F\u3002`,
10639
+ sessionId: ""
10640
+ };
10641
+ }
10642
+ if (activeSubagents.has(target)) {
10643
+ return {
10644
+ ok: false,
10645
+ output: `\u274C [\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50 Agent ${target} \u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u7981\u6B62\u5E76\u53D1\u7EED\u8DD1\u540C\u4E00\u5B50\u4F1A\u8BDD\uFF08\u4F1A\u4EA4\u53C9\u5199\u574F\u8F6C\u5F55\uFF09\u3002\u8BF7\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u8BD5\u3002`,
10646
+ sessionId: ""
10647
+ };
10648
+ }
10649
+ const existing = await readTranscriptLines(target);
10650
+ if (existing.length === 0) {
10651
+ return {
10652
+ ok: false,
10653
+ output: `\u274C [\u7EED\u8DD1\u5931\u8D25]\uFF1A\u5B50\u4F1A\u8BDD ${target} \u7684\u8F6C\u5F55\u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u7EED\u8DD1\u3002\u8BF7\u6539\u7528\u65B0\u5EFA\u6D3E\u751F\u3002`,
10654
+ sessionId: ""
10655
+ };
10656
+ }
10657
+ }
10658
+ const subSessionId = resuming ? resumeSessionId : `${ctx.sessionId}__sub__${createUUID()}`;
10525
10659
  const parentSystemPrompt = ctx.parentSystemPrompt || "";
10526
10660
  const manifest = name ? getAgent(name) : void 0;
10527
10661
  if (name && !manifest) {
@@ -10531,7 +10665,7 @@ var init_subagent = __esm({
10531
10665
  sessionId: ""
10532
10666
  };
10533
10667
  }
10534
- const subSystem = manifest ? [
10668
+ const subSystem = (manifest ? [
10535
10669
  `# \u5B50 Agent \u8EAB\u4EFD\uFF1A${manifest.name}`,
10536
10670
  manifest.body,
10537
10671
  `
@@ -10554,11 +10688,12 @@ var init_subagent = __esm({
10554
10688
  `
10555
10689
  # \u3010\u7EE7\u627F\u7684\u5168\u5C40\u4EE3\u7801\u89C4\u8303\u3011`,
10556
10690
  parentSystemPrompt
10557
- ].join("\n");
10691
+ ].join("\n")) + (resuming ? RESUME_NOTE : "");
10558
10692
  const allTools = getGlobalTools();
10559
10693
  const wantsMcp = manifest?.tools.some((t) => t === "mcp_call" || t === "mcp_list_tools" || t.startsWith("mcp__")) ?? false;
10560
10694
  const toolSchemas = manifest && manifest.tools.length > 0 ? allTools.filter((t) => manifest.tools.includes(t.function.name) || wantsMcp && (t.function.name === "mcp_call" || t.function.name === "mcp_list_tools")) : allTools.filter((t) => !SUBAGENT_DENYLIST.has(t.function.name));
10561
- console.log(`\u{1F423} \u6D3E\u751F\u5B50 Agent [\u6DF1\u5EA6: ${ctx.depth + 1}/${MAX_AGENT_DEPTH}]${manifest ? ` \u58F0\u660E\u5F0F=${manifest.name}` : ""} \u4EFB\u52A1: "${task.slice(0, 50)}..."`);
10695
+ console.log(`${resuming ? "\u{1F501} \u7EED\u8DD1" : "\u{1F423} \u6D3E\u751F"}\u5B50 Agent [\u6DF1\u5EA6: ${ctx.depth + 1}/${MAX_AGENT_DEPTH}][${subSessionId}]${manifest ? ` \u58F0\u660E\u5F0F=${manifest.name}` : ""} \u4EFB\u52A1: "${task.slice(0, 50)}..."`);
10696
+ activeSubagents.add(subSessionId);
10562
10697
  try {
10563
10698
  const subMessages = await buildContextMessages(subSessionId, { role: "user", content: task }, subSystem);
10564
10699
  await appendMessage({ sessionId: subSessionId, role: "user", content: task });
@@ -10582,6 +10717,8 @@ var init_subagent = __esm({
10582
10717
  // ★ 同步透传宿主审批钩子,子 agent 高危工具仍走同一审批通道
10583
10718
  permissionMode: ctx.permissionMode,
10584
10719
  // ★ P1-6 透传:子 agent 工作区文件编辑也走 auto 分类器
10720
+ noEarlyFinal: true,
10721
+ // ★ 子 agent final 是交付父级的汇报,EARLY_FINAL 误推一轮纯浪费;父可经续跑纠错
10585
10722
  model: manifest?.model
10586
10723
  // ★ per-agent 模型覆盖;undefined 时 model.ts 回退全局 MODEL_NAME
10587
10724
  };
@@ -10615,13 +10752,15 @@ var init_subagent = __esm({
10615
10752
  return result ?? { ok: false, output: `\u274C [\u6D3E\u751F\u6267\u884C\u5931\u8D25]: \u672A\u77E5\u9519\u8BEF`, sessionId: subSessionId, manifestName: manifest?.name };
10616
10753
  } catch (error) {
10617
10754
  return { ok: false, output: `\u274C [\u6D3E\u751F\u6267\u884C\u5931\u8D25]: ${error.message}`, sessionId: subSessionId, manifestName: manifest?.name };
10755
+ } finally {
10756
+ activeSubagents.delete(subSessionId);
10618
10757
  }
10619
10758
  };
10620
10759
  }
10621
10760
  });
10622
10761
 
10623
10762
  // ../core/src/hooks/loader.ts
10624
- import fs27 from "fs/promises";
10763
+ import fs28 from "fs/promises";
10625
10764
  import path30 from "path";
10626
10765
  var readHooksConfig, appendValidated, validateRule, parseStdoutDecision, parseAgentDecision, compileRule2, loadHooks, initHooks;
10627
10766
  var init_loader4 = __esm({
@@ -10645,7 +10784,7 @@ var init_loader4 = __esm({
10645
10784
  for (const configPath of paths) {
10646
10785
  let raw;
10647
10786
  try {
10648
- raw = await fs27.readFile(configPath, "utf-8");
10787
+ raw = await fs28.readFile(configPath, "utf-8");
10649
10788
  } catch {
10650
10789
  continue;
10651
10790
  }
@@ -10975,7 +11114,7 @@ ${m.context}`;
10975
11114
  });
10976
11115
 
10977
11116
  // ../core/src/skills/loader.ts
10978
- import fs28 from "fs/promises";
11117
+ import fs29 from "fs/promises";
10979
11118
  import path31 from "path";
10980
11119
  import { fileURLToPath as fileURLToPath2 } from "url";
10981
11120
  var BUILTIN_DIR, SOURCES2, MAX_SKILL_BODY_BYTES, splitList, parseSkillAt, loadSkills, initSkills;
@@ -10998,7 +11137,7 @@ var init_loader5 = __esm({
10998
11137
  parseSkillAt = async (skillFile, dir, source) => {
10999
11138
  let raw;
11000
11139
  try {
11001
- raw = await fs28.readFile(skillFile, "utf-8");
11140
+ raw = await fs29.readFile(skillFile, "utf-8");
11002
11141
  } catch {
11003
11142
  return null;
11004
11143
  }
@@ -11068,7 +11207,7 @@ var init_loader5 = __esm({
11068
11207
  });
11069
11208
 
11070
11209
  // ../core/src/agents/loader.ts
11071
- import fs29 from "fs/promises";
11210
+ import fs30 from "fs/promises";
11072
11211
  import path32 from "path";
11073
11212
  import { fileURLToPath as fileURLToPath3 } from "url";
11074
11213
  var BUILTIN_DIR2, SOURCES3, parseAgentAt, initAgents;
@@ -11088,7 +11227,7 @@ var init_loader6 = __esm({
11088
11227
  parseAgentAt = async (file, dir, source) => {
11089
11228
  let raw;
11090
11229
  try {
11091
- raw = await fs29.readFile(file, "utf-8");
11230
+ raw = await fs30.readFile(file, "utf-8");
11092
11231
  } catch {
11093
11232
  return null;
11094
11233
  }
@@ -11160,7 +11299,7 @@ var init_registry7 = __esm({
11160
11299
  });
11161
11300
 
11162
11301
  // ../core/src/commands/loader.ts
11163
- import fs30 from "fs/promises";
11302
+ import fs31 from "fs/promises";
11164
11303
  import path33 from "path";
11165
11304
  import { fileURLToPath as fileURLToPath4 } from "url";
11166
11305
  var BUILTIN_DIR3, SOURCES4, MAX_COMMAND_BODY_BYTES, parseCommandAt, loadCommands, initCommands;
@@ -11181,7 +11320,7 @@ var init_loader7 = __esm({
11181
11320
  parseCommandAt = async (file, _dir, source) => {
11182
11321
  let raw;
11183
11322
  try {
11184
- raw = await fs30.readFile(file, "utf-8");
11323
+ raw = await fs31.readFile(file, "utf-8");
11185
11324
  } catch {
11186
11325
  return null;
11187
11326
  }
@@ -11236,7 +11375,7 @@ var init_loader7 = __esm({
11236
11375
  });
11237
11376
 
11238
11377
  // ../core/src/outputStyles/loader.ts
11239
- import fs31 from "fs/promises";
11378
+ import fs32 from "fs/promises";
11240
11379
  import path34 from "path";
11241
11380
  import { fileURLToPath as fileURLToPath5 } from "url";
11242
11381
  var BUILTIN_DIR4, MAX_STYLE_BODY_BYTES, SOURCES5, parseStyleAt, loadOutputStyles, initOutputStyles;
@@ -11257,7 +11396,7 @@ var init_loader8 = __esm({
11257
11396
  parseStyleAt = async (file, _dir, source) => {
11258
11397
  let raw;
11259
11398
  try {
11260
- raw = await fs31.readFile(file, "utf-8");
11399
+ raw = await fs32.readFile(file, "utf-8");
11261
11400
  } catch {
11262
11401
  return null;
11263
11402
  }
@@ -11370,7 +11509,7 @@ var init_bootstrap = __esm({
11370
11509
  });
11371
11510
 
11372
11511
  // ../core/src/statusLine/config.ts
11373
- import fs32 from "fs/promises";
11512
+ import fs33 from "fs/promises";
11374
11513
  import path35 from "path";
11375
11514
  var parseBlock, readStatusLineConfig;
11376
11515
  var init_config2 = __esm({
@@ -11396,7 +11535,7 @@ var init_config2 = __esm({
11396
11535
  for (const configPath of paths) {
11397
11536
  let raw;
11398
11537
  try {
11399
- raw = await fs32.readFile(configPath, "utf-8");
11538
+ raw = await fs33.readFile(configPath, "utf-8");
11400
11539
  } catch {
11401
11540
  continue;
11402
11541
  }
@@ -11416,7 +11555,7 @@ var init_config2 = __esm({
11416
11555
 
11417
11556
  // ../core/src/statusLine/runner.ts
11418
11557
  import { spawn as spawn5 } from "child_process";
11419
- import fs33 from "fs";
11558
+ import fs34 from "fs";
11420
11559
  import path36 from "path";
11421
11560
  import { fileURLToPath as fileURLToPath6 } from "url";
11422
11561
  var STATUSLINE_TIMEOUT_MS, cachedVersion, runStatusLine;
@@ -11435,7 +11574,7 @@ var init_runner = __esm({
11435
11574
  ];
11436
11575
  for (const p of candidates) {
11437
11576
  try {
11438
- const pkg = JSON.parse(fs33.readFileSync(p, "utf-8"));
11577
+ const pkg = JSON.parse(fs34.readFileSync(p, "utf-8"));
11439
11578
  if (typeof pkg.version === "string" && pkg.version) {
11440
11579
  cachedVersion = pkg.version;
11441
11580
  break;
@@ -11495,7 +11634,7 @@ var init_createModel = __esm({
11495
11634
 
11496
11635
  // ../core/src/observability/store.ts
11497
11636
  import path37 from "path";
11498
- import fs34 from "fs/promises";
11637
+ import fs35 from "fs/promises";
11499
11638
  var getTraceDirPath, getTracePath, tracePathCache, ensuredTraceDirs, ensureTraceDir, getGlobalClockPath, getTraceStorePath;
11500
11639
  var init_store3 = __esm({
11501
11640
  "../core/src/observability/store.ts"() {
@@ -11515,7 +11654,7 @@ var init_store3 = __esm({
11515
11654
  ensureTraceDir = async (mainTraceId) => {
11516
11655
  const dir = getTraceDirPath(mainTraceId);
11517
11656
  if (ensuredTraceDirs.has(dir)) return;
11518
- await fs34.mkdir(dir, { recursive: true });
11657
+ await fs35.mkdir(dir, { recursive: true });
11519
11658
  ensuredTraceDirs.add(dir);
11520
11659
  };
11521
11660
  getGlobalClockPath = () => {
@@ -11528,7 +11667,7 @@ var init_store3 = __esm({
11528
11667
  if (cached2) return cached2;
11529
11668
  await ensureTraceDir(rootId);
11530
11669
  const dir = getTraceDirPath(rootId);
11531
- const files = await fs34.readdir(dir);
11670
+ const files = await fs35.readdir(dir);
11532
11671
  const existedFile = files.find((f) => f.endsWith(`__${rootId}.jsonl`));
11533
11672
  let fileName = "";
11534
11673
  if (existedFile) {
@@ -11545,7 +11684,7 @@ var init_store3 = __esm({
11545
11684
  });
11546
11685
 
11547
11686
  // ../core/src/observability/trace.ts
11548
- import fs35 from "fs/promises";
11687
+ import fs36 from "fs/promises";
11549
11688
  import path38 from "path";
11550
11689
  async function emitTrace(base) {
11551
11690
  const event = {
@@ -11568,7 +11707,7 @@ async function emitTrace(base) {
11568
11707
  async function appendTraceEvent(event) {
11569
11708
  const p = await getTraceStorePath(event.sessionId);
11570
11709
  const line = JSON.stringify(event) + "\n";
11571
- await fs35.appendFile(p, line, "utf-8");
11710
+ await fs36.appendFile(p, line, "utf-8");
11572
11711
  if (workspaceTraceBytes < 0) {
11573
11712
  workspaceTraceBytes = await getDirBytes(getTracePath());
11574
11713
  }
@@ -11600,8 +11739,8 @@ async function maybeCleanupAfterAppend(sessionId) {
11600
11739
  lastSizeCleanupAt2 = now;
11601
11740
  diskLastCleanupAt2 = now;
11602
11741
  const clockPath = getGlobalClockPath();
11603
- await fs35.mkdir(path38.dirname(clockPath), { recursive: true });
11604
- await fs35.writeFile(clockPath, JSON.stringify({ lastCleanupAt: now, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
11742
+ await fs36.mkdir(path38.dirname(clockPath), { recursive: true });
11743
+ await fs36.writeFile(clockPath, JSON.stringify({ lastCleanupAt: now, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
11605
11744
  if (deletedCount > 0) {
11606
11745
  console.log(`\u2728 [Trace\u8FD0\u7EF4] \u5171\u9500\u6BC1 ${deletedCount} \u4E2A\u4F1A\u8BDD\u65E5\u5FD7\uFF08\u8F6F\u89C4\u5219\u8FC7\u671F\u6E05\u7406 + \u5BB9\u91CF\u786C\u9A71\u9010\uFF09\uFF0C\u78C1\u76D8\u6C34\u4F4D\u56DE\u843D\u3002`);
11607
11746
  }
@@ -11621,7 +11760,7 @@ async function cleanupOldTraceFiles() {
11621
11760
  let deleted = 0;
11622
11761
  let relativePaths;
11623
11762
  try {
11624
- relativePaths = await fs35.readdir(dir, { recursive: true });
11763
+ relativePaths = await fs36.readdir(dir, { recursive: true });
11625
11764
  } catch {
11626
11765
  return 0;
11627
11766
  }
@@ -11634,9 +11773,9 @@ async function cleanupOldTraceFiles() {
11634
11773
  if (fileBornDay >= cutoff) continue;
11635
11774
  const fullPath = path38.join(dir, relativePath);
11636
11775
  try {
11637
- const st = await fs35.stat(fullPath);
11776
+ const st = await fs36.stat(fullPath);
11638
11777
  if (now - st.mtimeMs < ACTIVE_GRACE_MS2) continue;
11639
- await fs35.unlink(fullPath);
11778
+ await fs36.unlink(fullPath);
11640
11779
  deleted += 1;
11641
11780
  } catch {
11642
11781
  }
@@ -11647,7 +11786,7 @@ async function evictOldestForSize2(targetBytes) {
11647
11786
  const dir = getTracePath();
11648
11787
  let relativePaths;
11649
11788
  try {
11650
- relativePaths = await fs35.readdir(dir, { recursive: true });
11789
+ relativePaths = await fs36.readdir(dir, { recursive: true });
11651
11790
  } catch {
11652
11791
  return 0;
11653
11792
  }
@@ -11657,7 +11796,7 @@ async function evictOldestForSize2(targetBytes) {
11657
11796
  const fileName = path38.basename(relativePath);
11658
11797
  if (!re.test(fileName)) continue;
11659
11798
  try {
11660
- const st = await fs35.stat(path38.join(dir, relativePath));
11799
+ const st = await fs36.stat(path38.join(dir, relativePath));
11661
11800
  candidates.push({ mtime: st.mtimeMs, fullPath: path38.join(dir, relativePath), size: st.size });
11662
11801
  } catch {
11663
11802
  }
@@ -11668,7 +11807,7 @@ async function evictOldestForSize2(targetBytes) {
11668
11807
  for (const c of candidates) {
11669
11808
  if (totalBytes <= targetBytes) break;
11670
11809
  try {
11671
- await fs35.unlink(c.fullPath);
11810
+ await fs36.unlink(c.fullPath);
11672
11811
  totalBytes -= c.size;
11673
11812
  deleted += 1;
11674
11813
  } catch {
@@ -11700,7 +11839,7 @@ var init_trace = __esm({
11700
11839
  if (clockLoaded2) return;
11701
11840
  clockLoaded2 = true;
11702
11841
  try {
11703
- const rawClock = await fs35.readFile(getGlobalClockPath(), "utf-8");
11842
+ const rawClock = await fs36.readFile(getGlobalClockPath(), "utf-8");
11704
11843
  diskLastCleanupAt2 = JSON.parse(rawClock).lastCleanupAt || 0;
11705
11844
  } catch {
11706
11845
  diskLastCleanupAt2 = 0;
@@ -11719,6 +11858,7 @@ var pendingLocks, waitForUserApproval, resolveUserApprovalLock;
11719
11858
  var init_approvalGate = __esm({
11720
11859
  "../core/src/tool/approvalGate.ts"() {
11721
11860
  "use strict";
11861
+ init_lineage();
11722
11862
  pendingLocks = /* @__PURE__ */ new Map();
11723
11863
  waitForUserApproval = async (sessionId, toolsId, signal) => {
11724
11864
  if (signal?.aborted) return "deny";
@@ -11746,7 +11886,7 @@ var init_approvalGate = __esm({
11746
11886
  resolveUserApprovalLock = (sessionId, toolsId, decision) => {
11747
11887
  const entry = pendingLocks.get(toolsId);
11748
11888
  if (!entry) return false;
11749
- const isOwner = entry.sessionId === sessionId || entry.sessionId.startsWith(`${sessionId}__sub__`);
11889
+ const isOwner = ownerCanApprove(entry.sessionId, sessionId);
11750
11890
  if (!isOwner) return false;
11751
11891
  entry.resolver(decision);
11752
11892
  pendingLocks.delete(toolsId);
@@ -11846,7 +11986,7 @@ ${PLAN_MODE_AUTO_ENTER_HINT}
11846
11986
 
11847
11987
  // ../core/src/serve/chatProcessing.ts
11848
11988
  import path39 from "path";
11849
- import fs36 from "fs";
11989
+ import fs37 from "fs";
11850
11990
  var handleUnifiedChat;
11851
11991
  var init_chatProcessing = __esm({
11852
11992
  "../core/src/serve/chatProcessing.ts"() {
@@ -11900,7 +12040,7 @@ ${inject}`;
11900
12040
  if (roots.length > 1) {
11901
12041
  let activeReal = getActiveWorkspaceRoot();
11902
12042
  try {
11903
- activeReal = fs36.realpathSync(activeReal);
12043
+ activeReal = fs37.realpathSync(activeReal);
11904
12044
  } catch {
11905
12045
  }
11906
12046
  const lines = roots.map((r) => `- ${path39.basename(r) || r}: ${r}${r === activeReal ? "\uFF08\u9ED8\u8BA4\u6839\uFF1A\u76F8\u5BF9\u8DEF\u5F84/\u547D\u4EE4\u57FA\u51C6\uFF09" : "\uFF08\u53EF\u76F4\u63A5\u8BFB\u5199\uFF09"}`);
@@ -11997,7 +12137,7 @@ ${lines.join("\n")}
11997
12137
  });
11998
12138
 
11999
12139
  // ../core/src/session/fork.ts
12000
- import fs37 from "fs/promises";
12140
+ import fs38 from "fs/promises";
12001
12141
  var listForkAnchors, derivePrefix, forkSession;
12002
12142
  var init_fork = __esm({
12003
12143
  "../core/src/session/fork.ts"() {
@@ -12069,7 +12209,7 @@ var init_fork = __esm({
12069
12209
  const prefix = derivePrefix(lines, upToLineId);
12070
12210
  const newId = createUUID();
12071
12211
  await ensureSessionsDir(newId);
12072
- const rawText = await fs37.readFile(getTranscriptPath(sourceId), "utf-8");
12212
+ const rawText = await fs38.readFile(getTranscriptPath(sourceId), "utf-8");
12073
12213
  const kept = [];
12074
12214
  let good = 0;
12075
12215
  for (const s of rawText.split("\n")) {
@@ -12085,7 +12225,7 @@ var init_fork = __esm({
12085
12225
  }
12086
12226
  let payload = kept.join("\n");
12087
12227
  if (payload.length > 0 && !payload.endsWith("\n")) payload += "\n";
12088
- await fs37.writeFile(getTranscriptPath(newId), payload, "utf-8");
12228
+ await fs38.writeFile(getTranscriptPath(newId), payload, "utf-8");
12089
12229
  const srcState = await readStore(sourceId);
12090
12230
  const srcRolling = await getRollingState(sourceId);
12091
12231
  const legacy = !lines.some(isEventLine);
@@ -12101,6 +12241,9 @@ var init_fork = __esm({
12101
12241
  rollingSummary,
12102
12242
  consecutiveFailures: 0,
12103
12243
  title: `${srcTitle}(\u5206\u53C9)`.slice(0, 200),
12244
+ // ★ fork 血缘元数据:新 ID 是纯 UUID(字符串上无痕),血缘回溯全靠此字段
12245
+ // (session/lineage.ts 消费:审批网关归属 / 子 Agent 续跑归属)。
12246
+ forkedFrom: sourceId,
12104
12247
  // ★ 校准数据透传:fork 首轮压缩判定不从 1.4 重来(纯收益,语义同源会话续接)
12105
12248
  calibRatio: srcRolling.calibRatio,
12106
12249
  lastRealPromptTokens: srcRolling.lastRealPromptTokens,
@@ -13661,8 +13804,8 @@ var init_SlashMenu = __esm({
13661
13804
  init_theme();
13662
13805
  MAX_ROWS = 10;
13663
13806
  SlashMenu = ({ entries, selectedIndex, cols }) => {
13664
- const visible = entries.slice(0, MAX_ROWS);
13665
- const more = entries.length > MAX_ROWS;
13807
+ const start = entries.length <= MAX_ROWS ? 0 : Math.max(0, Math.min(selectedIndex - MAX_ROWS + 1, selectedIndex, entries.length - MAX_ROWS));
13808
+ const visible = entries.slice(start, start + MAX_ROWS);
13666
13809
  const cmdW = Math.min(22, Math.max(8, ...visible.map((e) => e.name.length)) + 1);
13667
13810
  const descMax = Math.max(12, cols - cmdW - 6);
13668
13811
  return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", borderStyle: "round", borderColor: THEME.coral, paddingX: 1, paddingY: 0, marginTop: 1, children: [
@@ -13680,9 +13823,11 @@ var init_SlashMenu = __esm({
13680
13823
  /* @__PURE__ */ jsx14(Text14, { color: active ? THEME.gray : THEME.grayDim, children: desc })
13681
13824
  ] }, e.name);
13682
13825
  }),
13683
- more ? /* @__PURE__ */ jsxs12(Text14, { color: THEME.coralMuted, children: [
13684
- "\u2026 \u8FD8\u6709 ",
13685
- entries.length - MAX_ROWS,
13826
+ entries.length > MAX_ROWS ? /* @__PURE__ */ jsxs12(Text14, { color: THEME.coralMuted, children: [
13827
+ "\u2191\u2193 \u6EDA\u52A8 \xB7 ",
13828
+ selectedIndex + 1,
13829
+ "/",
13830
+ entries.length,
13686
13831
  " \u6761"
13687
13832
  ] }) : null
13688
13833
  ] });
@@ -13778,11 +13923,11 @@ var init_TopPanel = __esm({
13778
13923
  }
13779
13924
  });
13780
13925
 
13781
- // src/inspect.ts
13782
- import fs38 from "fs";
13926
+ // ../core/src/observability/inspect.ts
13927
+ import fs39 from "fs";
13783
13928
  var CWD, fmt, readTrace, inspectUsage, inspectContext, inspectPermissions, inspectMcp, inspectHooks, inspectDebug;
13784
13929
  var init_inspect = __esm({
13785
- "src/inspect.ts"() {
13930
+ "../core/src/observability/inspect.ts"() {
13786
13931
  "use strict";
13787
13932
  init_config();
13788
13933
  init_permissions();
@@ -13797,7 +13942,7 @@ var init_inspect = __esm({
13797
13942
  if (!sessionId) return [];
13798
13943
  try {
13799
13944
  const p = await getTraceStorePath(sessionId);
13800
- const text = await fs38.promises.readFile(p, "utf8");
13945
+ const text = await fs39.promises.readFile(p, "utf8");
13801
13946
  const out = [];
13802
13947
  for (const line of text.split("\n")) {
13803
13948
  if (!line.trim()) continue;
@@ -13967,7 +14112,7 @@ var init_App = __esm({
13967
14112
  return /* @__PURE__ */ jsx17(MessageBlock, { row, wrapW, streamTail });
13968
14113
  };
13969
14114
  isDynamicRow = (r) => r.kind === "assistant" && !!r.streaming || r.kind === "thinking" && !!r.streaming || r.kind === "tool" && r.status === "running" || r.kind === "todos" && !!r.active;
13970
- App = ({ resumeSessionId, initialPlanMode, initialAutoMode, initialIncludeProject }) => {
14115
+ App = ({ resumeSessionId, initialPlanMode, initialAutoMode, initialIncludeProject, engineReady }) => {
13971
14116
  const state = useChatState(resumeSessionId, initialPlanMode, initialAutoMode);
13972
14117
  const { exit } = useApp();
13973
14118
  const { stdout } = useStdout();
@@ -13979,6 +14124,16 @@ var init_App = __esm({
13979
14124
  const [selectIdx, setSelectIdx] = useState4(0);
13980
14125
  const [modelDisplay, setModelDisplay] = useState4(MODEL_NAME);
13981
14126
  const [tick, setTick] = useState4(0);
14127
+ const [engineReadyFlag, setEngineReadyFlag] = useState4(false);
14128
+ useEffect4(() => {
14129
+ let alive = true;
14130
+ void engineReady.then(() => {
14131
+ if (alive) setEngineReadyFlag(true);
14132
+ });
14133
+ return () => {
14134
+ alive = false;
14135
+ };
14136
+ }, [engineReady]);
13982
14137
  const [planEditing, setPlanEditing] = useState4(false);
13983
14138
  const [planDraft, setPlanDraft] = useState4("");
13984
14139
  const [planCursor, setPlanCursor] = useState4(0);
@@ -14092,7 +14247,7 @@ var init_App = __esm({
14092
14247
  for (const name of LOCAL_COMMAND_NAMES) map.set(name, { name, description: descOf(name) });
14093
14248
  for (const c of listCommands()) if (!map.has(c.name)) map.set(c.name, { name: c.name, description: c.description });
14094
14249
  return [...map.values()];
14095
- }, [tick]);
14250
+ }, [tick, engineReadyFlag]);
14096
14251
  const filteredCommands = useMemo(() => {
14097
14252
  if (!input.startsWith("/")) return [];
14098
14253
  const q = input.slice(1).toLowerCase();
@@ -14256,12 +14411,18 @@ ${listText}`);
14256
14411
  return false;
14257
14412
  }
14258
14413
  };
14414
+ const ensureEngine = async () => {
14415
+ if (engineReadyFlag) return;
14416
+ state.pushInfo("\u23F3 \u5F15\u64CE\u52A0\u8F7D\u4E2D\uFF08MCP/skills/commands\uFF09\u2026");
14417
+ await engineReady;
14418
+ };
14259
14419
  const executeSlash = async (fullText) => {
14260
14420
  const text = fullText.trim();
14261
14421
  if (!text.startsWith("/")) {
14262
14422
  await state.submit(text);
14263
14423
  return;
14264
14424
  }
14425
+ await ensureEngine();
14265
14426
  if (await runLocalSlash(text)) return;
14266
14427
  const name = text.slice(1).split(/\s+/)[0] ?? "";
14267
14428
  if (listCommands().some((c) => c.name === name)) {
@@ -14288,6 +14449,7 @@ ${listText}`);
14288
14449
  await executeSlash(v);
14289
14450
  return;
14290
14451
  }
14452
+ await ensureEngine();
14291
14453
  await state.submit(v);
14292
14454
  };
14293
14455
  const confirmPlanEdit = () => {
@@ -14657,9 +14819,9 @@ var main = async () => {
14657
14819
  Promise.resolve().then(() => (init_App(), App_exports)),
14658
14820
  Promise.resolve().then(() => (init_config(), config_exports))
14659
14821
  ]);
14660
- const dispose = await initEngine2(agentTools2, { includeProject });
14822
+ const engineReady = initEngine2(agentTools2, { includeProject });
14661
14823
  const onExit = () => {
14662
- dispose();
14824
+ void engineReady.then((dispose) => dispose());
14663
14825
  };
14664
14826
  process.on("SIGINT", () => {
14665
14827
  onExit();
@@ -14691,7 +14853,7 @@ var main = async () => {
14691
14853
  resumeId = await getMostRecentSessionId2() ?? void 0;
14692
14854
  }
14693
14855
  const { waitUntilExit } = render(
14694
- /* @__PURE__ */ jsx18(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject }),
14856
+ /* @__PURE__ */ jsx18(App2, { resumeSessionId: resumeId, initialPlanMode: args.plan, initialAutoMode: args.auto, initialIncludeProject: includeProject, engineReady }),
14695
14857
  // exitOnCtrlC:false:Ctrl+C 交由 App useInput 处理(统一退出/中止语义);
14696
14858
  // patchConsole:false:避免 console 劫持与全屏重绘叠加闪屏。
14697
14859
  { exitOnCtrlC: false, patchConsole: false }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepseeker-code",
3
- "version": "1.0.42",
3
+ "version": "1.0.45",
4
4
  "description": "DeepSeeker-Code 终端客户端(React Ink,in-process 直驱 runAgent)",
5
5
  "homepage": "https://github.com/xknk/deepSeekCode#readme",
6
6
  "repository": {