deepseeker-code 1.0.63 → 1.0.64

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 +416 -182
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -1531,27 +1531,29 @@ var init_client = __esm({
1531
1531
  });
1532
1532
 
1533
1533
  // ../core/src/llm/providers/deepseek/stream.ts
1534
- var STREAM_IDLE_TIMEOUT_MS, stripHistoricalReasoning, streamChat;
1534
+ var STREAM_IDLE_TIMEOUT_MS, isEmptyAssistantMsg, stripHistoricalReasoning, streamChat;
1535
1535
  var init_stream = __esm({
1536
1536
  "../core/src/llm/providers/deepseek/stream.ts"() {
1537
1537
  "use strict";
1538
1538
  init_client();
1539
1539
  STREAM_IDLE_TIMEOUT_MS = Number(process.env.DEEP_SEEK_STREAM_IDLE_TIMEOUT_MS) || 12e4;
1540
+ isEmptyAssistantMsg = (m) => m?.role === "assistant" && !(typeof m.content === "string" && m.content.length > 0) && !(Array.isArray(m.tool_calls) && m.tool_calls.length > 0);
1540
1541
  stripHistoricalReasoning = (messages) => {
1541
- if (process.env.DEEP_SEEK_REASONING_PASSTHROUGH === "1") return messages;
1542
+ const wire = messages.some(isEmptyAssistantMsg) ? messages.filter((m) => !isEmptyAssistantMsg(m)) : messages;
1543
+ if (process.env.DEEP_SEEK_REASONING_PASSTHROUGH === "1") return wire;
1542
1544
  let lastNonSystem = null;
1543
- for (let i = messages.length - 1; i >= 0; i--) {
1544
- if (messages[i]?.role !== "system") {
1545
- lastNonSystem = messages[i];
1545
+ for (let i = wire.length - 1; i >= 0; i--) {
1546
+ if (wire[i]?.role !== "system") {
1547
+ lastNonSystem = wire[i];
1546
1548
  break;
1547
1549
  }
1548
1550
  }
1549
- const hasSplitToolTurn = messages.some((m, i) => i > 0 && m?.role === "assistant" && messages[i - 1]?.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0);
1550
- if (lastNonSystem?.role === "assistant" || hasSplitToolTurn) return messages;
1551
- return messages.map((m) => {
1552
- const wire = m;
1553
- if (wire?.role !== "assistant" || typeof wire.reasoning_content !== "string") return m;
1554
- const copy = { ...wire };
1551
+ const hasSplitToolTurn = wire.some((m, i) => i > 0 && m?.role === "assistant" && wire[i - 1]?.role === "assistant" && Array.isArray(m.tool_calls) && m.tool_calls.length > 0);
1552
+ if (lastNonSystem?.role === "assistant" || hasSplitToolTurn) return wire;
1553
+ return wire.map((m) => {
1554
+ const w = m;
1555
+ if (w?.role !== "assistant" || typeof w.reasoning_content !== "string") return m;
1556
+ const copy = { ...w };
1555
1557
  delete copy.reasoning_content;
1556
1558
  return copy;
1557
1559
  });
@@ -2601,13 +2603,17 @@ var init_transcript = __esm({
2601
2603
  console.warn(`\u26A0\uFE0F [transcript] \u6D88\u606F\u542B ${fffdCount} \u4E2A\u4E71\u7801\u5B57\u7B26\uFF08U+FFFD\uFF09\uFF0C\u5DF2\u539F\u6837\u5199\u5165\u5386\u53F2\u3002\u6765\u6E90\u53EF\u80FD\u662F GBK \u5B50\u8FDB\u7A0B\u8F93\u51FA\u6216\u4E71\u7801\u7C98\u8D34\u3002`);
2602
2604
  }
2603
2605
  await appendLineWithRetry(p, payload);
2606
+ return true;
2604
2607
  } catch (e) {
2605
2608
  console.warn(`\u26A0\uFE0F \u6D88\u606F\u843D\u76D8\u5931\u8D25\uFF08\u5DF2\u91CD\u8BD5 3 \u6B21\uFF0C\u5185\u5B58\u4E0E\u78C1\u76D8\u8F6C\u5F55\u53EF\u80FD\u4E0D\u4E00\u81F4\uFF09:`, e);
2609
+ return false;
2606
2610
  }
2607
2611
  };
2608
2612
  appendMessages = async (sessionId, entries) => {
2609
- if (entries.length === 0) return;
2610
- if (entries.length === 1) return appendMessage(entries[0]);
2613
+ if (entries.length === 0) return { written: 0, failed: 0 };
2614
+ if (entries.length === 1) {
2615
+ return await appendMessage(entries[0]) ? { written: 1, failed: 0 } : { written: 0, failed: 1 };
2616
+ }
2611
2617
  try {
2612
2618
  await ensureSessionsDir(sessionId);
2613
2619
  const p = getTranscriptPath(sessionId);
@@ -2620,28 +2626,35 @@ var init_transcript = __esm({
2620
2626
  if (fffdTotal > 0) console.warn(`[transcript] \u6279\u91CF\u6D88\u606F\u542B ${fffdTotal} \u4E2A\u4E71\u7801\u5B57\u7B26\uFF08U+FFFD\uFF09\uFF0C\u5DF2\u539F\u6837\u5199\u5165\u5386\u53F2\u3002\u6765\u6E90\u53EF\u80FD\u662F GBK \u5B50\u8FDB\u7A0B\u8F93\u51FA\u6216\u4E71\u7801\u7C98\u8D34\u3002`);
2621
2627
  let handle = null;
2622
2628
  let i = 0;
2629
+ let failed = 0;
2623
2630
  try {
2624
2631
  handle = await fs9.open(p, "a");
2625
2632
  for (; i < payloads.length; i++) await appendLineWithRetry(p, payloads[i], handle);
2626
2633
  } catch (e) {
2627
2634
  console.warn(`[transcript] \u6279\u91CF\u843D\u76D8\u7B2C ${i + 1}/${payloads.length} \u6761\u5931\u8D25\uFF0C\u8BE5\u6761\u8D77\u56DE\u843D\u9010\u6761\u65E7\u8DEF\u5F84:`, e instanceof Error ? e.message : e);
2628
- for (; i < entries.length; i++) await appendMessage(entries[i]);
2635
+ for (; i < entries.length; i++) {
2636
+ if (!await appendMessage(entries[i])) failed++;
2637
+ }
2629
2638
  } finally {
2630
2639
  if (handle) await handle.close().catch(() => {
2631
2640
  });
2632
2641
  }
2642
+ return { written: entries.length - failed, failed };
2633
2643
  } catch (e) {
2634
2644
  console.warn(`\u6279\u91CF\u6D88\u606F\u843D\u76D8\u5931\u8D25\uFF08\u5185\u5B58\u4E0E\u78C1\u76D8\u8F6C\u5F55\u53EF\u80FD\u4E0D\u4E00\u81F4\uFF09:`, e);
2645
+ return { written: 0, failed: entries.length };
2635
2646
  }
2636
2647
  };
2637
2648
  appendEvent = async (sessionId, ev) => {
2638
- if (!appConfig.transcriptEvents) return;
2649
+ if (!appConfig.transcriptEvents) return true;
2639
2650
  try {
2640
2651
  await ensureSessionsDir(sessionId);
2641
2652
  const line = { id: createUUID(), ts: (/* @__PURE__ */ new Date()).toISOString(), ...ev };
2642
2653
  await appendLineWithRetry(getTranscriptPath(sessionId), JSON.stringify(line) + "\n");
2654
+ return true;
2643
2655
  } catch (e) {
2644
2656
  console.warn(`\u26A0\uFE0F [transcript] \u4E8B\u4EF6\u884C\u843D\u76D8\u5931\u8D25\uFF08${ev.dscEvent}\uFF0C\u6062\u590D/\u5206\u53C9\u5C06\u56DE\u9000\u542F\u53D1\u5F0F\uFF09:`, e);
2657
+ return false;
2645
2658
  }
2646
2659
  };
2647
2660
  }
@@ -2667,8 +2680,8 @@ var init_recovery = __esm({
2667
2680
  if (!sawEvents || !lastStart) return { kind: "legacy" };
2668
2681
  return closed.has(lastStart.runId) ? { kind: "clean" } : { kind: "crashed", runId: lastStart.runId };
2669
2682
  };
2670
- repairOrphanToolCalls = (msgs, confirmedCrash) => {
2671
- const placeholder = confirmedCrash ? "\uFF08\u4E0A\u6B21\u4F1A\u8BDD\u5728\u5DE5\u5177\u6267\u884C\u4E2D\u5D29\u6E83\uFF08run \u672A\u95ED\u5408\uFF09\uFF0C\u8BE5\u8C03\u7528\u672A\u6267\u884C\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09" : "\uFF08\u8BE5\u5DE5\u5177\u8C03\u7528\u56E0\u4E0A\u6B21\u4F1A\u8BDD\u5F02\u5E38\u4E2D\u65AD\u672A\u7559\u4E0B\u7ED3\u679C\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09";
2683
+ repairOrphanToolCalls = (msgs, confirmedUnclosed) => {
2684
+ const placeholder = confirmedUnclosed ? "\uFF08\u4E0A\u6B21\u4F1A\u8BDD\u7684\u8FD9\u6B21\u5DE5\u5177\u8C03\u7528\u672A\u5B8C\u6210\uFF08run \u672A\u95ED\u5408\uFF09\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09" : "\uFF08\u8BE5\u5DE5\u5177\u8C03\u7528\u56E0\u4E0A\u6B21\u4F1A\u8BDD\u5F02\u5E38\u4E2D\u65AD\u672A\u7559\u4E0B\u7ED3\u679C\uFF0C\u5DF2\u8DF3\u8FC7\u3002\uFF09";
2672
2685
  const out = [];
2673
2686
  const repairedToolCallIds = [];
2674
2687
  for (let i = 0; i < msgs.length; i++) {
@@ -2689,7 +2702,7 @@ var init_recovery = __esm({
2689
2702
  }
2690
2703
  }
2691
2704
  }
2692
- return { msgs: out, repairedToolCallIds, confirmedCrash };
2705
+ return { msgs: out, repairedToolCallIds, confirmedUnclosed };
2693
2706
  };
2694
2707
  reconcileCompaction = (state, lines) => {
2695
2708
  let last;
@@ -2713,9 +2726,9 @@ var init_recovery = __esm({
2713
2726
  await appendEvent(sessionId, {
2714
2727
  dscEvent: "run.abandoned",
2715
2728
  runId: interruption.runId,
2716
- reason: "run.end missing: interrupted by process exit/kill"
2729
+ reason: "unclosed (cause unknown)"
2717
2730
  });
2718
- console.warn(`\u27E6recovery\u27E7 \u68C0\u6D4B\u5230\u672A\u95ED\u5408 run\uFF08${interruption.runId.slice(0, 8)}\u2026\uFF09\u2192 \u5DF2\u8865 run.abandoned \u95ED\u5893\u6807\u8BB0\uFF1B\u5B64\u513F\u4FEE\u590D\u6309\u300C\u5D29\u6E83\u786E\u8BA4\u300D\u6863\u6267\u884C\u3002`);
2731
+ console.warn(`\u27E6recovery\u27E7 \u68C0\u6D4B\u5230\u672A\u95ED\u5408 run\uFF08${interruption.runId.slice(0, 8)}\u2026\uFF09\u2192 \u5DF2\u8865 run.abandoned \u95ED\u5893\u6807\u8BB0\uFF1B\u5B64\u513F\u4FEE\u590D\u6309\u300C\u672A\u95ED\u5408\u300D\u6863\u6267\u884C\u3002`);
2719
2732
  }
2720
2733
  const state = precomputed?.state ?? await getRollingState(sessionId);
2721
2734
  const compaction = reconcileCompaction(
@@ -3545,19 +3558,63 @@ ${lines}
3545
3558
  const hitRate = event.lastRealPromptTokens && event.lastCachedTokens != null && event.lastRealPromptTokens > 0 ? event.lastCachedTokens / event.lastRealPromptTokens : null;
3546
3559
  const cacheFactor = hitRate == null ? 1 : Math.min(1.1, Math.max(0.9, 0.9 + hitRate * 0.2));
3547
3560
  const effectiveRatio = Math.min(event.compactRatio * cacheFactor, 0.82);
3548
- if (!event.forceCompact && estReal(event.messageArr) <= event.modelWindow * effectiveRatio) return;
3561
+ const thresholdAtCheck = Math.round(event.modelWindow * effectiveRatio);
3562
+ const reportCheck = (decision, decayed2) => {
3563
+ event.onWindowCheck?.({
3564
+ estReal: Math.round(estReal(event.messageArr)),
3565
+ threshold: thresholdAtCheck,
3566
+ correctionRatio,
3567
+ toolsTokens,
3568
+ hitRate,
3569
+ cacheFactor,
3570
+ decision,
3571
+ ...decayed2 === void 0 ? {} : { decayed: decayed2 }
3572
+ });
3573
+ };
3574
+ const estRealAtTrigger = Math.round(estReal(event.messageArr));
3575
+ const emitCheckEvent = (decision) => {
3576
+ event.events({
3577
+ sessionId: event.sessionId,
3578
+ eventType: "context.check",
3579
+ metadata: {
3580
+ depth: event.depth,
3581
+ decisionSource: "summary",
3582
+ ok: true,
3583
+ estReal: estRealAtTrigger,
3584
+ thresholdAtSend: thresholdAtCheck,
3585
+ correctionRatioUsed: correctionRatio,
3586
+ hitRateAtCheck: hitRate,
3587
+ cacheFactorAtCheck: cacheFactor,
3588
+ windowDecision: decision
3589
+ }
3590
+ });
3591
+ };
3592
+ if (!event.forceCompact && estReal(event.messageArr) <= event.modelWindow * effectiveRatio) {
3593
+ reportCheck("noop");
3594
+ return;
3595
+ }
3549
3596
  const preDecaySize = estReal(event.messageArr);
3550
3597
  const decayedArr = decayOldToolResults(event.messageArr);
3551
- if (decayedArr !== event.messageArr) {
3598
+ const decayed = decayedArr !== event.messageArr;
3599
+ if (decayed) {
3552
3600
  event.messageArr.length = 0;
3553
3601
  event.messageArr.push(...decayedArr);
3554
- }
3555
- if (!event.forceCompact && estReal(event.messageArr) <= event.modelWindow * effectiveRatio) {
3556
- console.log(`\u{1F4C9} [\u7A97\u53E3\u6CBB\u7406] \u514D\u8D39\u8870\u51CF\uFF08\u65E7\u5DE5\u5177\u7ED3\u679C\u6298\u53E0\uFF09\u540E\u56DE\u5230\u9608\u503C\u5185\uFF08${Math.round(preDecaySize)} \u2192 ${Math.round(estReal(event.messageArr))} token\uFF09\uFF0C\u8DF3\u8FC7 LLM \u538B\u7F29`);
3557
- return;
3602
+ event.events({
3603
+ sessionId: event.sessionId,
3604
+ eventType: "context.decay",
3605
+ metadata: {
3606
+ depth: event.depth,
3607
+ decisionSource: "summary",
3608
+ ok: true,
3609
+ decayTokensBefore: Math.round(preDecaySize),
3610
+ decayTokensAfter: Math.round(estReal(event.messageArr))
3611
+ }
3612
+ });
3558
3613
  }
3559
3614
  const tokensThreshold = Math.round(event.modelWindow * effectiveRatio);
3615
+ let preCompactSent = false;
3560
3616
  await dispatch("PreCompact", { sessionId: event.sessionId, depth: event.depth, tokensBefore: Math.round(estReal(event.messageArr)), tokensThreshold });
3617
+ preCompactSent = true;
3561
3618
  const systemMsg = event.messageArr[0];
3562
3619
  const summaryMsg = event.messageArr[1];
3563
3620
  let keep = event.keepRecentUnits;
@@ -3565,102 +3622,113 @@ ${lines}
3565
3622
  const preCompactSize = Math.round(lastSize);
3566
3623
  const startTime = performance.now();
3567
3624
  let round = 0;
3568
- let forcedPass = event.forceCompact === true;
3569
- while (forcedPass || lastSize > event.modelWindow * effectiveRatio) {
3570
- forcedPass = false;
3571
- if (event.signal?.aborted) {
3572
- return;
3573
- }
3574
- ;
3575
- const active = event.messageArr.slice(2);
3576
- const { toCompact, keepRecent } = splitUntils(active, keep);
3577
- try {
3578
- if (toCompact.length > 0) {
3579
- summaryMsg.content = mergeSummarySlot(summaryMsg?.content || "", "", extractArchiveEntities(toCompact));
3580
- const batches = splitIntoBatches(toCompact);
3581
- summaryMsg.content = batches.length === 1 ? await synthesizeSlotNarrativeFromBatch(summaryMsg.content, batches[0], event.signal, event.onAuxUsage) : await synthesizeSlotNarrative(summaryMsg.content, await compactBatches(batches, event.signal, event.depth === 0 ? event.onUIEvent : void 0, event.onAuxUsage), event.signal, event.onAuxUsage);
3582
- if (estimateTokens([summaryMsg]) > SUMMARY_SELF_COMPACT_THRESHOLD) {
3625
+ let compactDecision = "noop";
3626
+ let forcedPass = event.forceCompact === true || decayed;
3627
+ try {
3628
+ while (forcedPass || lastSize > event.modelWindow * effectiveRatio) {
3629
+ forcedPass = false;
3630
+ if (event.signal?.aborted) {
3631
+ return;
3632
+ }
3633
+ ;
3634
+ const active = event.messageArr.slice(2);
3635
+ const { toCompact, keepRecent } = splitUntils(active, keep);
3636
+ try {
3637
+ if (toCompact.length > 0) {
3638
+ summaryMsg.content = mergeSummarySlot(summaryMsg?.content || "", "", extractArchiveEntities(toCompact));
3639
+ const batches = splitIntoBatches(toCompact);
3640
+ summaryMsg.content = batches.length === 1 ? await synthesizeSlotNarrativeFromBatch(summaryMsg.content, batches[0], event.signal, event.onAuxUsage) : await synthesizeSlotNarrative(summaryMsg.content, await compactBatches(batches, event.signal, event.depth === 0 ? event.onUIEvent : void 0, event.onAuxUsage), event.signal, event.onAuxUsage);
3641
+ const slotNotes = parseSummarySlot(typeof summaryMsg?.content === "string" ? summaryMsg.content : "").notes;
3642
+ if (estimateTokens([{ role: summaryMsg?.role ?? "system", content: slotNotes }]) > SUMMARY_SELF_COMPACT_THRESHOLD) {
3643
+ summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
3644
+ }
3645
+ const endTime = performance.now();
3646
+ event.messageArr.length = 0;
3647
+ event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
3648
+ event.events({
3649
+ sessionId: event.sessionId,
3650
+ eventType: "session.summary",
3651
+ metadata: {
3652
+ depth: event.depth,
3653
+ decisionSource: "summary",
3654
+ ok: true,
3655
+ durationMs: endTime - startTime,
3656
+ round: round++
3657
+ },
3658
+ usage: {
3659
+ prompt_tokens: lastSize,
3660
+ compress_tokens: estReal(event.messageArr)
3661
+ }
3662
+ });
3663
+ const store = await getRollingState(event.sessionId);
3664
+ store.archivedMessageCount = (store.archivedMessageCount || 0) + toCompact.length;
3665
+ await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
3666
+ compactDecision = "compact";
3667
+ } else if (keep > 1) {
3668
+ keep--;
3669
+ continue;
3670
+ } else if (summaryMsg?.content) {
3583
3671
  summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
3672
+ compactDecision = "selfCompact";
3673
+ event.messageArr.length = 0;
3674
+ event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
3675
+ const store = await getRollingState(event.sessionId);
3676
+ await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
3677
+ } else {
3678
+ break;
3584
3679
  }
3680
+ } catch (error) {
3681
+ if (event.signal?.aborted) return;
3682
+ const err = error instanceof Error ? error : new Error(String(error));
3683
+ console.warn("\u26A0\uFE0F \u672C\u8F6E\u538B\u7F29\u5931\u8D25\uFF0C\u8DF3\u8FC7:", err.message);
3684
+ const store = await getRollingState(event.sessionId);
3685
+ const nextFailures = (store.consecutiveFailures || 0) + 1;
3686
+ await setRollingState(event.sessionId, {
3687
+ archivedMessageCount: store.archivedMessageCount || 0,
3688
+ rollingSummary: summaryMsg?.content || "",
3689
+ consecutiveFailures: nextFailures
3690
+ // 👈 同步落盘
3691
+ });
3585
3692
  const endTime = performance.now();
3586
- event.messageArr.length = 0;
3587
- event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
3588
3693
  event.events({
3589
3694
  sessionId: event.sessionId,
3695
+ parentId: event.depth > 0 ? event.sessionId : "",
3590
3696
  eventType: "session.summary",
3591
3697
  metadata: {
3592
3698
  depth: event.depth,
3699
+ messageId: summaryMsg?.id ?? "(rolling-summary-slot)",
3593
3700
  decisionSource: "summary",
3594
- ok: true,
3701
+ ok: false,
3595
3702
  durationMs: endTime - startTime,
3596
- round: round++
3703
+ attempt: nextFailures
3597
3704
  },
3598
3705
  usage: {
3599
3706
  prompt_tokens: lastSize,
3600
- compress_tokens: estReal(event.messageArr)
3707
+ compress_tokens: estimateTokens(event.messageArr)
3708
+ },
3709
+ payload: {
3710
+ output: err.message
3601
3711
  }
3602
3712
  });
3603
- const store = await getRollingState(event.sessionId);
3604
- store.archivedMessageCount = (store.archivedMessageCount || 0) + toCompact.length;
3605
- await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
3606
- } else if (keep > 1) {
3607
- keep--;
3608
- continue;
3609
- } else if (summaryMsg?.content) {
3610
- summaryMsg.content = await compactSlotNarrative(summaryMsg.content, event.modelWindow, event.signal, event.onAuxUsage);
3611
- event.messageArr.length = 0;
3612
- event.messageArr.push(systemMsg, summaryMsg, ...keepRecent);
3613
- const store = await getRollingState(event.sessionId);
3614
- await persistCompaction(event.sessionId, store.archivedMessageCount, summaryMsg.content);
3615
- } else {
3713
+ if (nextFailures >= 3) {
3714
+ throw new Error(
3715
+ `\u274C [\u538B\u7F29\u7194\u65AD] \u4E0A\u4E0B\u6587\u538B\u7F29\u8FDE\u7EED ${nextFailures} \u6B21\u5931\u8D25\uFF0C\u4E3A\u9632\u5929\u4EF7\u8D26\u5355\u6B7B\u5FAA\u73AF\u5DF2\u6682\u505C\u81EA\u52A8\u538B\u7F29\u3002\u5E38\u89C1\u6839\u56E0\uFF1A\u6458\u8981\u8F85\u52A9\u6A21\u578B\u670D\u52A1\u5F02\u5E38\u6216\u7F51\u7EDC\u4E0D\u901A\uFF08\u53EF\u67E5 DEEP_SEEK_AUX_MODEL \u670D\u52A1\u53EF\u7528\u6027\uFF1B\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u5DF2\u7531\u7CFB\u7EDF\u81EA\u52A8\u9884\u622A\u65AD\uFF0C\u4E00\u822C\u4E0D\u518D\u662F\u4E3B\u56E0\uFF09\u3002\u81EA\u6108\uFF1A\u76F4\u63A5\u91CD\u53D1\u6D88\u606F\u5373\u53EF\u91CD\u8BD5\uFF08\u6210\u529F\u538B\u7F29\u4E00\u6B21\u8BA1\u6570\u5373\u6E05\u96F6\uFF09\uFF1B\u6216 /fork \u5206\u53C9\u7EED\u63A5\u3001/new \u5F00\u65B0\u4F1A\u8BDD\u3002`
3716
+ );
3717
+ }
3616
3718
  break;
3617
3719
  }
3618
- } catch (error) {
3619
- if (event.signal?.aborted) return;
3620
- const err = error instanceof Error ? error : new Error(String(error));
3621
- console.warn("\u26A0\uFE0F \u672C\u8F6E\u538B\u7F29\u5931\u8D25\uFF0C\u8DF3\u8FC7:", err.message);
3622
- const store = await getRollingState(event.sessionId);
3623
- const nextFailures = (store.consecutiveFailures || 0) + 1;
3624
- await setRollingState(event.sessionId, {
3625
- archivedMessageCount: store.archivedMessageCount || 0,
3626
- rollingSummary: summaryMsg?.content || "",
3627
- consecutiveFailures: nextFailures
3628
- // 👈 同步落盘
3629
- });
3630
- const endTime = performance.now();
3631
- event.events({
3632
- sessionId: event.sessionId,
3633
- parentId: event.depth > 0 ? event.sessionId : "",
3634
- eventType: "session.summary",
3635
- metadata: {
3636
- depth: event.depth,
3637
- messageId: summaryMsg?.id ?? "(rolling-summary-slot)",
3638
- decisionSource: "summary",
3639
- ok: false,
3640
- durationMs: endTime - startTime,
3641
- attempt: nextFailures
3642
- },
3643
- usage: {
3644
- prompt_tokens: lastSize,
3645
- compress_tokens: estimateTokens(event.messageArr)
3646
- },
3647
- payload: {
3648
- output: err.message
3649
- }
3720
+ const newSize = estReal(event.messageArr);
3721
+ if (newSize >= lastSize) break;
3722
+ lastSize = newSize;
3723
+ }
3724
+ } finally {
3725
+ if (preCompactSent) {
3726
+ await dispatch("PostCompact", { sessionId: event.sessionId, depth: event.depth, tokensBefore: Math.round(lastSize), tokensAfter: Math.round(estReal(event.messageArr)) }).catch(() => {
3650
3727
  });
3651
- if (nextFailures >= 3) {
3652
- throw new Error(
3653
- `\u274C [\u538B\u7F29\u7194\u65AD] \u4E0A\u4E0B\u6587\u538B\u7F29\u8FDE\u7EED ${nextFailures} \u6B21\u5931\u8D25\uFF0C\u4E3A\u9632\u5929\u4EF7\u8D26\u5355\u6B7B\u5FAA\u73AF\u5DF2\u6682\u505C\u81EA\u52A8\u538B\u7F29\u3002\u5E38\u89C1\u6839\u56E0\uFF1A\u6458\u8981\u8F85\u52A9\u6A21\u578B\u670D\u52A1\u5F02\u5E38\u6216\u7F51\u7EDC\u4E0D\u901A\uFF08\u53EF\u67E5 DEEP_SEEK_AUX_MODEL \u670D\u52A1\u53EF\u7528\u6027\uFF1B\u8D85\u957F\u5DE5\u5177\u7ED3\u679C\u5DF2\u7531\u7CFB\u7EDF\u81EA\u52A8\u9884\u622A\u65AD\uFF0C\u4E00\u822C\u4E0D\u518D\u662F\u4E3B\u56E0\uFF09\u3002\u81EA\u6108\uFF1A\u76F4\u63A5\u91CD\u53D1\u6D88\u606F\u5373\u53EF\u91CD\u8BD5\uFF08\u6210\u529F\u538B\u7F29\u4E00\u6B21\u8BA1\u6570\u5373\u6E05\u96F6\uFF09\uFF1B\u6216 /fork \u5206\u53C9\u7EED\u63A5\u3001/new \u5F00\u65B0\u4F1A\u8BDD\u3002`
3654
- );
3655
- }
3656
- break;
3657
3728
  }
3658
- const newSize = estReal(event.messageArr);
3659
- if (newSize >= lastSize) break;
3660
- lastSize = newSize;
3661
3729
  }
3662
- await dispatch("PostCompact", { sessionId: event.sessionId, depth: event.depth, tokensBefore: Math.round(lastSize), tokensAfter: Math.round(estReal(event.messageArr)) }).catch(() => {
3663
- });
3730
+ reportCheck(compactDecision, decayed);
3731
+ if (compactDecision !== "noop") emitCheckEvent(compactDecision);
3664
3732
  if (event.onUIEvent && event.depth === 0) {
3665
3733
  const afterSize = Math.round(estReal(event.messageArr));
3666
3734
  if (preCompactSize - afterSize > 0) {
@@ -4242,13 +4310,19 @@ var init_inbox = __esm({
4242
4310
  };
4243
4311
  flushLeftoverToTranscript = async (sessionId) => {
4244
4312
  const texts = claimSessionInbox(sessionId);
4313
+ let failed = 0;
4245
4314
  for (const t of texts) {
4246
- await appendMessage({ sessionId, role: "user", content: t });
4315
+ const ok = await appendMessage({ sessionId, role: "user", content: t });
4316
+ if (!ok) failed++;
4247
4317
  }
4248
4318
  if (texts.length > 0) {
4249
- console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u7ED3\u675F\u65F6 ${texts.length} \u6761\u8865\u5145\u8F93\u5165\u672A\u8BA4\u9886\uFF0C\u5DF2\u5199\u5165 transcript\uFF08\u4E0B\u8F6E\u81EA\u52A8\u5E26\u5165\uFF09`);
4319
+ if (failed > 0) {
4320
+ console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u7ED3\u675F\u65F6 ${failed}/${texts.length} \u6761\u8865\u5145\u8F93\u5165\u843D\u76D8\u5931\u8D25\uFF08transcript \u4FA7\u6545\u969C\uFF0C\u4E0B\u8F6E\u65E0\u6CD5\u5E26\u5165\uFF09`);
4321
+ } else {
4322
+ console.warn(`[inbox] \u4F1A\u8BDD ${sessionId} \u7ED3\u675F\u65F6 ${texts.length} \u6761\u8865\u5145\u8F93\u5165\u672A\u8BA4\u9886\uFF0C\u5DF2\u5199\u5165 transcript\uFF08\u4E0B\u8F6E\u81EA\u52A8\u5E26\u5165\uFF09`);
4323
+ }
4250
4324
  }
4251
- return texts.length;
4325
+ return { flushed: texts.length - failed, failed };
4252
4326
  };
4253
4327
  }
4254
4328
  });
@@ -6134,7 +6208,7 @@ var init_registry6 = __esm({
6134
6208
  });
6135
6209
 
6136
6210
  // ../core/src/memory/inject.ts
6137
- var MEMORY_FENCE, MEMORY_MARKER, injectMemory;
6211
+ var MEMORY_FENCE, MEMORY_MARKER, memorySnapshots, injectMemory, clearMemorySnapshot;
6138
6212
  var init_inject5 = __esm({
6139
6213
  "../core/src/memory/inject.ts"() {
6140
6214
  "use strict";
@@ -6142,12 +6216,24 @@ var init_inject5 = __esm({
6142
6216
  init_registry6();
6143
6217
  MEMORY_FENCE = "\u27E6DSC:MEMORY\u27E7";
6144
6218
  MEMORY_MARKER = "\u3010\u8BB0\u5FC6\u7D22\u5F15\u3011";
6145
- injectMemory = (message) => {
6146
- const index = getMemoryIndex();
6219
+ memorySnapshots = /* @__PURE__ */ new Map();
6220
+ injectMemory = (message, sessionId) => {
6221
+ let index;
6222
+ if (sessionId === void 0) {
6223
+ index = getMemoryIndex();
6224
+ } else {
6225
+ if (!memorySnapshots.has(sessionId)) {
6226
+ memorySnapshots.set(sessionId, getMemoryIndex());
6227
+ }
6228
+ index = memorySnapshots.get(sessionId) ?? null;
6229
+ }
6147
6230
  if (!index) return;
6148
6231
  injectMarkedBlock(message, MEMORY_FENCE, `${MEMORY_MARKER}
6149
6232
  ${index}`);
6150
6233
  };
6234
+ clearMemorySnapshot = (sessionId) => {
6235
+ memorySnapshots.delete(sessionId);
6236
+ };
6151
6237
  }
6152
6238
  });
6153
6239
 
@@ -6226,7 +6312,7 @@ var init_systemInjections = __esm({
6226
6312
  injectSkillCatalog(message);
6227
6313
  injectAgentCatalog(message);
6228
6314
  injectProjectGuide(message);
6229
- injectMemory(message);
6315
+ injectMemory(message, options.sessionId);
6230
6316
  return { rawTools, cleanedToolSchemas, toolsTokens };
6231
6317
  };
6232
6318
  }
@@ -12866,13 +12952,21 @@ var init_toolScheduling = __esm({
12866
12952
  scheduleToolCalls = async function* (assistantMessage, message, ctx) {
12867
12953
  const { sessionId, signal, rawTools } = ctx;
12868
12954
  let abortedDuringTools = false;
12955
+ const degraded = [];
12956
+ const markDegraded = (name) => {
12957
+ if (!degraded.includes(name)) degraded.push(name);
12958
+ };
12959
+ const noteWrite = (ok, name) => {
12960
+ if (!ok) markDegraded(name);
12961
+ };
12962
+ const degradedField = () => degraded.length > 0 ? { degraded: [...degraded] } : {};
12869
12963
  const pendingImages = [];
12870
12964
  const fillRestPlaceholders = async (fromIdx) => {
12871
12965
  const tcs2 = assistantMessage.tool_calls;
12872
12966
  for (let j = fromIdx + 1; j < tcs2.length; j++) {
12873
12967
  const ph = "\uFF08\u5DF2\u8DF3\u8FC7\uFF1A\u7EC8\u7ED3\u7C7B\u5DE5\u5177\u4E4B\u540E\u7684\u5E76\u884C\u8C03\u7528\u672A\u6267\u884C\uFF09";
12874
12968
  message.push({ role: "tool", tool_call_id: tcs2[j].id, content: ph });
12875
- await appendMessage({ sessionId, role: "tool", tool_call_id: tcs2[j].id, content: ph });
12969
+ noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: tcs2[j].id, content: ph }), "appendMessage(placeholder)");
12876
12970
  }
12877
12971
  };
12878
12972
  const parallelSafeToolsEnabled = appConfig.parallelSafeTools;
@@ -12911,7 +13005,7 @@ var init_toolScheduling = __esm({
12911
13005
  for (let j = idx; j < tcs.length; j++) {
12912
13006
  const ph = "\uFF08\u5DF2\u4E2D\u6B62\uFF0C\u672A\u6267\u884C\uFF09";
12913
13007
  message.push({ role: "tool", tool_call_id: tcs[j].id, content: ph });
12914
- await appendMessage({ sessionId, role: "tool", tool_call_id: tcs[j].id, content: ph });
13008
+ noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: tcs[j].id, content: ph }), "appendMessage(placeholder)");
12915
13009
  }
12916
13010
  break;
12917
13011
  }
@@ -12957,7 +13051,8 @@ var init_toolScheduling = __esm({
12957
13051
  if (oc.imageAttachments?.length) pendingImages.push(...oc.imageAttachments);
12958
13052
  if (oc.aborted) abortedDuringTools = true;
12959
13053
  }
12960
- await appendMessages(sessionId, batchEntries);
13054
+ const batchWrite = await appendMessages(sessionId, batchEntries);
13055
+ if (batchWrite.failed > 0) markDegraded("appendMessages(tool-results)");
12961
13056
  if (budget) {
12962
13057
  try {
12963
13058
  const tokensAfter = estimateTokens(message);
@@ -12991,22 +13086,22 @@ var init_toolScheduling = __esm({
12991
13086
  const oc = await processToolCall(tc, ctx);
12992
13087
  if (oc.terminal) {
12993
13088
  message.push({ role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
12994
- await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
13089
+ noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel }), "appendMessage(tool-result)");
12995
13090
  await fillRestPlaceholders(idx);
12996
13091
  yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
12997
13092
  if (oc.terminal.kind === "exit_plan_mode") {
12998
13093
  yield { type: "plan.proposed", plan: oc.terminal.plan || "" };
12999
- return { kind: "terminal", terminalText: oc.terminal.plan || oc.resultForUser };
13094
+ return { kind: "terminal", terminalText: oc.terminal.plan || oc.resultForUser, ...degradedField() };
13000
13095
  } else if (oc.terminal.kind === "enter_plan_mode") {
13001
13096
  yield { type: "plan.enterRequested", reason: oc.terminal.reason || "" };
13002
- return { kind: "terminal", terminalText: oc.terminal.reason ? `\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF1A${oc.terminal.reason}` : oc.resultForUser };
13097
+ return { kind: "terminal", terminalText: oc.terminal.reason ? `\u{1F4CB} \u6A21\u578B\u8BF7\u6C42\u8FDB\u5165\u8BA1\u5212\u6A21\u5F0F\uFF1A${oc.terminal.reason}` : oc.resultForUser, ...degradedField() };
13003
13098
  } else {
13004
- return { kind: "terminal", terminalText: oc.resultForUser };
13099
+ return { kind: "terminal", terminalText: oc.resultForUser, ...degradedField() };
13005
13100
  }
13006
13101
  }
13007
13102
  yield { type: "tool.end", toolCallId: oc.toolCallId, toolName: oc.calledName, result: oc.resultForUser, ok: oc.ok };
13008
13103
  message.push({ role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
13009
- await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel });
13104
+ noteWrite(await appendMessage({ sessionId, role: "tool", tool_call_id: oc.toolCallId, content: oc.resultForModel }), "appendMessage(tool-result)");
13010
13105
  if (oc.imageAttachments?.length) pendingImages.push(...oc.imageAttachments);
13011
13106
  if (oc.aborted) abortedDuringTools = true;
13012
13107
  idx++;
@@ -13015,17 +13110,17 @@ var init_toolScheduling = __esm({
13015
13110
  if (!abortedDuringTools && pendingImages.length > 0) {
13016
13111
  const parts = buildImageFollowUpParts(pendingImages);
13017
13112
  message.push({ role: "user", content: parts });
13018
- await appendMessage({ sessionId, role: "user", content: parts });
13113
+ noteWrite(await appendMessage({ sessionId, role: "user", content: parts }), "appendMessage(image-followup)");
13019
13114
  }
13020
- if (abortedDuringTools) return { kind: "aborted" };
13021
- return { kind: "completed" };
13115
+ if (abortedDuringTools) return { kind: "aborted", ...degradedField() };
13116
+ return { kind: "completed", ...degradedField() };
13022
13117
  };
13023
13118
  }
13024
13119
  });
13025
13120
 
13026
13121
  // ../core/src/agent/streamInference.ts
13027
13122
  import { createHash as createHash2 } from "node:crypto";
13028
- var sha1short, toolsFingerprintCache, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
13123
+ var sha1short, toolsFingerprintCache, buildPrefixFingerprint, RETRY_BACKOFF_CAP_MS, extractRetryAfterMs, withNudgeTail, streamInference;
13029
13124
  var init_streamInference = __esm({
13030
13125
  "../core/src/agent/streamInference.ts"() {
13031
13126
  "use strict";
@@ -13038,6 +13133,19 @@ var init_streamInference = __esm({
13038
13133
  init_createModel();
13039
13134
  sha1short = (s) => createHash2("sha1").update(s).digest("hex").slice(0, 10);
13040
13135
  toolsFingerprintCache = /* @__PURE__ */ new WeakMap();
13136
+ buildPrefixFingerprint = (message, cleanedToolSchemas) => ({
13137
+ toolsHash: (() => {
13138
+ let h = toolsFingerprintCache.get(cleanedToolSchemas);
13139
+ if (h === void 0) {
13140
+ h = sha1short(JSON.stringify(cleanedToolSchemas));
13141
+ toolsFingerprintCache.set(cleanedToolSchemas, h);
13142
+ }
13143
+ return h;
13144
+ })(),
13145
+ sysHash: sha1short(String(message[0]?.content ?? "")),
13146
+ sumHash: sha1short(String(message[1]?.content ?? "")),
13147
+ msgCount: message.length
13148
+ });
13041
13149
  RETRY_BACKOFF_CAP_MS = 3e4;
13042
13150
  extractRetryAfterMs = (e) => {
13043
13151
  try {
@@ -13081,28 +13189,32 @@ var init_streamInference = __esm({
13081
13189
  keepRecentUnits,
13082
13190
  compactRatio,
13083
13191
  modelWindow,
13084
- toolsTokens = 0
13192
+ toolsTokens = 0,
13193
+ windowCheck
13085
13194
  } = ctx;
13086
13195
  let inferenceMessages = withNudgeTail(message, nudgeMsg);
13087
13196
  let assistantMessage = { role: "assistant", content: null };
13088
13197
  try {
13089
- const prefixFingerprint = {
13090
- toolsHash: (() => {
13091
- let h = toolsFingerprintCache.get(cleanedToolSchemas);
13092
- if (h === void 0) {
13093
- h = sha1short(JSON.stringify(cleanedToolSchemas));
13094
- toolsFingerprintCache.set(cleanedToolSchemas, h);
13095
- }
13096
- return h;
13097
- })(),
13098
- sysHash: sha1short(String(message[0]?.content ?? "")),
13099
- sumHash: sha1short(String(message[1]?.content ?? "")),
13100
- msgCount: message.length
13101
- };
13198
+ const prefixFingerprint = buildPrefixFingerprint(message, cleanedToolSchemas);
13199
+ const wc = windowCheck;
13102
13200
  events({
13103
13201
  sessionId,
13104
13202
  eventType: "llm.request",
13105
- metadata: { depth, decisionSource: userDecisionSource, ok: true, durationMs: performance.now() - startTime, round, model, ...prefixFingerprint },
13203
+ metadata: {
13204
+ depth,
13205
+ decisionSource: userDecisionSource,
13206
+ ok: true,
13207
+ durationMs: performance.now() - startTime,
13208
+ round,
13209
+ model,
13210
+ ...prefixFingerprint,
13211
+ ...wc ? {
13212
+ estReal: wc.estReal,
13213
+ thresholdAtSend: wc.threshold,
13214
+ correctionRatioUsed: wc.correctionRatio,
13215
+ windowDecision: wc.decision
13216
+ } : {}
13217
+ },
13106
13218
  // ★ P2 口径:估算加上工具 schema 常数项(与 API 真实 prompt_tokens 同口径,trace 里 est/real 才可比)
13107
13219
  usage: { prompt_tokens: estimateTokens(inferenceMessages) + toolsTokens },
13108
13220
  payload: { input: msgText(message[message.length - 1].content) }
@@ -13150,6 +13262,16 @@ var init_streamInference = __esm({
13150
13262
  break;
13151
13263
  }
13152
13264
  }
13265
+ if (!signal?.aborted && !contentBuf && toolCallsBuf.size === 0 && !lastUsage && idleRetries < MAX_STREAM_RETRIES) {
13266
+ idleRetries++;
13267
+ if (reasoningBuf) yield { type: "text.reset" };
13268
+ console.warn(`\u26A0\uFE0F \u6D41\u7A7A\u7ED3\u675F\uFF080 \u5185\u5BB9 0 usage\uFF0C\u7591\u4F3C\u670D\u52A1\u7AEF\u65AD\u6D41\uFF09\uFF0C\u7B2C ${idleRetries}/${MAX_STREAM_RETRIES} \u6B21\u91CD\u8BD5...`);
13269
+ contentBuf = "";
13270
+ reasoningBuf = "";
13271
+ toolCallsBuf.clear();
13272
+ lastUsage = void 0;
13273
+ continue;
13274
+ }
13153
13275
  break;
13154
13276
  } catch (streamErr) {
13155
13277
  if (signal?.aborted) throw streamErr;
@@ -13219,6 +13341,19 @@ var init_streamInference = __esm({
13219
13341
  onUIEvent: ctx.onUIEvent,
13220
13342
  onAuxUsage: ctx.onAuxUsage
13221
13343
  });
13344
+ events({
13345
+ sessionId,
13346
+ eventType: "llm.contextDowngraded",
13347
+ metadata: {
13348
+ depth,
13349
+ decisionSource: llmDecisionSource,
13350
+ ok: true,
13351
+ round,
13352
+ model,
13353
+ estReal: estimateTokens(withNudgeTail(message, nudgeMsg)) + (toolsTokens ?? 0)
13354
+ },
13355
+ payload: { output: streamErr instanceof Error ? streamErr.message : String(streamErr) }
13356
+ });
13222
13357
  inferenceMessages = withNudgeTail(message, nudgeMsg);
13223
13358
  compactedThisRound = true;
13224
13359
  contentBuf = "";
@@ -13266,7 +13401,17 @@ var init_streamInference = __esm({
13266
13401
  events({
13267
13402
  sessionId,
13268
13403
  eventType: "llm.response",
13269
- metadata: { depth, decisionSource: llmDecisionSource, ok: true, durationMs: performance.now() - startTime, round, model },
13404
+ // ★ emptyStream 标记:0 内容 0 usage 的退化完成(重试耗尽仍空)——修复前该形态记 ok:true
13405
+ // 无任何异常痕迹(0d245bd0 取证时 trace 里完全看不出 round 13 是空流),此处如实回报。
13406
+ metadata: {
13407
+ depth,
13408
+ decisionSource: llmDecisionSource,
13409
+ ok: true,
13410
+ durationMs: performance.now() - startTime,
13411
+ round,
13412
+ model,
13413
+ ...!contentBuf && toolCallsBuf.size === 0 && !lastUsage ? { emptyStream: true } : {}
13414
+ },
13270
13415
  usage: {
13271
13416
  prompt_tokens: lastUsage?.prompt_tokens,
13272
13417
  completion_tokens: lastUsage?.completion_tokens,
@@ -13745,7 +13890,7 @@ var init_usageLog = __esm({
13745
13890
  ensuredDir = null;
13746
13891
  getUsageLogPath = (month) => path37.join(usageDir(), `usage-${month}.jsonl`);
13747
13892
  recordUsageRun = async (rec) => {
13748
- if (USAGE_DISABLED) return;
13893
+ if (USAGE_DISABLED) return true;
13749
13894
  try {
13750
13895
  const full = { workspace: appConfig.userWorkspaceDir, ...rec };
13751
13896
  const month = (full.ts || (/* @__PURE__ */ new Date()).toISOString()).slice(0, 7);
@@ -13755,8 +13900,10 @@ var init_usageLog = __esm({
13755
13900
  ensuredDir = dir;
13756
13901
  }
13757
13902
  await fs34.appendFile(getUsageLogPath(month), JSON.stringify(full) + "\n", "utf-8");
13903
+ return true;
13758
13904
  } catch (e) {
13759
13905
  console.warn("\u26A0\uFE0F [usageLog] \u4F7F\u7528\u65E5\u5FD7\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4E3B\u4E1A\u52A1\uFF09:", e instanceof Error ? e.message : e);
13906
+ return false;
13760
13907
  }
13761
13908
  };
13762
13909
  USAGE_FILE_RE = /^usage-\d{4}-\d{2}\.jsonl$/;
@@ -14034,7 +14181,7 @@ async function evictOldestForSize2(targetBytes) {
14034
14181
  }
14035
14182
  return deleted;
14036
14183
  }
14037
- 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;
14184
+ 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, droppedBatches, traceTimers, cleanupTriggerCount, needImmediateFlush, scheduleTraceTimer, flushChains, runSerialized, flushTraceBuffer, ensureClockLoaded2, tracePayloadMaxChars, truncateTracePayload;
14038
14185
  var init_trace = __esm({
14039
14186
  "../core/src/observability/trace.ts"() {
14040
14187
  "use strict";
@@ -14054,6 +14201,7 @@ var init_trace = __esm({
14054
14201
  TRACE_BATCH_LINES = 50;
14055
14202
  TRACE_BATCH_MS = 200;
14056
14203
  traceBuf = /* @__PURE__ */ new Map();
14204
+ droppedBatches = /* @__PURE__ */ new Map();
14057
14205
  traceTimers = /* @__PURE__ */ new Map();
14058
14206
  cleanupTriggerCount = 0;
14059
14207
  needImmediateFlush = (ev) => ev.metadata?.ok === false || ev.eventType === "user.aborted" || ev.eventType === "tool.repeat_break" || ev.eventType === "session.end";
@@ -14066,34 +14214,55 @@ var init_trace = __esm({
14066
14214
  t.unref?.();
14067
14215
  traceTimers.set(sessionId, t);
14068
14216
  };
14217
+ flushChains = /* @__PURE__ */ new Map();
14218
+ runSerialized = (id, fn) => {
14219
+ const prev = flushChains.get(id) ?? Promise.resolve();
14220
+ const next = prev.then(fn, fn);
14221
+ flushChains.set(id, next.catch(() => void 0));
14222
+ return next;
14223
+ };
14069
14224
  flushTraceBuffer = async (sessionId) => {
14070
- const ids = sessionId !== void 0 ? [sessionId] : [...traceBuf.keys()];
14225
+ const reportSticky = sessionId === void 0;
14226
+ const ids = sessionId !== void 0 ? [sessionId] : [.../* @__PURE__ */ new Set([...traceBuf.keys(), ...droppedBatches.keys()])];
14227
+ let failedSessions = 0;
14071
14228
  for (const id of ids) {
14072
- const arr = traceBuf.get(id);
14073
- if (!arr || arr.length === 0) {
14074
- traceBuf.delete(id);
14075
- continue;
14076
- }
14077
- traceBuf.set(id, []);
14078
- const timer = traceTimers.get(id);
14079
- if (timer) {
14080
- clearTimeout(timer);
14081
- traceTimers.delete(id);
14082
- }
14083
- try {
14084
- const joined = arr.join("");
14085
- const p = await getTraceStorePath(id);
14086
- await fs36.appendFile(p, joined, "utf-8");
14087
- if (workspaceTraceBytes < 0) {
14088
- workspaceTraceBytes = await getDirBytes(getTracePath());
14229
+ const degraded = await runSerialized(id, async () => {
14230
+ let bad = false;
14231
+ if (reportSticky && droppedBatches.has(id)) {
14232
+ droppedBatches.delete(id);
14233
+ bad = true;
14089
14234
  }
14090
- workspaceTraceBytes += Buffer.byteLength(joined, "utf-8");
14091
- cleanupTriggerCount++;
14092
- void maybeCleanupAfterAppend(id);
14093
- } catch (error) {
14094
- 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);
14095
- }
14235
+ const arr = traceBuf.get(id);
14236
+ if (!arr || arr.length === 0) {
14237
+ traceBuf.delete(id);
14238
+ return bad;
14239
+ }
14240
+ traceBuf.set(id, []);
14241
+ const timer = traceTimers.get(id);
14242
+ if (timer) {
14243
+ clearTimeout(timer);
14244
+ traceTimers.delete(id);
14245
+ }
14246
+ try {
14247
+ const joined = arr.join("");
14248
+ const p = await getTraceStorePath(id);
14249
+ await fs36.appendFile(p, joined, "utf-8");
14250
+ if (workspaceTraceBytes < 0) {
14251
+ workspaceTraceBytes = await getDirBytes(getTracePath());
14252
+ }
14253
+ workspaceTraceBytes += Buffer.byteLength(joined, "utf-8");
14254
+ cleanupTriggerCount++;
14255
+ void maybeCleanupAfterAppend(id);
14256
+ } catch (error) {
14257
+ bad = true;
14258
+ if (!reportSticky) droppedBatches.set(id, (droppedBatches.get(id) ?? 0) + 1);
14259
+ 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);
14260
+ }
14261
+ return bad;
14262
+ });
14263
+ if (degraded) failedSessions++;
14096
14264
  }
14265
+ return failedSessions;
14097
14266
  };
14098
14267
  ensureClockLoaded2 = async () => {
14099
14268
  if (clockLoaded2) return;
@@ -14180,7 +14349,16 @@ async function* runAgent(message, options) {
14180
14349
  const inheritedBudgetCheck = options.budgetCheck ?? (depth === 0 ? ledger.overHardLimit : void 0);
14181
14350
  let planWriteRej = 0;
14182
14351
  const toolCounts = {};
14183
- await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
14352
+ const degraded = [];
14353
+ const markDegraded = (name) => {
14354
+ if (!degraded.includes(name)) degraded.push(name);
14355
+ };
14356
+ try {
14357
+ const ok = await appendEvent(sessionId, { dscEvent: "run.start", runId, depth });
14358
+ if (!ok) markDegraded("appendEvent(run.start)");
14359
+ } catch {
14360
+ markDegraded("appendEvent(run.start)");
14361
+ }
14184
14362
  try {
14185
14363
  while (true) {
14186
14364
  round++;
@@ -14227,11 +14405,17 @@ async function* runAgent(message, options) {
14227
14405
  clearQueuedWake(sessionId);
14228
14406
  for (const t of inboxTexts) {
14229
14407
  message.push({ role: "user", content: t });
14230
- await appendMessage({ sessionId, role: "user", content: t });
14408
+ try {
14409
+ const ok = await appendMessage({ sessionId, role: "user", content: t });
14410
+ if (!ok) markDegraded("appendMessage(user)");
14411
+ } catch {
14412
+ markDegraded("appendMessage(user)");
14413
+ }
14231
14414
  }
14232
14415
  yield { type: "inbox.claimed", texts: inboxTexts };
14233
14416
  }
14234
14417
  yield { type: "round.start", round };
14418
+ let lastWindowCheck;
14235
14419
  try {
14236
14420
  await ensureFitsWindow(
14237
14421
  {
@@ -14253,6 +14437,10 @@ async function* runAgent(message, options) {
14253
14437
  onUIEvent: (evt) => {
14254
14438
  if (evt.type === "compact.done") runCompactCount++;
14255
14439
  options.onUIEvent?.(evt);
14440
+ },
14441
+ // ★ M1:判定快照出口(含 noop;抛异常路径不回调——那轮推理本就不发生)
14442
+ onWindowCheck: (check) => {
14443
+ lastWindowCheck = check;
14256
14444
  }
14257
14445
  }
14258
14446
  );
@@ -14288,7 +14476,8 @@ async function* runAgent(message, options) {
14288
14476
  modelWindow,
14289
14477
  toolsTokens,
14290
14478
  onUIEvent: options.onUIEvent,
14291
- onAuxUsage: reportAuxUsage
14479
+ onAuxUsage: reportAuxUsage,
14480
+ windowCheck: lastWindowCheck
14292
14481
  });
14293
14482
  if (infResult.kind === "aborted") {
14294
14483
  yield { type: "final", text: infResult.partialText || lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
@@ -14310,13 +14499,28 @@ async function* runAgent(message, options) {
14310
14499
  }
14311
14500
  ledger.addUsage(infResult.usage);
14312
14501
  const assistantMessage = infResult.assistantMessage;
14313
- message.push(assistantMessage);
14314
- await appendMessage({ ...assistantMessage, sessionId });
14315
- if (typeof assistantMessage.content === "string" && assistantMessage.content) {
14316
- lastContent = assistantMessage.content;
14502
+ const emptyAssistant = !(typeof assistantMessage.content === "string" && assistantMessage.content.length > 0) && !(Array.isArray(assistantMessage.tool_calls) && assistantMessage.tool_calls.length > 0);
14503
+ if (emptyAssistant) {
14504
+ markDegraded("emptyAssistantSkip");
14505
+ } else {
14506
+ message.push(assistantMessage);
14507
+ try {
14508
+ const ok = await appendMessage({ ...assistantMessage, sessionId });
14509
+ if (!ok) markDegraded("appendMessage(assistant)");
14510
+ } catch {
14511
+ markDegraded("appendMessage(assistant)");
14512
+ }
14513
+ if (typeof assistantMessage.content === "string" && assistantMessage.content) {
14514
+ lastContent = assistantMessage.content;
14515
+ }
14317
14516
  }
14318
14517
  if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
14319
- await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
14518
+ try {
14519
+ const ok = await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
14520
+ if (!ok) markDegraded("appendEvent(round.end)");
14521
+ } catch {
14522
+ markDegraded("appendEvent(round.end)");
14523
+ }
14320
14524
  emitContextUsage();
14321
14525
  ledger.flushRelay();
14322
14526
  const finalText = (typeof assistantMessage.content === "string" ? assistantMessage.content : "") || "";
@@ -14337,7 +14541,12 @@ async function* runAgent(message, options) {
14337
14541
  const ph = "\uFF08\u91CD\u590D\u8C03\u7528\u7194\u65AD\uFF0C\u672A\u6267\u884C\uFF09";
14338
14542
  message.push({ role: "tool", tool_call_id: tc.id, content: ph });
14339
14543
  }
14340
- await appendMessages(sessionId, assistantMessage.tool_calls.map((tc) => ({ sessionId, role: "tool", tool_call_id: tc.id, content: "\uFF08\u91CD\u590D\u8C03\u7528\u7194\u65AD\uFF0C\u672A\u6267\u884C\uFF09" })));
14544
+ try {
14545
+ const phWrite = await appendMessages(sessionId, assistantMessage.tool_calls.map((tc) => ({ sessionId, role: "tool", tool_call_id: tc.id, content: "\uFF08\u91CD\u590D\u8C03\u7528\u7194\u65AD\uFF0C\u672A\u6267\u884C\uFF09" })));
14546
+ if (phWrite.failed > 0) markDegraded("appendMessages(repeat-placeholder)");
14547
+ } catch {
14548
+ markDegraded("appendMessages(repeat-placeholder)");
14549
+ }
14341
14550
  yield { type: "final", text: repeatVerdict.text };
14342
14551
  return;
14343
14552
  }
@@ -14388,6 +14597,7 @@ async function* runAgent(message, options) {
14388
14597
  batchBudgetChars: void 0
14389
14598
  };
14390
14599
  const scheduleResult = yield* scheduleToolCalls(assistantMessage, message, toolCallCtx);
14600
+ if (scheduleResult.degraded) for (const name of scheduleResult.degraded) markDegraded(name);
14391
14601
  if (scheduleResult.kind === "aborted") {
14392
14602
  yield { type: "final", text: lastContent || "\uFF08\u5DF2\u4E2D\u6B62\uFF09" };
14393
14603
  return;
@@ -14396,7 +14606,12 @@ async function* runAgent(message, options) {
14396
14606
  yield { type: "final", text: scheduleResult.terminalText };
14397
14607
  return;
14398
14608
  }
14399
- await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
14609
+ try {
14610
+ const ok = await appendEvent(sessionId, { dscEvent: "round.end", runId, round, usage: infResult.usage });
14611
+ if (!ok) markDegraded("appendEvent(round.end)");
14612
+ } catch {
14613
+ markDegraded("appendEvent(round.end)");
14614
+ }
14400
14615
  emitContextUsage();
14401
14616
  ledger.flushRelay();
14402
14617
  if (options.planMode && planWriteRej >= PLAN_REJECT_FORCE_AT) {
@@ -14418,20 +14633,26 @@ async function* runAgent(message, options) {
14418
14633
  return;
14419
14634
  } finally {
14420
14635
  try {
14421
- await flushLeftoverToTranscript(sessionId);
14636
+ const flushResult = await flushLeftoverToTranscript(sessionId);
14637
+ if (flushResult.failed > 0) markDegraded("flushLeftoverToTranscript");
14422
14638
  } catch {
14639
+ markDegraded("flushLeftoverToTranscript");
14423
14640
  }
14424
14641
  try {
14425
- await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: ledger.usageSummary() });
14642
+ const ok = await appendEvent(sessionId, { dscEvent: "run.end", runId, stopReason: signal?.aborted ? "aborted" : stopReason, rounds: round, usage: ledger.usageSummary() });
14643
+ if (!ok) markDegraded("appendEvent(run.end)");
14426
14644
  } catch {
14645
+ markDegraded("appendEvent(run.end)");
14427
14646
  }
14428
14647
  try {
14429
- await flushTraceBuffer();
14648
+ const failedSessions = await flushTraceBuffer();
14649
+ if (failedSessions > 0) markDegraded("flushTraceBuffer");
14430
14650
  } catch {
14651
+ markDegraded("flushTraceBuffer");
14431
14652
  }
14432
14653
  try {
14433
14654
  const us = ledger.usageSummary();
14434
- void recordUsageRun({
14655
+ const ok = await recordUsageRun({
14435
14656
  v: 1,
14436
14657
  ts: (/* @__PURE__ */ new Date()).toISOString(),
14437
14658
  sessionId,
@@ -14449,11 +14670,20 @@ async function* runAgent(message, options) {
14449
14670
  // costYuan/toolCostYuan/auxCostYuan(口径与舍入在 runLedger)
14450
14671
  tools: toolCounts
14451
14672
  });
14673
+ if (!ok) markDegraded("recordUsageRun");
14452
14674
  } catch {
14675
+ markDegraded("recordUsageRun");
14453
14676
  }
14454
14677
  try {
14455
14678
  ledger.flushRelay();
14456
14679
  } catch {
14680
+ markDegraded("ledger.flushRelay");
14681
+ }
14682
+ try {
14683
+ await updateCalibration(sessionId, effectiveModel, { calibRatio, lastRealPromptTokens, lastCachedTokens });
14684
+ } catch (e) {
14685
+ markDegraded("updateCalibration");
14686
+ console.warn("\u26A0\uFE0F \u538B\u7F29\u6821\u51C6\u72B6\u6001\u843D\u76D8\u5931\u8D25\uFF08\u4E0B run \u6821\u51C6\u91CD\u96F6\uFF0C\u7F13\u5B58\u611F\u77E5\u538B\u7F29\u9000\u5316\u56DE\u7F3A\u7701 1.4\uFF09:", e instanceof Error ? e.message : e);
14457
14687
  }
14458
14688
  try {
14459
14689
  const us = ledger.usageSummary();
@@ -14469,22 +14699,24 @@ async function* runAgent(message, options) {
14469
14699
  ...ledger.receiptFields(),
14470
14700
  // costYuan(null=无价)/toolCostYuan/auxCostYuan/subCostYuan(口径与舍入在 runLedger)
14471
14701
  durationMs: Math.round(performance.now() - startTime),
14472
- budgetTruncated: budgetTruncated || void 0
14702
+ budgetTruncated: budgetTruncated || void 0,
14473
14703
  // 预算截断标注(E2;undefined 自动省略)
14704
+ degraded: degraded.length > 0 ? [...degraded] : void 0
14705
+ // D5:结算单发出前已降级的旁路快照(边界=updateCalibration 之后、Stop hook 之前;undefined 自动省略)
14474
14706
  });
14475
14707
  }
14476
14708
  } catch {
14709
+ markDegraded("run.receipt");
14477
14710
  }
14478
14711
  try {
14479
14712
  const reason = signal?.aborted ? "aborted" : stopReason;
14480
14713
  await dispatch("Stop", { sessionId, cwd, lastText: lastContent || "", reason });
14481
14714
  } catch (e) {
14715
+ markDegraded("Stop-hook");
14482
14716
  console.warn("\u26A0\uFE0F Stop hook \u5916\u5C42\u515C\u5E95\uFF08dispatch \u5185\u90E8\u5BB9\u9519\u5931\u6548\uFF0C\u5C5E\u5F02\u5E38\u4FE1\u53F7\uFF09:", e instanceof Error ? e.message : e);
14483
14717
  }
14484
- try {
14485
- await updateCalibration(sessionId, effectiveModel, { calibRatio, lastRealPromptTokens, lastCachedTokens });
14486
- } catch (e) {
14487
- console.warn("\u26A0\uFE0F \u538B\u7F29\u6821\u51C6\u72B6\u6001\u843D\u76D8\u5931\u8D25\uFF08\u4E0B run \u6821\u51C6\u91CD\u96F6\uFF0C\u7F13\u5B58\u611F\u77E5\u538B\u7F29\u9000\u5316\u56DE\u7F3A\u7701 1.4\uFF09:", e instanceof Error ? e.message : e);
14718
+ if (degraded.length > 0) {
14719
+ console.warn(`\u26A0\uFE0F [run \u964D\u7EA7] ${degraded.length} \u9879\uFF08\u4E0D\u5F71\u54CD\u4E3B\u6D41\u7A0B\uFF1B\u5177\u4F53\u540E\u679C\u89C1\u7EC4\u4EF6\u540D\uFF09: ${degraded.join(", ")}`);
14488
14720
  }
14489
14721
  }
14490
14722
  }
@@ -15862,6 +16094,7 @@ var init_chatProcessing = __esm({
15862
16094
  init_systemPrompt();
15863
16095
  init_model();
15864
16096
  init_agentWake();
16097
+ init_inject5();
15865
16098
  handleUnifiedChat = async (inbound, sendOutbound, sseWrite, abortSignal, opts) => {
15866
16099
  let busyKey = typeof inbound.sessionId === "string" && inbound.sessionId ? inbound.sessionId : "";
15867
16100
  markRunStart(busyKey);
@@ -16029,6 +16262,7 @@ ${envLines.join("\n")}`;
16029
16262
  }
16030
16263
  await dispatch("SessionEnd", { sessionId, cwd: process.cwd() }).catch(() => {
16031
16264
  });
16265
+ clearMemorySnapshot(sessionId);
16032
16266
  await emitTrace({
16033
16267
  sessionId,
16034
16268
  eventType: "session.end",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepseeker-code",
3
- "version": "1.0.63",
3
+ "version": "1.0.64",
4
4
  "description": "DeepSeeker-Code 终端客户端(React Ink,in-process 直驱 runAgent)",
5
5
  "homepage": "https://github.com/xknk/deepSeekCode#readme",
6
6
  "repository": {