deepseeker-code 1.0.59 → 1.0.61

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 +695 -480
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -4228,7 +4228,7 @@ async function killBackgroundTasksUnder(dirPath) {
4228
4228
  }
4229
4229
  return killed;
4230
4230
  }
4231
- var _winShellCache, resolveWinShell, MAX_BUFFER_CHARS, registry, createBgTask, attachTaskLifecycle, makeTaskExitNotifier, adoptRunningProcess, backgroundTools, killAllBackgroundTasks;
4231
+ var _winShellCache, resolveWinShell, MAX_BUFFER_CHARS, registry, createBgTask, attachTaskLifecycle, makeTaskExitNotifier, adoptRunningProcess, BG_WAIT_SECONDS_CAP, clampWaitSeconds, backgroundTools, killAllBackgroundTasks;
4232
4232
  var init_background = __esm({
4233
4233
  "../core/src/tool/registry/background.ts"() {
4234
4234
  "use strict";
@@ -4393,6 +4393,8 @@ var init_background = __esm({
4393
4393
  void attachTaskLifecycle(task, proc, { signal: meta.signal, codec, onExit: meta.onExit });
4394
4394
  return task.taskId;
4395
4395
  };
4396
+ BG_WAIT_SECONDS_CAP = 90;
4397
+ clampWaitSeconds = (v) => Math.min(Math.max(0, Math.floor(v ?? 0)), BG_WAIT_SECONDS_CAP);
4396
4398
  backgroundTools = [
4397
4399
  {
4398
4400
  type: "function",
@@ -4481,13 +4483,13 @@ task_id: ${taskId}
4481
4483
  type: "function",
4482
4484
  function: {
4483
4485
  name: "get_background_output",
4484
- 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",
4486
+ 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\u6EE1 wait_seconds \u8FD4\u56DE\u5F53\u524D\u5FEB\u7167\uFF09\u2014\u2014\u7528\u5B83\u7B49\uFF0C\u4E0D\u8981\u4E0D\u5E26 wait \u53CD\u590D\u8F6E\u8BE2\u67E5\u8BE2\u3002",
4485
4487
  parameters: {
4486
4488
  type: "object",
4487
4489
  properties: {
4488
4490
  task_id: { type: "string", description: "\u540E\u53F0\u4EFB\u52A1\u7684 task_id\uFF08run_in_background \u8FD4\u56DE\uFF0C\u6216 run_command \u8D85\u65F6\u81EA\u52A8\u8F6C\u540E\u53F0\u8FD4\u56DE\uFF09" },
4489
4491
  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" },
4490
- 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" }
4492
+ 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 90 \u79D2\u2014\u2014\u987B\u4F4E\u4E8E\u8C03\u5EA6\u5C42 120s \u5DE5\u5177\u7194\u65AD\u7EBF\uFF0C\u66F4\u957F\u7684 wait \u4F1A\u88AB\u7194\u65AD\u3001\u771F\u5B9E\u7ED3\u679C\u88AB\u4E22\u5F03\u6362\u6210\u8D85\u65F6\u6587\u6848\u3002\u9884\u8BA1\u66F4\u4E45\u5C31\u4F20 90\uFF0C\u8FD4\u56DE\u4ECD running \u5C31\u518D\u8C03\u4E00\u6B21\u63A5\u7740\u7B49\uFF1B\u53EA\u770B\u5F53\u524D\u65E5\u5FD7\u5C31\u7701\u7565\u6B64\u53C2\u6570" }
4491
4493
  },
4492
4494
  required: ["task_id"]
4493
4495
  },
@@ -4498,7 +4500,7 @@ task_id: ${taskId}
4498
4500
  if (!task || task.sessionId !== ctx?.sessionId) {
4499
4501
  return toolFailure(`[\u67E5\u8BE2\u5931\u8D25]\uFF1A\u672A\u627E\u5230 task_id=${args.task_id}\uFF08\u53EF\u80FD\u5DF2\u968F\u670D\u52A1\u91CD\u542F\u4E22\u5931\uFF0C\u6216\u4E0D\u5C5E\u4E8E\u5F53\u524D\u4F1A\u8BDD\uFF09\u3002`);
4500
4502
  }
4501
- const waitMs = Math.min(Math.max(0, args.wait_seconds ?? 0), 120) * 1e3;
4503
+ const waitMs = clampWaitSeconds(args.wait_seconds) * 1e3;
4502
4504
  if (waitMs > 0 && task.status === "running") {
4503
4505
  const deadline = Date.now() + waitMs;
4504
4506
  while (task.status === "running" && Date.now() < deadline && !ctx?.abortSignal?.aborted) {
@@ -6027,13 +6029,17 @@ var init_toolResultCollect = __esm({
6027
6029
  const idleMs = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
6028
6030
  while (true) {
6029
6031
  let timer2;
6030
- const outcome = await Promise.race([
6031
- ret.next().then((step) => ({ timedOut: false, step })),
6032
- new Promise((resolve) => {
6033
- timer2 = setTimeout(() => resolve({ timedOut: true }), idleMs);
6034
- })
6035
- ]);
6036
- if (timer2) clearTimeout(timer2);
6032
+ let outcome;
6033
+ try {
6034
+ outcome = await Promise.race([
6035
+ ret.next().then((step) => ({ timedOut: false, step })),
6036
+ new Promise((resolve) => {
6037
+ timer2 = setTimeout(() => resolve({ timedOut: true }), idleMs);
6038
+ })
6039
+ ]);
6040
+ } finally {
6041
+ if (timer2) clearTimeout(timer2);
6042
+ }
6037
6043
  if (outcome.timedOut) {
6038
6044
  full += `
6039
6045
 
@@ -6054,18 +6060,22 @@ var init_toolResultCollect = __esm({
6054
6060
  const timeoutMs2 = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
6055
6061
  let timer;
6056
6062
  let onAbort;
6057
- const raced = await Promise.race([
6058
- Promise.resolve(ret).then((v2) => ({ kind: "ok", value: v2 })),
6059
- new Promise((resolve) => {
6060
- timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs2);
6061
- }),
6062
- ...signal ? [new Promise((resolve) => {
6063
- onAbort = () => resolve({ kind: "abort" });
6064
- signal.addEventListener("abort", onAbort, { once: true });
6065
- })] : []
6066
- ]);
6067
- if (timer) clearTimeout(timer);
6068
- if (onAbort && signal) signal.removeEventListener("abort", onAbort);
6063
+ let raced;
6064
+ try {
6065
+ raced = await Promise.race([
6066
+ Promise.resolve(ret).then((v2) => ({ kind: "ok", value: v2 })),
6067
+ new Promise((resolve) => {
6068
+ timer = setTimeout(() => resolve({ kind: "timeout" }), timeoutMs2);
6069
+ }),
6070
+ ...signal ? [new Promise((resolve) => {
6071
+ onAbort = () => resolve({ kind: "abort" });
6072
+ signal.addEventListener("abort", onAbort, { once: true });
6073
+ })] : []
6074
+ ]);
6075
+ } finally {
6076
+ if (timer) clearTimeout(timer);
6077
+ if (onAbort && signal) signal.removeEventListener("abort", onAbort);
6078
+ }
6069
6079
  if (raced.kind === "timeout") {
6070
6080
  return { content: `[\u23F3 \u5DE5\u5177\u6267\u884C\u8D85\u65F6\uFF08${Math.round(timeoutMs2 / 1e3)}s \u672A\u8FD4\u56DE\uFF09\uFF0C\u5DF2\u7194\u65AD\u8DF3\u8FC7\u3002\u8BE5\u5DE5\u5177\u53EF\u80FD hang \u6216\u4E0D\u54CD\u5E94\u4E2D\u6B62\u4FE1\u53F7\u3002]`, status: "failed", errorCategory: "runtime" };
6071
6081
  }
@@ -7801,9 +7811,15 @@ ${tail}`,
7801
7811
  cwd,
7802
7812
  detached: !isWin,
7803
7813
  // 非 Win 下支持整个进程组独立
7804
- env: scrubCommandEnv(),
7814
+ env: scrubCommandEnv()
7805
7815
  // ★ 剔除 agent 自身凭证(LLM key / 服务端 token / 搜索 key),防 LLM 经 env/printenv 读取外泄
7806
- signal: ctx?.abortSignal
7816
+ // ★ 中止唯一出口 = 下方 onAbort→killTree(2026-10-27 修「■ 终止无效」):
7817
+ // 勿把 signal 交给 spawn——Node 内部 abort 监听先于 onAbort 触发,TerminateProcess
7818
+ // 只杀最外层进程;随后 killTree 的 taskkill /T 对已死根「找不到进程」直接失败,
7819
+ // 孙进程(tsx 的 esbuild、node --test 的每文件子进程)存活并持有继承的 stdout 管道,
7820
+ // close 永不触发 → 工具永不结束(多层进程树命令下点终止永远转圈)。
7821
+ // 根存活时 taskkill /T 才能整树剿灭;进入工具时已 aborted 由上方前置检查拦截,
7822
+ // pre-check 与 addEventListener 之间全同步无 await,无竞态窗口。
7807
7823
  });
7808
7824
  } catch (e) {
7809
7825
  yield toolFailure(`[\u542F\u52A8\u5931\u8D25]\uFF1Aspawn \u629B\u51FA\u5F02\u5E38\uFF08signal \u5DF2\u4E2D\u6B62\u6216\u547D\u4EE4\u975E\u6CD5\uFF09: ${e?.message ?? e}`).content;
@@ -7813,6 +7829,7 @@ ${tail}`,
7813
7829
  const queue = [];
7814
7830
  let settled = false;
7815
7831
  let exitCode = null;
7832
+ let abortedByUser = false;
7816
7833
  let resolveWaiter = null;
7817
7834
  let idleTimer = null;
7818
7835
  const notifyNewData = () => {
@@ -7866,13 +7883,14 @@ ${tail}`,
7866
7883
  notifyNewData();
7867
7884
  });
7868
7885
  proc.on("close", (code) => {
7869
- exitCode ??= code == null ? -1 : code;
7886
+ exitCode ??= abortedByUser ? -1 : code == null ? -1 : code;
7870
7887
  if (decoder) queue.push(decoder.decode());
7871
7888
  queue.push(pendingUtf8.decode());
7872
7889
  settled = true;
7873
7890
  notifyNewData();
7874
7891
  });
7875
7892
  const onAbort = () => {
7893
+ abortedByUser = true;
7876
7894
  void killTree(proc);
7877
7895
  };
7878
7896
  ctx?.abortSignal?.addEventListener("abort", onAbort);
@@ -12033,7 +12051,7 @@ var init_autoPermission = __esm({
12033
12051
 
12034
12052
  // ../core/src/agent/toolExecution.ts
12035
12053
  import path36 from "path";
12036
- var applyPrivacyMasking, scaleTruncBudget, failResult, emitValidationFailed, resolveEarlyOutcome, runSecurityGates, executeTool, finalizeOutcome, processToolCall;
12054
+ var applyPrivacyMasking, scaleTruncBudget, failResult, emitValidationFailed, resolveEarlyOutcome, runSecurityGates, executeTool, finalizeOutcome, previewArgs, processToolCall, processToolCallInner;
12037
12055
  var init_toolExecution = __esm({
12038
12056
  "../core/src/agent/toolExecution.ts"() {
12039
12057
  "use strict";
@@ -12305,7 +12323,9 @@ ${state.result}`;
12305
12323
  const { calledName, calledArgs, matchedTool } = state;
12306
12324
  state.result = applyPrivacyMasking(matchedTool?.function?.privacyMaskingRules, calledArgs, state.result);
12307
12325
  const fill = contextTokensEst !== void 0 && modelWindow > 0 ? contextTokensEst / modelWindow : void 0;
12308
- const truncBudget = scaleTruncBudget(matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS, fill);
12326
+ const baseBudget = matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS;
12327
+ const capped = ctx.batchBudgetChars !== void 0 ? Math.min(baseBudget, ctx.batchBudgetChars) : baseBudget;
12328
+ const truncBudget = scaleTruncBudget(capped, fill);
12309
12329
  const sidecarNote = state.result.length > truncBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), state.result) : void 0;
12310
12330
  state.result = truncateToolResult(state.result, truncBudget, sidecarNote);
12311
12331
  const resultStatus = state.resultStatus;
@@ -12322,7 +12342,7 @@ ${state.result}`;
12322
12342
  }
12323
12343
  if (state.postHookOverride !== void 0) {
12324
12344
  console.log(`\u270F\uFE0F [Post-hook] [${calledName}] \u6A21\u578B\u89C6\u56FE\u7ED3\u679C\u5DF2\u88AB hook \u6539\u5199`);
12325
- const overrideBudget = scaleTruncBudget(matchedTool?.function?.maxOutputCharacters ?? appConfig.MAX_TOOL_RESULT_CHARS, fill);
12345
+ const overrideBudget = scaleTruncBudget(capped, fill);
12326
12346
  const overrideNote = state.postHookOverride.length > overrideBudget ? await writeSidecarArchive(sessionId, String(toolCall.id), state.postHookOverride) : void 0;
12327
12347
  resultForModel = truncateToolResult(state.postHookOverride, overrideBudget, overrideNote);
12328
12348
  }
@@ -12332,7 +12352,36 @@ ${state.result}`;
12332
12352
  }
12333
12353
  return { toolCallId: toolCall.id, calledName, calledArgs, resultForModel, resultForUser, ok, imageAttachments: state.imageAttachments };
12334
12354
  };
12355
+ previewArgs = (raw, max) => raw === void 0 ? void 0 : raw.length > max ? raw.slice(0, max) + `\u2026\uFF08\u5171 ${raw.length} \u5B57\u7B26\uFF09` : raw;
12335
12356
  processToolCall = async (toolCall, ctx) => {
12357
+ try {
12358
+ return await processToolCallInner(toolCall, ctx);
12359
+ } catch (e) {
12360
+ const msg = e instanceof Error ? e.message : String(e);
12361
+ console.error(`\u274C \u5DE5\u5177\u8C03\u7528\u5904\u7406\u5F02\u5E38\uFF08\u5DF2\u9694\u79BB\uFF0C\u4E0D\u5F71\u54CD\u540C\u6279\u5176\u4ED6\u5DE5\u5177\uFF09: ${msg}`);
12362
+ try {
12363
+ ctx.events({
12364
+ sessionId: ctx.sessionId,
12365
+ eventType: "tool.failed",
12366
+ metadata: { depth: ctx.depth, decisionSource: ctx.llmDecisionSource, durationMs: performance.now() - ctx.startTime, round: ctx.round, tools_id: toolCall?.id ?? "", toolName: toolCall?.function?.name ?? "unknown", toolSource: "builtin", ok: false, attempt: ctx.round, errorCategory: "runtime" },
12367
+ payload: { output: `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}` }
12368
+ });
12369
+ } catch {
12370
+ }
12371
+ return {
12372
+ toolCallId: toolCall?.id ?? "",
12373
+ calledName: toolCall?.function?.name ?? "unknown",
12374
+ calledArgs: { _preview: previewArgs(toolCall?.function?.arguments, 300) },
12375
+ // ★ 保留参数上下文(截断预览):否则 tool.failed 排查时看不到模型当时传了什么
12376
+ resultForModel: `\u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}\uFF08\u672C\u8C03\u7528\u5DF2\u9694\u79BB\uFF0C\u5176\u4ED6\u5DE5\u5177\u7ED3\u679C\u4E0D\u53D7\u5F71\u54CD\uFF09`,
12377
+ resultForUser: `\u274C \u5DE5\u5177\u6267\u884C\u5F02\u5E38\uFF1A${msg}`,
12378
+ ok: false,
12379
+ aborted: ctx.signal?.aborted ? true : void 0
12380
+ // ★ 不吞 abort
12381
+ };
12382
+ }
12383
+ };
12384
+ processToolCallInner = async (toolCall, ctx) => {
12336
12385
  const state = {
12337
12386
  calledName: "",
12338
12387
  calledArgs: {},
@@ -12354,7 +12403,9 @@ ${state.result}`;
12354
12403
  } catch {
12355
12404
  state.parseFailed = true;
12356
12405
  }
12357
- console.log(`\u{1F916} \u6A21\u578B\u8BF7\u6C42\u8C03\u7528\u5DE5\u5177: ${state.calledName}\uFF0C\u53C2\u6570:`, state.calledArgs);
12406
+ const argsLog = JSON.stringify(state.calledArgs) ?? "";
12407
+ const argsPreview = process.env.DSC_DEBUG_TOOL_ARGS === "1" ? argsLog : argsLog.length > 200 ? argsLog.slice(0, 200) + `\u2026\uFF08\u5171 ${argsLog.length} \u5B57\u7B26\uFF09` : argsLog;
12408
+ console.log(`\u{1F916} \u6A21\u578B\u8BF7\u6C42\u8C03\u7528\u5DE5\u5177: ${state.calledName}\uFF0C\u53C2\u6570: ${argsPreview}`);
12358
12409
  }
12359
12410
  state.matchedTool = ctx.rawTools.find((t) => t.function.name === state.calledName);
12360
12411
  const early = resolveEarlyOutcome(ctx, toolCall, state);
@@ -12396,13 +12447,14 @@ ${state.result}`;
12396
12447
  });
12397
12448
 
12398
12449
  // ../core/src/agent/toolScheduling.ts
12399
- var dedupeSameRoundCalls, scheduleToolCalls;
12450
+ var dedupeSameRoundCalls, BATCH_MIN_CHARS, BATCH_WINDOW_SHARE, CHAR_PER_TOKEN, computeBatchBudget, scheduleToolCalls;
12400
12451
  var init_toolScheduling = __esm({
12401
12452
  "../core/src/agent/toolScheduling.ts"() {
12402
12453
  "use strict";
12403
12454
  init_toolExecution();
12404
12455
  init_transcript();
12405
12456
  init_contentParts();
12457
+ init_contextCore();
12406
12458
  init_config();
12407
12459
  init_tool();
12408
12460
  init_permissions();
@@ -12422,6 +12474,14 @@ var init_toolScheduling = __esm({
12422
12474
  }
12423
12475
  return { batch, plan };
12424
12476
  };
12477
+ BATCH_MIN_CHARS = 12e3;
12478
+ BATCH_WINDOW_SHARE = 0.5;
12479
+ CHAR_PER_TOKEN = 4;
12480
+ computeBatchBudget = (modelWindow, compactRatio, contextTokensEst, batchSize) => {
12481
+ const budgetTokensLeft = Math.max(0, modelWindow * compactRatio - (contextTokensEst ?? 0));
12482
+ const batchCapChars = Math.max(BATCH_MIN_CHARS, Math.floor(budgetTokensLeft * CHAR_PER_TOKEN * BATCH_WINDOW_SHARE));
12483
+ return { batchCapChars, perToolCapChars: Math.floor(batchCapChars / batchSize) };
12484
+ };
12425
12485
  scheduleToolCalls = async function* (assistantMessage, message, ctx) {
12426
12486
  const { sessionId, signal, rawTools } = ctx;
12427
12487
  let abortedDuringTools = false;
@@ -12490,7 +12550,10 @@ var init_toolScheduling = __esm({
12490
12550
  const bp = parseTc(p.tc);
12491
12551
  yield { type: "tool.start", toolCallId: p.tc.id, toolName: bp.name, args: bp.args };
12492
12552
  }
12493
- const outcomes = await Promise.all(batch.map((btc) => processToolCall(btc, ctx)));
12553
+ const budget = batch.length >= 2 ? computeBatchBudget(ctx.modelWindow, ctx.compactRatio, ctx.contextTokensEst, batch.length) : void 0;
12554
+ const tokensBefore = budget ? estimateTokens(message) : 0;
12555
+ const batchCtx = budget ? { ...ctx, batchBudgetChars: budget.perToolCapChars } : ctx;
12556
+ const outcomes = await Promise.all(batch.map((btc) => processToolCall(btc, batchCtx)));
12494
12557
  const outcomeByTc = /* @__PURE__ */ new Map();
12495
12558
  batch.forEach((btc, i) => outcomeByTc.set(btc, outcomes[i]));
12496
12559
  const batchEntries = [];
@@ -12514,6 +12577,34 @@ var init_toolScheduling = __esm({
12514
12577
  if (oc.aborted) abortedDuringTools = true;
12515
12578
  }
12516
12579
  await appendMessages(sessionId, batchEntries);
12580
+ if (budget) {
12581
+ try {
12582
+ const tokensAfter = estimateTokens(message);
12583
+ ctx.events({
12584
+ sessionId,
12585
+ eventType: "tool.batch.budget",
12586
+ metadata: {
12587
+ depth: ctx.depth,
12588
+ decisionSource: ctx.llmDecisionSource,
12589
+ ok: true,
12590
+ durationMs: performance.now() - ctx.startTime,
12591
+ round: ctx.round,
12592
+ batchSize: batch.length,
12593
+ batchCapChars: budget.batchCapChars,
12594
+ perToolCapChars: budget.perToolCapChars
12595
+ },
12596
+ payload: {
12597
+ output: JSON.stringify({
12598
+ injectedChars: batch.map((btc) => outcomeByTc.get(btc)?.resultForModel.length ?? 0),
12599
+ tokensBefore,
12600
+ tokensAfter,
12601
+ tokensDelta: tokensAfter - tokensBefore
12602
+ })
12603
+ }
12604
+ });
12605
+ } catch {
12606
+ }
12607
+ }
12517
12608
  } else {
12518
12609
  yield { type: "tool.start", toolCallId: tc.id, toolName: pname, args: pargs };
12519
12610
  const oc = await processToolCall(tc, ctx);
@@ -12644,9 +12735,10 @@ var init_streamInference = __esm({
12644
12735
  const MAX_STREAM_RETRIES = 2;
12645
12736
  const MAX_API_RETRIES = 3;
12646
12737
  let apiRetries = 0;
12738
+ let idleRetries = 0;
12647
12739
  let compactedThisRound = false;
12648
12740
  let visionDegradedThisRound = false;
12649
- for (let streamAttempt = 0; ; streamAttempt++) {
12741
+ for (; ; ) {
12650
12742
  try {
12651
12743
  for await (const chunk of activeProvider.streamChat(inferenceMessages, cleanedToolSchemas, { signal, model, thinkingLevel })) {
12652
12744
  if (signal?.aborted) break;
@@ -12692,10 +12784,11 @@ var init_streamInference = __esm({
12692
12784
  }
12693
12785
  );
12694
12786
  const noOutputYet = !contentBuf && !reasoningBuf && toolCallsBuf.size === 0;
12695
- if (isIdleTimeout && streamAttempt < MAX_STREAM_RETRIES) {
12787
+ if (isIdleTimeout && idleRetries < MAX_STREAM_RETRIES) {
12788
+ idleRetries++;
12696
12789
  if (!noOutputYet) yield { type: "text.reset" };
12697
12790
  const stallShape = noOutputYet ? "\uFF0C\u5C1A\u65E0\u8F93\u51FA" : hasCompleteToolCall ? "\uFF0C\u5DF2\u6709\u5B8C\u6574 tool_call\uFF08\u6D41\u5C3E\u90E8 stall\uFF09" : "\uFF0Ctool_call \u672A\u6D41\u5B8C";
12698
- console.warn(`\u26A0\uFE0F \u6D41\u5F0F stall\uFF08idle \u8D85\u65F6${stallShape}\uFF09\uFF0C\u7B2C ${streamAttempt + 1}/${MAX_STREAM_RETRIES} \u6B21\u91CD\u8BD5...`);
12791
+ console.warn(`\u26A0\uFE0F \u6D41\u5F0F stall\uFF08idle \u8D85\u65F6${stallShape}\uFF09\uFF0C\u7B2C ${idleRetries}/${MAX_STREAM_RETRIES} \u6B21\u91CD\u8BD5...`);
12699
12792
  contentBuf = "";
12700
12793
  reasoningBuf = "";
12701
12794
  toolCallsBuf.clear();
@@ -12803,7 +12896,6 @@ var init_streamInference = __esm({
12803
12896
  payload: { input: msgText(message[message.length - 1].content) }
12804
12897
  // ★ 多模态:trace 只记文本视图
12805
12898
  });
12806
- console.log(`[DSC-DIAG] streamInference round ${round} \u5B8C\u6210: content=${contentBuf.length}chars reasoning=${reasoningBuf.length}chars toolCalls=${toolCallsBuf.size}`);
12807
12899
  return { kind: "completed", assistantMessage, usage: lastUsage };
12808
12900
  } catch (error) {
12809
12901
  if (signal?.aborted) {
@@ -12817,7 +12909,7 @@ var init_streamInference = __esm({
12817
12909
  payload: { input: msgText(message[message.length - 1].content), output: err.message }
12818
12910
  // ★ 多模态:文本视图
12819
12911
  });
12820
- console.log(`[DSC-DIAG] streamInference round ${round} \u2550\u2550\u2550 ERROR: ${err.message}`);
12912
+ console.error(`\u274C \u63A8\u7406\u5931\u8D25\uFF08round ${round}\uFF09\uFF1A${err.message}`);
12821
12913
  return { kind: "error", error: err };
12822
12914
  }
12823
12915
  };
@@ -13074,6 +13166,70 @@ ${PLAN_REJECT_TEXT(planRejectCount)}` };
13074
13166
  }
13075
13167
  });
13076
13168
 
13169
+ // ../core/src/config/prices.ts
13170
+ var MODEL_PRICES, PRICES_VERIFIED_AT, PRICES_STALE_DAYS, pricesAgeDays, isBeijingPeak, envPrice, ENV_OVERRIDE, priceFor, calcCostYuan;
13171
+ var init_prices = __esm({
13172
+ "../core/src/config/prices.ts"() {
13173
+ "use strict";
13174
+ MODEL_PRICES = {
13175
+ "deepseek-flash": {
13176
+ idle: { input: 1, output: 4, cacheHit: 0.02 },
13177
+ peak: { input: 2, output: 8, cacheHit: 0.04 }
13178
+ },
13179
+ // 官网脚注明示旧名按 Flash 价格计费(2026-10-19 核实),非猜测——与 flash 同价条目
13180
+ "deepseek-flash-vision-exp": {
13181
+ idle: { input: 1, output: 4, cacheHit: 0.02 },
13182
+ peak: { input: 2, output: 8, cacheHit: 0.04 }
13183
+ },
13184
+ "deepseek-v4-pro": {
13185
+ idle: { input: 4.5, output: 13.5, cacheHit: 0.15 },
13186
+ peak: { input: 9, output: 27, cacheHit: 0.3 }
13187
+ }
13188
+ };
13189
+ PRICES_VERIFIED_AT = "2026-10-19";
13190
+ PRICES_STALE_DAYS = 30;
13191
+ pricesAgeDays = () => Math.max(0, Math.floor((Date.now() - Date.parse(`${PRICES_VERIFIED_AT}T00:00:00Z`)) / 864e5));
13192
+ isBeijingPeak = (ts2 = /* @__PURE__ */ new Date()) => {
13193
+ const bj = new Date(ts2.getTime() + 8 * 36e5);
13194
+ const day = bj.getUTCDay();
13195
+ if (day === 0 || day === 6) return false;
13196
+ const h = bj.getUTCHours();
13197
+ return h >= 9 && h < 12 || h >= 14 && h < 18;
13198
+ };
13199
+ envPrice = (name) => {
13200
+ const raw = process.env[name];
13201
+ if (!raw) return void 0;
13202
+ const n = Number(raw);
13203
+ return Number.isFinite(n) && n >= 0 ? n : void 0;
13204
+ };
13205
+ ENV_OVERRIDE = (() => {
13206
+ const input = envPrice("DEEP_SEEK_PRICE_INPUT");
13207
+ const output = envPrice("DEEP_SEEK_PRICE_OUTPUT");
13208
+ const cacheHit = envPrice("DEEP_SEEK_PRICE_CACHEHIT");
13209
+ if (input === void 0 && output === void 0 && cacheHit === void 0) return null;
13210
+ return { input, output, cacheHit };
13211
+ })();
13212
+ priceFor = (model, ts2) => {
13213
+ const tier = MODEL_PRICES[model];
13214
+ const base = tier ? isBeijingPeak(ts2 ?? /* @__PURE__ */ new Date()) ? tier.peak : tier.idle : void 0;
13215
+ if (!ENV_OVERRIDE) return base ?? null;
13216
+ const input = ENV_OVERRIDE.input ?? base?.input;
13217
+ const output = ENV_OVERRIDE.output ?? base?.output;
13218
+ const cacheHit = ENV_OVERRIDE.cacheHit ?? base?.cacheHit;
13219
+ if (input === void 0 || output === void 0 || cacheHit === void 0) return null;
13220
+ return { input, output, cacheHit };
13221
+ };
13222
+ calcCostYuan = (model, u, ts2) => {
13223
+ const p = priceFor(model, ts2);
13224
+ if (!p) return null;
13225
+ const cached2 = Math.max(0, u.cachedTokens ?? 0);
13226
+ const miss = Math.max(0, (u.promptTokens ?? 0) - cached2);
13227
+ const completion = Math.max(0, u.completionTokens ?? 0);
13228
+ return (miss * p.input + cached2 * p.cacheHit + completion * p.output) / 1e6;
13229
+ };
13230
+ }
13231
+ });
13232
+
13077
13233
  // ../core/src/agent/costRelay.ts
13078
13234
  var createCostRelay;
13079
13235
  var init_costRelay = __esm({
@@ -13102,6 +13258,99 @@ var init_costRelay = __esm({
13102
13258
  }
13103
13259
  });
13104
13260
 
13261
+ // ../core/src/agent/runLedger.ts
13262
+ var BUDGET_HARD_LIMIT_RATIO, roundYuan6, createRunLedger;
13263
+ var init_runLedger = __esm({
13264
+ "../core/src/agent/runLedger.ts"() {
13265
+ "use strict";
13266
+ init_prices();
13267
+ init_costRelay();
13268
+ init_client();
13269
+ BUDGET_HARD_LIMIT_RATIO = 1.2;
13270
+ roundYuan6 = (y) => Math.round(y * 1e6) / 1e6;
13271
+ createRunLedger = (opts) => {
13272
+ let budgetSpentYuan = 0;
13273
+ let usageSum;
13274
+ let costYuanAcc = 0;
13275
+ let costKnown = true;
13276
+ let toolCostYuanAcc = 0;
13277
+ let auxCostYuanAcc = 0;
13278
+ let subCostYuanAcc = 0;
13279
+ const relay = createCostRelay(opts.costSink);
13280
+ const addUsage = (u) => {
13281
+ if (!u) return;
13282
+ usageSum = {
13283
+ prompt_tokens: (usageSum?.prompt_tokens ?? 0) + (u.prompt_tokens ?? 0),
13284
+ completion_tokens: (usageSum?.completion_tokens ?? 0) + (u.completion_tokens ?? 0),
13285
+ total_tokens: (usageSum?.total_tokens ?? 0) + (u.total_tokens ?? 0),
13286
+ cached_tokens: (usageSum?.cached_tokens ?? 0) + (u.cached_tokens ?? 0)
13287
+ };
13288
+ const cost = calcCostYuan(opts.model, { promptTokens: u.prompt_tokens, completionTokens: u.completion_tokens, cachedTokens: u.cached_tokens });
13289
+ if (cost == null) costKnown = false;
13290
+ else {
13291
+ costYuanAcc += cost;
13292
+ budgetSpentYuan += cost;
13293
+ }
13294
+ };
13295
+ const addToolCost = (yuan) => {
13296
+ if (!Number.isFinite(yuan) || yuan <= 0) return;
13297
+ toolCostYuanAcc += yuan;
13298
+ budgetSpentYuan += yuan;
13299
+ };
13300
+ const reportAuxUsage = (usage, kind) => {
13301
+ if (usage) {
13302
+ const cost = calcCostYuan(AUX_MODEL_NAME, { promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, cachedTokens: usage.cached_tokens });
13303
+ if (cost != null && Number.isFinite(cost) && cost > 0) {
13304
+ auxCostYuanAcc += cost;
13305
+ budgetSpentYuan += cost;
13306
+ }
13307
+ }
13308
+ try {
13309
+ opts.onAuxUsage?.(usage, kind);
13310
+ } catch {
13311
+ }
13312
+ };
13313
+ const reportSubCost = (yuan) => {
13314
+ if (Number.isFinite(yuan) && yuan > 0) {
13315
+ budgetSpentYuan += yuan;
13316
+ subCostYuanAcc += yuan;
13317
+ }
13318
+ };
13319
+ const snapshot = () => opts.budgetYuan != null ? { spent: budgetSpentYuan, budget: opts.budgetYuan } : null;
13320
+ return {
13321
+ addUsage,
13322
+ addToolCost,
13323
+ reportAuxUsage,
13324
+ reportSubCost,
13325
+ /** 预算 soft 判定读数(nudge 管线消费):未武装返回 null。O(1) 闭包读数。 */
13326
+ getBudgetStatus: snapshot,
13327
+ /** 预算硬线判定(E2):spent >= budget×1.2(>= 语义与收拢前逐字节等价,恰 1.2× 即触发)。
13328
+ * 自武装探测(inheritedBudgetCheck 兜底)与轮边界硬截断共用同一判定。 */
13329
+ overHardLimit: () => opts.budgetYuan != null && budgetSpentYuan >= opts.budgetYuan * BUDGET_HARD_LIMIT_RATIO,
13330
+ /** 子 run 成本中继 flush:基数=预算圈实时读数(子树完整口径,文件头★中继段)。主 run 无 sink 时 no-op。 */
13331
+ flushRelay: () => relay.flush(budgetSpentYuan),
13332
+ /** run.end 事件行与 receipt token 段共用的用量合计快照。 */
13333
+ usageSummary: () => usageSum,
13334
+ /** 预算读数快照:硬截断 final 文案金额源。未武装=null(调用方结构上只在已武装分支取,勿 ?? 0 兜底)。 */
13335
+ budgetSnapshot: snapshot,
13336
+ /** usageLog 落盘字段(recordUsageRun 入参金额段;costYuan 无价=undefined、各账 ≤0 省略——自动缺省容错)。 */
13337
+ usageLogFields: () => ({
13338
+ costYuan: costKnown ? roundYuan6(costYuanAcc) : void 0,
13339
+ toolCostYuan: toolCostYuanAcc > 0 ? roundYuan6(toolCostYuanAcc) : void 0,
13340
+ auxCostYuan: auxCostYuanAcc > 0 ? roundYuan6(auxCostYuanAcc) : void 0
13341
+ }),
13342
+ /** run.receipt 结算单金额段:costYuan 无价=null(显式「不知钱」,绝不编数);各账 ≤0 省略字段。 */
13343
+ receiptFields: () => ({
13344
+ costYuan: costKnown ? roundYuan6(costYuanAcc) : null,
13345
+ toolCostYuan: toolCostYuanAcc > 0 ? roundYuan6(toolCostYuanAcc) : void 0,
13346
+ auxCostYuan: auxCostYuanAcc > 0 ? roundYuan6(auxCostYuanAcc) : void 0,
13347
+ subCostYuan: subCostYuanAcc > 0 ? roundYuan6(subCostYuanAcc) : void 0
13348
+ })
13349
+ };
13350
+ };
13351
+ }
13352
+ });
13353
+
13105
13354
  // ../core/src/observability/usageLog.ts
13106
13355
  import fs34 from "fs/promises";
13107
13356
  import path37 from "path";
@@ -13209,106 +13458,323 @@ var init_usageLog = __esm({
13209
13458
  }
13210
13459
  });
13211
13460
 
13212
- // ../core/src/config/prices.ts
13213
- var MODEL_PRICES, PRICES_VERIFIED_AT, PRICES_STALE_DAYS, pricesAgeDays, isBeijingPeak, envPrice, ENV_OVERRIDE, priceFor, calcCostYuan;
13214
- var init_prices = __esm({
13215
- "../core/src/config/prices.ts"() {
13461
+ // ../core/src/observability/store.ts
13462
+ import path38 from "path";
13463
+ import fs35 from "fs/promises";
13464
+ var getTraceDirPath, getTracePath, tracePathCache, ensuredTraceDirs, ensureTraceDir, getGlobalClockPath, getTraceStorePath;
13465
+ var init_store3 = __esm({
13466
+ "../core/src/observability/store.ts"() {
13216
13467
  "use strict";
13217
- MODEL_PRICES = {
13218
- "deepseek-flash": {
13219
- idle: { input: 1, output: 4, cacheHit: 0.02 },
13220
- peak: { input: 2, output: 8, cacheHit: 0.04 }
13221
- },
13222
- // 官网脚注明示旧名按 Flash 价格计费(2026-10-19 核实),非猜测——与 flash 同价条目
13223
- "deepseek-flash-vision-exp": {
13224
- idle: { input: 1, output: 4, cacheHit: 0.02 },
13225
- peak: { input: 2, output: 8, cacheHit: 0.04 }
13226
- },
13227
- "deepseek-v4-pro": {
13228
- idle: { input: 4.5, output: 13.5, cacheHit: 0.15 },
13229
- peak: { input: 9, output: 27, cacheHit: 0.3 }
13230
- }
13468
+ init_config();
13469
+ init_traceCalculate();
13470
+ init_common();
13471
+ getTraceDirPath = (mainTraceId) => {
13472
+ assertSafeSessionId(mainTraceId, "traceId");
13473
+ return path38.join(appConfig.dataDir, "trace", appConfig.userWorkspaceDir, getFileName(mainTraceId));
13231
13474
  };
13232
- PRICES_VERIFIED_AT = "2026-10-19";
13233
- PRICES_STALE_DAYS = 30;
13234
- pricesAgeDays = () => Math.max(0, Math.floor((Date.now() - Date.parse(`${PRICES_VERIFIED_AT}T00:00:00Z`)) / 864e5));
13235
- isBeijingPeak = (ts2 = /* @__PURE__ */ new Date()) => {
13236
- const bj = new Date(ts2.getTime() + 8 * 36e5);
13237
- const day = bj.getUTCDay();
13238
- if (day === 0 || day === 6) return false;
13239
- const h = bj.getUTCHours();
13240
- return h >= 9 && h < 12 || h >= 14 && h < 18;
13475
+ getTracePath = () => {
13476
+ return path38.join(appConfig.dataDir, "trace");
13241
13477
  };
13242
- envPrice = (name) => {
13243
- const raw = process.env[name];
13244
- if (!raw) return void 0;
13245
- const n = Number(raw);
13246
- return Number.isFinite(n) && n >= 0 ? n : void 0;
13478
+ tracePathCache = /* @__PURE__ */ new Map();
13479
+ ensuredTraceDirs = /* @__PURE__ */ new Set();
13480
+ ensureTraceDir = async (mainTraceId) => {
13481
+ const dir = getTraceDirPath(mainTraceId);
13482
+ if (ensuredTraceDirs.has(dir)) return;
13483
+ await fs35.mkdir(dir, { recursive: true });
13484
+ ensuredTraceDirs.add(dir);
13247
13485
  };
13248
- ENV_OVERRIDE = (() => {
13249
- const input = envPrice("DEEP_SEEK_PRICE_INPUT");
13250
- const output = envPrice("DEEP_SEEK_PRICE_OUTPUT");
13251
- const cacheHit = envPrice("DEEP_SEEK_PRICE_CACHEHIT");
13252
- if (input === void 0 && output === void 0 && cacheHit === void 0) return null;
13253
- return { input, output, cacheHit };
13254
- })();
13255
- priceFor = (model, ts2) => {
13256
- const tier = MODEL_PRICES[model];
13257
- const base = tier ? isBeijingPeak(ts2 ?? /* @__PURE__ */ new Date()) ? tier.peak : tier.idle : void 0;
13258
- if (!ENV_OVERRIDE) return base ?? null;
13259
- const input = ENV_OVERRIDE.input ?? base?.input;
13260
- const output = ENV_OVERRIDE.output ?? base?.output;
13261
- const cacheHit = ENV_OVERRIDE.cacheHit ?? base?.cacheHit;
13262
- if (input === void 0 || output === void 0 || cacheHit === void 0) return null;
13263
- return { input, output, cacheHit };
13486
+ getGlobalClockPath = () => {
13487
+ return path38.join(getTracePath(), "trace-cleanup-clock.json");
13264
13488
  };
13265
- calcCostYuan = (model, u, ts2) => {
13266
- const p = priceFor(model, ts2);
13267
- if (!p) return null;
13268
- const cached2 = Math.max(0, u.cachedTokens ?? 0);
13269
- const miss = Math.max(0, (u.promptTokens ?? 0) - cached2);
13270
- const completion = Math.max(0, u.completionTokens ?? 0);
13271
- return (miss * p.input + cached2 * p.cacheHit + completion * p.output) / 1e6;
13489
+ getTraceStorePath = async (mainTraceId) => {
13490
+ assertSafeSessionId(mainTraceId, "traceId");
13491
+ const rootId = getFileName(mainTraceId);
13492
+ const cached2 = tracePathCache.get(rootId);
13493
+ if (cached2) return cached2;
13494
+ await ensureTraceDir(rootId);
13495
+ const dir = getTraceDirPath(rootId);
13496
+ const files = await fs35.readdir(dir);
13497
+ const existedFile = files.find((f) => f.endsWith(`__${rootId}.jsonl`));
13498
+ let fileName = "";
13499
+ if (existedFile) {
13500
+ fileName = existedFile;
13501
+ } else {
13502
+ const todayStr = getTodayDateString();
13503
+ fileName = `trace-${todayStr}__${rootId}.jsonl`;
13504
+ }
13505
+ const resolved = path38.join(dir, fileName);
13506
+ tracePathCache.set(rootId, resolved);
13507
+ return resolved;
13272
13508
  };
13273
13509
  }
13274
13510
  });
13275
13511
 
13276
- // ../core/src/agent/runAgent.ts
13277
- async function* runAgent(message, options) {
13278
- const sessionId = options.sessionId;
13279
- const runId = createUUID();
13280
- const rawEvents = options.events;
13281
- const events = async (base) => {
13282
- try {
13283
- await rawEvents({ ...base, metadata: { ...base.metadata, runId } });
13284
- } catch (e) {
13285
- console.warn("\u26A0\uFE0F \u57CB\u70B9\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u63A8\u7406\uFF09:", e instanceof Error ? e.message : e);
13512
+ // ../core/src/observability/trace.ts
13513
+ import fs36 from "fs/promises";
13514
+ import path39 from "path";
13515
+ async function emitTrace(base) {
13516
+ const event = {
13517
+ sessionId: base.sessionId,
13518
+ parentId: base.parentId,
13519
+ eventType: base.eventType,
13520
+ timestamp: base.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
13521
+ usage: base.usage,
13522
+ payload: base.payload,
13523
+ metadata: {
13524
+ ...base.metadata || {}
13286
13525
  }
13287
13526
  };
13288
- const modelWindow = options.modelWindow;
13289
- const signal = options.abortSignal;
13290
- const depth = options.depth ?? 0;
13291
- const cwd = options.cwd ?? process.cwd();
13292
- const keepRecentUnits = options.keepRecentUnits;
13293
- const compactRatio = options.compactRatio;
13294
- const parentSystemPrompt = options.parentSystemPrompt;
13295
- const effectiveModel = options.model?.trim() || MODEL_NAME;
13296
- const { rawTools, cleanedToolSchemas, toolsTokens } = await prepareToolsAndInjections(message, options, events);
13297
- let round = 0;
13298
- let lastContent = "";
13299
- let stopReason = "normal";
13300
- let budgetTruncated = false;
13301
- const MAX_AGENT_ROUNDS = 500;
13302
- const firstPrompt = resolveFirstPrompt(message, options);
13303
- let budgetSpentYuan = 0;
13304
- let subCostYuanAcc = 0;
13305
- let runCompactCount = 0;
13306
- const nudges = createNudgeScheduler({
13307
- firstPrompt,
13308
- planMode: !!options.planMode,
13527
+ if (event.payload) {
13528
+ if (typeof event.payload.input === "string") event.payload.input = truncateTracePayload(event.payload.input);
13529
+ if (typeof event.payload.output === "string") event.payload.output = truncateTracePayload(event.payload.output);
13530
+ }
13531
+ try {
13532
+ await appendTraceEvent(event);
13533
+ } catch (error) {
13534
+ console.warn("\u26A0\uFE0F [Trace] \u4E8B\u4EF6\u843D\u76D8\u5931\u8D25\uFF0C\u5DF2\u9632\u5FA1\u6027\u8DF3\u8FC7\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", error);
13535
+ }
13536
+ }
13537
+ async function appendTraceEvent(event) {
13538
+ const line = JSON.stringify(event) + "\n";
13539
+ const arr = traceBuf.get(event.sessionId);
13540
+ if (arr) arr.push(line);
13541
+ else traceBuf.set(event.sessionId, [line]);
13542
+ if (needImmediateFlush(event)) {
13543
+ await flushTraceBuffer(event.sessionId);
13544
+ return;
13545
+ }
13546
+ if ((traceBuf.get(event.sessionId)?.length ?? 0) >= TRACE_BATCH_LINES) {
13547
+ await flushTraceBuffer(event.sessionId);
13548
+ return;
13549
+ }
13550
+ scheduleTraceTimer(event.sessionId);
13551
+ }
13552
+ async function maybeCleanupAfterAppend(sessionId) {
13553
+ if (cleanupInProgress2) return;
13554
+ cleanupInProgress2 = true;
13555
+ try {
13556
+ await ensureClockLoaded2();
13557
+ const now = Date.now();
13558
+ const isTimeExpired = now - diskLastCleanupAt2 > CLEANUP_INTERVAL_MS2;
13559
+ const isSizeOverflow = workspaceTraceBytes > MAX_TRACE_FOLDER_BYTES;
13560
+ if (!isTimeExpired && !isSizeOverflow) return;
13561
+ if (isSizeOverflow && !isTimeExpired && now - lastSizeCleanupAt2 < SIZE_CLEANUP_COOLDOWN_MS2) {
13562
+ return;
13563
+ }
13564
+ const reason = isTimeExpired && isSizeOverflow ? "\u5BB9\u91CF+\u65F6\u95F4\u53CC\u95F8" : isSizeOverflow ? "\u5BB9\u91CF\u8D8A\u8FC750MB\u5927\u95F8" : "3\u5929\u51B7\u5374\u5468\u671F\u5DF2\u6EE1";
13565
+ console.log(`\u{1F9F9} [Trace\u8FD0\u7EF4] \u89E6\u53D1\u81EA\u9002\u5E94\u6E05\u7406\uFF08\u539F\u56E0: ${reason}\uFF09`);
13566
+ let deletedCount = 0;
13567
+ deletedCount += await cleanupOldTraceFiles();
13568
+ workspaceTraceBytes = await getDirBytes(getTracePath());
13569
+ if (workspaceTraceBytes > MAX_TRACE_FOLDER_BYTES) {
13570
+ const targetBytes = Math.floor(MAX_TRACE_FOLDER_BYTES * SIZE_RECLAIM_TARGET_RATIO2);
13571
+ deletedCount += await evictOldestForSize2(targetBytes);
13572
+ workspaceTraceBytes = await getDirBytes(getTracePath());
13573
+ }
13574
+ lastSizeCleanupAt2 = now;
13575
+ diskLastCleanupAt2 = now;
13576
+ const clockPath = getGlobalClockPath();
13577
+ await fs36.mkdir(path39.dirname(clockPath), { recursive: true });
13578
+ await fs36.writeFile(clockPath, JSON.stringify({ lastCleanupAt: now, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
13579
+ if (deletedCount > 0) {
13580
+ 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`);
13581
+ }
13582
+ } catch (error) {
13583
+ console.warn("\u26A0\uFE0F [Trace\u8FD0\u7EF4] \u81EA\u52A8\u6E05\u7406\u53D1\u751F\u8F7B\u5FAE\u5F02\u5E38\uFF0C\u5DF2\u9632\u5FA1\u6027\u8DF3\u8FC7:", error);
13584
+ } finally {
13585
+ cleanupInProgress2 = false;
13586
+ }
13587
+ }
13588
+ async function cleanupOldTraceFiles() {
13589
+ const dir = getTracePath();
13590
+ const retentionDays = Math.max(1, appConfig.traceRetentionDays || 7);
13591
+ const cutoff = /* @__PURE__ */ new Date();
13592
+ cutoff.setHours(0, 0, 0, 0);
13593
+ cutoff.setDate(cutoff.getDate() - retentionDays);
13594
+ const now = Date.now();
13595
+ let deleted = 0;
13596
+ let relativePaths;
13597
+ try {
13598
+ relativePaths = await fs36.readdir(dir, { recursive: true });
13599
+ } catch {
13600
+ return 0;
13601
+ }
13602
+ const re = /^trace-(\d{4}-\d{2}-\d{2})__([\w-]+)\.jsonl$/;
13603
+ for (const relativePath of relativePaths) {
13604
+ const fileName = path39.basename(relativePath);
13605
+ const m = fileName.match(re);
13606
+ if (!m) continue;
13607
+ const fileBornDay = /* @__PURE__ */ new Date(`${m[1]}T00:00:00`);
13608
+ if (fileBornDay >= cutoff) continue;
13609
+ const fullPath = path39.join(dir, relativePath);
13610
+ try {
13611
+ const st = await fs36.stat(fullPath);
13612
+ if (now - st.mtimeMs < ACTIVE_GRACE_MS2) continue;
13613
+ await fs36.unlink(fullPath);
13614
+ deleted += 1;
13615
+ } catch {
13616
+ }
13617
+ }
13618
+ return deleted;
13619
+ }
13620
+ async function evictOldestForSize2(targetBytes) {
13621
+ const dir = getTracePath();
13622
+ let relativePaths;
13623
+ try {
13624
+ relativePaths = await fs36.readdir(dir, { recursive: true });
13625
+ } catch {
13626
+ return 0;
13627
+ }
13628
+ const re = /^trace-(\d{4}-\d{2}-\d{2})__([\w-]+)\.jsonl$/;
13629
+ const candidates = [];
13630
+ for (const relativePath of relativePaths) {
13631
+ const fileName = path39.basename(relativePath);
13632
+ if (!re.test(fileName)) continue;
13633
+ try {
13634
+ const st = await fs36.stat(path39.join(dir, relativePath));
13635
+ candidates.push({ mtime: st.mtimeMs, fullPath: path39.join(dir, relativePath), size: st.size });
13636
+ } catch {
13637
+ }
13638
+ }
13639
+ candidates.sort((a, b) => a.mtime - b.mtime);
13640
+ let totalBytes = candidates.reduce((sum, c) => sum + c.size, 0);
13641
+ let deleted = 0;
13642
+ for (const c of candidates) {
13643
+ if (totalBytes <= targetBytes) break;
13644
+ try {
13645
+ await fs36.unlink(c.fullPath);
13646
+ totalBytes -= c.size;
13647
+ deleted += 1;
13648
+ } catch {
13649
+ }
13650
+ }
13651
+ if (deleted > 0) {
13652
+ console.warn(`\u26A0\uFE0F [Trace\u8FD0\u7EF4] \u5BB9\u91CF\u786C\u9A71\u9010\uFF1A\u8D8A\u8FC7\u6D3B\u8DC3\u4FDD\u62A4\u5F3A\u5220 ${deleted} \u4E2A\u6700\u65E7 trace \u6587\u4EF6\uFF08\u78C1\u76D8\u538B\u529B\u515C\u5E95\uFF09\u3002`);
13653
+ }
13654
+ return deleted;
13655
+ }
13656
+ var CLEANUP_INTERVAL_MS2, ACTIVE_GRACE_MS2, MAX_TRACE_FOLDER_BYTES, SIZE_RECLAIM_TARGET_RATIO2, SIZE_CLEANUP_COOLDOWN_MS2, workspaceTraceBytes, diskLastCleanupAt2, clockLoaded2, lastSizeCleanupAt2, cleanupInProgress2, TRACE_BATCH_LINES, TRACE_BATCH_MS, traceBuf, traceTimers, cleanupTriggerCount, needImmediateFlush, scheduleTraceTimer, flushTraceBuffer, ensureClockLoaded2, tracePayloadMaxChars, truncateTracePayload;
13657
+ var init_trace = __esm({
13658
+ "../core/src/observability/trace.ts"() {
13659
+ "use strict";
13660
+ init_config();
13661
+ init_store3();
13662
+ init_traceCalculate();
13663
+ CLEANUP_INTERVAL_MS2 = 3 * 24 * 60 * 60 * 1e3;
13664
+ ACTIVE_GRACE_MS2 = 24 * 60 * 60 * 1e3;
13665
+ MAX_TRACE_FOLDER_BYTES = 50 * 1024 * 1024;
13666
+ SIZE_RECLAIM_TARGET_RATIO2 = 0.8;
13667
+ SIZE_CLEANUP_COOLDOWN_MS2 = 60 * 1e3;
13668
+ workspaceTraceBytes = -1;
13669
+ diskLastCleanupAt2 = 0;
13670
+ clockLoaded2 = false;
13671
+ lastSizeCleanupAt2 = 0;
13672
+ cleanupInProgress2 = false;
13673
+ TRACE_BATCH_LINES = 50;
13674
+ TRACE_BATCH_MS = 200;
13675
+ traceBuf = /* @__PURE__ */ new Map();
13676
+ traceTimers = /* @__PURE__ */ new Map();
13677
+ cleanupTriggerCount = 0;
13678
+ needImmediateFlush = (ev) => ev.metadata?.ok === false || ev.eventType === "user.aborted" || ev.eventType === "tool.repeat_break" || ev.eventType === "session.end";
13679
+ scheduleTraceTimer = (sessionId) => {
13680
+ if (traceTimers.has(sessionId)) return;
13681
+ const t = setTimeout(() => {
13682
+ traceTimers.delete(sessionId);
13683
+ void flushTraceBuffer(sessionId);
13684
+ }, TRACE_BATCH_MS);
13685
+ t.unref?.();
13686
+ traceTimers.set(sessionId, t);
13687
+ };
13688
+ flushTraceBuffer = async (sessionId) => {
13689
+ const ids = sessionId !== void 0 ? [sessionId] : [...traceBuf.keys()];
13690
+ for (const id of ids) {
13691
+ const arr = traceBuf.get(id);
13692
+ if (!arr || arr.length === 0) {
13693
+ traceBuf.delete(id);
13694
+ continue;
13695
+ }
13696
+ traceBuf.set(id, []);
13697
+ const timer = traceTimers.get(id);
13698
+ if (timer) {
13699
+ clearTimeout(timer);
13700
+ traceTimers.delete(id);
13701
+ }
13702
+ try {
13703
+ const joined = arr.join("");
13704
+ const p = await getTraceStorePath(id);
13705
+ await fs36.appendFile(p, joined, "utf-8");
13706
+ if (workspaceTraceBytes < 0) {
13707
+ workspaceTraceBytes = await getDirBytes(getTracePath());
13708
+ }
13709
+ workspaceTraceBytes += Buffer.byteLength(joined, "utf-8");
13710
+ cleanupTriggerCount++;
13711
+ void maybeCleanupAfterAppend(id);
13712
+ } catch (error) {
13713
+ console.warn("\u26A0\uFE0F [Trace] \u6279\u91CF\u843D\u76D8\u5931\u8D25\uFF0C\u4E22\u5F03\u8BE5\u6279\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", error);
13714
+ }
13715
+ }
13716
+ };
13717
+ ensureClockLoaded2 = async () => {
13718
+ if (clockLoaded2) return;
13719
+ clockLoaded2 = true;
13720
+ try {
13721
+ const rawClock = await fs36.readFile(getGlobalClockPath(), "utf-8");
13722
+ diskLastCleanupAt2 = JSON.parse(rawClock).lastCleanupAt || 0;
13723
+ } catch {
13724
+ diskLastCleanupAt2 = 0;
13725
+ }
13726
+ };
13727
+ tracePayloadMaxChars = () => Number(process.env.TRACE_PAYLOAD_MAX_CHARS) || 2e3;
13728
+ truncateTracePayload = (s) => {
13729
+ const max = tracePayloadMaxChars();
13730
+ return s.length > max ? s.slice(0, max) + `\u2026[trace \u622A\u65AD ${s.length - max} \u5B57\u7B26\uFF08\u5171 ${s.length} \u5B57\u7B26\uFF09]` : s;
13731
+ };
13732
+ }
13733
+ });
13734
+
13735
+ // ../core/src/agent/runAgent.ts
13736
+ async function* runAgent(message, options) {
13737
+ const sessionId = options.sessionId;
13738
+ const runId = createUUID();
13739
+ const rawEvents = options.events;
13740
+ const events = async (base) => {
13741
+ try {
13742
+ await rawEvents({ ...base, metadata: { ...base.metadata, runId } });
13743
+ } catch (e) {
13744
+ console.warn("\u26A0\uFE0F \u57CB\u70B9\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u63A8\u7406\uFF09:", e instanceof Error ? e.message : e);
13745
+ }
13746
+ };
13747
+ const modelWindow = options.modelWindow;
13748
+ const signal = options.abortSignal;
13749
+ const depth = options.depth ?? 0;
13750
+ const cwd = options.cwd ?? process.cwd();
13751
+ const keepRecentUnits = options.keepRecentUnits;
13752
+ const compactRatio = options.compactRatio;
13753
+ const parentSystemPrompt = options.parentSystemPrompt;
13754
+ const effectiveModel = options.model?.trim() || MODEL_NAME;
13755
+ const { rawTools, cleanedToolSchemas, toolsTokens } = await prepareToolsAndInjections(message, options, events);
13756
+ let round = 0;
13757
+ let lastContent = "";
13758
+ let stopReason = "normal";
13759
+ let budgetTruncated = false;
13760
+ const MAX_AGENT_ROUNDS = 500;
13761
+ const firstPrompt = resolveFirstPrompt(message, options);
13762
+ const ledger = createRunLedger({
13763
+ model: effectiveModel,
13764
+ budgetYuan: depth === 0 && options.budgetYuan != null && options.budgetYuan > 0 ? options.budgetYuan : void 0,
13765
+ costSink: options.costSink,
13766
+ onAuxUsage: options.onAuxUsage
13767
+ });
13768
+ const reportAuxUsage = ledger.reportAuxUsage;
13769
+ const toolCostSink = process.env.DEEP_SEEK_TOOL_COST === "0" ? void 0 : ledger.addToolCost;
13770
+ let runCompactCount = 0;
13771
+ const nudges = createNudgeScheduler({
13772
+ firstPrompt,
13773
+ planMode: !!options.planMode,
13309
13774
  noEarlyFinal: !!options.noEarlyFinal,
13310
13775
  getContextUsage: () => ({ usedTokens: Math.round(estimateTokens(message) * calibRatio + toolsTokens), windowTokens: modelWindow }),
13311
- getBudgetStatus: depth === 0 && options.budgetYuan != null && options.budgetYuan > 0 ? () => ({ spent: budgetSpentYuan, budget: options.budgetYuan }) : void 0
13776
+ getBudgetStatus: ledger.getBudgetStatus
13777
+ // ★ E2 预算 soft 读数(闭包在账本内 O(1) 读数;未武装返回 null,管线自判)
13312
13778
  });
13313
13779
  const readTracker = /* @__PURE__ */ new Map();
13314
13780
  const userDecisionSource = depth > 0 ? "spawn_agent" : "user";
@@ -13330,47 +13796,7 @@ async function* runAgent(message, options) {
13330
13796
  let lastRealPromptTokens = persistedCalib.lastRealPromptTokens;
13331
13797
  let lastCachedTokens = persistedCalib.lastCachedTokens;
13332
13798
  const breaker = createRepeatBreaker({ sessionId, depth, llmDecisionSource, startTime, events });
13333
- let usageSum;
13334
- let costYuanAcc = 0;
13335
- let costKnown = true;
13336
- const addUsage = (u) => {
13337
- if (!u) return;
13338
- usageSum = {
13339
- prompt_tokens: (usageSum?.prompt_tokens ?? 0) + (u.prompt_tokens ?? 0),
13340
- completion_tokens: (usageSum?.completion_tokens ?? 0) + (u.completion_tokens ?? 0),
13341
- total_tokens: (usageSum?.total_tokens ?? 0) + (u.total_tokens ?? 0),
13342
- cached_tokens: (usageSum?.cached_tokens ?? 0) + (u.cached_tokens ?? 0)
13343
- };
13344
- const cost = calcCostYuan(effectiveModel, { promptTokens: u.prompt_tokens, completionTokens: u.completion_tokens, cachedTokens: u.cached_tokens });
13345
- if (cost == null) costKnown = false;
13346
- else {
13347
- costYuanAcc += cost;
13348
- budgetSpentYuan += cost;
13349
- }
13350
- };
13351
- let toolCostYuanAcc = 0;
13352
- const addToolCost = (yuan) => {
13353
- if (!Number.isFinite(yuan) || yuan <= 0) return;
13354
- toolCostYuanAcc += yuan;
13355
- budgetSpentYuan += yuan;
13356
- };
13357
- const toolCostSink = process.env.DEEP_SEEK_TOOL_COST === "0" ? void 0 : addToolCost;
13358
- let auxCostYuanAcc = 0;
13359
- const reportAuxUsage = (usage, kind) => {
13360
- if (usage) {
13361
- const cost = calcCostYuan(AUX_MODEL_NAME, { promptTokens: usage.prompt_tokens, completionTokens: usage.completion_tokens, cachedTokens: usage.cached_tokens });
13362
- if (cost != null && Number.isFinite(cost) && cost > 0) {
13363
- auxCostYuanAcc += cost;
13364
- budgetSpentYuan += cost;
13365
- }
13366
- }
13367
- try {
13368
- options.onAuxUsage?.(usage, kind);
13369
- } catch {
13370
- }
13371
- };
13372
- const costRelay = createCostRelay(options.costSink);
13373
- const inheritedBudgetCheck = options.budgetCheck ?? (depth === 0 && options.budgetYuan != null && options.budgetYuan > 0 ? () => budgetSpentYuan >= options.budgetYuan * 1.2 : void 0);
13799
+ const inheritedBudgetCheck = options.budgetCheck ?? (depth === 0 ? ledger.overHardLimit : void 0);
13374
13800
  let planWriteRej = 0;
13375
13801
  const toolCounts = {};
13376
13802
  await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
@@ -13401,11 +13827,12 @@ async function* runAgent(message, options) {
13401
13827
  yield { type: "final", text: lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
13402
13828
  return;
13403
13829
  }
13404
- if (depth === 0 && options.budgetYuan != null && options.budgetYuan > 0 && budgetSpentYuan >= options.budgetYuan * 1.2) {
13830
+ if (depth === 0 && ledger.overHardLimit()) {
13831
+ const snap = ledger.budgetSnapshot();
13405
13832
  stopReason = "limit";
13406
13833
  budgetTruncated = true;
13407
13834
  yield { type: "final", text: (lastContent || "") + `
13408
- \uFF08\u{1F4B0} \u9884\u7B97\u622A\u65AD\uFF1A\u672C run \u5DF2\u82B1\u8D39 \xA5${budgetSpentYuan.toFixed(3)} / \u9884\u7B97 \xA5${options.budgetYuan.toFixed(3)}\u3002/budget \u8C03\u6574\u9884\u7B97\u540E\u56DE\u590D"\u7EE7\u7EED"\u5373\u53EF\u63A5\u7EED\u3002\uFF09` };
13835
+ \uFF08\u{1F4B0} \u9884\u7B97\u622A\u65AD\uFF1A\u672C run \u5DF2\u82B1\u8D39 \xA5${snap.spent.toFixed(3)} / \u9884\u7B97 \xA5${snap.budget.toFixed(3)}\u3002/budget \u8C03\u6574\u9884\u7B97\u540E\u56DE\u590D"\u7EE7\u7EED"\u5373\u53EF\u63A5\u7EED\u3002\uFF09` };
13409
13836
  return;
13410
13837
  }
13411
13838
  if (depth > 0 && options.budgetCheck?.()) {
@@ -13494,12 +13921,12 @@ async function* runAgent(message, options) {
13494
13921
  const estAtInfer = estimateTokens(message) + toolsTokens;
13495
13922
  if (estAtInfer > 1e3) {
13496
13923
  const observed = infResult.usage.prompt_tokens / estAtInfer;
13497
- calibRatio = calibRatio * 0.6 + observed * 0.4;
13924
+ calibRatio = calibRatio * CALIB_EMA_KEEP + observed * CALIB_EMA_NEW;
13498
13925
  }
13499
13926
  lastRealPromptTokens = infResult.usage.prompt_tokens;
13500
13927
  lastCachedTokens = infResult.usage.cached_tokens;
13501
13928
  }
13502
- addUsage(infResult.usage);
13929
+ ledger.addUsage(infResult.usage);
13503
13930
  const assistantMessage = infResult.assistantMessage;
13504
13931
  message.push(assistantMessage);
13505
13932
  await appendMessage({ ...assistantMessage, sessionId });
@@ -13509,7 +13936,7 @@ async function* runAgent(message, options) {
13509
13936
  if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
13510
13937
  await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
13511
13938
  emitContextUsage();
13512
- costRelay.flush(budgetSpentYuan);
13939
+ ledger.flushRelay();
13513
13940
  const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
13514
13941
  const intercepted = nudges.interceptFinal(finalText, round);
13515
13942
  if (intercepted) continue;
@@ -13559,14 +13986,9 @@ async function* runAgent(message, options) {
13559
13986
  contextTokensEst: Math.round(estimateTokens(message) * calibRatio + toolsTokens),
13560
13987
  readTracker,
13561
13988
  // ★ P3-8:run 级读取追踪(同一引用透传,跨轮累计)
13562
- // ★ 子 run 成本回灌(成本信任链第二环 E1):runSubagent 经 toolCtx.reportCost 拿到闭包,
13563
- // 子 run 结束时把总花费记进本 run 预算累计器——父预算自动继承约束(子花费计入父 spent)。
13564
- reportCost: (yuan) => {
13565
- if (Number.isFinite(yuan) && yuan > 0) {
13566
- budgetSpentYuan += yuan;
13567
- subCostYuanAcc += yuan;
13568
- }
13569
- },
13989
+ // ★ 子 run 成本回灌(成本信任链第二环 E1):runSubagent 经 toolCtx.reportCost 把子 run 花费
13990
+ // 记进账本预算圈 + 展示账——父预算自动继承约束(守卫与分账语义在 runLedger.reportSubCost)。
13991
+ reportCost: ledger.reportSubCost,
13570
13992
  // ★ 按次计费工具成本上报(DeepSeek Search/Fetch):web 工具经 toolCtx.toolCostSink 拿到闭包,
13571
13993
  // 成功调用后按次上报进本 run 累计器。
13572
13994
  toolCostSink,
@@ -13578,7 +14000,10 @@ async function* runAgent(message, options) {
13578
14000
  nudges.notePlanWriteRejected(planWriteRej);
13579
14001
  },
13580
14002
  // ★ P1-5 子任务预算熔断:runSubagent 经 toolCtx.budgetCheck 拿到探测闭包,子 runAgent 每轮边界自检
13581
- budgetCheck: inheritedBudgetCheck
14003
+ budgetCheck: inheritedBudgetCheck,
14004
+ // ★ A 批批级预算:主循环不设(undefined);仅并行批 ≥2 时由调度层浅拷贝下发(R4/R5:
14005
+ // required 字段,构造处显式对齐,漏写即 tsc 打红)
14006
+ batchBudgetChars: void 0
13582
14007
  };
13583
14008
  const scheduleResult = yield* scheduleToolCalls(assistantMessage, message, toolCallCtx);
13584
14009
  if (scheduleResult.kind === "aborted") {
@@ -13591,7 +14016,7 @@ async function* runAgent(message, options) {
13591
14016
  }
13592
14017
  await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
13593
14018
  emitContextUsage();
13594
- costRelay.flush(budgetSpentYuan);
14019
+ ledger.flushRelay();
13595
14020
  if (options.planMode && planWriteRej >= PLAN_REJECT_FORCE_AT) {
13596
14021
  stopReason = "limit";
13597
14022
  yield { type: "final", text: PLAN_REJECT_FORCE_FINAL_TEXT(planWriteRej) };
@@ -13615,10 +14040,15 @@ async function* runAgent(message, options) {
13615
14040
  } catch {
13616
14041
  }
13617
14042
  try {
13618
- await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: usageSum });
14043
+ await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: ledger.usageSummary() });
13619
14044
  } catch {
13620
14045
  }
13621
14046
  try {
14047
+ await flushTraceBuffer();
14048
+ } catch {
14049
+ }
14050
+ try {
14051
+ const us = ledger.usageSummary();
13622
14052
  void recordUsageRun({
13623
14053
  v: 1,
13624
14054
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -13629,40 +14059,33 @@ async function* runAgent(message, options) {
13629
14059
  stopReason: signal?.aborted ? "aborted" : stopReason,
13630
14060
  rounds: round,
13631
14061
  durationMs: Math.round(performance.now() - startTime),
13632
- promptTokens: usageSum?.prompt_tokens,
13633
- completionTokens: usageSum?.completion_tokens,
13634
- totalTokens: usageSum?.total_tokens,
13635
- cachedTokens: usageSum?.cached_tokens,
13636
- costYuan: costKnown ? Math.round(costYuanAcc * 1e6) / 1e6 : void 0,
13637
- toolCostYuan: toolCostYuanAcc > 0 ? Math.round(toolCostYuanAcc * 1e6) / 1e6 : void 0,
13638
- // 按次计费工具花费(Search/Fetch)
13639
- auxCostYuan: auxCostYuanAcc > 0 ? Math.round(auxCostYuanAcc * 1e6) / 1e6 : void 0,
13640
- // 辅助模型花费(压缩摘要+风险分类器,P0-2)
14062
+ promptTokens: us?.prompt_tokens,
14063
+ completionTokens: us?.completion_tokens,
14064
+ totalTokens: us?.total_tokens,
14065
+ cachedTokens: us?.cached_tokens,
14066
+ ...ledger.usageLogFields(),
14067
+ // costYuan/toolCostYuan/auxCostYuan(口径与舍入在 runLedger)
13641
14068
  tools: toolCounts
13642
14069
  });
13643
14070
  } catch {
13644
14071
  }
13645
14072
  try {
13646
- costRelay.flush(budgetSpentYuan);
14073
+ ledger.flushRelay();
13647
14074
  } catch {
13648
14075
  }
13649
14076
  try {
13650
- if (depth === 0 && options.onUIEvent && usageSum) {
13651
- const prompt = usageSum.prompt_tokens ?? 0;
14077
+ const us = ledger.usageSummary();
14078
+ if (depth === 0 && options.onUIEvent && us) {
14079
+ const prompt = us.prompt_tokens ?? 0;
13652
14080
  options.onUIEvent({
13653
14081
  type: "run.receipt",
13654
14082
  rounds: round,
13655
14083
  promptTokens: prompt,
13656
- completionTokens: usageSum.completion_tokens ?? 0,
13657
- cachedTokens: usageSum.cached_tokens ?? 0,
13658
- cacheHitRate: prompt > 0 ? (usageSum.cached_tokens ?? 0) / prompt : 0,
13659
- costYuan: costKnown ? Math.round(costYuanAcc * 1e6) / 1e6 : null,
13660
- toolCostYuan: toolCostYuanAcc > 0 ? Math.round(toolCostYuanAcc * 1e6) / 1e6 : void 0,
13661
- // 按次计费工具花费(undefined 自动省略)
13662
- auxCostYuan: auxCostYuanAcc > 0 ? Math.round(auxCostYuanAcc * 1e6) / 1e6 : void 0,
13663
- // 辅助模型花费(压缩摘要+风险分类器;undefined 自动省略)
13664
- subCostYuan: subCostYuanAcc > 0 ? Math.round(subCostYuanAcc * 1e6) / 1e6 : void 0,
13665
- // ★ Q12:子 run 花费并列展示(展示层补偿;undefined 自动省略)
14084
+ completionTokens: us.completion_tokens ?? 0,
14085
+ cachedTokens: us.cached_tokens ?? 0,
14086
+ cacheHitRate: prompt > 0 ? (us.cached_tokens ?? 0) / prompt : 0,
14087
+ ...ledger.receiptFields(),
14088
+ // costYuan(null=无价)/toolCostYuan/auxCostYuan/subCostYuan(口径与舍入在 runLedger)
13666
14089
  durationMs: Math.round(performance.now() - startTime),
13667
14090
  budgetTruncated: budgetTruncated || void 0
13668
14091
  // 预算截断标注(E2;undefined 自动省略)
@@ -13683,7 +14106,7 @@ async function* runAgent(message, options) {
13683
14106
  }
13684
14107
  }
13685
14108
  }
13686
- var resolveFirstPrompt;
14109
+ var CALIB_EMA_KEEP, CALIB_EMA_NEW, resolveFirstPrompt;
13687
14110
  var init_runAgent = __esm({
13688
14111
  "../core/src/agent/runAgent.ts"() {
13689
14112
  "use strict";
@@ -13700,11 +14123,12 @@ var init_runAgent = __esm({
13700
14123
  init_toolScheduling();
13701
14124
  init_streamInference();
13702
14125
  init_agentNudges();
13703
- init_costRelay();
14126
+ init_runLedger();
13704
14127
  init_usageLog();
13705
- init_prices();
14128
+ init_trace();
13706
14129
  init_createModel();
13707
- init_client();
14130
+ CALIB_EMA_KEEP = 0.6;
14131
+ CALIB_EMA_NEW = 0.4;
13708
14132
  resolveFirstPrompt = (message, options) => {
13709
14133
  if ((options.depth ?? 0) !== 0) return "";
13710
14134
  const explicit = options.currentUserText?.trim();
@@ -13721,7 +14145,7 @@ var init_runAgent = __esm({
13721
14145
  });
13722
14146
 
13723
14147
  // ../core/src/session/lineage.ts
13724
- import fs35 from "node:fs";
14148
+ import fs37 from "node:fs";
13725
14149
  var parentOfSubsession, stringAncestorsOf, readForkedFromSync, lineageClosureOf, ownerCanApprove;
13726
14150
  var init_lineage = __esm({
13727
14151
  "../core/src/session/lineage.ts"() {
@@ -13744,7 +14168,7 @@ var init_lineage = __esm({
13744
14168
  };
13745
14169
  readForkedFromSync = (sessionId) => {
13746
14170
  try {
13747
- const raw = fs35.readFileSync(getStatePath(sessionId), "utf-8");
14171
+ const raw = fs37.readFileSync(getStatePath(sessionId), "utf-8");
13748
14172
  const v = JSON.parse(raw)?.forkedFrom;
13749
14173
  return typeof v === "string" && v.length > 0 ? v : void 0;
13750
14174
  } catch {
@@ -13873,6 +14297,7 @@ var init_subagent = __esm({
13873
14297
  sessionId: ""
13874
14298
  };
13875
14299
  }
14300
+ const SUBAGENT_REPORT_REQ = `\u3010\u6C47\u62A5\u8981\u6C42\u3011\u6700\u7EC8\u6C47\u62A5\u63A7\u5236\u5728\u4E00\u5C4F\u5185\uFF1A\u5DF2\u5B8C\u6210/\u6539\u52A8\u70B9\uFF08\u6587\u4EF6\u8DEF\u5F84\uFF09/\u9A8C\u8BC1\u7ED3\u679C/\u672A\u5B8C\u6210\u9879\u3002\u4E0D\u8981\u590D\u8FF0\u8FC7\u7A0B\u4E0E\u5DE5\u5177\u8F93\u51FA\u2014\u2014\u7236\u7EA7\u53EF\u76F4\u63A5\u7ECF resume \u8FFD\u95EE\u7EC6\u8282\u3002`;
13876
14301
  const subSystem = manifest ? [
13877
14302
  `# \u5B50 Agent \u8EAB\u4EFD\uFF1A${manifest.name}`,
13878
14303
  manifest.body,
@@ -13881,7 +14306,8 @@ var init_subagent = __esm({
13881
14306
  `\u5F53\u524D\u6240\u5904\u7684\u4EFB\u52A1\u6267\u884C\u5D4C\u5957\u6DF1\u5EA6: Depth ${ctx.depth + 1} / ${MAX_AGENT_DEPTH}`,
13882
14307
  `
13883
14308
  # \u3010\u7EE7\u627F\u7684\u5168\u5C40\u4EE3\u7801\u89C4\u8303\u3011`,
13884
- parentSystemPrompt
14309
+ parentSystemPrompt,
14310
+ SUBAGENT_REPORT_REQ
13885
14311
  ].filter(Boolean).join("\n") : [
13886
14312
  `# \u4EFB\u52A1\u4E0A\u4E0B\u6587`,
13887
14313
  `\u5F53\u524D\u6240\u5904\u7684\u4EFB\u52A1\u6267\u884C\u5D4C\u5957\u6DF1\u5EA6: Depth ${ctx.depth + 1} / ${MAX_AGENT_DEPTH}`,
@@ -13890,7 +14316,8 @@ var init_subagent = __esm({
13890
14316
  `\u4F60\u76F4\u63A5\u5171\u4EAB\u5E76\u64CD\u4F5C\u672C\u5730\u6587\u4EF6\u7CFB\u7EDF\u3002\u5F53\u4F60\u5BF9\u6587\u4EF6\u5168\u91CF\u5199\u5165\u6216\u5C40\u90E8\u4FEE\u6539\u65F6\uFF0C\u5FC5\u987B\u786E\u4FDD\u64CD\u4F5C\u7684\u7EDD\u5BF9\u7CBE\u51C6\u3002\u5B8C\u6210\u6307\u5B9A\u5B50\u4EFB\u52A1\u540E\uFF0C\u8BF7\u4F7F\u7528\u6700\u7EC8\u7684\u6587\u672C\u603B\u7ED3\u5411\u7236\u7EA7 Agent \u4EA4\u4ED8\u7ED3\u679C\u3002`,
13891
14317
  `
13892
14318
  # \u3010\u7EE7\u627F\u7684\u5168\u5C40\u4EE3\u7801\u89C4\u8303\u3011`,
13893
- parentSystemPrompt
14319
+ parentSystemPrompt,
14320
+ SUBAGENT_REPORT_REQ
13894
14321
  ].join("\n");
13895
14322
  const subTaskPrompt = [
13896
14323
  role ? manifest ? `# \u672C\u6B21\u89D2\u8272\u8865\u5145: ${role}` : `\u3010\u4F60\u7684\u4E13\u5C5E\u89D2\u8272\u3011: ${role}` : manifest ? "" : `\u3010\u4F60\u7684\u4E13\u5C5E\u89D2\u8272\u3011: \u4E13\u6CE8\u6267\u884C\u7279\u5B9A\u5C40\u90E8\u5B50\u4EFB\u52A1\u7684\u72EC\u7ACB\u667A\u80FD\u4F53\u52A9\u624B`,
@@ -13984,15 +14411,15 @@ var init_subagent = __esm({
13984
14411
  });
13985
14412
 
13986
14413
  // ../core/src/hooks/trust.ts
13987
- import path38 from "path";
13988
- import fs36 from "fs/promises";
14414
+ import path40 from "path";
14415
+ import fs38 from "fs/promises";
13989
14416
  var TRUSTED_HOOKS_FILE, approved, loaded, ensureLoaded, isHookCommandApproved, approveHookCommand;
13990
14417
  var init_trust2 = __esm({
13991
14418
  "../core/src/hooks/trust.ts"() {
13992
14419
  "use strict";
13993
14420
  init_config();
13994
14421
  init_common();
13995
- TRUSTED_HOOKS_FILE = path38.join(appConfig.dataDir, "trusted_hooks.json");
14422
+ TRUSTED_HOOKS_FILE = path40.join(appConfig.dataDir, "trusted_hooks.json");
13996
14423
  approved = /* @__PURE__ */ new Set();
13997
14424
  loaded = false;
13998
14425
  ensureLoaded = async () => {
@@ -14012,7 +14439,7 @@ var init_trust2 = __esm({
14012
14439
  if (approved.has(ident)) return;
14013
14440
  approved.add(ident);
14014
14441
  try {
14015
- await fs36.mkdir(appConfig.dataDir, { recursive: true });
14442
+ await fs38.mkdir(appConfig.dataDir, { recursive: true });
14016
14443
  await atomicWriteJSON(TRUSTED_HOOKS_FILE, [...approved]);
14017
14444
  } catch (e) {
14018
14445
  console.warn(`\u26A0\uFE0F [hooks] \u5BA1\u6279\u8BB0\u5FC6\u843D\u76D8\u5931\u8D25\uFF08\u672C\u6B21\u5DF2\u653E\u884C\uFF0C\u4E0B\u6B21\u9996\u8DD1\u4F1A\u518D\u95EE\uFF09: ${e?.message ?? e}`);
@@ -14022,8 +14449,8 @@ var init_trust2 = __esm({
14022
14449
  });
14023
14450
 
14024
14451
  // ../core/src/hooks/loader.ts
14025
- import fs37 from "fs/promises";
14026
- import path39 from "path";
14452
+ import fs39 from "fs/promises";
14453
+ import path41 from "path";
14027
14454
  var slimHookContext, readHooksConfig, appendValidated, validateRule, parseStdoutDecision, parseAgentDecision, gateSeq, firstRunGate, compileRule2, loadHooks, initHooks;
14028
14455
  var init_loader4 = __esm({
14029
14456
  "../core/src/hooks/loader.ts"() {
@@ -14052,15 +14479,15 @@ var init_loader4 = __esm({
14052
14479
  readHooksConfig = async (includeProject) => {
14053
14480
  const merged = {};
14054
14481
  const paths = [
14055
- path39.join(appConfig.dataDir, "settings.json"),
14482
+ path41.join(appConfig.dataDir, "settings.json"),
14056
14483
  // 全局用户级
14057
- ...includeProject ? [path39.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
14484
+ ...includeProject ? [path41.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
14058
14485
  // 项目级(叠加);未信任时省略
14059
14486
  ];
14060
14487
  for (const configPath of paths) {
14061
14488
  let raw;
14062
14489
  try {
14063
- raw = await fs37.readFile(configPath, "utf-8");
14490
+ raw = await fs39.readFile(configPath, "utf-8");
14064
14491
  } catch {
14065
14492
  continue;
14066
14493
  }
@@ -14433,8 +14860,8 @@ ${m.context}`;
14433
14860
  });
14434
14861
 
14435
14862
  // ../core/src/skills/loader.ts
14436
- import fs38 from "fs/promises";
14437
- import path40 from "path";
14863
+ import fs40 from "fs/promises";
14864
+ import path42 from "path";
14438
14865
  import { fileURLToPath as fileURLToPath2 } from "url";
14439
14866
  var BUILTIN_DIR, SOURCES2, MAX_SKILL_BODY_BYTES, splitList, parseSkillAt, loadSkills, initSkills;
14440
14867
  var init_loader5 = __esm({
@@ -14445,18 +14872,18 @@ var init_loader5 = __esm({
14445
14872
  init_registry3();
14446
14873
  init_skill();
14447
14874
  init_registry2();
14448
- BUILTIN_DIR = path40.join(path40.dirname(fileURLToPath2(import.meta.url)), "builtin");
14875
+ BUILTIN_DIR = path42.join(path42.dirname(fileURLToPath2(import.meta.url)), "builtin");
14449
14876
  SOURCES2 = [
14450
14877
  { dir: BUILTIN_DIR, source: "builtin" },
14451
- { dir: path40.join(appConfig.dataDir, "skills"), source: "global" },
14452
- { dir: path40.join(process.cwd(), ".deepseeker-code", "skills"), source: "project" }
14878
+ { dir: path42.join(appConfig.dataDir, "skills"), source: "global" },
14879
+ { dir: path42.join(process.cwd(), ".deepseeker-code", "skills"), source: "project" }
14453
14880
  ];
14454
14881
  MAX_SKILL_BODY_BYTES = 64 * 1024;
14455
14882
  splitList = (raw) => (raw ?? "").split(",").map((s) => s.trim()).filter(Boolean);
14456
14883
  parseSkillAt = async (skillFile, dir, source) => {
14457
14884
  let raw;
14458
14885
  try {
14459
- raw = await fs38.readFile(skillFile, "utf-8");
14886
+ raw = await fs40.readFile(skillFile, "utf-8");
14460
14887
  } catch {
14461
14888
  return null;
14462
14889
  }
@@ -14495,7 +14922,7 @@ var init_loader5 = __esm({
14495
14922
  loadSkills = async (includeProject) => {
14496
14923
  const picked = await scanSources(
14497
14924
  filterSources(SOURCES2, includeProject),
14498
- (e, dir) => e.isDirectory() ? path40.join(dir, e.name, "SKILL.md") : void 0,
14925
+ (e, dir) => e.isDirectory() ? path42.join(dir, e.name, "SKILL.md") : void 0,
14499
14926
  parseSkillAt
14500
14927
  );
14501
14928
  for (const { item } of picked) registerSkill(item);
@@ -14526,8 +14953,8 @@ var init_loader5 = __esm({
14526
14953
  });
14527
14954
 
14528
14955
  // ../core/src/agents/loader.ts
14529
- import fs39 from "fs/promises";
14530
- import path41 from "path";
14956
+ import fs41 from "fs/promises";
14957
+ import path43 from "path";
14531
14958
  import { fileURLToPath as fileURLToPath3 } from "url";
14532
14959
  var BUILTIN_DIR2, SOURCES3, parseAgentAt, initAgents;
14533
14960
  var init_loader6 = __esm({
@@ -14537,16 +14964,16 @@ var init_loader6 = __esm({
14537
14964
  init_frontmatter();
14538
14965
  init_registry4();
14539
14966
  init_registry2();
14540
- BUILTIN_DIR2 = path41.join(path41.dirname(fileURLToPath3(import.meta.url)), "builtin");
14967
+ BUILTIN_DIR2 = path43.join(path43.dirname(fileURLToPath3(import.meta.url)), "builtin");
14541
14968
  SOURCES3 = [
14542
14969
  { dir: BUILTIN_DIR2, source: "builtin" },
14543
- { dir: path41.join(appConfig.dataDir, "agents"), source: "global" },
14544
- { dir: path41.join(process.cwd(), ".deepseeker-code", "agents"), source: "project" }
14970
+ { dir: path43.join(appConfig.dataDir, "agents"), source: "global" },
14971
+ { dir: path43.join(process.cwd(), ".deepseeker-code", "agents"), source: "project" }
14545
14972
  ];
14546
14973
  parseAgentAt = async (file, dir, source) => {
14547
14974
  let raw;
14548
14975
  try {
14549
- raw = await fs39.readFile(file, "utf-8");
14976
+ raw = await fs41.readFile(file, "utf-8");
14550
14977
  } catch {
14551
14978
  return null;
14552
14979
  }
@@ -14581,7 +15008,7 @@ var init_loader6 = __esm({
14581
15008
  const known = new Set(into.map((t) => t.function.name));
14582
15009
  const picked = await scanSources(
14583
15010
  filterSources(SOURCES3, includeProject),
14584
- (e, dir) => e.isFile() && e.name.endsWith(".agent.md") ? path41.join(dir, e.name) : void 0,
15011
+ (e, dir) => e.isFile() && e.name.endsWith(".agent.md") ? path43.join(dir, e.name) : void 0,
14585
15012
  parseAgentAt
14586
15013
  );
14587
15014
  for (const { item: m, file, source } of picked) {
@@ -14618,8 +15045,8 @@ var init_registry7 = __esm({
14618
15045
  });
14619
15046
 
14620
15047
  // ../core/src/commands/loader.ts
14621
- import fs40 from "fs/promises";
14622
- import path42 from "path";
15048
+ import fs42 from "fs/promises";
15049
+ import path44 from "path";
14623
15050
  import { fileURLToPath as fileURLToPath4 } from "url";
14624
15051
  var BUILTIN_DIR3, SOURCES4, MAX_COMMAND_BODY_BYTES, parseCommandAt, loadCommands, initCommands;
14625
15052
  var init_loader7 = __esm({
@@ -14629,17 +15056,17 @@ var init_loader7 = __esm({
14629
15056
  init_frontmatter();
14630
15057
  init_registry7();
14631
15058
  init_registry2();
14632
- BUILTIN_DIR3 = path42.join(path42.dirname(fileURLToPath4(import.meta.url)), "builtin");
15059
+ BUILTIN_DIR3 = path44.join(path44.dirname(fileURLToPath4(import.meta.url)), "builtin");
14633
15060
  SOURCES4 = [
14634
15061
  { dir: BUILTIN_DIR3, source: "builtin" },
14635
- { dir: path42.join(appConfig.dataDir, "commands"), source: "global" },
14636
- { dir: path42.join(process.cwd(), ".deepseeker-code", "commands"), source: "project" }
15062
+ { dir: path44.join(appConfig.dataDir, "commands"), source: "global" },
15063
+ { dir: path44.join(process.cwd(), ".deepseeker-code", "commands"), source: "project" }
14637
15064
  ];
14638
15065
  MAX_COMMAND_BODY_BYTES = 64 * 1024;
14639
15066
  parseCommandAt = async (file, _dir, source) => {
14640
15067
  let raw;
14641
15068
  try {
14642
- raw = await fs40.readFile(file, "utf-8");
15069
+ raw = await fs42.readFile(file, "utf-8");
14643
15070
  } catch {
14644
15071
  return null;
14645
15072
  }
@@ -14650,7 +15077,7 @@ var init_loader7 = __esm({
14650
15077
  }
14651
15078
  const name = parsed.frontmatter.name;
14652
15079
  const description = parsed.frontmatter.description;
14653
- const fallbackName = path42.basename(file, ".md");
15080
+ const fallbackName = path44.basename(file, ".md");
14654
15081
  const finalName = name || fallbackName;
14655
15082
  if (!description) {
14656
15083
  console.warn(`\u26A0\uFE0F [commands] \u7F3A\u5C11 description\uFF08${file}\uFF09\uFF0C\u5DF2\u8DF3\u8FC7`);
@@ -14676,7 +15103,7 @@ var init_loader7 = __esm({
14676
15103
  loadCommands = async (includeProject) => {
14677
15104
  const picked = await scanSources(
14678
15105
  filterSources(SOURCES4, includeProject),
14679
- (e, dir) => e.isFile() && e.name.endsWith(".md") ? path42.join(dir, e.name) : void 0,
15106
+ (e, dir) => e.isFile() && e.name.endsWith(".md") ? path44.join(dir, e.name) : void 0,
14680
15107
  parseCommandAt
14681
15108
  );
14682
15109
  for (const { item } of picked) registerCommand(item);
@@ -14694,8 +15121,8 @@ var init_loader7 = __esm({
14694
15121
  });
14695
15122
 
14696
15123
  // ../core/src/outputStyles/loader.ts
14697
- import fs41 from "fs/promises";
14698
- import path43 from "path";
15124
+ import fs43 from "fs/promises";
15125
+ import path45 from "path";
14699
15126
  import { fileURLToPath as fileURLToPath5 } from "url";
14700
15127
  var BUILTIN_DIR4, MAX_STYLE_BODY_BYTES, SOURCES5, parseStyleAt, loadOutputStyles, initOutputStyles;
14701
15128
  var init_loader8 = __esm({
@@ -14705,17 +15132,17 @@ var init_loader8 = __esm({
14705
15132
  init_frontmatter();
14706
15133
  init_registry5();
14707
15134
  init_registry2();
14708
- BUILTIN_DIR4 = path43.join(path43.dirname(fileURLToPath5(import.meta.url)), "builtin");
15135
+ BUILTIN_DIR4 = path45.join(path45.dirname(fileURLToPath5(import.meta.url)), "builtin");
14709
15136
  MAX_STYLE_BODY_BYTES = 64 * 1024;
14710
15137
  SOURCES5 = [
14711
15138
  { dir: BUILTIN_DIR4, source: "builtin" },
14712
- { dir: path43.join(appConfig.dataDir, "output-styles"), source: "global" },
14713
- { dir: path43.join(process.cwd(), ".deepseeker-code", "output-styles"), source: "project" }
15139
+ { dir: path45.join(appConfig.dataDir, "output-styles"), source: "global" },
15140
+ { dir: path45.join(process.cwd(), ".deepseeker-code", "output-styles"), source: "project" }
14714
15141
  ];
14715
15142
  parseStyleAt = async (file, _dir, source) => {
14716
15143
  let raw;
14717
15144
  try {
14718
- raw = await fs41.readFile(file, "utf-8");
15145
+ raw = await fs43.readFile(file, "utf-8");
14719
15146
  } catch {
14720
15147
  return null;
14721
15148
  }
@@ -14747,7 +15174,7 @@ var init_loader8 = __esm({
14747
15174
  loadOutputStyles = async (includeProject) => {
14748
15175
  const picked = await scanSources(
14749
15176
  filterSources(SOURCES5, includeProject),
14750
- (e, dir) => e.isFile() && e.name.endsWith(".md") ? path43.join(dir, e.name) : void 0,
15177
+ (e, dir) => e.isFile() && e.name.endsWith(".md") ? path45.join(dir, e.name) : void 0,
14751
15178
  parseStyleAt
14752
15179
  );
14753
15180
  for (const { item } of picked) registerOutputStyle(item);
@@ -14828,8 +15255,8 @@ var init_bootstrap = __esm({
14828
15255
  });
14829
15256
 
14830
15257
  // ../core/src/statusLine/config.ts
14831
- import fs42 from "fs/promises";
14832
- import path44 from "path";
15258
+ import fs44 from "fs/promises";
15259
+ import path46 from "path";
14833
15260
  var parseBlock, readStatusLineConfig;
14834
15261
  var init_config2 = __esm({
14835
15262
  "../core/src/statusLine/config.ts"() {
@@ -14845,16 +15272,16 @@ var init_config2 = __esm({
14845
15272
  };
14846
15273
  readStatusLineConfig = async (includeProject) => {
14847
15274
  const paths = [
14848
- path44.join(appConfig.dataDir, "settings.json"),
15275
+ path46.join(appConfig.dataDir, "settings.json"),
14849
15276
  // 全局用户级
14850
- ...includeProject ? [path44.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
15277
+ ...includeProject ? [path46.join(process.cwd(), ".deepseeker-code", "settings.json")] : []
14851
15278
  // 项目级(覆盖);未信任时省略
14852
15279
  ];
14853
15280
  let result = null;
14854
15281
  for (const configPath of paths) {
14855
15282
  let raw;
14856
15283
  try {
14857
- raw = await fs42.readFile(configPath, "utf-8");
15284
+ raw = await fs44.readFile(configPath, "utf-8");
14858
15285
  } catch {
14859
15286
  continue;
14860
15287
  }
@@ -14874,8 +15301,8 @@ var init_config2 = __esm({
14874
15301
 
14875
15302
  // ../core/src/statusLine/runner.ts
14876
15303
  import { spawn as spawn5 } from "child_process";
14877
- import fs43 from "fs";
14878
- import path45 from "path";
15304
+ import fs45 from "fs";
15305
+ import path47 from "path";
14879
15306
  import { fileURLToPath as fileURLToPath6 } from "url";
14880
15307
  var STATUSLINE_TIMEOUT_MS, cachedVersion, runStatusLine;
14881
15308
  var init_runner = __esm({
@@ -14884,16 +15311,16 @@ var init_runner = __esm({
14884
15311
  init_guard();
14885
15312
  STATUSLINE_TIMEOUT_MS = 3e3;
14886
15313
  try {
14887
- const here = path45.dirname(fileURLToPath6(import.meta.url));
15314
+ const here = path47.dirname(fileURLToPath6(import.meta.url));
14888
15315
  const candidates = [
14889
- path45.join(here, "../../../package.json"),
15316
+ path47.join(here, "../../../package.json"),
14890
15317
  // src/core/package.json
14891
- path45.join(here, "../../../../package.json")
15318
+ path47.join(here, "../../../../package.json")
14892
15319
  // 仓库根 package.json
14893
15320
  ];
14894
15321
  for (const p of candidates) {
14895
15322
  try {
14896
- const pkg = JSON.parse(fs43.readFileSync(p, "utf-8"));
15323
+ const pkg = JSON.parse(fs45.readFileSync(p, "utf-8"));
14897
15324
  if (typeof pkg.version === "string" && pkg.version) {
14898
15325
  cachedVersion = pkg.version;
14899
15326
  break;
@@ -14943,222 +15370,6 @@ var init_runner = __esm({
14943
15370
  }
14944
15371
  });
14945
15372
 
14946
- // ../core/src/observability/store.ts
14947
- import path46 from "path";
14948
- import fs44 from "fs/promises";
14949
- var getTraceDirPath, getTracePath, tracePathCache, ensuredTraceDirs, ensureTraceDir, getGlobalClockPath, getTraceStorePath;
14950
- var init_store3 = __esm({
14951
- "../core/src/observability/store.ts"() {
14952
- "use strict";
14953
- init_config();
14954
- init_traceCalculate();
14955
- init_common();
14956
- getTraceDirPath = (mainTraceId) => {
14957
- assertSafeSessionId(mainTraceId, "traceId");
14958
- return path46.join(appConfig.dataDir, "trace", appConfig.userWorkspaceDir, getFileName(mainTraceId));
14959
- };
14960
- getTracePath = () => {
14961
- return path46.join(appConfig.dataDir, "trace");
14962
- };
14963
- tracePathCache = /* @__PURE__ */ new Map();
14964
- ensuredTraceDirs = /* @__PURE__ */ new Set();
14965
- ensureTraceDir = async (mainTraceId) => {
14966
- const dir = getTraceDirPath(mainTraceId);
14967
- if (ensuredTraceDirs.has(dir)) return;
14968
- await fs44.mkdir(dir, { recursive: true });
14969
- ensuredTraceDirs.add(dir);
14970
- };
14971
- getGlobalClockPath = () => {
14972
- return path46.join(getTracePath(), "trace-cleanup-clock.json");
14973
- };
14974
- getTraceStorePath = async (mainTraceId) => {
14975
- assertSafeSessionId(mainTraceId, "traceId");
14976
- const rootId = getFileName(mainTraceId);
14977
- const cached2 = tracePathCache.get(rootId);
14978
- if (cached2) return cached2;
14979
- await ensureTraceDir(rootId);
14980
- const dir = getTraceDirPath(rootId);
14981
- const files = await fs44.readdir(dir);
14982
- const existedFile = files.find((f) => f.endsWith(`__${rootId}.jsonl`));
14983
- let fileName = "";
14984
- if (existedFile) {
14985
- fileName = existedFile;
14986
- } else {
14987
- const todayStr = getTodayDateString();
14988
- fileName = `trace-${todayStr}__${rootId}.jsonl`;
14989
- }
14990
- const resolved = path46.join(dir, fileName);
14991
- tracePathCache.set(rootId, resolved);
14992
- return resolved;
14993
- };
14994
- }
14995
- });
14996
-
14997
- // ../core/src/observability/trace.ts
14998
- import fs45 from "fs/promises";
14999
- import path47 from "path";
15000
- async function emitTrace(base) {
15001
- const event = {
15002
- sessionId: base.sessionId,
15003
- parentId: base.parentId,
15004
- eventType: base.eventType,
15005
- timestamp: base.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
15006
- usage: base.usage,
15007
- payload: base.payload,
15008
- metadata: {
15009
- ...base.metadata || {}
15010
- }
15011
- };
15012
- try {
15013
- await appendTraceEvent(event);
15014
- } catch (error) {
15015
- console.warn("\u26A0\uFE0F [Trace] \u4E8B\u4EF6\u843D\u76D8\u5931\u8D25\uFF0C\u5DF2\u9632\u5FA1\u6027\u8DF3\u8FC7\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", error);
15016
- }
15017
- }
15018
- async function appendTraceEvent(event) {
15019
- const p = await getTraceStorePath(event.sessionId);
15020
- const line = JSON.stringify(event) + "\n";
15021
- await fs45.appendFile(p, line, "utf-8");
15022
- if (workspaceTraceBytes < 0) {
15023
- workspaceTraceBytes = await getDirBytes(getTracePath());
15024
- }
15025
- workspaceTraceBytes += Buffer.byteLength(line, "utf-8");
15026
- void maybeCleanupAfterAppend(event.sessionId);
15027
- }
15028
- async function maybeCleanupAfterAppend(sessionId) {
15029
- if (cleanupInProgress2) return;
15030
- cleanupInProgress2 = true;
15031
- try {
15032
- await ensureClockLoaded2();
15033
- const now = Date.now();
15034
- const isTimeExpired = now - diskLastCleanupAt2 > CLEANUP_INTERVAL_MS2;
15035
- const isSizeOverflow = workspaceTraceBytes > MAX_TRACE_FOLDER_BYTES;
15036
- if (!isTimeExpired && !isSizeOverflow) return;
15037
- if (isSizeOverflow && !isTimeExpired && now - lastSizeCleanupAt2 < SIZE_CLEANUP_COOLDOWN_MS2) {
15038
- return;
15039
- }
15040
- const reason = isTimeExpired && isSizeOverflow ? "\u5BB9\u91CF+\u65F6\u95F4\u53CC\u95F8" : isSizeOverflow ? "\u5BB9\u91CF\u8D8A\u8FC750MB\u5927\u95F8" : "3\u5929\u51B7\u5374\u5468\u671F\u5DF2\u6EE1";
15041
- console.log(`\u{1F9F9} [Trace\u8FD0\u7EF4] \u89E6\u53D1\u81EA\u9002\u5E94\u6E05\u7406\uFF08\u539F\u56E0: ${reason}\uFF09`);
15042
- let deletedCount = 0;
15043
- deletedCount += await cleanupOldTraceFiles();
15044
- workspaceTraceBytes = await getDirBytes(getTracePath());
15045
- if (workspaceTraceBytes > MAX_TRACE_FOLDER_BYTES) {
15046
- const targetBytes = Math.floor(MAX_TRACE_FOLDER_BYTES * SIZE_RECLAIM_TARGET_RATIO2);
15047
- deletedCount += await evictOldestForSize2(targetBytes);
15048
- workspaceTraceBytes = await getDirBytes(getTracePath());
15049
- }
15050
- lastSizeCleanupAt2 = now;
15051
- diskLastCleanupAt2 = now;
15052
- const clockPath = getGlobalClockPath();
15053
- await fs45.mkdir(path47.dirname(clockPath), { recursive: true });
15054
- await fs45.writeFile(clockPath, JSON.stringify({ lastCleanupAt: now, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), "utf-8");
15055
- if (deletedCount > 0) {
15056
- 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`);
15057
- }
15058
- } catch (error) {
15059
- console.warn("\u26A0\uFE0F [Trace\u8FD0\u7EF4] \u81EA\u52A8\u6E05\u7406\u53D1\u751F\u8F7B\u5FAE\u5F02\u5E38\uFF0C\u5DF2\u9632\u5FA1\u6027\u8DF3\u8FC7:", error);
15060
- } finally {
15061
- cleanupInProgress2 = false;
15062
- }
15063
- }
15064
- async function cleanupOldTraceFiles() {
15065
- const dir = getTracePath();
15066
- const retentionDays = Math.max(1, appConfig.traceRetentionDays || 7);
15067
- const cutoff = /* @__PURE__ */ new Date();
15068
- cutoff.setHours(0, 0, 0, 0);
15069
- cutoff.setDate(cutoff.getDate() - retentionDays);
15070
- const now = Date.now();
15071
- let deleted = 0;
15072
- let relativePaths;
15073
- try {
15074
- relativePaths = await fs45.readdir(dir, { recursive: true });
15075
- } catch {
15076
- return 0;
15077
- }
15078
- const re = /^trace-(\d{4}-\d{2}-\d{2})__([\w-]+)\.jsonl$/;
15079
- for (const relativePath of relativePaths) {
15080
- const fileName = path47.basename(relativePath);
15081
- const m = fileName.match(re);
15082
- if (!m) continue;
15083
- const fileBornDay = /* @__PURE__ */ new Date(`${m[1]}T00:00:00`);
15084
- if (fileBornDay >= cutoff) continue;
15085
- const fullPath = path47.join(dir, relativePath);
15086
- try {
15087
- const st = await fs45.stat(fullPath);
15088
- if (now - st.mtimeMs < ACTIVE_GRACE_MS2) continue;
15089
- await fs45.unlink(fullPath);
15090
- deleted += 1;
15091
- } catch {
15092
- }
15093
- }
15094
- return deleted;
15095
- }
15096
- async function evictOldestForSize2(targetBytes) {
15097
- const dir = getTracePath();
15098
- let relativePaths;
15099
- try {
15100
- relativePaths = await fs45.readdir(dir, { recursive: true });
15101
- } catch {
15102
- return 0;
15103
- }
15104
- const re = /^trace-(\d{4}-\d{2}-\d{2})__([\w-]+)\.jsonl$/;
15105
- const candidates = [];
15106
- for (const relativePath of relativePaths) {
15107
- const fileName = path47.basename(relativePath);
15108
- if (!re.test(fileName)) continue;
15109
- try {
15110
- const st = await fs45.stat(path47.join(dir, relativePath));
15111
- candidates.push({ mtime: st.mtimeMs, fullPath: path47.join(dir, relativePath), size: st.size });
15112
- } catch {
15113
- }
15114
- }
15115
- candidates.sort((a, b) => a.mtime - b.mtime);
15116
- let totalBytes = candidates.reduce((sum, c) => sum + c.size, 0);
15117
- let deleted = 0;
15118
- for (const c of candidates) {
15119
- if (totalBytes <= targetBytes) break;
15120
- try {
15121
- await fs45.unlink(c.fullPath);
15122
- totalBytes -= c.size;
15123
- deleted += 1;
15124
- } catch {
15125
- }
15126
- }
15127
- if (deleted > 0) {
15128
- console.warn(`\u26A0\uFE0F [Trace\u8FD0\u7EF4] \u5BB9\u91CF\u786C\u9A71\u9010\uFF1A\u8D8A\u8FC7\u6D3B\u8DC3\u4FDD\u62A4\u5F3A\u5220 ${deleted} \u4E2A\u6700\u65E7 trace \u6587\u4EF6\uFF08\u78C1\u76D8\u538B\u529B\u515C\u5E95\uFF09\u3002`);
15129
- }
15130
- return deleted;
15131
- }
15132
- var CLEANUP_INTERVAL_MS2, ACTIVE_GRACE_MS2, MAX_TRACE_FOLDER_BYTES, SIZE_RECLAIM_TARGET_RATIO2, SIZE_CLEANUP_COOLDOWN_MS2, workspaceTraceBytes, diskLastCleanupAt2, clockLoaded2, lastSizeCleanupAt2, cleanupInProgress2, ensureClockLoaded2;
15133
- var init_trace = __esm({
15134
- "../core/src/observability/trace.ts"() {
15135
- "use strict";
15136
- init_config();
15137
- init_store3();
15138
- init_traceCalculate();
15139
- CLEANUP_INTERVAL_MS2 = 3 * 24 * 60 * 60 * 1e3;
15140
- ACTIVE_GRACE_MS2 = 24 * 60 * 60 * 1e3;
15141
- MAX_TRACE_FOLDER_BYTES = 50 * 1024 * 1024;
15142
- SIZE_RECLAIM_TARGET_RATIO2 = 0.8;
15143
- SIZE_CLEANUP_COOLDOWN_MS2 = 60 * 1e3;
15144
- workspaceTraceBytes = -1;
15145
- diskLastCleanupAt2 = 0;
15146
- clockLoaded2 = false;
15147
- lastSizeCleanupAt2 = 0;
15148
- cleanupInProgress2 = false;
15149
- ensureClockLoaded2 = async () => {
15150
- if (clockLoaded2) return;
15151
- clockLoaded2 = true;
15152
- try {
15153
- const rawClock = await fs45.readFile(getGlobalClockPath(), "utf-8");
15154
- diskLastCleanupAt2 = JSON.parse(rawClock).lastCleanupAt || 0;
15155
- } catch {
15156
- diskLastCleanupAt2 = 0;
15157
- }
15158
- };
15159
- }
15160
- });
15161
-
15162
15373
  // ../core/src/tool/approvalGate.ts
15163
15374
  var approvalGate_exports = {};
15164
15375
  __export(approvalGate_exports, {
@@ -16874,7 +17085,7 @@ var init_StreamingCursor = __esm({
16874
17085
  // src/components/MessageBlock.tsx
16875
17086
  import { Box, Text as Text3 } from "ink";
16876
17087
  import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
16877
- var UsageFooter, listPrefix, AssistantLine, isFenceMark, isMdRow, isMdSep, mdCells, parseBlocks, fitCell, tableWidths, buildVLines, AssistantVLine, MessageBlock;
17088
+ var UsageFooter, listPrefix, AssistantLine, fenceLen, isMdRow, isMdSep, mdCells, parseBlocks, fitCell, tableWidths, buildVLines, AssistantVLine, MessageBlock;
16878
17089
  var init_MessageBlock = __esm({
16879
17090
  "src/components/MessageBlock.tsx"() {
16880
17091
  "use strict";
@@ -16913,7 +17124,10 @@ var init_MessageBlock = __esm({
16913
17124
  }
16914
17125
  return /* @__PURE__ */ jsx3(RichText, { text: line });
16915
17126
  };
16916
- isFenceMark = (l) => /^\s*```/.test(l);
17127
+ fenceLen = (l, bareOnly) => {
17128
+ const m = l.match(/^\s*(`{3,})([a-z0-9_-]*)\s*$/);
17129
+ return m && (!bareOnly || !m[2]) ? m[1].length : 0;
17130
+ };
16917
17131
  isMdRow = (l) => l.includes("|");
16918
17132
  isMdSep = (l) => /^[ \t|:-]+$/.test(l) && l.includes("-") && l.includes("|");
16919
17133
  mdCells = (l) => l.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((c) => c.trim());
@@ -16922,10 +17136,11 @@ var init_MessageBlock = __esm({
16922
17136
  const blocks = [];
16923
17137
  let i = 0;
16924
17138
  while (i < src.length) {
16925
- if (isFenceMark(src[i])) {
17139
+ const openLen = fenceLen(src[i], false);
17140
+ if (openLen) {
16926
17141
  const body = [];
16927
17142
  i++;
16928
- while (i < src.length && !isFenceMark(src[i])) body.push(src[i++]);
17143
+ while (i < src.length && fenceLen(src[i], true) < openLen) body.push(src[i++]);
16929
17144
  if (i < src.length) i++;
16930
17145
  blocks.push({ kind: "fence", lines: body });
16931
17146
  continue;