mixdog 0.9.38 → 0.9.40

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 (124) hide show
  1. package/package.json +9 -4
  2. package/scripts/abort-recovery-test.mjs +43 -2
  3. package/scripts/agent-tag-reuse-smoke.mjs +146 -6
  4. package/scripts/agent-terminal-reap-test.mjs +127 -0
  5. package/scripts/agent-trace-io-test.mjs +69 -0
  6. package/scripts/code-graph-disk-hit-test.mjs +224 -0
  7. package/scripts/execution-completion-dedup-test.mjs +157 -0
  8. package/scripts/execution-pending-resume-kick-test.mjs +57 -2
  9. package/scripts/execution-resume-esc-integration-test.mjs +176 -0
  10. package/scripts/explore-bench.mjs +38 -2
  11. package/scripts/explore-prompt-policy-test.mjs +152 -11
  12. package/scripts/find-fuzzy-hidden-test.mjs +122 -0
  13. package/scripts/internal-comms-bench-test.mjs +226 -0
  14. package/scripts/internal-comms-bench.mjs +185 -58
  15. package/scripts/internal-comms-smoke.mjs +171 -23
  16. package/scripts/live-worker-smoke.mjs +38 -2
  17. package/scripts/memory-cycle-routing-test.mjs +111 -0
  18. package/scripts/memory-rule-contract-test.mjs +93 -0
  19. package/scripts/output-style-smoke.mjs +2 -2
  20. package/scripts/rg-runner-test.mjs +240 -0
  21. package/scripts/routing-corpus-test.mjs +349 -0
  22. package/scripts/routing-corpus.mjs +211 -32
  23. package/scripts/session-orphan-sweep-test.mjs +83 -0
  24. package/scripts/steering-drain-buckets-test.mjs +316 -2
  25. package/scripts/tool-smoke.mjs +21 -13
  26. package/scripts/tool-tui-presentation-test.mjs +202 -0
  27. package/scripts/tui-transcript-perf-test.mjs +279 -0
  28. package/src/agents/heavy-worker/AGENT.md +10 -7
  29. package/src/agents/reviewer/AGENT.md +6 -4
  30. package/src/agents/worker/AGENT.md +7 -5
  31. package/src/rules/agent/00-common.md +4 -4
  32. package/src/rules/agent/00-core.md +11 -14
  33. package/src/rules/agent/20-skip-protocol.md +3 -3
  34. package/src/rules/agent/30-explorer.md +50 -60
  35. package/src/rules/agent/40-cycle1-agent.md +15 -24
  36. package/src/rules/agent/41-cycle2-agent.md +33 -57
  37. package/src/rules/agent/42-cycle3-agent.md +28 -42
  38. package/src/rules/lead/01-general.md +7 -10
  39. package/src/rules/lead/lead-brief.md +11 -14
  40. package/src/rules/lead/lead-tool.md +6 -5
  41. package/src/rules/shared/01-tool.md +44 -45
  42. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +44 -1
  43. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +20 -5
  44. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +21 -1
  45. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +17 -38
  46. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +17 -8
  47. package/src/runtime/agent/orchestrator/session/context-utils.mjs +270 -78
  48. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -0
  50. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +123 -0
  51. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +15 -2
  52. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +67 -2
  53. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  54. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  55. package/src/runtime/agent/orchestrator/session/store.mjs +232 -43
  56. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +43 -1
  57. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -1
  58. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +27 -13
  59. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -20
  60. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +6 -2
  61. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +118 -53
  62. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +106 -29
  63. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +8 -1
  64. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +4 -2
  65. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +3 -1
  66. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +4 -1
  67. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +33 -2
  68. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +151 -64
  69. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +37 -13
  70. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +84 -49
  71. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +172 -23
  72. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +3 -0
  73. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +68 -5
  74. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +17 -3
  75. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +24 -10
  76. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +6 -3
  77. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -7
  78. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -0
  79. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
  80. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -4
  81. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +4 -3
  82. package/src/runtime/memory/lib/memory-cycle3.mjs +12 -2
  83. package/src/runtime/shared/buffered-appender.mjs +13 -2
  84. package/src/runtime/shared/tool-primitives.mjs +4 -1
  85. package/src/runtime/shared/tool-status.mjs +27 -0
  86. package/src/runtime/shared/tool-surface.mjs +6 -3
  87. package/src/session-runtime/config-helpers.mjs +14 -0
  88. package/src/session-runtime/context-status.mjs +1 -0
  89. package/src/session-runtime/effort.mjs +6 -2
  90. package/src/session-runtime/lifecycle-api.mjs +4 -0
  91. package/src/session-runtime/model-recency.mjs +5 -2
  92. package/src/session-runtime/runtime-core.mjs +36 -2
  93. package/src/session-runtime/session-turn-api.mjs +12 -0
  94. package/src/session-runtime/tool-catalog.mjs +34 -0
  95. package/src/standalone/agent-tool/notify.mjs +13 -0
  96. package/src/standalone/agent-tool.mjs +53 -70
  97. package/src/standalone/explore-tool.mjs +6 -7
  98. package/src/tui/App.jsx +33 -1
  99. package/src/tui/app/model-options.mjs +5 -3
  100. package/src/tui/app/model-picker.mjs +12 -24
  101. package/src/tui/app/transcript-window.mjs +10 -10
  102. package/src/tui/app/use-transcript-window.mjs +38 -56
  103. package/src/tui/components/ToolExecution.jsx +11 -6
  104. package/src/tui/components/TranscriptItem.jsx +1 -1
  105. package/src/tui/components/tool-execution/surface-detail.mjs +24 -10
  106. package/src/tui/components/tool-execution/text-format.mjs +10 -19
  107. package/src/tui/dist/index.mjs +866 -300
  108. package/src/tui/engine/agent-job-feed.mjs +216 -19
  109. package/src/tui/engine/agent-response-tail.mjs +68 -0
  110. package/src/tui/engine/notification-plan.mjs +16 -0
  111. package/src/tui/engine/session-api.mjs +14 -2
  112. package/src/tui/engine/session-flow.mjs +22 -2
  113. package/src/tui/engine/tool-card-results.mjs +64 -37
  114. package/src/tui/engine/tool-result-status.mjs +75 -21
  115. package/src/tui/engine/turn.mjs +172 -77
  116. package/src/tui/engine.mjs +199 -39
  117. package/src/tui/index.jsx +2 -2
  118. package/src/workflows/bench/WORKFLOW.md +25 -35
  119. package/src/workflows/default/WORKFLOW.md +38 -32
  120. package/src/workflows/solo/WORKFLOW.md +19 -22
  121. package/scripts/_jitter-fuzz.mjs +0 -44410
  122. package/scripts/_jitter-fuzz2.mjs +0 -44400
  123. package/scripts/_jitter-probe.mjs +0 -44397
  124. package/scripts/_jp2.mjs +0 -45614
@@ -1976,10 +1976,11 @@ function bridgeAgentModelSummary(args) {
1976
1976
  ]);
1977
1977
  }
1978
1978
  function summarizeLineWindow(a) {
1979
+ const hasOffset = a.offset != null;
1979
1980
  const offset = a.offset ?? a.start_line ?? a.startLine ?? a.line;
1980
1981
  const limit = a.limit ?? a.line_count ?? a.lineCount ?? a.lines;
1981
1982
  if (offset == null && limit == null) return "";
1982
- const start = Number(offset);
1983
+ const start = Number(offset) + (hasOffset ? 1 : 0);
1983
1984
  const count = Number(limit);
1984
1985
  if (Number.isFinite(start) && Number.isFinite(count) && count > 0) {
1985
1986
  return `lines ${start}-${Math.max(start, start + count - 1)}`;
@@ -2839,7 +2840,9 @@ function toolWorkUnit(name, args = {}, category = "") {
2839
2840
  case "read_mcp_resource":
2840
2841
  return unitDescriptor("Read", { count: queryCount(a, "uri", "uris") || 1, noun: "resource" });
2841
2842
  case "apply_patch": {
2842
- const creating = a.old_string === "";
2843
+ const patchText = String(a.patch ?? "");
2844
+ const creating = a.old_string === "" || /^\*\*\*\s+Add File:/mi.test(patchText);
2845
+ const deleting = !creating && a.new_string === "" && a.old_string != null || /^\*\*\*\s+Delete File:/mi.test(patchText);
2843
2846
  if (a.dry_run === true) {
2844
2847
  return unitDescriptor("Patch", {
2845
2848
  count: patchFileCount(a) || 1,
@@ -2850,8 +2853,8 @@ function toolWorkUnit(name, args = {}, category = "") {
2850
2853
  }
2851
2854
  return unitDescriptor("Patch", {
2852
2855
  count: patchFileCount(a) || 1,
2853
- active: creating ? "Creating" : "Editing",
2854
- done: creating ? "Created" : "Edited",
2856
+ active: creating ? "Creating" : deleting ? "Deleting" : "Editing",
2857
+ done: creating ? "Created" : deleting ? "Deleted" : "Edited",
2855
2858
  noun: "file"
2856
2859
  });
2857
2860
  }
@@ -10233,6 +10236,7 @@ function toolHasDisplayResultForRows(item) {
10233
10236
  }
10234
10237
  function toolExpandedRawTextForRows(item, rawRt) {
10235
10238
  if (item?.aggregate) return rawRt;
10239
+ if (item?.agentResponseAggregate) return rawRt;
10236
10240
  const hasDisplayResult = toolHasDisplayResultForRows(item);
10237
10241
  if (hasDisplayResult) return toolDisplayedResultTextForRows(item);
10238
10242
  return stripLeadingStatusMarkerFromTextForRows(rawRt || "");
@@ -10433,22 +10437,6 @@ function fnv1a32(str) {
10433
10437
  }
10434
10438
  return h >>> 0;
10435
10439
  }
10436
- function fnvStepA(hash, str) {
10437
- let h = hash >>> 0;
10438
- for (let i = 0; i < str.length; i++) {
10439
- h ^= str.charCodeAt(i);
10440
- h = h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24)) >>> 0;
10441
- }
10442
- return h >>> 0;
10443
- }
10444
- function fnvStepB(hash, str) {
10445
- let h = hash >>> 0;
10446
- for (let i = 0; i < str.length; i++) {
10447
- h = Math.imul(h ^ str.charCodeAt(i), 2246822519) >>> 0;
10448
- h = (h ^ h >>> 13) >>> 0;
10449
- }
10450
- return h >>> 0;
10451
- }
10452
10440
  function textShapeFingerprint(value) {
10453
10441
  if (value == null) return "z";
10454
10442
  const text = String(value);
@@ -10633,7 +10621,7 @@ function buildTranscriptRowIndexIncremental(items, {
10633
10621
  suppressMeasuredRowHeights = false,
10634
10622
  measuredRowsVersion = 0,
10635
10623
  cacheRef = null,
10636
- prefixSig = null
10624
+ prefixRevision = null
10637
10625
  } = {}) {
10638
10626
  const allItems = Array.isArray(items) ? items : [];
10639
10627
  const tail = trailingStreamingItem(allItems);
@@ -10648,7 +10636,7 @@ function buildTranscriptRowIndexIncremental(items, {
10648
10636
  }
10649
10637
  const prefixLen = allItems.length - 1;
10650
10638
  const cache = holder.current;
10651
- if (cache && cache.columns === columns && cache.toolExpanded === (toolOutputExpanded ? 1 : 0) && cache.suppress === suppressMeasuredRowHeights && cache.version === measuredRowsVersion && cache.prefixLen === prefixLen && prefixSig != null && cache.prefixSig === prefixSig && cache.tailId === tail.id) {
10639
+ if (cache && cache.columns === columns && cache.toolExpanded === (toolOutputExpanded ? 1 : 0) && cache.suppress === suppressMeasuredRowHeights && cache.version === measuredRowsVersion && cache.prefixLen === prefixLen && prefixRevision != null && cache.prefixRevision === prefixRevision && cache.tailId === tail.id) {
10652
10640
  {
10653
10641
  const tailMeasured = suppressMeasuredRowHeights ? null : measuredTranscriptRows(tail, columns, toolOutputExpanded);
10654
10642
  const tailRows = tailMeasured != null ? tailMeasured : estimateTranscriptItemRowsCached(tail, columns, toolOutputExpanded);
@@ -10670,7 +10658,7 @@ function buildTranscriptRowIndexIncremental(items, {
10670
10658
  suppress: suppressMeasuredRowHeights,
10671
10659
  version: measuredRowsVersion,
10672
10660
  prefixLen,
10673
- prefixSig,
10661
+ prefixRevision,
10674
10662
  tailId: tail.id,
10675
10663
  prefixRowsArr: full.rows.slice(0, prefixLen),
10676
10664
  prefixPrefixRows: full.prefixRows.slice(0, prefixLen + 1),
@@ -10678,36 +10666,6 @@ function buildTranscriptRowIndexIncremental(items, {
10678
10666
  };
10679
10667
  return full;
10680
10668
  }
10681
- var transcriptSigPartCache = /* @__PURE__ */ new WeakMap();
10682
- function transcriptStructureSignature(items, columns, toolOutputExpanded) {
10683
- const list = Array.isArray(items) ? items : [];
10684
- let hA = fnvStepA(2166136261, `${list.length}|${columns}|${toolOutputExpanded ? 1 : 0}`);
10685
- let hB = fnvStepB(3421674724, `${list.length}|${columns}|${toolOutputExpanded ? 1 : 0}`);
10686
- for (let i = 0; i < list.length; i++) {
10687
- const it = list[i];
10688
- let sigPart;
10689
- if (!it) {
10690
- sigPart = "_";
10691
- } else if (it.kind === "assistant" && it.streaming) {
10692
- const resolvedRows = estimateTranscriptItemRowsCached(it, columns, toolOutputExpanded);
10693
- sigPart = `a${it.id}:${resolvedRows}`;
10694
- } else {
10695
- const variantKey = transcriptItemVariantKey(it);
10696
- const cached = transcriptSigPartCache.get(it);
10697
- if (cached && cached.variantKey === variantKey && cached.columns === columns && cached.id === it.id && cached.kind === it.kind) {
10698
- sigPart = cached.sigPart;
10699
- } else {
10700
- sigPart = `${it.kind?.[0] || "?"}${it.id}:${variantKey}`;
10701
- transcriptSigPartCache.set(it, { id: it.id, kind: it.kind, variantKey, columns, sigPart });
10702
- }
10703
- }
10704
- hA = fnvStepA(hA, `;${i};`);
10705
- hA = fnvStepA(hA, sigPart);
10706
- hB = fnvStepB(hB, `;${i};`);
10707
- hB = fnvStepB(hB, sigPart);
10708
- }
10709
- return `${hA.toString(36)}.${hB.toString(36)}`;
10710
- }
10711
10669
  function transcriptRenderWindow(items, { scrollOffset = 0, viewportHeight = 24, columns = 80, toolOutputExpanded = false, rowIndex = null } = {}) {
10712
10670
  const allItems = Array.isArray(items) ? items : [];
10713
10671
  const itemCount = allItems.length;
@@ -11701,7 +11659,9 @@ function useTranscriptScroll({
11701
11659
  // src/tui/app/use-transcript-window.mjs
11702
11660
  import { useCallback as useCallback4, useEffect as useEffect9, useLayoutEffect as useLayoutEffect3, useMemo, useRef as useRef9, useState as useState7 } from "react";
11703
11661
  function useTranscriptWindow({
11704
- items,
11662
+ items: settledItems,
11663
+ structureRevision,
11664
+ streamingTail,
11705
11665
  themeEpoch,
11706
11666
  frameColumns,
11707
11667
  toolOutputExpanded,
@@ -11759,41 +11719,34 @@ function useTranscriptWindow({
11759
11719
  }
11760
11720
  return fn;
11761
11721
  }, []);
11762
- const streamingTailItem = useMemo(() => {
11763
- const last = (items || []).length > 0 ? items[items.length - 1] : null;
11764
- return last && last.kind === "assistant" && last.streaming ? last : null;
11765
- }, [items]);
11766
- const prefixLen = streamingTailItem ? (items || []).length - 1 : (items || []).length;
11767
- const prefixSig = useMemo(
11768
- () => transcriptStructureSignature(
11769
- streamingTailItem ? (items || []).slice(0, prefixLen) : items || [],
11770
- frameColumns,
11771
- toolOutputExpanded
11772
- ),
11773
- [items, streamingTailItem, prefixLen, frameColumns, toolOutputExpanded]
11774
- );
11722
+ const streamingTailItem = streamingTail?.kind === "assistant" && streamingTail.streaming ? streamingTail : null;
11775
11723
  const scrolledUpRowsForPin = Math.max(0, Number(scrollTargetRef.current) || 0);
11776
11724
  const transcriptPinnedForStreaming = followingRef.current || scrolledUpRowsForPin <= transcriptBottomSlackRows;
11777
11725
  setStreamingBottomPinned(transcriptPinnedForStreaming);
11778
- const tailSig = streamingTailItem ? `a${streamingTailItem.id}:${estimateTranscriptItemRowsCached(streamingTailItem, frameColumns, toolOutputExpanded)}` : "_";
11779
- const transcriptStructureSig = `${prefixSig}#${tailSig}`;
11780
- const transcriptStreamingActive = (items || []).some(
11781
- (item) => item?.kind === "assistant" && item?.streaming
11726
+ const tailRows = streamingTailItem ? estimateTranscriptItemRowsCached(streamingTailItem, frameColumns, toolOutputExpanded) : 0;
11727
+ const tailSig = streamingTailItem ? `${streamingTailItem.id}:${tailRows}` : "_";
11728
+ const revision = Math.max(0, Number(structureRevision) || 0);
11729
+ const transcriptItems = useMemo(
11730
+ () => streamingTailItem ? [...settledItems || [], streamingTailItem] : settledItems || [],
11731
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- tail text at the same resolved height is injected into the bounded visible slice below
11732
+ [settledItems, revision, streamingTailItem?.id, tailRows]
11782
11733
  );
11734
+ const transcriptStructureSig = `${revision}#${tailSig}`;
11735
+ const transcriptStreamingActive = !!streamingTailItem;
11783
11736
  const scrolledUpForStreamingMeasure = scrolledUpRowsForPin > transcriptBottomSlackRows;
11784
11737
  const prevEstimateGeometry = transcriptGeomRef.current?.suppressMeasuredRowHeights === true;
11785
11738
  const hasStreamingReadingAnchor = !!transcriptAnchorRef.current || transcriptAnchorDirtyRef.current;
11786
11739
  const bottomPinnedForMeasure = transcriptPinnedForStreaming;
11787
11740
  const suppressMeasuredRowHeights = bottomPinnedForMeasure || transcriptStreamingActive && (scrolledUpForStreamingMeasure && hasStreamingReadingAnchor || scrolledUpForStreamingMeasure && prevEstimateGeometry && !transcriptPinnedForStreaming);
11788
- const transcriptRowIndex = useMemo(() => buildTranscriptRowIndexIncremental(items, {
11741
+ const transcriptRowIndex = useMemo(() => buildTranscriptRowIndexIncremental(transcriptItems, {
11789
11742
  columns: frameColumns,
11790
11743
  toolOutputExpanded,
11791
11744
  suppressMeasuredRowHeights,
11792
11745
  measuredRowsVersion,
11793
11746
  cacheRef: incrementalRowIndexCacheRef,
11794
- prefixSig
11795
- // eslint-disable-next-line react-hooks/exhaustive-deps -- intentional: prefixSig/tailSig capture the relevant item changes (raw `items` dropped so a tail-only flush never re-enters the builder via array identity); measuredRowsVersion folds in app-level measured height corrections
11796
- }), [prefixSig, tailSig, measuredRowsVersion, suppressMeasuredRowHeights]);
11747
+ prefixRevision: revision
11748
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- revision/tail height capture structural geometry; measuredRowsVersion folds in measured corrections
11749
+ }), [revision, tailSig, frameColumns, toolOutputExpanded, measuredRowsVersion, suppressMeasuredRowHeights]);
11797
11750
  const hasReadingAnchor = !!transcriptAnchorRef.current && !transcriptAnchorDirtyRef.current;
11798
11751
  const scrolledUpRows = Math.max(0, Number(scrollTargetRef.current) || 0);
11799
11752
  const scrolledUp = scrolledUpRows > transcriptBottomSlackRows;
@@ -11808,7 +11761,7 @@ function useTranscriptWindow({
11808
11761
  if (anchorLockActive) {
11809
11762
  const locked = resolveAnchorScrollOffset({
11810
11763
  anchor: transcriptAnchorRef.current,
11811
- items,
11764
+ items: transcriptItems,
11812
11765
  curPrefix: curPrefixForLock,
11813
11766
  totalRows: lockTotalRows,
11814
11767
  viewRows: lockViewRows,
@@ -11832,7 +11785,7 @@ function useTranscriptWindow({
11832
11785
  const captured = { id: anchorItem.id, offset: Math.max(0, prevTopRow - (prevPrefix[idx] || 0)) };
11833
11786
  const locked = resolveAnchorScrollOffset({
11834
11787
  anchor: captured,
11835
- items,
11788
+ items: transcriptItems,
11836
11789
  curPrefix: curPrefixForLock,
11837
11790
  totalRows: lockTotalRows,
11838
11791
  viewRows: lockViewRows,
@@ -11865,7 +11818,7 @@ function useTranscriptWindow({
11865
11818
  const captured = { id: anchorItem.id, offset: Math.max(0, prevTopRow - (prevPrefix[idx] || 0)) };
11866
11819
  const locked = resolveAnchorScrollOffset({
11867
11820
  anchor: captured,
11868
- items,
11821
+ items: transcriptItems,
11869
11822
  curPrefix: curPrefixForLock,
11870
11823
  totalRows: lockTotalRows,
11871
11824
  viewRows: lockViewRows,
@@ -11884,7 +11837,7 @@ function useTranscriptWindow({
11884
11837
  floatingPanelRows: Number(floatingPanelRows) || 0
11885
11838
  };
11886
11839
  if (scrolledUp && !followingRef.current) {
11887
- const growth = streamingTailMountedGrowth(items, frameColumns, toolOutputExpanded);
11840
+ const growth = streamingTailMountedGrowth(transcriptItems, frameColumns, toolOutputExpanded);
11888
11841
  if (growth && growth.delta > 0) {
11889
11842
  const maxOffsetKeepingTailMounted = growth.tailRows + TRANSCRIPT_WINDOW_OVERSCAN_ROWS - 1;
11890
11843
  if (renderScrollOffset <= maxOffsetKeepingTailMounted) {
@@ -11892,7 +11845,7 @@ function useTranscriptWindow({
11892
11845
  }
11893
11846
  }
11894
11847
  }
11895
- const transcriptWindow = useMemo(() => transcriptRenderWindow(items, {
11848
+ const transcriptWindow = useMemo(() => transcriptRenderWindow(transcriptItems, {
11896
11849
  scrollOffset: renderScrollOffset,
11897
11850
  viewportHeight: transcriptContentHeight,
11898
11851
  columns: frameColumns,
@@ -11929,7 +11882,7 @@ function useTranscriptWindow({
11929
11882
  prefixRows: transcriptRowIndex?.prefixRows || null,
11930
11883
  totalRows: Math.max(0, Number(transcriptWindow.totalRows) || 0),
11931
11884
  viewRows: Math.max(1, Number(transcriptContentHeight) || 1),
11932
- items: items || null,
11885
+ items: transcriptItems || null,
11933
11886
  // The offset THIS frame actually rendered with. The same-frame anchor
11934
11887
  // CAPTURE for a missing/dirty anchor (above) reads this from the PREVIOUS
11935
11888
  // frame to reconstruct the exact top-edge row that was on screen, so the
@@ -11941,10 +11894,16 @@ function useTranscriptWindow({
11941
11894
  renderOffset: Math.max(0, Number(transcriptWindow.effectiveScrollOffset) || 0),
11942
11895
  suppressMeasuredRowHeights
11943
11896
  };
11944
- const transcriptVisibleItems = (items || []).slice(
11897
+ const transcriptVisibleItems = (transcriptItems || []).slice(
11945
11898
  transcriptWindow.startIndex,
11946
11899
  transcriptWindow.endIndex
11947
11900
  );
11901
+ if (streamingTailItem && transcriptVisibleItems.length > 0) {
11902
+ const last = transcriptVisibleItems.length - 1;
11903
+ if (transcriptVisibleItems[last]?.id === streamingTailItem.id) {
11904
+ transcriptVisibleItems[last] = streamingTailItem;
11905
+ }
11906
+ }
11948
11907
  const renderedTranscriptItems = transcriptVisibleItems;
11949
11908
  let overlayHintAttachItemIndex = -1;
11950
11909
  for (let i = renderedTranscriptItems.length - 1; i >= 0; i--) {
@@ -12064,7 +12023,7 @@ function useTranscriptWindow({
12064
12023
  return;
12065
12024
  }
12066
12025
  const viewRows = Math.max(1, Number(transcriptContentHeight) || 1);
12067
- const itemList = items || [];
12026
+ const itemList = transcriptItems || [];
12068
12027
  let anchor = transcriptAnchorRef.current;
12069
12028
  if (!followingRef.current && (!anchor || transcriptAnchorDirtyRef.current)) {
12070
12029
  if (curPrefix && curPrefix.length > 1) {
@@ -12383,10 +12342,11 @@ var compareModelRecency = (a, b) => {
12383
12342
  }
12384
12343
  const ta = releaseTime(a);
12385
12344
  const tb = releaseTime(b);
12386
- if (ta !== tb) return tb - ta;
12387
- if (!!a?.latest !== !!b?.latest) return a?.latest ? -1 : 1;
12388
12345
  const versionDelta = compareModelVersion(a, b);
12346
+ if (ta > 0 && tb > 0 && ta !== tb) return tb - ta;
12389
12347
  if (versionDelta) return versionDelta;
12348
+ if (!!a?.latest !== !!b?.latest) return a?.latest ? -1 : 1;
12349
+ if (ta !== tb) return tb - ta;
12390
12350
  return String(a?.display || a?.id || "").localeCompare(String(b?.display || b?.id || ""));
12391
12351
  };
12392
12352
  var modelFamily = (m) => {
@@ -15835,11 +15795,9 @@ function createModelPicker({
15835
15795
  setChannelPrompt(null);
15836
15796
  setHookPrompt(null);
15837
15797
  setSettingsPrompt(null);
15838
- const modelPickerRequest = ++modelPickerRequestRef.current;
15798
+ modelPickerRequestRef.current += 1;
15839
15799
  let modelPickerClosed = false;
15840
- let activeModelProvider = null;
15841
15800
  let providerListHighlightProvider = null;
15842
- const isActiveModelPicker = () => !modelPickerClosed && modelPickerRequestRef.current === modelPickerRequest;
15843
15801
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
15844
15802
  const returnLabel = String(options.returnLabel || "Agents");
15845
15803
  const returnOnNestedCancel = options.returnOnNestedCancel === true;
@@ -15853,7 +15811,6 @@ function createModelPicker({
15853
15811
  let providerModels = Array.isArray(cacheRef.current.models) ? cacheRef.current.models : [];
15854
15812
  let refreshModelsPromise = null;
15855
15813
  let renderedQuickModels = false;
15856
- let renderActiveProviderModels = null;
15857
15814
  if (!providerModels.length || options.refreshModels === true) {
15858
15815
  setPicker({
15859
15816
  title: options.title || "Model",
@@ -15904,16 +15861,12 @@ function createModelPicker({
15904
15861
  providerListHighlightProvider = renderOptions.highlightProvider;
15905
15862
  }
15906
15863
  const highlightProvider = renderOptions.highlightProvider || providerListHighlightProvider || null;
15907
- activeModelProvider = null;
15908
- renderActiveProviderModels = null;
15909
15864
  const openProviderModelsPicker = (provider) => {
15910
15865
  if (!provider) return;
15911
- activeModelProvider = provider;
15912
- renderActiveProviderModels = () => openProviderModelsPicker(provider);
15913
15866
  const providerModels2 = models.filter((model) => model.provider === provider);
15914
15867
  const preferredEffort = (values = []) => {
15915
15868
  const allowed = values.filter(Boolean);
15916
- for (const value of ["high", "medium", "low", "none", "xhigh", "max"]) {
15869
+ for (const value of ["high", "medium", "low", "none", "xhigh", "max", "ultra"]) {
15917
15870
  if (allowed.includes(value)) return value;
15918
15871
  }
15919
15872
  return allowed[0] || null;
@@ -15986,6 +15939,7 @@ ${model?.id || ""}`;
15986
15939
  if (value === "medium") return "\u25D1";
15987
15940
  if (value === "high") return "\u25D5";
15988
15941
  if (value === "max") return "\u25C6";
15942
+ if (value === "ultra") return "\u2726";
15989
15943
  return "\u25CF";
15990
15944
  };
15991
15945
  const effortColor = (value) => {
@@ -15994,6 +15948,7 @@ ${model?.id || ""}`;
15994
15948
  if (value === "medium") return theme.claude;
15995
15949
  if (value === "high") return theme.error;
15996
15950
  if (value === "max") return theme.permission;
15951
+ if (value === "ultra") return theme.permission;
15997
15952
  return theme.error;
15998
15953
  };
15999
15954
  const modelFooter = (model = null) => {
@@ -16128,33 +16083,23 @@ ${model?.id || ""}`;
16128
16083
  });
16129
16084
  };
16130
16085
  renderModelPicker();
16131
- const applyFreshModels = (freshModels) => {
16132
- if (!isActiveModelPicker()) return;
16086
+ const adoptFreshModels = (freshModels) => {
16133
16087
  if (!Array.isArray(freshModels) || freshModels.length === 0) return;
16134
- providerModels = freshModels;
16135
- models = normalizeModelOptions(providerModels);
16136
- cacheRef.current = { models: providerModels, at: Date.now() };
16137
- if (activeModelProvider === null) {
16138
- renderModelPicker();
16139
- } else if (typeof renderActiveProviderModels === "function") {
16140
- renderActiveProviderModels();
16141
- }
16088
+ cacheRef.current = { models: freshModels, at: Date.now() };
16142
16089
  };
16143
16090
  if (renderedQuickModels && refreshModelsPromise) {
16144
- void refreshModelsPromise.then(applyFreshModels).catch(() => {
16091
+ void refreshModelsPromise.then(adoptFreshModels).catch(() => {
16145
16092
  });
16146
16093
  } else if (cacheIsStale) {
16147
16094
  if (!providerModelsTtlRefreshPromise) {
16148
16095
  providerModelsTtlRefreshPromise = Promise.resolve(loadModels({ force: true })).then((freshModels) => {
16149
- if (Array.isArray(freshModels) && freshModels.length > 0) {
16150
- cacheRef.current = { models: freshModels, at: Date.now() };
16151
- }
16096
+ adoptFreshModels(freshModels);
16152
16097
  return freshModels;
16153
16098
  }).finally(() => {
16154
16099
  providerModelsTtlRefreshPromise = null;
16155
16100
  });
16156
16101
  }
16157
- void providerModelsTtlRefreshPromise.then(applyFreshModels).catch(() => {
16102
+ void providerModelsTtlRefreshPromise.catch(() => {
16158
16103
  });
16159
16104
  }
16160
16105
  };
@@ -18531,6 +18476,29 @@ import stringWidth9 from "string-width";
18531
18476
 
18532
18477
  // src/tui/components/tool-execution/text-format.mjs
18533
18478
  import stripAnsi6 from "strip-ansi";
18479
+
18480
+ // src/runtime/shared/tool-status.mjs
18481
+ function normalizeToolTerminalStatus(value) {
18482
+ const raw = String(value || "").trim().toLowerCase();
18483
+ if (!raw) return "";
18484
+ if (/^(running|pending|queued|in_progress|in-progress)$/.test(raw)) return "running";
18485
+ if (/^(completed|complete|done|success|succeeded|ok)$/.test(raw)) return "completed";
18486
+ if (/^(failed|fail|error|errored|timeout|timed_out|killed)$/.test(raw)) return "failed";
18487
+ if (/^(cancelled|canceled|cancel)$/.test(raw)) return "cancelled";
18488
+ if (/^(denied|deny|refused|rejected)$/.test(raw)) return "denied";
18489
+ return "";
18490
+ }
18491
+ function toolResultTerminalStatus(text) {
18492
+ const body = String(text || "");
18493
+ const tagged = body.match(/<status[^>]*>([\s\S]*?)<\/status>/i)?.[1]?.trim();
18494
+ if (tagged) return normalizeToolTerminalStatus(tagged);
18495
+ const bracketed = body.match(/^\[status:\s*([^\]]*)\]/mi)?.[1]?.trim();
18496
+ if (bracketed) return normalizeToolTerminalStatus(bracketed);
18497
+ const inline = body.match(/^(?:status|state):\s*([^\s·,;]+)/mi)?.[1]?.trim();
18498
+ return normalizeToolTerminalStatus(inline);
18499
+ }
18500
+
18501
+ // src/tui/components/tool-execution/text-format.mjs
18534
18502
  var MIN_RESULT_LINE_CHARS = 24;
18535
18503
  var RESULT_LINE_HARD_MAX = 80;
18536
18504
  var SUMMARY_MAX_CHARS = 48;
@@ -18600,30 +18568,20 @@ function shellResultStatus(value) {
18600
18568
  return match ? String(match[1] || "").toLowerCase() : "";
18601
18569
  }
18602
18570
  function normalizeTerminalStatus(value) {
18603
- const raw = String(value || "").trim().toLowerCase();
18604
- if (!raw) return "";
18605
- if (/^(running|pending|queued|in_progress|in-progress)$/.test(raw)) return "running";
18606
- if (/^(completed|complete|done|success|succeeded|ok)$/.test(raw)) return "completed";
18607
- if (/^(failed|fail|error|errored|timeout|timed_out|killed)$/.test(raw)) return "failed";
18608
- if (/^(cancelled|canceled|cancel)$/.test(raw)) return "cancelled";
18609
- return "";
18571
+ return normalizeToolTerminalStatus(value);
18610
18572
  }
18611
18573
  function displayTerminalStatus(value) {
18574
+ if (String(value || "").trim().toLowerCase() === "exit") return "Exit";
18612
18575
  const status = normalizeTerminalStatus(value);
18613
18576
  if (status === "running") return "Running";
18614
18577
  if (status === "completed") return "Finished";
18615
18578
  if (status === "failed") return "Failed";
18616
18579
  if (status === "cancelled") return "Cancelled";
18580
+ if (status === "denied") return "Denied";
18617
18581
  return "";
18618
18582
  }
18619
18583
  function resultTerminalStatus(value) {
18620
- const text = String(value || "");
18621
- const tagged = text.match(/<status[^>]*>([\s\S]*?)<\/status>/i)?.[1]?.trim();
18622
- if (tagged) return normalizeTerminalStatus(tagged);
18623
- const bracketed = text.match(/^\[status:\s*([^\]]*)\]/mi)?.[1]?.trim();
18624
- if (bracketed) return normalizeTerminalStatus(bracketed);
18625
- const inline = text.match(/^(?:status|state):\s*([^\s·,;]+)/mi)?.[1]?.trim();
18626
- return normalizeTerminalStatus(inline);
18584
+ return toolResultTerminalStatus(value);
18627
18585
  }
18628
18586
  var LEADING_STATUS_MARKER_LINE_RE2 = /^\[status:\s*[^\]]*\]\s*$/i;
18629
18587
  function stripLeadingStatusMarkerLines(lines) {
@@ -18647,11 +18605,15 @@ function isShellTool(normalizedName, label = "") {
18647
18605
  const l = String(label || "").toLowerCase();
18648
18606
  return n === "shell" || n === "bash" || n === "bash_session" || n === "shell_command" || n === "job_wait" || l === "run";
18649
18607
  }
18650
- function shellDisplayStatus({ pending = false, failedCount = 0, isError = false, result = "" } = {}) {
18608
+ function shellDisplayStatus({ pending = false, failedCount = 0, exitFailedCount = 0, isError = false, result = "" } = {}) {
18651
18609
  const status = shellResultStatus(result);
18652
18610
  if (pending || /^(running|pending|queued)$/.test(status)) return "running";
18653
18611
  if (/^cancel/.test(status)) return "cancelled";
18654
- if (/^(failed|error|killed|timeout)$/.test(status) || isError || failedCount > 0) return "failed";
18612
+ if (/^(failed|error|killed|timeout)$/.test(status)) return "failed";
18613
+ const realFailed = Math.max(0, Number(failedCount) - Number(exitFailedCount));
18614
+ if (realFailed > 0) return "failed";
18615
+ if (Number(exitFailedCount) > 0) return "exit";
18616
+ if (isError || failedCount > 0) return "failed";
18655
18617
  return "completed";
18656
18618
  }
18657
18619
  function shellHeader(status, count = 1) {
@@ -18707,7 +18669,9 @@ function withModelAndTag(label, args) {
18707
18669
  function joinActionAgent(action, agent) {
18708
18670
  return agent ? `${action} ${agent}` : action;
18709
18671
  }
18710
- function agentResponseTitle(args) {
18672
+ function agentResponseTitle(args, count = 1) {
18673
+ const total = Math.max(1, Number(count) || 1);
18674
+ if (total > 1) return `Responses ${total} agents`;
18711
18675
  const name = titleizeAgentName(args?.agent || args?.subagent_type || args?.name || "");
18712
18676
  return withModelAndTag(joinActionAgent("Response", name), args);
18713
18677
  }
@@ -18962,13 +18926,17 @@ function clampFailureCount(errorCount, groupCount, isError) {
18962
18926
  if (Number.isFinite(explicit)) return Math.max(0, Math.min(groupCount, Math.floor(explicit)));
18963
18927
  return isError ? groupCount : 0;
18964
18928
  }
18965
- function toolStatusColor({ pending, groupCount, callFailedCount = 0, terminalStatus = "" }) {
18929
+ function toolStatusColor({ pending, groupCount, callFailedCount = 0, exitFailedCount = 0, terminalStatus = "" }) {
18966
18930
  if (pending) return theme.text;
18967
18931
  const status = normalizeTerminalStatus(terminalStatus);
18968
18932
  if (status === "cancelled") return theme.warning;
18969
- if (callFailedCount <= 0) return theme.success;
18970
- if (groupCount > 1 && callFailedCount < groupCount) return theme.mixdogOrange || theme.warning;
18971
- return theme.error;
18933
+ if (status === "denied") return theme.warning;
18934
+ if (callFailedCount > 0) {
18935
+ if (groupCount > 1 && callFailedCount < groupCount) return theme.mixdogOrange || theme.warning;
18936
+ return theme.error;
18937
+ }
18938
+ if (exitFailedCount > 0) return theme.warning;
18939
+ return theme.success;
18972
18940
  }
18973
18941
 
18974
18942
  // src/tui/components/tool-execution/ResultBody.jsx
@@ -18999,7 +18967,7 @@ var TOOL_ANIM_TICK_MS = TOOL_BLINK_MS;
18999
18967
  function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
19000
18968
  return formatToolActionHeader(name, args, { pending, count });
19001
18969
  }
19002
- function ToolExecution({ name, args, result, rawResult, isError, errorCount, callErrorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, doneCategories = null, headerFinalized = true, deferredDisplayReady = false }) {
18970
+ function ToolExecution({ name, args, result, rawResult, isError, errorCount, callErrorCount, exitErrorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, doneCategories = null, headerFinalized = true, deferredDisplayReady = false, agentResponseAggregate = false }) {
19003
18971
  const rowWidth = Math.max(1, Number(columns || 80));
19004
18972
  const groupCount = Math.max(1, Number(count || 1));
19005
18973
  const doneCount = Math.max(0, Math.min(groupCount, Number(completedCount || (result == null ? 0 : groupCount))));
@@ -19023,6 +18991,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19023
18991
  const elapsed = elapsedMs >= 1e3 ? formatElapsed(elapsedMs) : "";
19024
18992
  const failedCount = clampFailureCount(errorCount, groupCount, isError);
19025
18993
  const callFailedCount = clampFailureCount(callErrorCount, groupCount, false);
18994
+ const exitFailedCount = clampFailureCount(exitErrorCount, groupCount, false);
19026
18995
  const displayGroupCount = groupCount;
19027
18996
  const displayCategories = normalizeCountMap(categories || {});
19028
18997
  const normalizedDoneCategories = doneCategories ? normalizeCountMap(doneCategories) : displayCategories;
@@ -19048,7 +19017,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19048
19017
  detailText = "";
19049
19018
  }
19050
19019
  const aggregateTerminalStatus = pending ? "running" : resultTerminalStatus(rt) || (isError || failedCount > 0 ? "failed" : "completed");
19051
- const dotColor2 = toolStatusColor({ pending, groupCount, callFailedCount, terminalStatus: aggregateTerminalStatus });
19020
+ const dotColor2 = toolStatusColor({ pending, groupCount, callFailedCount, exitFailedCount, terminalStatus: aggregateTerminalStatus });
19052
19021
  const dotText2 = pending && !blinkOn ? " " : TURN_MARKER;
19053
19022
  const gutter2 = 2;
19054
19023
  const showHeaderExpandHint2 = hasRawResult;
@@ -19103,7 +19072,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19103
19072
  const firstResultLine = hasDisplayResult ? String(lines[0] ?? "") : "";
19104
19073
  const firstResultLineClipped = hasDisplayBody && stringWidth9(firstResultLine) > maxResultChars;
19105
19074
  const hasHiddenDetail = !pending && hasDisplayBody && (totalLines > 1 || firstResultLineClipped || Boolean(resultSummary));
19106
- const shellStatus = isShellSurface ? shellDisplayStatus({ pending, failedCount, isError, result: displayedResultText }) : "";
19075
+ const shellStatus = isShellSurface ? shellDisplayStatus({ pending, failedCount, exitFailedCount, isError, result: displayedResultText }) : "";
19107
19076
  const shellElapsed = isShellSurface ? shellResultElapsed(displayedResultText) || elapsed : "";
19108
19077
  const backgroundElapsed = backgroundMeta ? backgroundTaskElapsed(backgroundMeta, elapsed) : isBackgroundTaskTool(normalizedName) ? backgroundTaskElapsed(parsedArgs, elapsed) : "";
19109
19078
  const toolArgPath = parsedArgs?.path ?? parsedArgs?.file_path ?? parsedArgs?.file ?? "";
@@ -19128,7 +19097,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19128
19097
  const collapsedDetail = pending ? pendingDetailPlaceholder : isShellSurface ? prefixElapsed(mergeTerminalDetail(shellStatus, shellCollapsedSummary), shellElapsed) : mergeTerminalDetail(terminalStatus, nonShellDetail);
19129
19098
  const backgroundMetadataExpandable = isBackgroundMetadataResult && hasRawResult && !pending;
19130
19099
  const showRawResult = expanded && (hasDisplayBody || hasRawResult) && (!isBackgroundMetadataResult || hasRawResult);
19131
- const detailLines = showRawResult ? hasDisplayBody ? lines : rawRt ? stripLeadingStatusMarkerLines(rawRt.split("\n")) : [] : collapsedDetail ? [collapsedDetail] : [];
19100
+ const detailLines = showRawResult ? agentResponseAggregate && hasRawResult ? stripLeadingStatusMarkerLines(rawRt.split("\n")) : hasDisplayBody ? lines : rawRt ? stripLeadingStatusMarkerLines(rawRt.split("\n")) : [] : collapsedDetail ? [collapsedDetail] : [];
19132
19101
  const isPendingPlaceholderDetail = !showRawResult && Boolean(pendingDetailPlaceholder);
19133
19102
  const detailColor = isPendingPlaceholderDetail ? theme.subtle : theme.text;
19134
19103
  const isAgentResult = !isBackgroundResult && !pending && isAgentTool(normalizedName) && hasDisplayResult;
@@ -19148,14 +19117,14 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19148
19117
  const keepAgentDetail = (isError || agentFailureText) && !(agentHeaderFailure && !agentSurfaceBrief);
19149
19118
  visibleDetailLines = keepAgentDetail ? [agentDetailLine] : [];
19150
19119
  }
19151
- const finalStatusColor = toolStatusColor({ pending, groupCount, callFailedCount, terminalStatus });
19120
+ const finalStatusColor = toolStatusColor({ pending, groupCount, callFailedCount, exitFailedCount, terminalStatus });
19152
19121
  const dotColor = finalStatusColor;
19153
19122
  const markerGlyph = isAgentResponse ? AGENT_RESPONSE_MARKER : isAgentSurfaceCard ? AGENT_CALL_MARKER : TURN_MARKER;
19154
19123
  const isDirectionalMarker = isAgentResponse || isAgentSurfaceCard;
19155
19124
  const markerText = isDirectionalMarker ? `${markerGlyph} ` : markerGlyph;
19156
19125
  const dotText = pending && !blinkOn ? " " : markerText;
19157
19126
  let labelText;
19158
- if (isAgentResponse) labelText = agentResponseTitle(parsedArgs);
19127
+ if (isAgentResponse) labelText = agentResponseTitle(parsedArgs, displayGroupCount);
19159
19128
  else if (isBackgroundResponse) labelText = backgroundTaskResultTitle(normalizedName, backgroundMeta || parsedArgs);
19160
19129
  else if (isBackgroundMetadataResult) labelText = backgroundTaskActionTitle(normalizedName, backgroundMeta);
19161
19130
  else if (isShellSurface) labelText = shellHeader(shellStatus, displayGroupCount);
@@ -19364,7 +19333,7 @@ var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpa
19364
19333
  node = /* @__PURE__ */ jsx19(ToolHookDenialCard, { item, columns });
19365
19334
  break;
19366
19335
  }
19367
- node = /* @__PURE__ */ jsx19(ToolExecution, { name: item.name, args: item.args, result: item.result, rawResult: item.rawResult, isError: item.isError, errorCount: item.errorCount, callErrorCount: item.callErrorCount, expanded: toolOutputExpanded || item.expanded, columns, attached: false, count: item.count, completedCount: item.completedCount, startedAt: item.startedAt, completedAt: item.completedAt, aggregate: item.aggregate, categories: item.categories, doneCategories: item.doneCategories, headerFinalized: item.headerFinalized, deferredDisplayReady: item.deferredDisplayReady });
19336
+ node = /* @__PURE__ */ jsx19(ToolExecution, { name: item.name, args: item.args, result: item.result, rawResult: item.rawResult, isError: item.isError, errorCount: item.errorCount, callErrorCount: item.callErrorCount, exitErrorCount: item.exitErrorCount, expanded: toolOutputExpanded || item.expanded, columns, attached: false, count: item.count, completedCount: item.completedCount, startedAt: item.startedAt, completedAt: item.completedAt, aggregate: item.aggregate, categories: item.categories, doneCategories: item.doneCategories, headerFinalized: item.headerFinalized, deferredDisplayReady: item.deferredDisplayReady, agentResponseAggregate: item.agentResponseAggregate });
19368
19337
  break;
19369
19338
  }
19370
19339
  case "notice":
@@ -19672,6 +19641,32 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19672
19641
  searchModelsCacheRef.current = { models: null, at: 0 };
19673
19642
  }
19674
19643
  }, []);
19644
+ useEffect10(() => {
19645
+ let alive = true;
19646
+ const timer2 = setTimeout(async () => {
19647
+ const seq = onboardingPrefetchSeqRef.current;
19648
+ try {
19649
+ const models = await Promise.resolve(store.listProviderModels?.() || []);
19650
+ if (alive && seq === onboardingPrefetchSeqRef.current && Array.isArray(models) && models.length > 0 && !Array.isArray(providerModelsCacheRef.current.models)) {
19651
+ providerModelsCacheRef.current = { models, at: Date.now() };
19652
+ }
19653
+ } catch {
19654
+ }
19655
+ if (!alive) return;
19656
+ try {
19657
+ const searchModels = await Promise.resolve(store.listSearchModels?.() || []);
19658
+ if (alive && Array.isArray(searchModels) && searchModels.length > 0 && !Array.isArray(searchModelsCacheRef.current.models)) {
19659
+ searchModelsCacheRef.current = { models: searchModels, at: Date.now() };
19660
+ }
19661
+ } catch {
19662
+ }
19663
+ }, 1500);
19664
+ timer2.unref?.();
19665
+ return () => {
19666
+ alive = false;
19667
+ clearTimeout(timer2);
19668
+ };
19669
+ }, [store]);
19675
19670
  const { onboardingWarnReopen, openOnboardingAuthStep } = createOnboardingSteps({
19676
19671
  store,
19677
19672
  setPicker,
@@ -21435,6 +21430,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
21435
21430
  transcriptMeasureRef
21436
21431
  } = useTranscriptWindow({
21437
21432
  items: state.items,
21433
+ structureRevision: state.structureRevision,
21434
+ streamingTail: state.streamingTail,
21438
21435
  themeEpoch: state.themeEpoch,
21439
21436
  frameColumns,
21440
21437
  toolOutputExpanded,
@@ -22507,18 +22504,6 @@ function bracketField(text, name) {
22507
22504
  const re = new RegExp(`^\\[${name}:\\s*([^\\]]*)\\]`, "mi");
22508
22505
  return re.exec(String(text ?? ""))?.[1]?.trim() || "";
22509
22506
  }
22510
- function toolResultStatus(text) {
22511
- const value = String(text ?? "");
22512
- const tagged = textBetweenTag2(value, "status");
22513
- if (tagged) return tagged.trim();
22514
- const bracketed = bracketField(value, "status");
22515
- if (bracketed) return bracketed.trim();
22516
- const inline = /^(?:status|state):\s*([^\s·,;]+)/mi.exec(value);
22517
- return inline ? inline[1].trim() : "";
22518
- }
22519
- function isErrorToolStatus(status) {
22520
- return /^(failed|error|timeout|cancelled|canceled|killed)$/i.test(String(status || "").trim());
22521
- }
22522
22507
  function parseSyntheticAgentMessage(text) {
22523
22508
  const value = String(text ?? "").trim();
22524
22509
  if (!value) return null;
@@ -22771,6 +22756,13 @@ function notificationQueueKey(event, text, parsed) {
22771
22756
  const hasBody = /\n\s*\n[\s\S]*\S/.test(String(text || "")) ? "b1" : "b0";
22772
22757
  return [id, type || fallbackKind, status, hasBody].filter(Boolean).join(":");
22773
22758
  }
22759
+ function executionCardKey(event, text, parsed) {
22760
+ const meta = event?.meta && typeof event.meta === "object" ? event.meta : {};
22761
+ const executionId = String(meta.execution_id || "").trim();
22762
+ if (!executionId) return notificationQueueKey(event, text, parsed);
22763
+ const hasBody = /\n\s*\n[\s\S]*\S/.test(String(text || "")) ? "b1" : "b0";
22764
+ return `card:${executionId}:${hasBody}`;
22765
+ }
22774
22766
  function isExecutionNotification(event, text, parsed) {
22775
22767
  const meta = event?.meta && typeof event.meta === "object" ? event.meta : {};
22776
22768
  if (meta.execution_id || meta.execution_surface) return true;
@@ -22869,23 +22861,43 @@ var yieldToRenderer = ({ frames = 1 } = {}) => new Promise((resolve5) => {
22869
22861
 
22870
22862
  // src/tui/engine/tool-result-status.mjs
22871
22863
  var CANCELLED_RESULT_STATUS_LINE = "[status: cancelled]";
22864
+ function shellCommandExitCode(text) {
22865
+ const body = String(text || "");
22866
+ if (!/^\s*(?:Error:\s*)?\[shell-run-failed\]/i.test(body)) return null;
22867
+ const header = body.split("\n", 1)[0] || "";
22868
+ if (/\[timeout:|\[signal:|timed out|aborted|interrupted/i.test(header)) return null;
22869
+ const m = header.match(/\[exit code:\s*(\d+)\]/i);
22870
+ if (!m) return null;
22871
+ const code = Number(m[1]);
22872
+ return Number.isFinite(code) ? code : null;
22873
+ }
22874
+ function toolCallOutcome(message, rawText) {
22875
+ const exitCode = shellCommandExitCode(rawText);
22876
+ if (exitCode != null) {
22877
+ return { isCallError: false, isExitError: true, exitCode };
22878
+ }
22879
+ const isCallError = message?.isError === true || message?.toolKind === "error";
22880
+ return {
22881
+ isCallError,
22882
+ isExitError: false,
22883
+ exitCode
22884
+ };
22885
+ }
22886
+ function failureDetailText({ succeeded = 0, realErrors = 0, exitErrors = 0, exitCode } = {}) {
22887
+ const parts = [];
22888
+ if (succeeded > 0) parts.push(`${succeeded} Ok`);
22889
+ if (realErrors > 0) parts.push(`${realErrors} Failed`);
22890
+ if (exitErrors > 0) {
22891
+ const solo = exitErrors === 1 && realErrors === 0 && succeeded === 0;
22892
+ parts.push(solo && Number.isFinite(exitCode) ? `Exit ${exitCode}` : `${exitErrors} Exit`);
22893
+ }
22894
+ return parts.join(" \xB7 ");
22895
+ }
22872
22896
  function normalizedResultStatusToken(value) {
22873
- const raw = String(value || "").trim().toLowerCase();
22874
- if (!raw) return "";
22875
- if (/^(running|pending|queued|in_progress|in-progress)$/.test(raw)) return "running";
22876
- if (/^(completed|complete|done|success|succeeded|ok)$/.test(raw)) return "completed";
22877
- if (/^(failed|fail|error|errored|timeout|timed_out|killed)$/.test(raw)) return "failed";
22878
- if (/^(cancelled|canceled|cancel)$/.test(raw)) return "cancelled";
22879
- return "";
22897
+ return normalizeToolTerminalStatus(value);
22880
22898
  }
22881
22899
  function resultTextTerminalStatus(text) {
22882
- const body = String(text || "");
22883
- const tagged = body.match(/<status[^>]*>([\s\S]*?)<\/status>/i)?.[1]?.trim();
22884
- if (tagged) return normalizedResultStatusToken(tagged);
22885
- const bracketed = body.match(/^\[status:\s*([^\]]*)\]/mi)?.[1]?.trim();
22886
- if (bracketed) return normalizedResultStatusToken(bracketed);
22887
- const inline = body.match(/^(?:status|state):\s*([^\s·,;]+)/mi)?.[1]?.trim();
22888
- return normalizedResultStatusToken(inline);
22900
+ return toolResultTerminalStatus(text);
22889
22901
  }
22890
22902
  function itemHasKnownTerminalStatus(item, texts = []) {
22891
22903
  const settled = (token) => token === "completed" || token === "failed" || token === "cancelled";
@@ -22907,11 +22919,14 @@ ${body}`;
22907
22919
  function groupedToolResultText(group) {
22908
22920
  const completed = Math.min(group.count, group.completed);
22909
22921
  if (group.count <= 1) return group.results.at(-1)?.text ?? "";
22910
- if (group.errors > 0) {
22911
- const succeeded = Math.max(0, completed - group.errors);
22912
- const reasons = group.results.filter((result) => result?.isError).map((result) => firstErrorLine(result?.text)).filter(Boolean);
22922
+ const exitErrors = Number(group.exitErrors || 0);
22923
+ if (group.errors > 0 || exitErrors > 0) {
22924
+ const realErrors = Math.max(0, Number(group.callErrors || group.errors || 0));
22925
+ const succeeded = Math.max(0, completed - group.errors - exitErrors);
22926
+ const exitCode = group.results.find((result) => result?.isExitError)?.exitCode;
22927
+ const reasons = group.results.filter((result) => result?.isError && !result?.isExitError).map((result) => firstErrorLine(result?.text)).filter(Boolean);
22913
22928
  const uniqueReasons = [...new Set(reasons)].slice(0, 2);
22914
- const base = succeeded > 0 ? `${succeeded} Ok \xB7 ${group.errors} Failed` : `${group.errors} Failed`;
22929
+ const base = failureDetailText({ succeeded, realErrors, exitErrors, exitCode });
22915
22930
  return [
22916
22931
  `${base}${uniqueReasons[0] ? ` \xB7 ${uniqueReasons[0]}` : ""}`,
22917
22932
  ...uniqueReasons.slice(1)
@@ -22945,9 +22960,10 @@ ${text}`);
22945
22960
  }
22946
22961
  return chunks.join("\n\n");
22947
22962
  }
22948
- function aggregateBucketForCategory(category) {
22963
+ function aggregateBucketForCategory(category, { agentBatch = "" } = {}) {
22949
22964
  const key = String(category || "").trim();
22950
22965
  if (key === "Read" || key === "Search") return "category:Read+Search";
22966
+ if (key === "Agent") return agentBatch ? `category:Agent:${agentBatch}` : "category:Agent";
22951
22967
  return key ? `category:${key}` : "default";
22952
22968
  }
22953
22969
  function aggregateSummaries(aggregate) {
@@ -23064,13 +23080,24 @@ function createToolCardResults({
23064
23080
  markToolCallDone,
23065
23081
  updateAgentJobCard,
23066
23082
  buildAgentJobCardPatch,
23067
- agentStatusState
23083
+ agentStatusState,
23084
+ itemIndexById
23068
23085
  }) {
23086
+ const itemById = (id) => {
23087
+ const index = itemIndexById?.get(id);
23088
+ return Number.isInteger(index) ? getState().items[index]?.id === id ? getState().items[index] : null : null;
23089
+ };
23090
+ function finalizedErrorFallbackBody(body, text, exitCode) {
23091
+ if (String(body || "").trim()) return body;
23092
+ if (String(text || "").trim()) return text;
23093
+ if (exitCode != null) return `Exit ${exitCode}`;
23094
+ return "Failed";
23095
+ }
23069
23096
  function patchToolItem(id, patch) {
23070
- const prev = getState().items.find((it) => it.id === id);
23097
+ const prev = itemById(id);
23071
23098
  const ok = patchItem(id, patch);
23072
23099
  if (!ok || !prev) return ok;
23073
- const next = getState().items.find((it) => it.id === id);
23100
+ const next = itemById(id);
23074
23101
  if (next && next !== prev) carryTranscriptMeasuredRowsCache(prev, next);
23075
23102
  return ok;
23076
23103
  }
@@ -23083,10 +23110,8 @@ function createToolCardResults({
23083
23110
  const rawText = toolResultText(message?.content);
23084
23111
  const aggregate = card.aggregate;
23085
23112
  const callRec = aggregate && callId ? aggregate.calls.get(callId) : null;
23086
- const isMemoryCall = classifyToolCategory(callRec?.name || card?.name || "", callRec?.args || {}) === "Memory";
23087
- const isCallError = message?.isError === true || message?.toolKind === "error";
23088
- const isResultError = /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText)) || isMemoryCall && memoryCoreResultErrorText(rawText) != null;
23089
- const isError = isCallError || isResultError;
23113
+ const { exitCode, isExitError, isCallError } = toolCallOutcome(message, rawText);
23114
+ const isError = isCallError;
23090
23115
  const text = isError ? toolErrorDisplay(rawText, card?.name || "tool") : rawText;
23091
23116
  if (aggregate && card.itemId === aggregate.itemId) {
23092
23117
  if (!callRec) return false;
@@ -23099,15 +23124,18 @@ function createToolCardResults({
23099
23124
  assignAggregateSummaryOrder(aggregate, callRec);
23100
23125
  callRec.isError = isError;
23101
23126
  callRec.isCallError = isCallError;
23127
+ callRec.isExitError = isExitError;
23128
+ callRec.exitCode = exitCode;
23102
23129
  callRec.resultText = text;
23103
23130
  callRec.resolved = true;
23104
23131
  const allCalls = [...aggregate.calls.values()];
23105
23132
  const completed = allCalls.filter((r) => r.resolved).length;
23106
23133
  const errors = allCalls.filter((r) => r.isError).length;
23107
23134
  const callErrors = allCalls.filter((r) => r.isCallError).length;
23108
- const succeeded = completed - errors;
23109
- const detailText = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
23110
- const currentItem = getState().items.find((it) => it.id === card.itemId);
23135
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
23136
+ const succeeded = Math.max(0, completed - errors - exitErrors);
23137
+ const detailText = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
23138
+ const currentItem = itemById(card.itemId);
23111
23139
  const earlyCompleted = allCalls.filter((r) => r.resolved || r.completedEarly).length;
23112
23140
  const visualCompleted = Math.max(completed, earlyCompleted, Math.min(allCalls.length, Number(currentItem?.completedCount || 0)));
23113
23141
  const rawResult = aggregateRawResult(allCalls);
@@ -23119,6 +23147,7 @@ function createToolCardResults({
23119
23147
  isError: errors > 0,
23120
23148
  errorCount: errors,
23121
23149
  callErrorCount: callErrors,
23150
+ exitErrorCount: exitErrors,
23122
23151
  count: allCalls.length,
23123
23152
  completedCount: visualCompleted,
23124
23153
  doneCategories: aggregateDoneCategories(allCalls),
@@ -23128,20 +23157,25 @@ function createToolCardResults({
23128
23157
  if (callId) done.add(callId);
23129
23158
  return true;
23130
23159
  }
23131
- const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, callErrors: 0, results: [] };
23160
+ const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, callErrors: 0, exitErrors: 0, results: [] };
23132
23161
  group.completed = Math.min(group.count, group.completed + 1);
23133
23162
  group.errors += isError ? 1 : 0;
23134
23163
  group.callErrors = (group.callErrors || 0) + (isCallError ? 1 : 0);
23135
- group.results.push({ text, isError });
23164
+ group.exitErrors = (group.exitErrors || 0) + (isExitError ? 1 : 0);
23165
+ group.results.push({ text, isError, isExitError, exitCode });
23136
23166
  toolGroups.set(card.itemId, group);
23137
23167
  const resultText = groupedToolResultText(group);
23138
- const displayResult = toolGroupedDisplayFallback(resultText, text, rawText);
23168
+ let displayResult = toolGroupedDisplayFallback(resultText, text, rawText);
23169
+ if (group.errors > 0 && !String(displayResult || "").trim()) {
23170
+ displayResult = finalizedErrorFallbackBody(displayResult, text, exitCode);
23171
+ }
23139
23172
  const patch = {
23140
23173
  result: displayResult,
23141
23174
  text: displayResult,
23142
23175
  isError: group.errors > 0,
23143
23176
  errorCount: group.errors,
23144
23177
  callErrorCount: group.callErrors || 0,
23178
+ exitErrorCount: group.exitErrors || 0,
23145
23179
  count: group.count,
23146
23180
  completedCount: group.completed,
23147
23181
  completedAt: Date.now()
@@ -23154,6 +23188,9 @@ function createToolCardResults({
23154
23188
  set(agentStatusState({ force: true }));
23155
23189
  }
23156
23190
  Object.assign(patch, buildAgentJobCardPatch(card.itemId, rawText, isError));
23191
+ if (group.errors > 0 && !String(patch.result || "").trim()) {
23192
+ patch.result = patch.text = finalizedErrorFallbackBody(patch.result, text, exitCode);
23193
+ }
23157
23194
  }
23158
23195
  patchToolItem(card.itemId, patch);
23159
23196
  card.done = true;
@@ -23204,11 +23241,12 @@ function createToolCardResults({
23204
23241
  const totalCompleted = completed;
23205
23242
  const errors = allCalls.filter((r) => r.isError).length;
23206
23243
  const callErrors = allCalls.filter((r) => r.isCallError).length;
23207
- const succeeded = completed - errors;
23244
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
23245
+ const succeeded = Math.max(0, completed - errors - exitErrors);
23208
23246
  const rawResult = aggregateRawResult(allCalls);
23209
- let displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
23247
+ let displayDetail = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
23210
23248
  if (cancelled) {
23211
- const currentItem = getState().items.find((it) => it.id === card.itemId);
23249
+ const currentItem = itemById(card.itemId);
23212
23250
  displayDetail = withCancelledResultMarker(displayDetail, currentItem);
23213
23251
  }
23214
23252
  patchToolItem(card.itemId, {
@@ -23218,6 +23256,7 @@ function createToolCardResults({
23218
23256
  isError: errors > 0,
23219
23257
  errorCount: errors,
23220
23258
  callErrorCount: callErrors,
23259
+ exitErrorCount: exitErrors,
23221
23260
  count: allCalls.length,
23222
23261
  completedCount: totalCompleted,
23223
23262
  doneCategories: aggregateDoneCategories(allCalls),
@@ -23230,15 +23269,19 @@ function createToolCardResults({
23230
23269
  }
23231
23270
  continue;
23232
23271
  }
23233
- const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, results: [] };
23272
+ const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, exitErrors: 0, results: [] };
23234
23273
  group.completed = Math.min(group.count, group.completed + 1);
23235
23274
  toolGroups.set(card.itemId, group);
23236
23275
  let resultText = groupedToolResultText(group);
23276
+ if (group.errors > 0 && !String(resultText || "").trim()) {
23277
+ const exitRec = (group.results || []).find((r) => r && r.isExitError);
23278
+ resultText = finalizedErrorFallbackBody(resultText, exitRec?.text, exitRec?.exitCode);
23279
+ }
23237
23280
  if (cancelled) {
23238
- const currentItem = getState().items.find((it) => it.id === card.itemId);
23281
+ const currentItem = itemById(card.itemId);
23239
23282
  resultText = withCancelledResultMarker(resultText, currentItem);
23240
23283
  }
23241
- patchToolItem(card.itemId, { result: resultText, text: resultText, isError: group.errors > 0, errorCount: group.errors, callErrorCount: group.callErrors || 0, count: group.count, completedCount: group.completed, completedAt: Date.now() });
23284
+ patchToolItem(card.itemId, { result: resultText, text: resultText, isError: group.errors > 0, errorCount: group.errors, callErrorCount: group.callErrors || 0, exitErrorCount: group.exitErrors || 0, count: group.count, completedCount: group.completed, completedAt: Date.now() });
23242
23285
  card.done = true;
23243
23286
  if (card.callId) done.add(card.callId);
23244
23287
  }
@@ -23246,6 +23289,73 @@ function createToolCardResults({
23246
23289
  return { patchToolCardResult, flushToolResults };
23247
23290
  }
23248
23291
 
23292
+ // src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs
23293
+ import { createHash as createHash3 } from "node:crypto";
23294
+ var DELIVERED_TTL_MS = 6 * 60 * 60 * 1e3;
23295
+ var DELIVERED_MAX_ENTRIES = 512;
23296
+ var _delivered = /* @__PURE__ */ new Map();
23297
+ function hashCompletionText(text) {
23298
+ const value = String(text ?? "").trim();
23299
+ if (!value) return null;
23300
+ return createHash3("sha1").update(value).digest("hex");
23301
+ }
23302
+ function execKey(executionId) {
23303
+ const id = executionId == null ? "" : String(executionId).trim();
23304
+ return id ? `id:${id}` : null;
23305
+ }
23306
+ function textKey(text) {
23307
+ const h = hashCompletionText(text);
23308
+ return h ? `tx:${h}` : null;
23309
+ }
23310
+ function pruneExpired(now) {
23311
+ for (const [key, expiresAt] of _delivered) {
23312
+ if (expiresAt <= now) _delivered.delete(key);
23313
+ }
23314
+ }
23315
+ function enforceBound() {
23316
+ while (_delivered.size > DELIVERED_MAX_ENTRIES) {
23317
+ const oldest = _delivered.keys().next().value;
23318
+ if (oldest === void 0) break;
23319
+ _delivered.delete(oldest);
23320
+ }
23321
+ }
23322
+ function recordDeliveredCompletion({ executionId, text } = {}) {
23323
+ const now = Date.now();
23324
+ pruneExpired(now);
23325
+ const expiresAt = now + DELIVERED_TTL_MS;
23326
+ let recorded = false;
23327
+ for (const key of [execKey(executionId), textKey(text)]) {
23328
+ if (!key) continue;
23329
+ _delivered.delete(key);
23330
+ _delivered.set(key, expiresAt);
23331
+ recorded = true;
23332
+ }
23333
+ if (recorded) enforceBound();
23334
+ return recorded;
23335
+ }
23336
+ function isDeliveredCompletion({ executionId, text } = {}) {
23337
+ const now = Date.now();
23338
+ const keys = [execKey(executionId), textKey(text)].filter(Boolean);
23339
+ let hit = false;
23340
+ for (const key of keys) {
23341
+ const expiresAt2 = _delivered.get(key);
23342
+ if (expiresAt2 === void 0) continue;
23343
+ if (expiresAt2 <= now) {
23344
+ _delivered.delete(key);
23345
+ continue;
23346
+ }
23347
+ hit = true;
23348
+ }
23349
+ if (!hit) return false;
23350
+ const expiresAt = now + DELIVERED_TTL_MS;
23351
+ for (const key of keys) {
23352
+ if (!_delivered.has(key)) continue;
23353
+ _delivered.delete(key);
23354
+ _delivered.set(key, expiresAt);
23355
+ }
23356
+ return true;
23357
+ }
23358
+
23249
23359
  // src/tui/engine/agent-job-feed.mjs
23250
23360
  function parseInboundImagePaths(raw) {
23251
23361
  if (typeof raw !== "string" || !raw) return [];
@@ -23266,16 +23376,111 @@ function createAgentJobFeed({
23266
23376
  enqueue,
23267
23377
  drain,
23268
23378
  pushUserOrSyntheticItem,
23379
+ pushAsyncAgentResponse,
23269
23380
  makeQueueEntry,
23270
23381
  getPending,
23271
23382
  agentStatusState,
23272
23383
  displayedExecutionNotificationKeys,
23273
- pushNotice
23384
+ itemIndexById,
23385
+ pushNotice,
23386
+ now = () => Date.now(),
23387
+ executionResumeTombstoneTtlMs = 3e4,
23388
+ executionResumeTombstoneLimit = 128
23274
23389
  }) {
23390
+ const executionDedupLimit = 256;
23275
23391
  let executionResumeKickDeferred = false;
23392
+ const discardedExecutionResumeKeys = /* @__PURE__ */ new Map();
23393
+ const displayedExecutionResponseStates = /* @__PURE__ */ new Map();
23394
+ const terminalExecutionNotificationKeys = /* @__PURE__ */ new Set();
23395
+ const terminalExecutionResponseKeys = /* @__PURE__ */ new Set();
23396
+ const executionNotificationKeys = /* @__PURE__ */ new Map();
23397
+ const clearExecutionDedupState = () => {
23398
+ displayedExecutionNotificationKeys.clear();
23399
+ displayedExecutionResponseStates.clear();
23400
+ terminalExecutionNotificationKeys.clear();
23401
+ terminalExecutionResponseKeys.clear();
23402
+ executionNotificationKeys.clear();
23403
+ };
23404
+ const rememberDisplayedExecutionNotificationKey = (key, terminal = false, executionId = "") => {
23405
+ if (!key) return;
23406
+ displayedExecutionNotificationKeys.delete(key);
23407
+ if (executionId) executionNotificationKeys.set(key, executionId);
23408
+ if (terminal) terminalExecutionNotificationKeys.add(key);
23409
+ else terminalExecutionNotificationKeys.delete(key);
23410
+ while (displayedExecutionNotificationKeys.size >= executionDedupLimit) {
23411
+ const oldestTerminal = [...displayedExecutionNotificationKeys].find((candidate) => terminalExecutionNotificationKeys.has(candidate));
23412
+ if (oldestTerminal == null) break;
23413
+ displayedExecutionNotificationKeys.delete(oldestTerminal);
23414
+ terminalExecutionNotificationKeys.delete(oldestTerminal);
23415
+ executionNotificationKeys.delete(oldestTerminal);
23416
+ }
23417
+ displayedExecutionNotificationKeys.add(key);
23418
+ };
23419
+ const promoteExecutionNotificationKeys = (executionId) => {
23420
+ if (!executionId) return;
23421
+ for (const [key, keyExecutionId] of executionNotificationKeys) {
23422
+ if (keyExecutionId !== executionId || !displayedExecutionNotificationKeys.has(key)) continue;
23423
+ terminalExecutionNotificationKeys.add(key);
23424
+ }
23425
+ };
23426
+ const promoteExecutionDedupState = (executionId) => {
23427
+ if (!executionId) return;
23428
+ promoteExecutionNotificationKeys(executionId);
23429
+ const responseState = displayedExecutionResponseStates.get(executionId);
23430
+ if (responseState) rememberDisplayedExecutionResponseState(executionId, responseState, true);
23431
+ };
23432
+ const rememberDisplayedExecutionResponseState = (key, value, terminal = false) => {
23433
+ if (!key) return;
23434
+ displayedExecutionResponseStates.delete(key);
23435
+ if (terminal) terminalExecutionResponseKeys.add(key);
23436
+ else terminalExecutionResponseKeys.delete(key);
23437
+ while (displayedExecutionResponseStates.size >= executionDedupLimit) {
23438
+ const oldestTerminal = [...displayedExecutionResponseStates.keys()].find((candidate) => terminalExecutionResponseKeys.has(candidate));
23439
+ if (oldestTerminal == null) break;
23440
+ displayedExecutionResponseStates.delete(oldestTerminal);
23441
+ terminalExecutionResponseKeys.delete(oldestTerminal);
23442
+ }
23443
+ displayedExecutionResponseStates.set(key, value);
23444
+ };
23276
23445
  const executionResumeKickBodies = [];
23277
- function kickExecutionPendingResume(body = "") {
23278
- if (body) executionResumeKickBodies.push(body);
23446
+ function executionResumeKey(body, completionKey = "") {
23447
+ if (completionKey && typeof completionKey === "object") {
23448
+ completionKey = completionKey.executionId || completionKey.key || "";
23449
+ }
23450
+ const explicitKey = String(completionKey || "").trim();
23451
+ if (explicitKey.startsWith("execution:") || explicitKey.startsWith("body:")) return explicitKey;
23452
+ if (explicitKey) return `execution:${explicitKey}`;
23453
+ const value = String(body || "").trim();
23454
+ return value ? `body:${shortTextFingerprint(value)}` : "";
23455
+ }
23456
+ function pruneDiscardedExecutionResumeKeys() {
23457
+ const nowMs = Number(now()) || Date.now();
23458
+ for (const [key, expiresAt] of discardedExecutionResumeKeys) {
23459
+ if (expiresAt <= nowMs) discardedExecutionResumeKeys.delete(key);
23460
+ }
23461
+ }
23462
+ function isDiscardedExecutionResumeKey(key) {
23463
+ if (!key) return false;
23464
+ pruneDiscardedExecutionResumeKeys();
23465
+ return discardedExecutionResumeKeys.has(key);
23466
+ }
23467
+ function rememberDiscardedExecutionResumeKey(key) {
23468
+ if (!key) return;
23469
+ pruneDiscardedExecutionResumeKeys();
23470
+ const limit = Math.max(1, Number(executionResumeTombstoneLimit) || 128);
23471
+ while (!discardedExecutionResumeKeys.has(key) && discardedExecutionResumeKeys.size >= limit) {
23472
+ const oldest = discardedExecutionResumeKeys.keys().next().value;
23473
+ if (oldest == null) break;
23474
+ discardedExecutionResumeKeys.delete(oldest);
23475
+ }
23476
+ const ttlMs = Math.max(1, Number(executionResumeTombstoneTtlMs) || 3e4);
23477
+ discardedExecutionResumeKeys.delete(key);
23478
+ discardedExecutionResumeKeys.set(key, (Number(now()) || Date.now()) + ttlMs);
23479
+ }
23480
+ function kickExecutionPendingResume(body = "", completionKey = "") {
23481
+ const key = executionResumeKey(body, completionKey);
23482
+ if (body && isDiscardedExecutionResumeKey(key)) return;
23483
+ if (body) executionResumeKickBodies.push({ body, key });
23279
23484
  if (getDisposed()) return;
23280
23485
  if (getState().busy) {
23281
23486
  executionResumeKickDeferred = true;
@@ -23287,20 +23492,39 @@ function createAgentJobFeed({
23287
23492
  return;
23288
23493
  }
23289
23494
  executionResumeKickDeferred = false;
23290
- const resumeBody = executionResumeKickBodies.splice(0).filter(Boolean).join("\n\n");
23291
- pending.push(makeQueueEntry(resumeBody, { mode: "pending-resume", priority: "next" }));
23495
+ const resumeBodies = executionResumeKickBodies.splice(0);
23496
+ const resumeBody = resumeBodies.map(({ body: value }) => value).filter(Boolean).join("\n\n");
23497
+ const resumeCompletionKeys = resumeBodies.map(({ key: key2 }) => key2).filter(Boolean);
23498
+ pending.push(makeQueueEntry(resumeBody, {
23499
+ mode: "pending-resume",
23500
+ priority: "next",
23501
+ abortDiscardOnAbort: true,
23502
+ resumeCompletionKeys
23503
+ }));
23292
23504
  void drain();
23293
23505
  }
23294
23506
  function flushDeferredExecutionPendingResumeKick() {
23295
23507
  if (!executionResumeKickDeferred || getDisposed() || getState().busy) return;
23296
23508
  kickExecutionPendingResume();
23297
23509
  }
23298
- function scheduleExecutionPendingResumeKick(body = "") {
23299
- queueMicrotask(() => kickExecutionPendingResume(body));
23510
+ function scheduleExecutionPendingResumeKick(body = "", completionKey = "") {
23511
+ queueMicrotask(() => kickExecutionPendingResume(body, completionKey));
23512
+ }
23513
+ function discardExecutionPendingResume(completionKeys = []) {
23514
+ const keys = (Array.isArray(completionKeys) ? completionKeys : [completionKeys]).map((key) => executionResumeKey("", key)).filter(Boolean);
23515
+ if (keys.length === 0) return;
23516
+ for (const key of keys) rememberDiscardedExecutionResumeKey(key);
23517
+ for (let i = executionResumeKickBodies.length - 1; i >= 0; i -= 1) {
23518
+ if (isDiscardedExecutionResumeKey(executionResumeKickBodies[i].key)) {
23519
+ executionResumeKickBodies.splice(i, 1);
23520
+ }
23521
+ }
23522
+ executionResumeKickDeferred = executionResumeKickBodies.length > 0;
23300
23523
  }
23301
23524
  function buildAgentJobCardPatch(itemId, text, isError = false) {
23302
23525
  const parsed = parseAgentJob(text);
23303
- const current = getState().items.find((it) => it.id === itemId);
23526
+ const index = itemIndexById?.get(itemId);
23527
+ const current = Number.isInteger(index) && getState().items[index]?.id === itemId ? getState().items[index] : null;
23304
23528
  const rawDisplayText = agentJobResultText(text, parsed) || String(text ?? "").trim();
23305
23529
  const displayText = isError ? toolErrorDisplay(rawDisplayText, "agent") : rawDisplayText;
23306
23530
  return {
@@ -23314,53 +23538,81 @@ function createAgentJobFeed({
23314
23538
  function updateAgentJobCard(itemId, text, isError = false) {
23315
23539
  patchItem(itemId, buildAgentJobCardPatch(itemId, text, isError));
23316
23540
  }
23541
+ function refreshAgentStatus(parsed) {
23542
+ if (!parsed?.taskId) return;
23543
+ const status = String(parsed.status || "").toLowerCase();
23544
+ const terminal = /^(completed|complete|done|success|succeeded|ok|failed|error|timeout|killed|cancelled|canceled|denied)$/.test(status);
23545
+ set(agentStatusState(terminal ? { force: true } : void 0));
23546
+ }
23317
23547
  function subscribeRuntimeNotifications() {
23318
23548
  if (typeof runtime.onNotification !== "function") return null;
23319
- return runtime.onNotification((event) => {
23549
+ const unsubscribe = runtime.onNotification((event) => {
23320
23550
  if (getDisposed()) return;
23321
23551
  const text = String(event?.content ?? event?.text ?? event ?? "").trim();
23322
23552
  if (!text) return;
23323
23553
  const parsed = parseAgentJob(text);
23324
23554
  const notificationKey = notificationQueueKey(event, text, parsed);
23325
23555
  const delivery = resolveTuiRuntimeNotificationDelivery(event, text);
23556
+ const executionId = String(event?.meta?.execution_id || parsed?.taskId || "").trim();
23557
+ const status = String(event?.meta?.status || parsed?.status || "").toLowerCase();
23558
+ const terminalStatus = /^(completed|complete|done|success|succeeded|ok|failed|error|timeout|killed|cancelled|canceled|denied)$/.test(status);
23559
+ if (terminalStatus) promoteExecutionDedupState(executionId);
23326
23560
  if (delivery.action === "ignore") return;
23327
23561
  if (delivery.action === "notice") {
23328
23562
  pushNotice?.(delivery.displayText, delivery.tone || "info", { transcript: delivery.transcript === true });
23329
23563
  return true;
23330
23564
  }
23331
23565
  if (delivery.action === "status-only") {
23332
- if (parsed?.taskId) set(agentStatusState({ force: true }));
23566
+ refreshAgentStatus(parsed);
23333
23567
  return true;
23334
23568
  }
23335
23569
  if (delivery.action === "execution-ui") {
23336
- const firstDelivery = !notificationKey || !displayedExecutionNotificationKeys.has(notificationKey);
23337
- if (firstDelivery) {
23338
- if (notificationKey) displayedExecutionNotificationKeys.add(notificationKey);
23339
- pushUserOrSyntheticItem(delivery.displayText, nextId2());
23570
+ const cardKey = executionCardKey(event, text, parsed);
23571
+ const firstDelivery = !cardKey || !displayedExecutionNotificationKeys.has(cardKey);
23572
+ const hasBody = /\n\s*\n[\s\S]*\S/.test(text);
23573
+ const isFailure = /^(failed|error|timeout|killed|cancelled|canceled|denied)$/.test(status);
23574
+ const successfulPreview = !hasBody && !isFailure && /^(completed|complete|done|success|succeeded|ok)$/.test(status);
23575
+ const terminal = terminalStatus;
23576
+ const responseState = executionId ? displayedExecutionResponseStates.get(executionId) : "";
23577
+ const bodyAlreadyDisplayed = responseState === "body";
23578
+ if (cardKey && terminal && displayedExecutionNotificationKeys.has(cardKey)) {
23579
+ rememberDisplayedExecutionNotificationKey(cardKey, true, executionId);
23580
+ }
23581
+ if (terminal) promoteExecutionDedupState(executionId);
23582
+ if (firstDelivery && !successfulPreview && !bodyAlreadyDisplayed) {
23583
+ if (cardKey) rememberDisplayedExecutionNotificationKey(cardKey, terminal, executionId);
23584
+ if (executionId) rememberDisplayedExecutionResponseState(executionId, hasBody ? "body" : "preview", terminal);
23585
+ (pushAsyncAgentResponse || pushUserOrSyntheticItem)(delivery.displayText, nextId2(), "injected", { responseKey: executionId });
23340
23586
  }
23341
- if (parsed?.taskId) set(agentStatusState({ force: true }));
23587
+ refreshAgentStatus(parsed);
23342
23588
  const resumeBody = String(delivery.modelContent || "").trim();
23343
23589
  if (resumeBody) {
23344
- enqueue(resumeBody, {
23590
+ const completionKey = executionResumeKey(resumeBody, executionId);
23591
+ if (isDiscardedExecutionResumeKey(completionKey) || isDeliveredCompletion({ executionId, text: resumeBody })) {
23592
+ if (event && typeof event === "object") event.modelVisibleDelivered = true;
23593
+ return true;
23594
+ }
23595
+ const enqueued = enqueue(resumeBody, {
23345
23596
  mode: "task-notification",
23346
23597
  // Claude Code parity: live execution completions are queued as
23347
23598
  // task notifications so the active loop can attach them after the
23348
23599
  // next tool batch; no special pending-resume bypass.
23349
23600
  priority: "next",
23350
23601
  key: notificationKey || void 0,
23602
+ abortDiscardOnAbort: true,
23603
+ resumeCompletionKeys: completionKey ? [completionKey] : [],
23351
23604
  displayText: delivery.displayText || text,
23352
23605
  // The immediate Response card was already pushed above
23353
23606
  // (pushUserOrSyntheticItem). Keep this queued twin model-visible
23354
23607
  // but suppress its drain-time transcript card to avoid a duplicate.
23355
23608
  suppressDisplay: true
23356
23609
  });
23610
+ if (enqueued) recordDeliveredCompletion({ executionId, text: resumeBody });
23357
23611
  if (event && typeof event === "object") event.modelVisibleDelivered = true;
23358
23612
  }
23359
23613
  return true;
23360
23614
  }
23361
- if (parsed?.taskId) {
23362
- set(agentStatusState({ force: true }));
23363
- }
23615
+ refreshAgentStatus(parsed);
23364
23616
  const modelContent = String(delivery.modelContent ?? delivery.displayText ?? text).trim();
23365
23617
  const imagePaths = parseInboundImagePaths(event?.meta?.image_paths);
23366
23618
  if (!modelContent && imagePaths.length === 0) return true;
@@ -23398,14 +23650,78 @@ function createAgentJobFeed({
23398
23650
  enqueue(modelContent, enqueueOpts);
23399
23651
  return true;
23400
23652
  });
23653
+ return () => {
23654
+ try {
23655
+ unsubscribe?.();
23656
+ } finally {
23657
+ clearExecutionDedupState();
23658
+ }
23659
+ };
23401
23660
  }
23402
23661
  return {
23403
23662
  kickExecutionPendingResume,
23404
23663
  flushDeferredExecutionPendingResumeKick,
23405
23664
  scheduleExecutionPendingResumeKick,
23665
+ discardExecutionPendingResume,
23406
23666
  updateAgentJobCard,
23407
23667
  buildAgentJobCardPatch,
23408
- subscribeRuntimeNotifications
23668
+ subscribeRuntimeNotifications,
23669
+ clearExecutionDedupState
23670
+ };
23671
+ }
23672
+
23673
+ // src/tui/engine/agent-response-tail.mjs
23674
+ function responseEntry(response = {}) {
23675
+ return {
23676
+ key: String(response.key || ""),
23677
+ raw: String(response.rawResult ?? response.raw ?? "").trim(),
23678
+ result: response.result,
23679
+ hasBody: response.hasBody === true,
23680
+ isError: response.isError === true
23681
+ };
23682
+ }
23683
+ function formatAgentResponseRaw(entries = []) {
23684
+ return entries.map((entry, index) => {
23685
+ const raw = String(entry?.raw || "").trim();
23686
+ return raw ? `${index + 1}. agent
23687
+ ${raw}` : "";
23688
+ }).filter(Boolean).join("\n\n");
23689
+ }
23690
+ function priorEntries(previous) {
23691
+ if (Array.isArray(previous?.agentResponseEntries) && previous.agentResponseEntries.length) {
23692
+ return previous.agentResponseEntries.map(responseEntry);
23693
+ }
23694
+ return [responseEntry({
23695
+ key: previous?.agentResponseKey,
23696
+ rawResult: previous?.rawResult ?? previous?.result,
23697
+ result: previous?.result,
23698
+ hasBody: previous?.agentResponseHasBody,
23699
+ isError: previous?.isError
23700
+ })];
23701
+ }
23702
+ function appendAgentResponseTail(previous, response, now = Date.now()) {
23703
+ if (previous?.kind !== "tool" || previous.agentDirection !== "inbound") return null;
23704
+ const next = responseEntry(response);
23705
+ const entries = priorEntries(previous);
23706
+ const existingIndex = next.key ? entries.findIndex((entry) => entry.key === next.key) : -1;
23707
+ if (existingIndex >= 0) {
23708
+ entries[existingIndex] = next;
23709
+ } else {
23710
+ if (previous.agentResponseHasBody !== next.hasBody) return null;
23711
+ entries.push(next);
23712
+ }
23713
+ return {
23714
+ args: response.args,
23715
+ result: response.result,
23716
+ rawResult: formatAgentResponseRaw(entries) || null,
23717
+ isError: entries.some((entry) => entry.isError),
23718
+ count: entries.length,
23719
+ completedCount: entries.length,
23720
+ completedAt: now,
23721
+ agentResponseEntries: entries,
23722
+ agentResponseKeys: entries.map((entry) => entry.key).filter(Boolean),
23723
+ agentResponseHasBody: entries.every((entry) => entry.hasBody),
23724
+ agentResponseAggregate: entries.length > 1
23409
23725
  };
23410
23726
  }
23411
23727
 
@@ -23737,6 +24053,7 @@ function createSessionFlow(bag) {
23737
24053
  pending,
23738
24054
  pendingNotificationKeys,
23739
24055
  displayedExecutionNotificationKeys,
24056
+ clearExecutionDedupState,
23740
24057
  getState,
23741
24058
  set,
23742
24059
  pushItem,
@@ -23778,6 +24095,10 @@ function createSessionFlow(bag) {
23778
24095
  skipSlashCommands: options.skipSlashCommands === true,
23779
24096
  displayText: mode === "task-notification" ? notificationDisplayText(displayText) : String(displayText || ""),
23780
24097
  suppressDisplay: options.suppressDisplay === true,
24098
+ // Completion resumes are consumed exactly once: Esc abandons their
24099
+ // uncommitted body instead of putting it back at the queue front.
24100
+ abortDiscardOnAbort: options.abortDiscardOnAbort === true,
24101
+ resumeCompletionKeys: Array.isArray(options.resumeCompletionKeys) ? options.resumeCompletionKeys.filter((key) => key != null && String(key).trim()) : [],
23781
24102
  steeringPersistId: options.steeringPersistId || null,
23782
24103
  steeringPersistRestored: options.steeringPersistRestored === true
23783
24104
  };
@@ -23831,6 +24152,7 @@ function createSessionFlow(bag) {
23831
24152
  if (predicate(entry) && (entry.mode || "prompt") === targetMode && queuePriorityValue(entry.priority) === bestPriority) {
23832
24153
  batch.push(entry);
23833
24154
  pending.splice(i, 1);
24155
+ if (entry.mode === "task-notification" && entry.key) pendingNotificationKeys.delete(entry.key);
23834
24156
  if (batch.length >= limit) break;
23835
24157
  } else {
23836
24158
  i += 1;
@@ -23893,6 +24215,13 @@ function createSessionFlow(bag) {
23893
24215
  pushUserOrSyntheticItem(entry.text, entry.id, isQueuedEntryEditable(entry) ? "user" : "injected");
23894
24216
  }
23895
24217
  const nonEditable = batch.filter((entry) => !isQueuedEntryEditable(entry));
24218
+ const discardOnAbort = nonEditable.filter(
24219
+ (entry) => entry?.abortDiscardOnAbort === true || entry?.mode === "pending-resume"
24220
+ );
24221
+ const requeueOnAbort = nonEditable.filter((entry) => !discardOnAbort.includes(entry));
24222
+ const discardExecutionPendingResumeKeys = discardOnAbort.flatMap(
24223
+ (entry) => Array.isArray(entry?.resumeCompletionKeys) ? entry.resumeCompletionKeys : []
24224
+ );
23896
24225
  const batchPastedImages = mergePastedImages(batch);
23897
24226
  const batchPastedTexts = mergePastedTexts(batch);
23898
24227
  const turnStatus = await bag.runTurn(merged, {
@@ -23902,7 +24231,8 @@ function createSessionFlow(bag) {
23902
24231
  onCommitted: () => commitSteeringQueueEntries(batch),
23903
24232
  submittedIds: [...ids],
23904
24233
  restorable: nonEditable.length === 0,
23905
- requeueOnAbort: nonEditable
24234
+ requeueOnAbort,
24235
+ discardExecutionPendingResumeKeys
23906
24236
  });
23907
24237
  if (flags.drainEpoch !== drainEpoch) return;
23908
24238
  flushDeferredClearedSessionUi();
@@ -24120,6 +24450,7 @@ function createSessionFlow(bag) {
24120
24450
  getState().busy = false;
24121
24451
  pendingNotificationKeys.clear();
24122
24452
  displayedExecutionNotificationKeys.clear();
24453
+ clearExecutionDedupState?.();
24123
24454
  };
24124
24455
  const applyClearedSessionUi = (doneLabel) => {
24125
24456
  resetStats();
@@ -24215,6 +24546,9 @@ function createRunTurn(bag) {
24215
24546
  set,
24216
24547
  pushItem,
24217
24548
  patchItem,
24549
+ updateStreamingTail: updateStreamingTailFromStore,
24550
+ settleStreamingTail: settleStreamingTailFromStore,
24551
+ clearStreamingTail: clearStreamingTailFromStore,
24218
24552
  pushNotice,
24219
24553
  pushUserOrSyntheticItem,
24220
24554
  markToolCallActive,
@@ -24231,6 +24565,21 @@ function createRunTurn(bag) {
24231
24565
  drain,
24232
24566
  drainPendingSteering
24233
24567
  } = bag;
24568
+ const updateStreamingTail = updateStreamingTailFromStore || ((id, patch = {}) => {
24569
+ set({ streamingTail: { ...getState().streamingTail || {}, ...patch, kind: "assistant", id, streaming: true } });
24570
+ return true;
24571
+ });
24572
+ const settleStreamingTail = settleStreamingTailFromStore || ((id, patch = {}) => {
24573
+ const tail = getState().streamingTail;
24574
+ if (!tail || tail.id !== id) return false;
24575
+ pushItem({ ...tail, ...patch, kind: "assistant", id, streaming: false });
24576
+ set({ streamingTail: null });
24577
+ return true;
24578
+ });
24579
+ const clearStreamingTail = clearStreamingTailFromStore || ((id = null) => {
24580
+ if (id == null || getState().streamingTail?.id === id) set({ streamingTail: null });
24581
+ return true;
24582
+ });
24234
24583
  async function runTurn(userText, options = {}) {
24235
24584
  const turnIndex = getState().stats.turns || 0;
24236
24585
  const startedAt = Date.now();
@@ -24250,7 +24599,8 @@ function createRunTurn(bag) {
24250
24599
  submittedIds,
24251
24600
  reclaimed: false,
24252
24601
  committed: false,
24253
- requeueEntries: Array.isArray(options.requeueOnAbort) ? options.requeueOnAbort.slice() : []
24602
+ requeueEntries: Array.isArray(options.requeueOnAbort) ? options.requeueOnAbort.slice() : [],
24603
+ discardExecutionPendingResumeKeys: Array.isArray(options.discardExecutionPendingResumeKeys) ? options.discardExecutionPendingResumeKeys.slice() : []
24254
24604
  };
24255
24605
  set({ busy: true, lastTurn: null, spinner: { active: true, verb: pickVerb(turnIndex), startedAt, responseLength: 0, inputTokens: 0, outputTokens: 0, mode: "requesting" } });
24256
24606
  let assistantText = "";
@@ -24261,6 +24611,9 @@ function createRunTurn(bag) {
24261
24611
  let watchdogGraceTimer = null;
24262
24612
  let lastProgressAt = startedAt;
24263
24613
  let lastProgressLabel = "start";
24614
+ let watchdogDeferralCeilingAt = 0;
24615
+ const configuredLeadToolMaxMs = Number(process.env.MIXDOG_LEAD_TOOL_MAX_MS);
24616
+ const leadToolMaxMs = Number.isFinite(configuredLeadToolMaxMs) && configuredLeadToolMaxMs > 0 ? configuredLeadToolMaxMs : 30 * 60 * 1e3;
24264
24617
  const clearWatchdog = () => {
24265
24618
  if (watchdogTimer) {
24266
24619
  clearTimeout(watchdogTimer);
@@ -24271,21 +24624,57 @@ function createRunTurn(bag) {
24271
24624
  watchdogGraceTimer = null;
24272
24625
  }
24273
24626
  };
24627
+ const refreshWatchdogFromRuntimeLiveness = () => {
24628
+ let liveness;
24629
+ try {
24630
+ liveness = runtime.getTurnLiveness?.();
24631
+ } catch {
24632
+ return false;
24633
+ }
24634
+ if (liveness?.stage !== "tool_running") watchdogDeferralCeilingAt = 0;
24635
+ const progressAt = Number(liveness?.lastProgressAt);
24636
+ const now = Date.now();
24637
+ if (!liveness || !Number.isFinite(progressAt) || progressAt <= now - LEAD_TURN_TIMEOUT_MS2) return false;
24638
+ if (liveness.stage === "tool_running") {
24639
+ watchdogDeferralCeilingAt = 0;
24640
+ const toolStartedAt = Number(liveness.toolStartedAt);
24641
+ if (!Number.isFinite(toolStartedAt) || toolStartedAt <= 0) return false;
24642
+ const toolSelfDeadlineMs = Number(liveness.toolSelfDeadlineMs);
24643
+ const toolCeilingMs = Math.max(
24644
+ Number.isFinite(toolSelfDeadlineMs) && toolSelfDeadlineMs > 0 ? toolSelfDeadlineMs + 6e4 : 0,
24645
+ leadToolMaxMs
24646
+ );
24647
+ if (now - toolStartedAt >= toolCeilingMs) return false;
24648
+ watchdogDeferralCeilingAt = toolStartedAt + toolCeilingMs;
24649
+ }
24650
+ lastProgressAt = progressAt;
24651
+ lastProgressLabel = `orchestrator:${String(liveness.stage || "unknown")}`;
24652
+ armWatchdog();
24653
+ return true;
24654
+ };
24274
24655
  const armWatchdog = () => {
24275
24656
  if (watchdogTimer) {
24276
24657
  clearTimeout(watchdogTimer);
24277
24658
  watchdogTimer = null;
24278
24659
  }
24279
24660
  if (watchdogTripped) return;
24280
- const remaining = Math.max(1, LEAD_TURN_TIMEOUT_MS2 - Math.max(0, Date.now() - lastProgressAt));
24661
+ const now = Date.now();
24662
+ const remaining = Math.max(1, LEAD_TURN_TIMEOUT_MS2 - Math.max(0, now - lastProgressAt));
24663
+ const ceilingRemaining = watchdogDeferralCeilingAt > 0 ? Math.max(1, watchdogDeferralCeilingAt - now) : Infinity;
24664
+ const delay = Math.min(remaining, ceilingRemaining);
24281
24665
  watchdogTimer = setTimeout(() => {
24282
24666
  if (!isCurrentTurn()) return;
24283
24667
  if (watchdogTripped) return;
24284
- const idleMs = Date.now() - lastProgressAt;
24668
+ const now2 = Date.now();
24669
+ const idleMs = now2 - lastProgressAt;
24285
24670
  if (idleMs < LEAD_TURN_TIMEOUT_MS2) {
24286
- armWatchdog();
24287
- return;
24288
- }
24671
+ if (watchdogDeferralCeilingAt > 0 && now2 >= watchdogDeferralCeilingAt) {
24672
+ if (refreshWatchdogFromRuntimeLiveness()) return;
24673
+ } else {
24674
+ armWatchdog();
24675
+ return;
24676
+ }
24677
+ } else if (refreshWatchdogFromRuntimeLiveness()) return;
24289
24678
  watchdogTripped = true;
24290
24679
  if (_batchTimer !== null) {
24291
24680
  clearTimeout(_batchTimer);
@@ -24296,7 +24685,7 @@ function createRunTurn(bag) {
24296
24685
  _pendingThinkingLastEndedAt = 0;
24297
24686
  const elapsed = Date.now() - startedAt;
24298
24687
  tuiDebug2(`runTurn WATCHDOG TRIP turn=${turnIndex} elapsedMs=${elapsed} idleMs=${idleMs} lastProgress=${lastProgressLabel} \u2014 aborting stuck turn`);
24299
- pushNotice(`Turn timed out after ${Math.round(idleMs / 1e3)}s idle \u2014 aborting stuck request. Input will be released shortly if abort does not unwind.`, "warn", { transcript: true });
24688
+ pushNotice(`Turn timed out after ${Math.round(idleMs / 1e3)}s idle (last progress: ${lastProgressLabel}) \u2014 aborting stuck request. Input will be released shortly if abort does not unwind.`, "warn", { transcript: true });
24300
24689
  try {
24301
24690
  runtime.abort("cli-react-abort-watchdog");
24302
24691
  } catch {
@@ -24312,6 +24701,11 @@ function createRunTurn(bag) {
24312
24701
  finalizeToolHeaders();
24313
24702
  clearDeferredTimers();
24314
24703
  flags.flushDeferredBeforeImmediatePush = null;
24704
+ if (currentAssistantId && currentAssistantText.trim()) {
24705
+ settleStreamingTail(currentAssistantId, { text: currentAssistantText });
24706
+ } else {
24707
+ clearStreamingTail(currentAssistantId);
24708
+ }
24315
24709
  flags.leadTurnEpoch++;
24316
24710
  set({ busy: false, spinner: null, thinking: null, lastTurn: null });
24317
24711
  flags.activePromptRestore = null;
@@ -24320,7 +24714,7 @@ function createRunTurn(bag) {
24320
24714
  flushDeferredExecutionPendingResumeKick();
24321
24715
  }, 5e3);
24322
24716
  watchdogGraceTimer.unref?.();
24323
- }, remaining);
24717
+ }, delay);
24324
24718
  watchdogTimer.unref?.();
24325
24719
  };
24326
24720
  const markTurnProgress = (label) => {
@@ -24328,11 +24722,11 @@ function createRunTurn(bag) {
24328
24722
  if (watchdogTripped) return;
24329
24723
  lastProgressAt = Date.now();
24330
24724
  lastProgressLabel = String(label || "progress");
24331
- armWatchdog();
24332
24725
  return true;
24333
24726
  };
24334
24727
  armWatchdog();
24335
24728
  let currentAssistantText = "";
24729
+ const committedSegments = [];
24336
24730
  let thinkingText = "";
24337
24731
  let thinkingStartedAt = 0;
24338
24732
  let thinkingSegmentStartedAt = 0;
@@ -24348,7 +24742,8 @@ function createRunTurn(bag) {
24348
24742
  const earlyResultBuffer = /* @__PURE__ */ new Map();
24349
24743
  const aggregateCards = [];
24350
24744
  let tailAggregate = null;
24351
- const TOOL_CARD_PUSH_DELAY_MS = 0;
24745
+ let providerToolBatch = 0;
24746
+ const TOOL_CARD_PUSH_DELAY_MS = 1e3;
24352
24747
  let deferredSeqCounter = 0;
24353
24748
  const deferredEntries = [];
24354
24749
  const flushDeferredUpTo = (entry) => {
@@ -24470,7 +24865,7 @@ function createRunTurn(bag) {
24470
24865
  const it = newItems[i];
24471
24866
  if (it?.id != null) itemIndexById.set(it.id, base + i);
24472
24867
  }
24473
- set({ items, ...extra });
24868
+ set({ items, structureRevision: (Number(getState().structureRevision) || 0) + 1, ...extra });
24474
24869
  };
24475
24870
  const markPromptCommitted = () => {
24476
24871
  if (flags.activePromptRestore) {
@@ -24505,7 +24900,7 @@ function createRunTurn(bag) {
24505
24900
  changed = true;
24506
24901
  return { ...item, headerFinalized: true };
24507
24902
  });
24508
- if (changed) set({ items });
24903
+ if (changed) set({ items, structureRevision: (Number(getState().structureRevision) || 0) + 1 });
24509
24904
  return changed;
24510
24905
  };
24511
24906
  const completeAggregateVisual = () => {
@@ -24515,10 +24910,11 @@ function createRunTurn(bag) {
24515
24910
  aggregate.ensureVisible?.();
24516
24911
  const errors = allCalls.filter((r) => r.isError).length;
24517
24912
  const callErrors = allCalls.filter((r) => r.isCallError).length;
24913
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
24518
24914
  const completed = allCalls.filter((r) => r.resolved).length;
24519
- const succeeded = completed - errors;
24915
+ const succeeded = Math.max(0, completed - errors - exitErrors);
24520
24916
  const rawResult = aggregateRawResult(allCalls);
24521
- const displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
24917
+ const displayDetail = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
24522
24918
  patchItem(aggregate.itemId, {
24523
24919
  result: displayDetail,
24524
24920
  text: displayDetail,
@@ -24526,6 +24922,7 @@ function createRunTurn(bag) {
24526
24922
  isError: errors > 0,
24527
24923
  errorCount: errors,
24528
24924
  callErrorCount: callErrors,
24925
+ exitErrorCount: exitErrors,
24529
24926
  count: allCalls.length,
24530
24927
  completedCount: allCalls.length,
24531
24928
  doneCategories: aggregateDoneCategories(allCalls),
@@ -24594,7 +24991,7 @@ function createRunTurn(bag) {
24594
24991
  const ensureAssistant = (initialText = "") => {
24595
24992
  if (!currentAssistantId) {
24596
24993
  currentAssistantId = nextId2();
24597
- pushItem({ kind: "assistant", id: currentAssistantId, text: String(initialText || ""), streaming: true });
24994
+ updateStreamingTail(currentAssistantId, { text: String(initialText || "") });
24598
24995
  }
24599
24996
  return currentAssistantId;
24600
24997
  };
@@ -24605,7 +25002,6 @@ function createRunTurn(bag) {
24605
25002
  _lastNewlineIdx = -1;
24606
25003
  _emittedNewlineIdx = -2;
24607
25004
  _emittedVisibleText = "";
24608
- _cachedAssistantIndex = -1;
24609
25005
  };
24610
25006
  const commitAssistantSegment = ({ sealToolBlock = false } = {}) => {
24611
25007
  const text = currentAssistantText || "";
@@ -24615,7 +25011,8 @@ function createRunTurn(bag) {
24615
25011
  }
24616
25012
  if (sealToolBlock) clearAggregateContinuation();
24617
25013
  const id = currentAssistantId || ensureAssistant(text);
24618
- patchItem(id, { text, streaming: false });
25014
+ settleStreamingTail(id, { text });
25015
+ committedSegments.push(text);
24619
25016
  closeAssistantSegment();
24620
25017
  return true;
24621
25018
  };
@@ -24642,7 +25039,6 @@ function createRunTurn(bag) {
24642
25039
  let _lastNewlineIdx = -1;
24643
25040
  let _emittedNewlineIdx = -2;
24644
25041
  let _emittedVisibleText = "";
24645
- let _cachedAssistantIndex = -1;
24646
25042
  let _publishedThinkingActive = false;
24647
25043
  const flushStreamBatch = () => {
24648
25044
  if (_batchTimer !== null) {
@@ -24677,22 +25073,13 @@ function createRunTurn(bag) {
24677
25073
  const patch = {};
24678
25074
  if (currentAssistantId || streamingVisibleText.trim()) {
24679
25075
  const id = ensureAssistant(streamingVisibleText);
24680
- let index = _cachedAssistantIndex;
24681
- if (index < 0 || getState().items[index]?.id !== id) {
24682
- index = getState().items.findIndex((it) => it.id === id);
24683
- _cachedAssistantIndex = index;
24684
- }
24685
- if (index >= 0) {
24686
- const current = getState().items[index];
24687
- if (!Object.is(current.text, streamingVisibleText) || current.streaming !== true) {
24688
- const items = getState().items.slice();
24689
- items[index] = { ...current, text: streamingVisibleText, streaming: true };
24690
- patch.items = items;
24691
- }
25076
+ const current = getState().streamingTail;
25077
+ if (!current || current.id !== id || !Object.is(current.text, streamingVisibleText)) {
25078
+ patch.streamingTail = { kind: "assistant", id, text: streamingVisibleText, streaming: true };
24692
25079
  }
24693
25080
  }
24694
25081
  const responseLengthVal = assistantText.length + thinkingText.length;
24695
- const visibleLineChanged = patch.items !== void 0;
25082
+ const visibleLineChanged = patch.streamingTail !== void 0;
24696
25083
  const thinkingTransition = _publishedThinkingActive === true;
24697
25084
  if (getState().spinner && (visibleLineChanged || thinkingTransition || _pendingThinkingLastEndedAt)) {
24698
25085
  patch.spinner = { ...getState().spinner, responseLength: responseLengthVal, thinking: false, thinkingLastEndedAt: _pendingThinkingLastEndedAt || getState().spinner.thinkingLastEndedAt, mode: compactingActive ? "compacting" : "responding" };
@@ -24732,25 +25119,27 @@ function createRunTurn(bag) {
24732
25119
  if (!callRec || callRec.resolved || callRec.completedEarly) return;
24733
25120
  aggregate.ensureVisible?.();
24734
25121
  const rawText = toolResultText(message?.content);
24735
- const isMemoryCall = classifyToolCategory(callRec.name, callRec.args) === "Memory";
24736
- const isCallError = message?.isError === true || message?.toolKind === "error";
24737
- const isResultError = /^\s*\[?error/i.test(rawText) || isErrorToolStatus(toolResultStatus(rawText)) || isMemoryCall && memoryCoreResultErrorText(rawText) != null;
24738
- const isError = isCallError || isResultError;
25122
+ const { exitCode, isExitError, isCallError } = toolCallOutcome(message, rawText);
25123
+ const isError = isCallError;
24739
25124
  const text = isError ? toolErrorDisplay(rawText, callRec.name || "tool") : rawText;
24740
25125
  callRec.summary = !isError ? summarizeToolResult(callRec.name, callRec.args, rawText, isError) : null;
24741
25126
  assignAggregateSummaryOrder(aggregate, callRec);
24742
25127
  callRec.isError = isError;
24743
25128
  callRec.isCallError = isCallError;
25129
+ callRec.isExitError = isExitError;
25130
+ callRec.exitCode = exitCode;
24744
25131
  callRec.resultText = text;
24745
25132
  callRec.completedEarly = true;
24746
25133
  const allCalls = [...aggregate.calls.values()];
24747
25134
  const completedCount = allCalls.filter((r) => r.resolved || r.completedEarly).length;
24748
25135
  const errors = allCalls.filter((r) => r.isError).length;
24749
25136
  const callErrors = allCalls.filter((r) => r.isCallError).length;
24750
- const succeeded = completedCount - errors;
25137
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
25138
+ const succeeded = Math.max(0, completedCount - errors - exitErrors);
24751
25139
  const rawResult = aggregateRawResult(allCalls);
24752
- const displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
24753
- const currentItem = getState().items.find((it) => it.id === card.itemId);
25140
+ const displayDetail = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
25141
+ const currentIndex = itemIndexById.get(card.itemId);
25142
+ const currentItem = Number.isInteger(currentIndex) && getState().items[currentIndex]?.id === card.itemId ? getState().items[currentIndex] : null;
24754
25143
  const visualCompleted = Math.max(
24755
25144
  completedCount,
24756
25145
  Math.min(allCalls.length, Number(currentItem?.completedCount || 0))
@@ -24761,6 +25150,7 @@ function createRunTurn(bag) {
24761
25150
  isError: errors > 0,
24762
25151
  errorCount: errors,
24763
25152
  callErrorCount: callErrors,
25153
+ exitErrorCount: exitErrors,
24764
25154
  count: allCalls.length,
24765
25155
  completedCount: visualCompleted
24766
25156
  };
@@ -24782,14 +25172,14 @@ function createRunTurn(bag) {
24782
25172
  try {
24783
25173
  const { result, session } = await runtime.ask(userText, {
24784
25174
  drainSteering: (_sessionId, drainOptions) => isCurrentTurn() ? drainPendingSteering(drainOptions) : [],
25175
+ onStreamDelta: () => {
25176
+ markTurnProgress("stream-delta");
25177
+ },
24785
25178
  onSteerMessage: (text) => {
24786
25179
  if (!markTurnProgress("steer-message")) return;
24787
25180
  if (text === STEERING_SUPPRESSED_DISPLAY) return;
24788
25181
  flushStreamBatch();
24789
- if (currentAssistantId) {
24790
- patchItem(currentAssistantId, { text: currentAssistantText || assistantText, streaming: false });
24791
- closeAssistantSegment();
24792
- }
25182
+ commitAssistantSegment({ sealToolBlock: true });
24793
25183
  assistantText = "";
24794
25184
  const value = String(text || "").trim();
24795
25185
  if (value) {
@@ -24810,6 +25200,7 @@ function createRunTurn(bag) {
24810
25200
  }
24811
25201
  const batchCalls = (calls || []).filter(Boolean);
24812
25202
  if (batchCalls.length === 0) return;
25203
+ const agentBatch = ++providerToolBatch;
24813
25204
  const committedAssistantSegment = commitAssistantSegment({ sealToolBlock: true });
24814
25205
  if (committedAssistantSegment) {
24815
25206
  await yieldToRenderer({ frames: 2 });
@@ -24822,7 +25213,7 @@ function createRunTurn(bag) {
24822
25213
  const name = toolCallName(c);
24823
25214
  const args = toolCallArgs(c);
24824
25215
  const category = classifyToolCategory(name, args);
24825
- const bucket = category === "Agent" ? null : aggregateBucketForCategory(category);
25216
+ const bucket = aggregateBucketForCategory(category, { agentBatch });
24826
25217
  const callId = toolCallId(c);
24827
25218
  const callKey = callId || `__tool_${toolCards.length}_${i}`;
24828
25219
  const activeCount = Number(aggregateToolCategoryEntry(name, args, category)?.count || 1);
@@ -24865,7 +25256,7 @@ function createRunTurn(bag) {
24865
25256
  ...categoryEntry,
24866
25257
  count: Number(prevCategory?.count || 0) + Number(categoryEntry.count || 1)
24867
25258
  });
24868
- aggregateCard.calls.set(callKey, { name, args, category, summary: null, summarySeq: null, isError: false, isCallError: false, resultText: null, resolved: false, completedEarly: false });
25259
+ aggregateCard.calls.set(callKey, { name, args, category, summary: null, summarySeq: null, isError: false, isCallError: false, isExitError: false, exitCode: null, resultText: null, resolved: false, completedEarly: false });
24869
25260
  touchedAggregates.add(aggregateCard);
24870
25261
  const card = { itemId: aggregateCard.itemId, callId: callKey, done: false, aggregate: aggregateCard };
24871
25262
  if (callId) {
@@ -25013,13 +25404,21 @@ function createRunTurn(bag) {
25013
25404
  flushStreamBatch();
25014
25405
  syncContextStats({ allowEstimated: true });
25015
25406
  const finalText = result?.content != null ? String(result.content) : "";
25016
- if (finalText.trim()) {
25017
- const id = currentAssistantId || ensureAssistant(finalText);
25018
- currentAssistantText = finalText;
25019
- patchItem(id, { text: finalText, streaming: false });
25407
+ let finalRemainder = finalText;
25408
+ for (const seg of committedSegments) {
25409
+ const skipped = finalRemainder.replace(/^\s+/, "");
25410
+ const trimmedSeg = seg ? seg.replace(/^\s+/, "") : "";
25411
+ if (trimmedSeg && skipped.startsWith(trimmedSeg)) {
25412
+ finalRemainder = skipped.slice(trimmedSeg.length);
25413
+ }
25414
+ }
25415
+ if (finalRemainder.trim()) {
25416
+ const id = currentAssistantId || ensureAssistant(finalRemainder);
25417
+ currentAssistantText = finalRemainder;
25418
+ settleStreamingTail(id, { text: finalRemainder });
25020
25419
  } else if (currentAssistantId && (currentAssistantText.trim() || assistantText.trim())) {
25021
25420
  const streamedText = currentAssistantText || assistantText;
25022
- patchItem(currentAssistantId, { text: streamedText, streaming: false });
25421
+ settleStreamingTail(currentAssistantId, { text: streamedText });
25023
25422
  }
25024
25423
  turnFinishedNormally = true;
25025
25424
  }
@@ -25032,7 +25431,7 @@ function createRunTurn(bag) {
25032
25431
  if (error?.name === "SessionClosedError") {
25033
25432
  cancelled = true;
25034
25433
  if (assistantText.trim() && currentAssistantId) {
25035
- patchItem(currentAssistantId, { text: currentAssistantText || assistantText, streaming: false });
25434
+ settleStreamingTail(currentAssistantId, { text: currentAssistantText || assistantText });
25036
25435
  }
25037
25436
  flushToolResults([], toolCards, cardByCallId, toolGroups, resultsDone, { finalize: true, cancelled: true });
25038
25437
  finalizeToolHeaders();
@@ -25058,6 +25457,13 @@ function createRunTurn(bag) {
25058
25457
  if (isStaleUnwind) {
25059
25458
  tuiDebug2(`runTurn STALE UNWIND turn=${turnIndex} \u2014 force-released; skipping shared UI/state writes`);
25060
25459
  } else {
25460
+ if (currentAssistantId && getState().streamingTail?.id === currentAssistantId) {
25461
+ if (currentAssistantText.trim()) {
25462
+ settleStreamingTail(currentAssistantId, { text: currentAssistantText });
25463
+ } else {
25464
+ clearStreamingTail(currentAssistantId);
25465
+ }
25466
+ }
25061
25467
  const producedTranscriptItem = getState().items.length + closingItems.length > itemsAtTurnStart;
25062
25468
  const reclaimed = cancelled && flags.activePromptRestore?.reclaimed === true;
25063
25469
  flags.activePromptRestore = null;
@@ -25794,6 +26200,8 @@ function createEngineApiA(bag) {
25794
26200
  pushItem,
25795
26201
  patchItem,
25796
26202
  replaceItems,
26203
+ settleStreamingTail,
26204
+ clearStreamingTail,
25797
26205
  pushNotice,
25798
26206
  autoClearState,
25799
26207
  agentStatusState,
@@ -25807,7 +26215,8 @@ function createEngineApiA(bag) {
25807
26215
  restoreQueued,
25808
26216
  resetStatsAndSyncContext,
25809
26217
  drain,
25810
- flushDeferredExecutionPendingResumeKick
26218
+ flushDeferredExecutionPendingResumeKick,
26219
+ discardExecutionPendingResume
25811
26220
  } = bag;
25812
26221
  return {
25813
26222
  getState: () => getState(),
@@ -26301,9 +26710,14 @@ function createEngineApiA(bag) {
26301
26710
  const restorePastedTexts = canRestore && restoreState?.pastedTexts ? restoreState.pastedTexts : null;
26302
26711
  const discardPastedImages = restoreState?.restorable && hasPendingSteering && restoreState?.pastedImages ? restoreState.pastedImages : null;
26303
26712
  const discardPastedTexts = restoreState?.restorable && hasPendingSteering && restoreState?.pastedTexts ? restoreState.pastedTexts : null;
26304
- const requeueEntries = restoreState && !restoreState.committed && Array.isArray(restoreState.requeueEntries) ? restoreState.requeueEntries.slice() : [];
26713
+ const requeueEntries = restoreState && !restoreState.committed && Array.isArray(restoreState.requeueEntries) ? restoreState.requeueEntries.filter(
26714
+ (entry) => entry?.abortDiscardOnAbort !== true && entry?.mode !== "pending-resume"
26715
+ ) : [];
26305
26716
  const aborted = runtime.abort("cli-react-abort");
26306
26717
  if (restoreState) {
26718
+ if (aborted !== false && Array.isArray(restoreState.discardExecutionPendingResumeKeys)) {
26719
+ discardExecutionPendingResume?.(restoreState.discardExecutionPendingResumeKeys);
26720
+ }
26307
26721
  if ((restoreText || requeueEntries.length > 0) && aborted !== false) {
26308
26722
  restoreState.reclaimed = true;
26309
26723
  const idSet = new Set((restoreState.submittedIds || []).filter((id) => id != null));
@@ -26319,6 +26733,7 @@ function createEngineApiA(bag) {
26319
26733
  }
26320
26734
  restoreState.restorable = false;
26321
26735
  restoreState.requeueEntries = [];
26736
+ restoreState.discardExecutionPendingResumeKeys = [];
26322
26737
  }
26323
26738
  const abortEpoch = flags.leadTurnEpoch;
26324
26739
  const recoveryMs = Number(flags.manualAbortRecoveryMs) > 0 ? Number(flags.manualAbortRecoveryMs) : MANUAL_ABORT_RECOVERY_MS;
@@ -26326,6 +26741,12 @@ function createEngineApiA(bag) {
26326
26741
  if (flags.disposed) return;
26327
26742
  if (!getState().busy) return;
26328
26743
  if (flags.leadTurnEpoch !== abortEpoch) return;
26744
+ const streamingTail = getState().streamingTail;
26745
+ if (streamingTail?.text?.trim()) {
26746
+ settleStreamingTail?.(streamingTail.id, {});
26747
+ } else {
26748
+ clearStreamingTail?.();
26749
+ }
26329
26750
  flags.leadTurnEpoch = (Number(flags.leadTurnEpoch) || 0) + 1;
26330
26751
  set({ busy: false, spinner: null, thinking: null, lastTurn: null });
26331
26752
  flags.activePromptRestore = null;
@@ -26362,6 +26783,79 @@ var tuiDebug = (msg) => {
26362
26783
  };
26363
26784
  var _idSeq = 0;
26364
26785
  var nextId = () => `it_${++_idSeq}`;
26786
+ function replaceEngineItemsState({
26787
+ state,
26788
+ items,
26789
+ itemIndexById,
26790
+ preserveStreamingTail = false,
26791
+ extra = {}
26792
+ }) {
26793
+ const nextItems = Array.isArray(items) ? items : [];
26794
+ itemIndexById.clear();
26795
+ for (let i = 0; i < nextItems.length; i++) {
26796
+ const id = nextItems[i]?.id;
26797
+ if (id != null) itemIndexById.set(id, i);
26798
+ }
26799
+ return {
26800
+ ...state,
26801
+ ...extra,
26802
+ items: nextItems,
26803
+ structureRevision: (Number(state.structureRevision) || 0) + 1,
26804
+ streamingTail: preserveStreamingTail ? state.streamingTail : null
26805
+ };
26806
+ }
26807
+ function createEngineItemMutators({ getState, set, itemIndexById }) {
26808
+ const patchItem = (id, patch) => {
26809
+ const state = getState();
26810
+ let index = itemIndexById.get(id);
26811
+ if (!Number.isInteger(index) || state.items[index]?.id !== id) {
26812
+ index = state.items.findIndex((it) => it.id === id);
26813
+ if (index >= 0) itemIndexById.set(id, index);
26814
+ }
26815
+ if (index < 0) return false;
26816
+ const current = state.items[index];
26817
+ let changed = false;
26818
+ for (const [key, value] of Object.entries(patch || {})) {
26819
+ if (!Object.is(current[key], value)) {
26820
+ changed = true;
26821
+ break;
26822
+ }
26823
+ }
26824
+ if (!changed) return false;
26825
+ const items = state.items.slice();
26826
+ items[index] = { ...current, ...patch };
26827
+ set({ items, structureRevision: (Number(state.structureRevision) || 0) + 1 });
26828
+ return true;
26829
+ };
26830
+ const settleStreamingTail = (id, patch = {}, extra = {}) => {
26831
+ const state = getState();
26832
+ const tail = state.streamingTail?.id === id ? state.streamingTail : null;
26833
+ if (!tail) return false;
26834
+ let existingIndex = itemIndexById.get(id);
26835
+ if (!Number.isInteger(existingIndex) || state.items[existingIndex]?.id !== id) {
26836
+ existingIndex = state.items.findIndex((item2) => item2?.id === id);
26837
+ }
26838
+ if (existingIndex >= 0) return false;
26839
+ const item = {
26840
+ ...tail || {},
26841
+ ...patch,
26842
+ kind: "assistant",
26843
+ id,
26844
+ streaming: false
26845
+ };
26846
+ const index = state.items.length;
26847
+ const items = [...state.items, item];
26848
+ itemIndexById.set(id, index);
26849
+ set({
26850
+ items,
26851
+ structureRevision: (Number(state.structureRevision) || 0) + 1,
26852
+ streamingTail: null,
26853
+ ...extra
26854
+ });
26855
+ return true;
26856
+ };
26857
+ return { patchItem, settleStreamingTail };
26858
+ }
26365
26859
  async function createEngineSession({
26366
26860
  provider: providerName,
26367
26861
  model,
@@ -26416,6 +26910,8 @@ async function createEngineSession({
26416
26910
  };
26417
26911
  let state = {
26418
26912
  items: [],
26913
+ structureRevision: 0,
26914
+ streamingTail: null,
26419
26915
  toasts: [],
26420
26916
  progressHint: null,
26421
26917
  busy: false,
@@ -26481,20 +26977,20 @@ async function createEngineSession({
26481
26977
  return true;
26482
26978
  };
26483
26979
  const itemIndexById = /* @__PURE__ */ new Map();
26484
- const replaceItems = (items) => {
26980
+ const replaceItems = (items, { preserveStreamingTail = false } = {}) => {
26485
26981
  const nextItems = Array.isArray(items) ? items : [];
26486
- itemIndexById.clear();
26487
- for (let i = 0; i < nextItems.length; i++) {
26488
- const id = nextItems[i]?.id;
26489
- if (id != null) itemIndexById.set(id, i);
26490
- }
26491
26982
  activeToolCalls.clear();
26492
- state = {
26493
- ...state,
26983
+ state = replaceEngineItemsState({
26984
+ state,
26494
26985
  items: nextItems,
26495
- promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
26496
- activeToolSummary: null
26497
- };
26986
+ itemIndexById,
26987
+ preserveStreamingTail,
26988
+ extra: {
26989
+ promptHistoryList: buildMergedPromptHistory(recomputePromptHistory(nextItems), loadPromptHistory(state.cwd)),
26990
+ activeToolSummary: null
26991
+ }
26992
+ });
26993
+ emit();
26498
26994
  return nextItems;
26499
26995
  };
26500
26996
  const activeToolCalls = /* @__PURE__ */ new Map();
@@ -26540,11 +27036,37 @@ async function createEngineSession({
26540
27036
  if (item?.id != null) itemIndexById.set(item.id, index);
26541
27037
  if (item?.kind === "user") {
26542
27038
  const promptHistoryList = buildMergedPromptHistory(recomputePromptHistory(items), loadPromptHistory(state.cwd));
26543
- set({ items, promptHistoryList });
27039
+ set({ items, structureRevision: state.structureRevision + 1, promptHistoryList });
26544
27040
  } else {
26545
- set({ items });
27041
+ set({ items, structureRevision: state.structureRevision + 1 });
26546
27042
  }
26547
27043
  };
27044
+ const updateStreamingTail = (id, patch = {}, extra = {}) => {
27045
+ if (id == null) return false;
27046
+ const current = state.streamingTail?.id === id ? state.streamingTail : { kind: "assistant", id, text: "", streaming: true };
27047
+ const next = { ...current, ...patch, kind: "assistant", id, streaming: true };
27048
+ let changed = state.streamingTail !== current;
27049
+ if (!changed) {
27050
+ for (const [key, value] of Object.entries(next)) {
27051
+ if (!Object.is(current[key], value)) {
27052
+ changed = true;
27053
+ break;
27054
+ }
27055
+ }
27056
+ }
27057
+ return set(changed ? { streamingTail: next, ...extra } : extra);
27058
+ };
27059
+ const clearStreamingTail = (id = null, extra = {}) => {
27060
+ if (!state.streamingTail || id != null && state.streamingTail.id !== id) {
27061
+ return set(extra);
27062
+ }
27063
+ return set({ streamingTail: null, ...extra });
27064
+ };
27065
+ const { patchItem, settleStreamingTail } = createEngineItemMutators({
27066
+ getState: () => state,
27067
+ set,
27068
+ itemIndexById
27069
+ });
26548
27070
  const upsertSyntheticToolItem = (text, id = nextId(), parsed = null) => {
26549
27071
  const synthetic = parseSyntheticAgentMessage(text);
26550
27072
  if (!synthetic) return false;
@@ -26579,6 +27101,59 @@ async function createEngineSession({
26579
27101
  if (origin === "user") appendPromptHistory(state.cwd, text);
26580
27102
  pushItem({ kind: "user", id, text });
26581
27103
  };
27104
+ const pushAsyncAgentResponse = (text, id = nextId(), origin = "injected", metadata = {}) => {
27105
+ const synthetic = parseSyntheticAgentMessage(text);
27106
+ const isAgent = synthetic?.name === "agent";
27107
+ if (!isAgent) return pushUserOrSyntheticItem(text, id, origin);
27108
+ const responseHasBody = /\n\s*\n[\s\S]*\S/.test(String(text || ""));
27109
+ const rawResult = synthetic.rawResult ?? text;
27110
+ const args = {
27111
+ ...synthetic.args && typeof synthetic.args === "object" ? synthetic.args : {},
27112
+ type: "result"
27113
+ };
27114
+ const responseKey = String(metadata.responseKey || metadata.executionId || args.task_id || "").trim();
27115
+ const previous = state.items.at(-1);
27116
+ if (previous?.kind === "tool" && previous.agentDirection === "inbound") {
27117
+ const patch = appendAgentResponseTail(previous, {
27118
+ key: responseKey,
27119
+ args,
27120
+ result: synthetic.result,
27121
+ rawResult,
27122
+ hasBody: responseHasBody,
27123
+ isError: synthetic.isError === true
27124
+ });
27125
+ if (patch) {
27126
+ patchItem(previous.id, patch);
27127
+ return true;
27128
+ }
27129
+ }
27130
+ pushItem({
27131
+ kind: "tool",
27132
+ id,
27133
+ name: "agent",
27134
+ args,
27135
+ result: synthetic.result,
27136
+ rawResult,
27137
+ isError: synthetic.isError === true,
27138
+ expanded: false,
27139
+ count: 1,
27140
+ completedCount: 1,
27141
+ startedAt: Date.now(),
27142
+ completedAt: Date.now(),
27143
+ agentDirection: "inbound",
27144
+ agentResponseKey: responseKey,
27145
+ agentResponseHasBody: responseHasBody,
27146
+ agentResponseAggregate: false,
27147
+ agentResponseEntries: [{
27148
+ key: responseKey,
27149
+ raw: String(rawResult ?? "").trim(),
27150
+ result: synthetic.result,
27151
+ hasBody: responseHasBody,
27152
+ isError: synthetic.isError === true
27153
+ }]
27154
+ });
27155
+ return true;
27156
+ };
26582
27157
  const pushToast = (text, tone = "info", ttlMs = 3e3) => {
26583
27158
  const id = nextId();
26584
27159
  const value = String(text ?? "").trim();
@@ -26606,7 +27181,7 @@ async function createEngineSession({
26606
27181
  if (id == null) return false;
26607
27182
  const items = state.items.filter((it) => !(it?.kind === "notice" && it?.id === id));
26608
27183
  if (items.length === state.items.length) return false;
26609
- set({ items: replaceItems(items) });
27184
+ set({ items: replaceItems(items, { preserveStreamingTail: true }) });
26610
27185
  return true;
26611
27186
  };
26612
27187
  const setProgressHint = (text, tone = "info") => {
@@ -26625,27 +27200,6 @@ async function createEngineSession({
26625
27200
  getDisposed: () => flags.disposed,
26626
27201
  timeoutMs: TOOL_APPROVAL_TIMEOUT_MS
26627
27202
  });
26628
- const patchItem = (id, patch) => {
26629
- let index = itemIndexById.get(id);
26630
- if (!Number.isInteger(index) || state.items[index]?.id !== id) {
26631
- index = state.items.findIndex((it) => it.id === id);
26632
- if (index >= 0) itemIndexById.set(id, index);
26633
- }
26634
- if (index < 0) return false;
26635
- const current = state.items[index];
26636
- let changed = false;
26637
- for (const [key, value] of Object.entries(patch || {})) {
26638
- if (!Object.is(current[key], value)) {
26639
- changed = true;
26640
- break;
26641
- }
26642
- }
26643
- if (!changed) return false;
26644
- const items = state.items.slice();
26645
- items[index] = { ...current, ...patch };
26646
- set({ items });
26647
- return true;
26648
- };
26649
27203
  const toastTimers = /* @__PURE__ */ new Set();
26650
27204
  lifecycle.runtimePulseTimer = setInterval(() => {
26651
27205
  if (flags.disposed) return;
@@ -26668,9 +27222,11 @@ async function createEngineSession({
26668
27222
  kickExecutionPendingResume,
26669
27223
  flushDeferredExecutionPendingResumeKick,
26670
27224
  scheduleExecutionPendingResumeKick,
27225
+ discardExecutionPendingResume,
26671
27226
  updateAgentJobCard,
26672
27227
  buildAgentJobCardPatch,
26673
- subscribeRuntimeNotifications
27228
+ subscribeRuntimeNotifications,
27229
+ clearExecutionDedupState
26674
27230
  } = createAgentJobFeed({
26675
27231
  runtime,
26676
27232
  getState: () => state,
@@ -26681,11 +27237,13 @@ async function createEngineSession({
26681
27237
  enqueue: (...args) => bag.enqueue(...args),
26682
27238
  drain: (...args) => bag.drain(...args),
26683
27239
  pushUserOrSyntheticItem,
27240
+ pushAsyncAgentResponse,
26684
27241
  makeQueueEntry: (...args) => bag.makeQueueEntry(...args),
26685
27242
  getPending: () => pending,
26686
27243
  agentStatusState,
26687
27244
  displayedExecutionNotificationKeys,
26688
- pushNotice
27245
+ pushNotice,
27246
+ itemIndexById
26689
27247
  });
26690
27248
  lifecycle.unsubscribeRuntimeNotifications = subscribeRuntimeNotifications();
26691
27249
  if (typeof runtime.onRemoteStateChange === "function") {
@@ -26704,7 +27262,8 @@ async function createEngineSession({
26704
27262
  markToolCallDone,
26705
27263
  updateAgentJobCard,
26706
27264
  buildAgentJobCardPatch,
26707
- agentStatusState
27265
+ agentStatusState,
27266
+ itemIndexById
26708
27267
  });
26709
27268
  Object.assign(bag, {
26710
27269
  runtime,
@@ -26716,6 +27275,7 @@ async function createEngineSession({
26716
27275
  pending,
26717
27276
  pendingNotificationKeys,
26718
27277
  displayedExecutionNotificationKeys,
27278
+ clearExecutionDedupState,
26719
27279
  listeners,
26720
27280
  itemIndexById,
26721
27281
  getState: () => state,
@@ -26723,11 +27283,15 @@ async function createEngineSession({
26723
27283
  pushItem,
26724
27284
  patchItem,
26725
27285
  replaceItems,
27286
+ updateStreamingTail,
27287
+ settleStreamingTail,
27288
+ clearStreamingTail,
26726
27289
  pushToast,
26727
27290
  pushNotice,
26728
27291
  removeNotice,
26729
27292
  setProgressHint,
26730
27293
  pushUserOrSyntheticItem,
27294
+ pushAsyncAgentResponse,
26731
27295
  upsertSyntheticToolItem,
26732
27296
  markToolCallActive,
26733
27297
  markToolCallDone,
@@ -26744,9 +27308,11 @@ async function createEngineSession({
26744
27308
  requestToolApproval,
26745
27309
  patchToolCardResult,
26746
27310
  flushToolResults,
27311
+ clearExecutionDedupState,
26747
27312
  kickExecutionPendingResume,
26748
27313
  flushDeferredExecutionPendingResumeKick,
26749
27314
  scheduleExecutionPendingResumeKick,
27315
+ discardExecutionPendingResume,
26750
27316
  updateAgentJobCard,
26751
27317
  subscribeRuntimeNotifications
26752
27318
  });
@@ -27136,7 +27702,7 @@ function paintBootSplash() {
27136
27702
  const rows = Math.max(1, Number(process.stdout.rows) || 24);
27137
27703
  const windowsLikeTerminal = process.platform === "win32" || Boolean(process.env.WT_SESSION);
27138
27704
  const frameCols = Math.max(1, cols - (windowsLikeTerminal ? 1 : 0));
27139
- const center = (s) => `${" ".repeat(Math.max(0, Math.floor((frameCols - displayWidth(s)) / 2)))}${s}`;
27705
+ const center = (s, reserve = 0) => `${" ".repeat(Math.max(0, Math.floor((frameCols - reserve - displayWidth(s)) / 2)))}${s}`;
27140
27706
  const logo = [
27141
27707
  "\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ",
27142
27708
  "\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D ",
@@ -27156,7 +27722,7 @@ function paintBootSplash() {
27156
27722
  `;
27157
27723
  }
27158
27724
  out += "\r\n";
27159
- out += `${subtleFg}${center(`mixdog coding agent \xB7 v${localPackageVersion()} \xB7 ${process.cwd()}`)}${reset}`;
27725
+ out += `${subtleFg}${center(`mixdog coding agent \xB7 v${localPackageVersion()} \xB7 ${process.cwd()}`, 4)}${reset}`;
27160
27726
  out += "\x1B[H";
27161
27727
  process.stdout.write(out);
27162
27728
  return { stop: () => {