mixdog 0.9.38 → 0.9.39

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 (114) 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 +174 -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 +179 -0
  25. package/scripts/tool-smoke.mjs +21 -13
  26. package/scripts/tool-tui-presentation-test.mjs +202 -0
  27. package/src/agents/heavy-worker/AGENT.md +10 -7
  28. package/src/agents/reviewer/AGENT.md +6 -4
  29. package/src/agents/worker/AGENT.md +7 -5
  30. package/src/rules/agent/00-common.md +4 -4
  31. package/src/rules/agent/00-core.md +11 -14
  32. package/src/rules/agent/20-skip-protocol.md +3 -3
  33. package/src/rules/agent/30-explorer.md +50 -60
  34. package/src/rules/agent/40-cycle1-agent.md +15 -24
  35. package/src/rules/agent/41-cycle2-agent.md +33 -57
  36. package/src/rules/agent/42-cycle3-agent.md +28 -42
  37. package/src/rules/lead/01-general.md +7 -10
  38. package/src/rules/lead/lead-brief.md +11 -14
  39. package/src/rules/lead/lead-tool.md +6 -5
  40. package/src/rules/shared/01-tool.md +44 -45
  41. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +37 -1
  42. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +20 -5
  43. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +21 -1
  44. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +17 -38
  45. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +17 -8
  46. package/src/runtime/agent/orchestrator/session/context-utils.mjs +270 -78
  47. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -1
  48. package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +123 -0
  49. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +15 -2
  50. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +41 -2
  51. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
  52. package/src/runtime/agent/orchestrator/session/store.mjs +223 -42
  53. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -1
  54. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -20
  55. package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +6 -2
  56. package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +118 -53
  57. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +106 -29
  58. package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +8 -1
  59. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +4 -2
  60. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +3 -1
  61. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +4 -1
  62. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +33 -2
  63. package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +151 -64
  64. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +37 -13
  65. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +84 -49
  66. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +172 -23
  67. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +3 -0
  68. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +68 -5
  69. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +17 -3
  70. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +24 -10
  71. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +6 -3
  72. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -7
  73. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -0
  74. package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
  75. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -4
  76. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +4 -3
  77. package/src/runtime/memory/lib/memory-cycle3.mjs +12 -2
  78. package/src/runtime/shared/tool-primitives.mjs +4 -1
  79. package/src/runtime/shared/tool-status.mjs +27 -0
  80. package/src/runtime/shared/tool-surface.mjs +6 -3
  81. package/src/session-runtime/config-helpers.mjs +14 -0
  82. package/src/session-runtime/context-status.mjs +1 -0
  83. package/src/session-runtime/effort.mjs +6 -2
  84. package/src/session-runtime/model-recency.mjs +5 -2
  85. package/src/session-runtime/runtime-core.mjs +35 -2
  86. package/src/session-runtime/tool-catalog.mjs +34 -0
  87. package/src/standalone/agent-tool/notify.mjs +13 -0
  88. package/src/standalone/agent-tool.mjs +45 -69
  89. package/src/standalone/explore-tool.mjs +6 -7
  90. package/src/tui/App.jsx +31 -0
  91. package/src/tui/app/model-options.mjs +5 -3
  92. package/src/tui/app/model-picker.mjs +12 -24
  93. package/src/tui/app/transcript-window.mjs +1 -0
  94. package/src/tui/components/ToolExecution.jsx +11 -6
  95. package/src/tui/components/TranscriptItem.jsx +1 -1
  96. package/src/tui/components/tool-execution/surface-detail.mjs +24 -10
  97. package/src/tui/components/tool-execution/text-format.mjs +10 -19
  98. package/src/tui/dist/index.mjs +517 -142
  99. package/src/tui/engine/agent-job-feed.mjs +144 -17
  100. package/src/tui/engine/agent-response-tail.mjs +68 -0
  101. package/src/tui/engine/notification-plan.mjs +16 -0
  102. package/src/tui/engine/session-api.mjs +8 -2
  103. package/src/tui/engine/session-flow.mjs +19 -1
  104. package/src/tui/engine/tool-card-results.mjs +54 -32
  105. package/src/tui/engine/tool-result-status.mjs +75 -21
  106. package/src/tui/engine/turn.mjs +77 -42
  107. package/src/tui/engine.mjs +63 -2
  108. package/src/workflows/bench/WORKFLOW.md +25 -35
  109. package/src/workflows/default/WORKFLOW.md +38 -32
  110. package/src/workflows/solo/WORKFLOW.md +19 -22
  111. package/scripts/_jitter-fuzz.mjs +0 -44410
  112. package/scripts/_jitter-fuzz2.mjs +0 -44400
  113. package/scripts/_jitter-probe.mjs +0 -44397
  114. 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 || "");
@@ -12383,10 +12387,11 @@ var compareModelRecency = (a, b) => {
12383
12387
  }
12384
12388
  const ta = releaseTime(a);
12385
12389
  const tb = releaseTime(b);
12386
- if (ta !== tb) return tb - ta;
12387
- if (!!a?.latest !== !!b?.latest) return a?.latest ? -1 : 1;
12388
12390
  const versionDelta = compareModelVersion(a, b);
12391
+ if (ta > 0 && tb > 0 && ta !== tb) return tb - ta;
12389
12392
  if (versionDelta) return versionDelta;
12393
+ if (!!a?.latest !== !!b?.latest) return a?.latest ? -1 : 1;
12394
+ if (ta !== tb) return tb - ta;
12390
12395
  return String(a?.display || a?.id || "").localeCompare(String(b?.display || b?.id || ""));
12391
12396
  };
12392
12397
  var modelFamily = (m) => {
@@ -15835,11 +15840,9 @@ function createModelPicker({
15835
15840
  setChannelPrompt(null);
15836
15841
  setHookPrompt(null);
15837
15842
  setSettingsPrompt(null);
15838
- const modelPickerRequest = ++modelPickerRequestRef.current;
15843
+ modelPickerRequestRef.current += 1;
15839
15844
  let modelPickerClosed = false;
15840
- let activeModelProvider = null;
15841
15845
  let providerListHighlightProvider = null;
15842
- const isActiveModelPicker = () => !modelPickerClosed && modelPickerRequestRef.current === modelPickerRequest;
15843
15846
  const returnTo = typeof options.returnTo === "function" ? options.returnTo : null;
15844
15847
  const returnLabel = String(options.returnLabel || "Agents");
15845
15848
  const returnOnNestedCancel = options.returnOnNestedCancel === true;
@@ -15853,7 +15856,6 @@ function createModelPicker({
15853
15856
  let providerModels = Array.isArray(cacheRef.current.models) ? cacheRef.current.models : [];
15854
15857
  let refreshModelsPromise = null;
15855
15858
  let renderedQuickModels = false;
15856
- let renderActiveProviderModels = null;
15857
15859
  if (!providerModels.length || options.refreshModels === true) {
15858
15860
  setPicker({
15859
15861
  title: options.title || "Model",
@@ -15904,16 +15906,12 @@ function createModelPicker({
15904
15906
  providerListHighlightProvider = renderOptions.highlightProvider;
15905
15907
  }
15906
15908
  const highlightProvider = renderOptions.highlightProvider || providerListHighlightProvider || null;
15907
- activeModelProvider = null;
15908
- renderActiveProviderModels = null;
15909
15909
  const openProviderModelsPicker = (provider) => {
15910
15910
  if (!provider) return;
15911
- activeModelProvider = provider;
15912
- renderActiveProviderModels = () => openProviderModelsPicker(provider);
15913
15911
  const providerModels2 = models.filter((model) => model.provider === provider);
15914
15912
  const preferredEffort = (values = []) => {
15915
15913
  const allowed = values.filter(Boolean);
15916
- for (const value of ["high", "medium", "low", "none", "xhigh", "max"]) {
15914
+ for (const value of ["high", "medium", "low", "none", "xhigh", "max", "ultra"]) {
15917
15915
  if (allowed.includes(value)) return value;
15918
15916
  }
15919
15917
  return allowed[0] || null;
@@ -15986,6 +15984,7 @@ ${model?.id || ""}`;
15986
15984
  if (value === "medium") return "\u25D1";
15987
15985
  if (value === "high") return "\u25D5";
15988
15986
  if (value === "max") return "\u25C6";
15987
+ if (value === "ultra") return "\u2726";
15989
15988
  return "\u25CF";
15990
15989
  };
15991
15990
  const effortColor = (value) => {
@@ -15994,6 +15993,7 @@ ${model?.id || ""}`;
15994
15993
  if (value === "medium") return theme.claude;
15995
15994
  if (value === "high") return theme.error;
15996
15995
  if (value === "max") return theme.permission;
15996
+ if (value === "ultra") return theme.permission;
15997
15997
  return theme.error;
15998
15998
  };
15999
15999
  const modelFooter = (model = null) => {
@@ -16128,33 +16128,23 @@ ${model?.id || ""}`;
16128
16128
  });
16129
16129
  };
16130
16130
  renderModelPicker();
16131
- const applyFreshModels = (freshModels) => {
16132
- if (!isActiveModelPicker()) return;
16131
+ const adoptFreshModels = (freshModels) => {
16133
16132
  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
- }
16133
+ cacheRef.current = { models: freshModels, at: Date.now() };
16142
16134
  };
16143
16135
  if (renderedQuickModels && refreshModelsPromise) {
16144
- void refreshModelsPromise.then(applyFreshModels).catch(() => {
16136
+ void refreshModelsPromise.then(adoptFreshModels).catch(() => {
16145
16137
  });
16146
16138
  } else if (cacheIsStale) {
16147
16139
  if (!providerModelsTtlRefreshPromise) {
16148
16140
  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
- }
16141
+ adoptFreshModels(freshModels);
16152
16142
  return freshModels;
16153
16143
  }).finally(() => {
16154
16144
  providerModelsTtlRefreshPromise = null;
16155
16145
  });
16156
16146
  }
16157
- void providerModelsTtlRefreshPromise.then(applyFreshModels).catch(() => {
16147
+ void providerModelsTtlRefreshPromise.catch(() => {
16158
16148
  });
16159
16149
  }
16160
16150
  };
@@ -18531,6 +18521,29 @@ import stringWidth9 from "string-width";
18531
18521
 
18532
18522
  // src/tui/components/tool-execution/text-format.mjs
18533
18523
  import stripAnsi6 from "strip-ansi";
18524
+
18525
+ // src/runtime/shared/tool-status.mjs
18526
+ function normalizeToolTerminalStatus(value) {
18527
+ const raw = String(value || "").trim().toLowerCase();
18528
+ if (!raw) return "";
18529
+ if (/^(running|pending|queued|in_progress|in-progress)$/.test(raw)) return "running";
18530
+ if (/^(completed|complete|done|success|succeeded|ok)$/.test(raw)) return "completed";
18531
+ if (/^(failed|fail|error|errored|timeout|timed_out|killed)$/.test(raw)) return "failed";
18532
+ if (/^(cancelled|canceled|cancel)$/.test(raw)) return "cancelled";
18533
+ if (/^(denied|deny|refused|rejected)$/.test(raw)) return "denied";
18534
+ return "";
18535
+ }
18536
+ function toolResultTerminalStatus(text) {
18537
+ const body = String(text || "");
18538
+ const tagged = body.match(/<status[^>]*>([\s\S]*?)<\/status>/i)?.[1]?.trim();
18539
+ if (tagged) return normalizeToolTerminalStatus(tagged);
18540
+ const bracketed = body.match(/^\[status:\s*([^\]]*)\]/mi)?.[1]?.trim();
18541
+ if (bracketed) return normalizeToolTerminalStatus(bracketed);
18542
+ const inline = body.match(/^(?:status|state):\s*([^\s·,;]+)/mi)?.[1]?.trim();
18543
+ return normalizeToolTerminalStatus(inline);
18544
+ }
18545
+
18546
+ // src/tui/components/tool-execution/text-format.mjs
18534
18547
  var MIN_RESULT_LINE_CHARS = 24;
18535
18548
  var RESULT_LINE_HARD_MAX = 80;
18536
18549
  var SUMMARY_MAX_CHARS = 48;
@@ -18600,30 +18613,20 @@ function shellResultStatus(value) {
18600
18613
  return match ? String(match[1] || "").toLowerCase() : "";
18601
18614
  }
18602
18615
  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 "";
18616
+ return normalizeToolTerminalStatus(value);
18610
18617
  }
18611
18618
  function displayTerminalStatus(value) {
18619
+ if (String(value || "").trim().toLowerCase() === "exit") return "Exit";
18612
18620
  const status = normalizeTerminalStatus(value);
18613
18621
  if (status === "running") return "Running";
18614
18622
  if (status === "completed") return "Finished";
18615
18623
  if (status === "failed") return "Failed";
18616
18624
  if (status === "cancelled") return "Cancelled";
18625
+ if (status === "denied") return "Denied";
18617
18626
  return "";
18618
18627
  }
18619
18628
  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);
18629
+ return toolResultTerminalStatus(value);
18627
18630
  }
18628
18631
  var LEADING_STATUS_MARKER_LINE_RE2 = /^\[status:\s*[^\]]*\]\s*$/i;
18629
18632
  function stripLeadingStatusMarkerLines(lines) {
@@ -18647,11 +18650,15 @@ function isShellTool(normalizedName, label = "") {
18647
18650
  const l = String(label || "").toLowerCase();
18648
18651
  return n === "shell" || n === "bash" || n === "bash_session" || n === "shell_command" || n === "job_wait" || l === "run";
18649
18652
  }
18650
- function shellDisplayStatus({ pending = false, failedCount = 0, isError = false, result = "" } = {}) {
18653
+ function shellDisplayStatus({ pending = false, failedCount = 0, exitFailedCount = 0, isError = false, result = "" } = {}) {
18651
18654
  const status = shellResultStatus(result);
18652
18655
  if (pending || /^(running|pending|queued)$/.test(status)) return "running";
18653
18656
  if (/^cancel/.test(status)) return "cancelled";
18654
- if (/^(failed|error|killed|timeout)$/.test(status) || isError || failedCount > 0) return "failed";
18657
+ if (/^(failed|error|killed|timeout)$/.test(status)) return "failed";
18658
+ const realFailed = Math.max(0, Number(failedCount) - Number(exitFailedCount));
18659
+ if (realFailed > 0) return "failed";
18660
+ if (Number(exitFailedCount) > 0) return "exit";
18661
+ if (isError || failedCount > 0) return "failed";
18655
18662
  return "completed";
18656
18663
  }
18657
18664
  function shellHeader(status, count = 1) {
@@ -18707,7 +18714,9 @@ function withModelAndTag(label, args) {
18707
18714
  function joinActionAgent(action, agent) {
18708
18715
  return agent ? `${action} ${agent}` : action;
18709
18716
  }
18710
- function agentResponseTitle(args) {
18717
+ function agentResponseTitle(args, count = 1) {
18718
+ const total = Math.max(1, Number(count) || 1);
18719
+ if (total > 1) return `Responses ${total} agents`;
18711
18720
  const name = titleizeAgentName(args?.agent || args?.subagent_type || args?.name || "");
18712
18721
  return withModelAndTag(joinActionAgent("Response", name), args);
18713
18722
  }
@@ -18962,13 +18971,17 @@ function clampFailureCount(errorCount, groupCount, isError) {
18962
18971
  if (Number.isFinite(explicit)) return Math.max(0, Math.min(groupCount, Math.floor(explicit)));
18963
18972
  return isError ? groupCount : 0;
18964
18973
  }
18965
- function toolStatusColor({ pending, groupCount, callFailedCount = 0, terminalStatus = "" }) {
18974
+ function toolStatusColor({ pending, groupCount, callFailedCount = 0, exitFailedCount = 0, terminalStatus = "" }) {
18966
18975
  if (pending) return theme.text;
18967
18976
  const status = normalizeTerminalStatus(terminalStatus);
18968
18977
  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;
18978
+ if (status === "denied") return theme.warning;
18979
+ if (callFailedCount > 0) {
18980
+ if (groupCount > 1 && callFailedCount < groupCount) return theme.mixdogOrange || theme.warning;
18981
+ return theme.error;
18982
+ }
18983
+ if (exitFailedCount > 0) return theme.warning;
18984
+ return theme.success;
18972
18985
  }
18973
18986
 
18974
18987
  // src/tui/components/tool-execution/ResultBody.jsx
@@ -18999,7 +19012,7 @@ var TOOL_ANIM_TICK_MS = TOOL_BLINK_MS;
18999
19012
  function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
19000
19013
  return formatToolActionHeader(name, args, { pending, count });
19001
19014
  }
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 }) {
19015
+ 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
19016
  const rowWidth = Math.max(1, Number(columns || 80));
19004
19017
  const groupCount = Math.max(1, Number(count || 1));
19005
19018
  const doneCount = Math.max(0, Math.min(groupCount, Number(completedCount || (result == null ? 0 : groupCount))));
@@ -19023,6 +19036,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19023
19036
  const elapsed = elapsedMs >= 1e3 ? formatElapsed(elapsedMs) : "";
19024
19037
  const failedCount = clampFailureCount(errorCount, groupCount, isError);
19025
19038
  const callFailedCount = clampFailureCount(callErrorCount, groupCount, false);
19039
+ const exitFailedCount = clampFailureCount(exitErrorCount, groupCount, false);
19026
19040
  const displayGroupCount = groupCount;
19027
19041
  const displayCategories = normalizeCountMap(categories || {});
19028
19042
  const normalizedDoneCategories = doneCategories ? normalizeCountMap(doneCategories) : displayCategories;
@@ -19048,7 +19062,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19048
19062
  detailText = "";
19049
19063
  }
19050
19064
  const aggregateTerminalStatus = pending ? "running" : resultTerminalStatus(rt) || (isError || failedCount > 0 ? "failed" : "completed");
19051
- const dotColor2 = toolStatusColor({ pending, groupCount, callFailedCount, terminalStatus: aggregateTerminalStatus });
19065
+ const dotColor2 = toolStatusColor({ pending, groupCount, callFailedCount, exitFailedCount, terminalStatus: aggregateTerminalStatus });
19052
19066
  const dotText2 = pending && !blinkOn ? " " : TURN_MARKER;
19053
19067
  const gutter2 = 2;
19054
19068
  const showHeaderExpandHint2 = hasRawResult;
@@ -19103,7 +19117,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19103
19117
  const firstResultLine = hasDisplayResult ? String(lines[0] ?? "") : "";
19104
19118
  const firstResultLineClipped = hasDisplayBody && stringWidth9(firstResultLine) > maxResultChars;
19105
19119
  const hasHiddenDetail = !pending && hasDisplayBody && (totalLines > 1 || firstResultLineClipped || Boolean(resultSummary));
19106
- const shellStatus = isShellSurface ? shellDisplayStatus({ pending, failedCount, isError, result: displayedResultText }) : "";
19120
+ const shellStatus = isShellSurface ? shellDisplayStatus({ pending, failedCount, exitFailedCount, isError, result: displayedResultText }) : "";
19107
19121
  const shellElapsed = isShellSurface ? shellResultElapsed(displayedResultText) || elapsed : "";
19108
19122
  const backgroundElapsed = backgroundMeta ? backgroundTaskElapsed(backgroundMeta, elapsed) : isBackgroundTaskTool(normalizedName) ? backgroundTaskElapsed(parsedArgs, elapsed) : "";
19109
19123
  const toolArgPath = parsedArgs?.path ?? parsedArgs?.file_path ?? parsedArgs?.file ?? "";
@@ -19128,7 +19142,7 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19128
19142
  const collapsedDetail = pending ? pendingDetailPlaceholder : isShellSurface ? prefixElapsed(mergeTerminalDetail(shellStatus, shellCollapsedSummary), shellElapsed) : mergeTerminalDetail(terminalStatus, nonShellDetail);
19129
19143
  const backgroundMetadataExpandable = isBackgroundMetadataResult && hasRawResult && !pending;
19130
19144
  const showRawResult = expanded && (hasDisplayBody || hasRawResult) && (!isBackgroundMetadataResult || hasRawResult);
19131
- const detailLines = showRawResult ? hasDisplayBody ? lines : rawRt ? stripLeadingStatusMarkerLines(rawRt.split("\n")) : [] : collapsedDetail ? [collapsedDetail] : [];
19145
+ const detailLines = showRawResult ? agentResponseAggregate && hasRawResult ? stripLeadingStatusMarkerLines(rawRt.split("\n")) : hasDisplayBody ? lines : rawRt ? stripLeadingStatusMarkerLines(rawRt.split("\n")) : [] : collapsedDetail ? [collapsedDetail] : [];
19132
19146
  const isPendingPlaceholderDetail = !showRawResult && Boolean(pendingDetailPlaceholder);
19133
19147
  const detailColor = isPendingPlaceholderDetail ? theme.subtle : theme.text;
19134
19148
  const isAgentResult = !isBackgroundResult && !pending && isAgentTool(normalizedName) && hasDisplayResult;
@@ -19148,14 +19162,14 @@ function ToolExecution({ name, args, result, rawResult, isError, errorCount, cal
19148
19162
  const keepAgentDetail = (isError || agentFailureText) && !(agentHeaderFailure && !agentSurfaceBrief);
19149
19163
  visibleDetailLines = keepAgentDetail ? [agentDetailLine] : [];
19150
19164
  }
19151
- const finalStatusColor = toolStatusColor({ pending, groupCount, callFailedCount, terminalStatus });
19165
+ const finalStatusColor = toolStatusColor({ pending, groupCount, callFailedCount, exitFailedCount, terminalStatus });
19152
19166
  const dotColor = finalStatusColor;
19153
19167
  const markerGlyph = isAgentResponse ? AGENT_RESPONSE_MARKER : isAgentSurfaceCard ? AGENT_CALL_MARKER : TURN_MARKER;
19154
19168
  const isDirectionalMarker = isAgentResponse || isAgentSurfaceCard;
19155
19169
  const markerText = isDirectionalMarker ? `${markerGlyph} ` : markerGlyph;
19156
19170
  const dotText = pending && !blinkOn ? " " : markerText;
19157
19171
  let labelText;
19158
- if (isAgentResponse) labelText = agentResponseTitle(parsedArgs);
19172
+ if (isAgentResponse) labelText = agentResponseTitle(parsedArgs, displayGroupCount);
19159
19173
  else if (isBackgroundResponse) labelText = backgroundTaskResultTitle(normalizedName, backgroundMeta || parsedArgs);
19160
19174
  else if (isBackgroundMetadataResult) labelText = backgroundTaskActionTitle(normalizedName, backgroundMeta);
19161
19175
  else if (isShellSurface) labelText = shellHeader(shellStatus, displayGroupCount);
@@ -19364,7 +19378,7 @@ var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpa
19364
19378
  node = /* @__PURE__ */ jsx19(ToolHookDenialCard, { item, columns });
19365
19379
  break;
19366
19380
  }
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 });
19381
+ 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
19382
  break;
19369
19383
  }
19370
19384
  case "notice":
@@ -19672,6 +19686,32 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
19672
19686
  searchModelsCacheRef.current = { models: null, at: 0 };
19673
19687
  }
19674
19688
  }, []);
19689
+ useEffect10(() => {
19690
+ let alive = true;
19691
+ const timer2 = setTimeout(async () => {
19692
+ const seq = onboardingPrefetchSeqRef.current;
19693
+ try {
19694
+ const models = await Promise.resolve(store.listProviderModels?.() || []);
19695
+ if (alive && seq === onboardingPrefetchSeqRef.current && Array.isArray(models) && models.length > 0 && !Array.isArray(providerModelsCacheRef.current.models)) {
19696
+ providerModelsCacheRef.current = { models, at: Date.now() };
19697
+ }
19698
+ } catch {
19699
+ }
19700
+ if (!alive) return;
19701
+ try {
19702
+ const searchModels = await Promise.resolve(store.listSearchModels?.() || []);
19703
+ if (alive && Array.isArray(searchModels) && searchModels.length > 0 && !Array.isArray(searchModelsCacheRef.current.models)) {
19704
+ searchModelsCacheRef.current = { models: searchModels, at: Date.now() };
19705
+ }
19706
+ } catch {
19707
+ }
19708
+ }, 1500);
19709
+ timer2.unref?.();
19710
+ return () => {
19711
+ alive = false;
19712
+ clearTimeout(timer2);
19713
+ };
19714
+ }, [store]);
19675
19715
  const { onboardingWarnReopen, openOnboardingAuthStep } = createOnboardingSteps({
19676
19716
  store,
19677
19717
  setPicker,
@@ -22507,18 +22547,6 @@ function bracketField(text, name) {
22507
22547
  const re = new RegExp(`^\\[${name}:\\s*([^\\]]*)\\]`, "mi");
22508
22548
  return re.exec(String(text ?? ""))?.[1]?.trim() || "";
22509
22549
  }
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
22550
  function parseSyntheticAgentMessage(text) {
22523
22551
  const value = String(text ?? "").trim();
22524
22552
  if (!value) return null;
@@ -22771,6 +22799,13 @@ function notificationQueueKey(event, text, parsed) {
22771
22799
  const hasBody = /\n\s*\n[\s\S]*\S/.test(String(text || "")) ? "b1" : "b0";
22772
22800
  return [id, type || fallbackKind, status, hasBody].filter(Boolean).join(":");
22773
22801
  }
22802
+ function executionCardKey(event, text, parsed) {
22803
+ const meta = event?.meta && typeof event.meta === "object" ? event.meta : {};
22804
+ const executionId = String(meta.execution_id || "").trim();
22805
+ if (!executionId) return notificationQueueKey(event, text, parsed);
22806
+ const hasBody = /\n\s*\n[\s\S]*\S/.test(String(text || "")) ? "b1" : "b0";
22807
+ return `card:${executionId}:${hasBody}`;
22808
+ }
22774
22809
  function isExecutionNotification(event, text, parsed) {
22775
22810
  const meta = event?.meta && typeof event.meta === "object" ? event.meta : {};
22776
22811
  if (meta.execution_id || meta.execution_surface) return true;
@@ -22869,23 +22904,43 @@ var yieldToRenderer = ({ frames = 1 } = {}) => new Promise((resolve5) => {
22869
22904
 
22870
22905
  // src/tui/engine/tool-result-status.mjs
22871
22906
  var CANCELLED_RESULT_STATUS_LINE = "[status: cancelled]";
22907
+ function shellCommandExitCode(text) {
22908
+ const body = String(text || "");
22909
+ if (!/^\s*(?:Error:\s*)?\[shell-run-failed\]/i.test(body)) return null;
22910
+ const header = body.split("\n", 1)[0] || "";
22911
+ if (/\[timeout:|\[signal:|timed out|aborted|interrupted/i.test(header)) return null;
22912
+ const m = header.match(/\[exit code:\s*(\d+)\]/i);
22913
+ if (!m) return null;
22914
+ const code = Number(m[1]);
22915
+ return Number.isFinite(code) ? code : null;
22916
+ }
22917
+ function toolCallOutcome(message, rawText) {
22918
+ const exitCode = shellCommandExitCode(rawText);
22919
+ if (exitCode != null) {
22920
+ return { isCallError: false, isExitError: true, exitCode };
22921
+ }
22922
+ const isCallError = message?.isError === true || message?.toolKind === "error";
22923
+ return {
22924
+ isCallError,
22925
+ isExitError: false,
22926
+ exitCode
22927
+ };
22928
+ }
22929
+ function failureDetailText({ succeeded = 0, realErrors = 0, exitErrors = 0, exitCode } = {}) {
22930
+ const parts = [];
22931
+ if (succeeded > 0) parts.push(`${succeeded} Ok`);
22932
+ if (realErrors > 0) parts.push(`${realErrors} Failed`);
22933
+ if (exitErrors > 0) {
22934
+ const solo = exitErrors === 1 && realErrors === 0 && succeeded === 0;
22935
+ parts.push(solo && Number.isFinite(exitCode) ? `Exit ${exitCode}` : `${exitErrors} Exit`);
22936
+ }
22937
+ return parts.join(" \xB7 ");
22938
+ }
22872
22939
  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 "";
22940
+ return normalizeToolTerminalStatus(value);
22880
22941
  }
22881
22942
  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);
22943
+ return toolResultTerminalStatus(text);
22889
22944
  }
22890
22945
  function itemHasKnownTerminalStatus(item, texts = []) {
22891
22946
  const settled = (token) => token === "completed" || token === "failed" || token === "cancelled";
@@ -22907,11 +22962,14 @@ ${body}`;
22907
22962
  function groupedToolResultText(group) {
22908
22963
  const completed = Math.min(group.count, group.completed);
22909
22964
  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);
22965
+ const exitErrors = Number(group.exitErrors || 0);
22966
+ if (group.errors > 0 || exitErrors > 0) {
22967
+ const realErrors = Math.max(0, Number(group.callErrors || group.errors || 0));
22968
+ const succeeded = Math.max(0, completed - group.errors - exitErrors);
22969
+ const exitCode = group.results.find((result) => result?.isExitError)?.exitCode;
22970
+ const reasons = group.results.filter((result) => result?.isError && !result?.isExitError).map((result) => firstErrorLine(result?.text)).filter(Boolean);
22913
22971
  const uniqueReasons = [...new Set(reasons)].slice(0, 2);
22914
- const base = succeeded > 0 ? `${succeeded} Ok \xB7 ${group.errors} Failed` : `${group.errors} Failed`;
22972
+ const base = failureDetailText({ succeeded, realErrors, exitErrors, exitCode });
22915
22973
  return [
22916
22974
  `${base}${uniqueReasons[0] ? ` \xB7 ${uniqueReasons[0]}` : ""}`,
22917
22975
  ...uniqueReasons.slice(1)
@@ -22945,9 +23003,10 @@ ${text}`);
22945
23003
  }
22946
23004
  return chunks.join("\n\n");
22947
23005
  }
22948
- function aggregateBucketForCategory(category) {
23006
+ function aggregateBucketForCategory(category, { agentBatch = "" } = {}) {
22949
23007
  const key = String(category || "").trim();
22950
23008
  if (key === "Read" || key === "Search") return "category:Read+Search";
23009
+ if (key === "Agent") return agentBatch ? `category:Agent:${agentBatch}` : "category:Agent";
22951
23010
  return key ? `category:${key}` : "default";
22952
23011
  }
22953
23012
  function aggregateSummaries(aggregate) {
@@ -23066,6 +23125,12 @@ function createToolCardResults({
23066
23125
  buildAgentJobCardPatch,
23067
23126
  agentStatusState
23068
23127
  }) {
23128
+ function finalizedErrorFallbackBody(body, text, exitCode) {
23129
+ if (String(body || "").trim()) return body;
23130
+ if (String(text || "").trim()) return text;
23131
+ if (exitCode != null) return `Exit ${exitCode}`;
23132
+ return "Failed";
23133
+ }
23069
23134
  function patchToolItem(id, patch) {
23070
23135
  const prev = getState().items.find((it) => it.id === id);
23071
23136
  const ok = patchItem(id, patch);
@@ -23083,10 +23148,8 @@ function createToolCardResults({
23083
23148
  const rawText = toolResultText(message?.content);
23084
23149
  const aggregate = card.aggregate;
23085
23150
  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;
23151
+ const { exitCode, isExitError, isCallError } = toolCallOutcome(message, rawText);
23152
+ const isError = isCallError;
23090
23153
  const text = isError ? toolErrorDisplay(rawText, card?.name || "tool") : rawText;
23091
23154
  if (aggregate && card.itemId === aggregate.itemId) {
23092
23155
  if (!callRec) return false;
@@ -23099,14 +23162,17 @@ function createToolCardResults({
23099
23162
  assignAggregateSummaryOrder(aggregate, callRec);
23100
23163
  callRec.isError = isError;
23101
23164
  callRec.isCallError = isCallError;
23165
+ callRec.isExitError = isExitError;
23166
+ callRec.exitCode = exitCode;
23102
23167
  callRec.resultText = text;
23103
23168
  callRec.resolved = true;
23104
23169
  const allCalls = [...aggregate.calls.values()];
23105
23170
  const completed = allCalls.filter((r) => r.resolved).length;
23106
23171
  const errors = allCalls.filter((r) => r.isError).length;
23107
23172
  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));
23173
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
23174
+ const succeeded = Math.max(0, completed - errors - exitErrors);
23175
+ const detailText = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
23110
23176
  const currentItem = getState().items.find((it) => it.id === card.itemId);
23111
23177
  const earlyCompleted = allCalls.filter((r) => r.resolved || r.completedEarly).length;
23112
23178
  const visualCompleted = Math.max(completed, earlyCompleted, Math.min(allCalls.length, Number(currentItem?.completedCount || 0)));
@@ -23119,6 +23185,7 @@ function createToolCardResults({
23119
23185
  isError: errors > 0,
23120
23186
  errorCount: errors,
23121
23187
  callErrorCount: callErrors,
23188
+ exitErrorCount: exitErrors,
23122
23189
  count: allCalls.length,
23123
23190
  completedCount: visualCompleted,
23124
23191
  doneCategories: aggregateDoneCategories(allCalls),
@@ -23128,20 +23195,25 @@ function createToolCardResults({
23128
23195
  if (callId) done.add(callId);
23129
23196
  return true;
23130
23197
  }
23131
- const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, callErrors: 0, results: [] };
23198
+ const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, callErrors: 0, exitErrors: 0, results: [] };
23132
23199
  group.completed = Math.min(group.count, group.completed + 1);
23133
23200
  group.errors += isError ? 1 : 0;
23134
23201
  group.callErrors = (group.callErrors || 0) + (isCallError ? 1 : 0);
23135
- group.results.push({ text, isError });
23202
+ group.exitErrors = (group.exitErrors || 0) + (isExitError ? 1 : 0);
23203
+ group.results.push({ text, isError, isExitError, exitCode });
23136
23204
  toolGroups.set(card.itemId, group);
23137
23205
  const resultText = groupedToolResultText(group);
23138
- const displayResult = toolGroupedDisplayFallback(resultText, text, rawText);
23206
+ let displayResult = toolGroupedDisplayFallback(resultText, text, rawText);
23207
+ if (group.errors > 0 && !String(displayResult || "").trim()) {
23208
+ displayResult = finalizedErrorFallbackBody(displayResult, text, exitCode);
23209
+ }
23139
23210
  const patch = {
23140
23211
  result: displayResult,
23141
23212
  text: displayResult,
23142
23213
  isError: group.errors > 0,
23143
23214
  errorCount: group.errors,
23144
23215
  callErrorCount: group.callErrors || 0,
23216
+ exitErrorCount: group.exitErrors || 0,
23145
23217
  count: group.count,
23146
23218
  completedCount: group.completed,
23147
23219
  completedAt: Date.now()
@@ -23154,6 +23226,9 @@ function createToolCardResults({
23154
23226
  set(agentStatusState({ force: true }));
23155
23227
  }
23156
23228
  Object.assign(patch, buildAgentJobCardPatch(card.itemId, rawText, isError));
23229
+ if (group.errors > 0 && !String(patch.result || "").trim()) {
23230
+ patch.result = patch.text = finalizedErrorFallbackBody(patch.result, text, exitCode);
23231
+ }
23157
23232
  }
23158
23233
  patchToolItem(card.itemId, patch);
23159
23234
  card.done = true;
@@ -23204,9 +23279,10 @@ function createToolCardResults({
23204
23279
  const totalCompleted = completed;
23205
23280
  const errors = allCalls.filter((r) => r.isError).length;
23206
23281
  const callErrors = allCalls.filter((r) => r.isCallError).length;
23207
- const succeeded = completed - errors;
23282
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
23283
+ const succeeded = Math.max(0, completed - errors - exitErrors);
23208
23284
  const rawResult = aggregateRawResult(allCalls);
23209
- let displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
23285
+ let displayDetail = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
23210
23286
  if (cancelled) {
23211
23287
  const currentItem = getState().items.find((it) => it.id === card.itemId);
23212
23288
  displayDetail = withCancelledResultMarker(displayDetail, currentItem);
@@ -23218,6 +23294,7 @@ function createToolCardResults({
23218
23294
  isError: errors > 0,
23219
23295
  errorCount: errors,
23220
23296
  callErrorCount: callErrors,
23297
+ exitErrorCount: exitErrors,
23221
23298
  count: allCalls.length,
23222
23299
  completedCount: totalCompleted,
23223
23300
  doneCategories: aggregateDoneCategories(allCalls),
@@ -23230,15 +23307,19 @@ function createToolCardResults({
23230
23307
  }
23231
23308
  continue;
23232
23309
  }
23233
- const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, results: [] };
23310
+ const group = toolGroups.get(card.itemId) || { count: 1, completed: 0, errors: 0, exitErrors: 0, results: [] };
23234
23311
  group.completed = Math.min(group.count, group.completed + 1);
23235
23312
  toolGroups.set(card.itemId, group);
23236
23313
  let resultText = groupedToolResultText(group);
23314
+ if (group.errors > 0 && !String(resultText || "").trim()) {
23315
+ const exitRec = (group.results || []).find((r) => r && r.isExitError);
23316
+ resultText = finalizedErrorFallbackBody(resultText, exitRec?.text, exitRec?.exitCode);
23317
+ }
23237
23318
  if (cancelled) {
23238
23319
  const currentItem = getState().items.find((it) => it.id === card.itemId);
23239
23320
  resultText = withCancelledResultMarker(resultText, currentItem);
23240
23321
  }
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() });
23322
+ 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
23323
  card.done = true;
23243
23324
  if (card.callId) done.add(card.callId);
23244
23325
  }
@@ -23246,6 +23327,73 @@ function createToolCardResults({
23246
23327
  return { patchToolCardResult, flushToolResults };
23247
23328
  }
23248
23329
 
23330
+ // src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs
23331
+ import { createHash as createHash3 } from "node:crypto";
23332
+ var DELIVERED_TTL_MS = 6 * 60 * 60 * 1e3;
23333
+ var DELIVERED_MAX_ENTRIES = 512;
23334
+ var _delivered = /* @__PURE__ */ new Map();
23335
+ function hashCompletionText(text) {
23336
+ const value = String(text ?? "").trim();
23337
+ if (!value) return null;
23338
+ return createHash3("sha1").update(value).digest("hex");
23339
+ }
23340
+ function execKey(executionId) {
23341
+ const id = executionId == null ? "" : String(executionId).trim();
23342
+ return id ? `id:${id}` : null;
23343
+ }
23344
+ function textKey(text) {
23345
+ const h = hashCompletionText(text);
23346
+ return h ? `tx:${h}` : null;
23347
+ }
23348
+ function pruneExpired(now) {
23349
+ for (const [key, expiresAt] of _delivered) {
23350
+ if (expiresAt <= now) _delivered.delete(key);
23351
+ }
23352
+ }
23353
+ function enforceBound() {
23354
+ while (_delivered.size > DELIVERED_MAX_ENTRIES) {
23355
+ const oldest = _delivered.keys().next().value;
23356
+ if (oldest === void 0) break;
23357
+ _delivered.delete(oldest);
23358
+ }
23359
+ }
23360
+ function recordDeliveredCompletion({ executionId, text } = {}) {
23361
+ const now = Date.now();
23362
+ pruneExpired(now);
23363
+ const expiresAt = now + DELIVERED_TTL_MS;
23364
+ let recorded = false;
23365
+ for (const key of [execKey(executionId), textKey(text)]) {
23366
+ if (!key) continue;
23367
+ _delivered.delete(key);
23368
+ _delivered.set(key, expiresAt);
23369
+ recorded = true;
23370
+ }
23371
+ if (recorded) enforceBound();
23372
+ return recorded;
23373
+ }
23374
+ function isDeliveredCompletion({ executionId, text } = {}) {
23375
+ const now = Date.now();
23376
+ const keys = [execKey(executionId), textKey(text)].filter(Boolean);
23377
+ let hit = false;
23378
+ for (const key of keys) {
23379
+ const expiresAt2 = _delivered.get(key);
23380
+ if (expiresAt2 === void 0) continue;
23381
+ if (expiresAt2 <= now) {
23382
+ _delivered.delete(key);
23383
+ continue;
23384
+ }
23385
+ hit = true;
23386
+ }
23387
+ if (!hit) return false;
23388
+ const expiresAt = now + DELIVERED_TTL_MS;
23389
+ for (const key of keys) {
23390
+ if (!_delivered.has(key)) continue;
23391
+ _delivered.delete(key);
23392
+ _delivered.set(key, expiresAt);
23393
+ }
23394
+ return true;
23395
+ }
23396
+
23249
23397
  // src/tui/engine/agent-job-feed.mjs
23250
23398
  function parseInboundImagePaths(raw) {
23251
23399
  if (typeof raw !== "string" || !raw) return [];
@@ -23266,16 +23414,58 @@ function createAgentJobFeed({
23266
23414
  enqueue,
23267
23415
  drain,
23268
23416
  pushUserOrSyntheticItem,
23417
+ pushAsyncAgentResponse,
23269
23418
  makeQueueEntry,
23270
23419
  getPending,
23271
23420
  agentStatusState,
23272
23421
  displayedExecutionNotificationKeys,
23273
- pushNotice
23422
+ pushNotice,
23423
+ now = () => Date.now(),
23424
+ executionResumeTombstoneTtlMs = 3e4,
23425
+ executionResumeTombstoneLimit = 128
23274
23426
  }) {
23275
23427
  let executionResumeKickDeferred = false;
23428
+ const discardedExecutionResumeKeys = /* @__PURE__ */ new Map();
23429
+ const displayedExecutionResponseStates = /* @__PURE__ */ new Map();
23276
23430
  const executionResumeKickBodies = [];
23277
- function kickExecutionPendingResume(body = "") {
23278
- if (body) executionResumeKickBodies.push(body);
23431
+ function executionResumeKey(body, completionKey = "") {
23432
+ if (completionKey && typeof completionKey === "object") {
23433
+ completionKey = completionKey.executionId || completionKey.key || "";
23434
+ }
23435
+ const explicitKey = String(completionKey || "").trim();
23436
+ if (explicitKey.startsWith("execution:") || explicitKey.startsWith("body:")) return explicitKey;
23437
+ if (explicitKey) return `execution:${explicitKey}`;
23438
+ const value = String(body || "").trim();
23439
+ return value ? `body:${shortTextFingerprint(value)}` : "";
23440
+ }
23441
+ function pruneDiscardedExecutionResumeKeys() {
23442
+ const nowMs = Number(now()) || Date.now();
23443
+ for (const [key, expiresAt] of discardedExecutionResumeKeys) {
23444
+ if (expiresAt <= nowMs) discardedExecutionResumeKeys.delete(key);
23445
+ }
23446
+ }
23447
+ function isDiscardedExecutionResumeKey(key) {
23448
+ if (!key) return false;
23449
+ pruneDiscardedExecutionResumeKeys();
23450
+ return discardedExecutionResumeKeys.has(key);
23451
+ }
23452
+ function rememberDiscardedExecutionResumeKey(key) {
23453
+ if (!key) return;
23454
+ pruneDiscardedExecutionResumeKeys();
23455
+ const limit = Math.max(1, Number(executionResumeTombstoneLimit) || 128);
23456
+ while (!discardedExecutionResumeKeys.has(key) && discardedExecutionResumeKeys.size >= limit) {
23457
+ const oldest = discardedExecutionResumeKeys.keys().next().value;
23458
+ if (oldest == null) break;
23459
+ discardedExecutionResumeKeys.delete(oldest);
23460
+ }
23461
+ const ttlMs = Math.max(1, Number(executionResumeTombstoneTtlMs) || 3e4);
23462
+ discardedExecutionResumeKeys.delete(key);
23463
+ discardedExecutionResumeKeys.set(key, (Number(now()) || Date.now()) + ttlMs);
23464
+ }
23465
+ function kickExecutionPendingResume(body = "", completionKey = "") {
23466
+ const key = executionResumeKey(body, completionKey);
23467
+ if (body && isDiscardedExecutionResumeKey(key)) return;
23468
+ if (body) executionResumeKickBodies.push({ body, key });
23279
23469
  if (getDisposed()) return;
23280
23470
  if (getState().busy) {
23281
23471
  executionResumeKickDeferred = true;
@@ -23287,16 +23477,34 @@ function createAgentJobFeed({
23287
23477
  return;
23288
23478
  }
23289
23479
  executionResumeKickDeferred = false;
23290
- const resumeBody = executionResumeKickBodies.splice(0).filter(Boolean).join("\n\n");
23291
- pending.push(makeQueueEntry(resumeBody, { mode: "pending-resume", priority: "next" }));
23480
+ const resumeBodies = executionResumeKickBodies.splice(0);
23481
+ const resumeBody = resumeBodies.map(({ body: value }) => value).filter(Boolean).join("\n\n");
23482
+ const resumeCompletionKeys = resumeBodies.map(({ key: key2 }) => key2).filter(Boolean);
23483
+ pending.push(makeQueueEntry(resumeBody, {
23484
+ mode: "pending-resume",
23485
+ priority: "next",
23486
+ abortDiscardOnAbort: true,
23487
+ resumeCompletionKeys
23488
+ }));
23292
23489
  void drain();
23293
23490
  }
23294
23491
  function flushDeferredExecutionPendingResumeKick() {
23295
23492
  if (!executionResumeKickDeferred || getDisposed() || getState().busy) return;
23296
23493
  kickExecutionPendingResume();
23297
23494
  }
23298
- function scheduleExecutionPendingResumeKick(body = "") {
23299
- queueMicrotask(() => kickExecutionPendingResume(body));
23495
+ function scheduleExecutionPendingResumeKick(body = "", completionKey = "") {
23496
+ queueMicrotask(() => kickExecutionPendingResume(body, completionKey));
23497
+ }
23498
+ function discardExecutionPendingResume(completionKeys = []) {
23499
+ const keys = (Array.isArray(completionKeys) ? completionKeys : [completionKeys]).map((key) => executionResumeKey("", key)).filter(Boolean);
23500
+ if (keys.length === 0) return;
23501
+ for (const key of keys) rememberDiscardedExecutionResumeKey(key);
23502
+ for (let i = executionResumeKickBodies.length - 1; i >= 0; i -= 1) {
23503
+ if (isDiscardedExecutionResumeKey(executionResumeKickBodies[i].key)) {
23504
+ executionResumeKickBodies.splice(i, 1);
23505
+ }
23506
+ }
23507
+ executionResumeKickDeferred = executionResumeKickBodies.length > 0;
23300
23508
  }
23301
23509
  function buildAgentJobCardPatch(itemId, text, isError = false) {
23302
23510
  const parsed = parseAgentJob(text);
@@ -23314,6 +23522,12 @@ function createAgentJobFeed({
23314
23522
  function updateAgentJobCard(itemId, text, isError = false) {
23315
23523
  patchItem(itemId, buildAgentJobCardPatch(itemId, text, isError));
23316
23524
  }
23525
+ function refreshAgentStatus(parsed) {
23526
+ if (!parsed?.taskId) return;
23527
+ const status = String(parsed.status || "").toLowerCase();
23528
+ const terminal = /^(completed|complete|done|success|succeeded|ok|failed|error|timeout|killed|cancelled|canceled|denied)$/.test(status);
23529
+ set(agentStatusState(terminal ? { force: true } : void 0));
23530
+ }
23317
23531
  function subscribeRuntimeNotifications() {
23318
23532
  if (typeof runtime.onNotification !== "function") return null;
23319
23533
  return runtime.onNotification((event) => {
@@ -23329,38 +23543,53 @@ function createAgentJobFeed({
23329
23543
  return true;
23330
23544
  }
23331
23545
  if (delivery.action === "status-only") {
23332
- if (parsed?.taskId) set(agentStatusState({ force: true }));
23546
+ refreshAgentStatus(parsed);
23333
23547
  return true;
23334
23548
  }
23335
23549
  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());
23550
+ const cardKey = executionCardKey(event, text, parsed);
23551
+ const firstDelivery = !cardKey || !displayedExecutionNotificationKeys.has(cardKey);
23552
+ const executionId = String(event?.meta?.execution_id || parsed?.taskId || "").trim();
23553
+ const status = String(event?.meta?.status || parsed?.status || "").toLowerCase();
23554
+ const hasBody = /\n\s*\n[\s\S]*\S/.test(text);
23555
+ const isFailure = /^(failed|error|timeout|killed|cancelled|canceled|denied)$/.test(status);
23556
+ const successfulPreview = !hasBody && !isFailure && /^(completed|complete|done|success|succeeded|ok)$/.test(status);
23557
+ const responseState = executionId ? displayedExecutionResponseStates.get(executionId) : "";
23558
+ const bodyAlreadyDisplayed = responseState === "body";
23559
+ if (firstDelivery && !successfulPreview && !bodyAlreadyDisplayed) {
23560
+ if (cardKey) displayedExecutionNotificationKeys.add(cardKey);
23561
+ if (executionId) displayedExecutionResponseStates.set(executionId, hasBody ? "body" : "preview");
23562
+ (pushAsyncAgentResponse || pushUserOrSyntheticItem)(delivery.displayText, nextId2(), "injected", { responseKey: executionId });
23340
23563
  }
23341
- if (parsed?.taskId) set(agentStatusState({ force: true }));
23564
+ refreshAgentStatus(parsed);
23342
23565
  const resumeBody = String(delivery.modelContent || "").trim();
23343
23566
  if (resumeBody) {
23344
- enqueue(resumeBody, {
23567
+ const completionKey = executionResumeKey(resumeBody, executionId);
23568
+ if (isDiscardedExecutionResumeKey(completionKey) || isDeliveredCompletion({ executionId, text: resumeBody })) {
23569
+ if (event && typeof event === "object") event.modelVisibleDelivered = true;
23570
+ return true;
23571
+ }
23572
+ const enqueued = enqueue(resumeBody, {
23345
23573
  mode: "task-notification",
23346
23574
  // Claude Code parity: live execution completions are queued as
23347
23575
  // task notifications so the active loop can attach them after the
23348
23576
  // next tool batch; no special pending-resume bypass.
23349
23577
  priority: "next",
23350
23578
  key: notificationKey || void 0,
23579
+ abortDiscardOnAbort: true,
23580
+ resumeCompletionKeys: completionKey ? [completionKey] : [],
23351
23581
  displayText: delivery.displayText || text,
23352
23582
  // The immediate Response card was already pushed above
23353
23583
  // (pushUserOrSyntheticItem). Keep this queued twin model-visible
23354
23584
  // but suppress its drain-time transcript card to avoid a duplicate.
23355
23585
  suppressDisplay: true
23356
23586
  });
23587
+ if (enqueued) recordDeliveredCompletion({ executionId, text: resumeBody });
23357
23588
  if (event && typeof event === "object") event.modelVisibleDelivered = true;
23358
23589
  }
23359
23590
  return true;
23360
23591
  }
23361
- if (parsed?.taskId) {
23362
- set(agentStatusState({ force: true }));
23363
- }
23592
+ refreshAgentStatus(parsed);
23364
23593
  const modelContent = String(delivery.modelContent ?? delivery.displayText ?? text).trim();
23365
23594
  const imagePaths = parseInboundImagePaths(event?.meta?.image_paths);
23366
23595
  if (!modelContent && imagePaths.length === 0) return true;
@@ -23403,12 +23632,68 @@ function createAgentJobFeed({
23403
23632
  kickExecutionPendingResume,
23404
23633
  flushDeferredExecutionPendingResumeKick,
23405
23634
  scheduleExecutionPendingResumeKick,
23635
+ discardExecutionPendingResume,
23406
23636
  updateAgentJobCard,
23407
23637
  buildAgentJobCardPatch,
23408
23638
  subscribeRuntimeNotifications
23409
23639
  };
23410
23640
  }
23411
23641
 
23642
+ // src/tui/engine/agent-response-tail.mjs
23643
+ function responseEntry(response = {}) {
23644
+ return {
23645
+ key: String(response.key || ""),
23646
+ raw: String(response.rawResult ?? response.raw ?? "").trim(),
23647
+ result: response.result,
23648
+ hasBody: response.hasBody === true,
23649
+ isError: response.isError === true
23650
+ };
23651
+ }
23652
+ function formatAgentResponseRaw(entries = []) {
23653
+ return entries.map((entry, index) => {
23654
+ const raw = String(entry?.raw || "").trim();
23655
+ return raw ? `${index + 1}. agent
23656
+ ${raw}` : "";
23657
+ }).filter(Boolean).join("\n\n");
23658
+ }
23659
+ function priorEntries(previous) {
23660
+ if (Array.isArray(previous?.agentResponseEntries) && previous.agentResponseEntries.length) {
23661
+ return previous.agentResponseEntries.map(responseEntry);
23662
+ }
23663
+ return [responseEntry({
23664
+ key: previous?.agentResponseKey,
23665
+ rawResult: previous?.rawResult ?? previous?.result,
23666
+ result: previous?.result,
23667
+ hasBody: previous?.agentResponseHasBody,
23668
+ isError: previous?.isError
23669
+ })];
23670
+ }
23671
+ function appendAgentResponseTail(previous, response, now = Date.now()) {
23672
+ if (previous?.kind !== "tool" || previous.agentDirection !== "inbound") return null;
23673
+ const next = responseEntry(response);
23674
+ const entries = priorEntries(previous);
23675
+ const existingIndex = next.key ? entries.findIndex((entry) => entry.key === next.key) : -1;
23676
+ if (existingIndex >= 0) {
23677
+ entries[existingIndex] = next;
23678
+ } else {
23679
+ if (previous.agentResponseHasBody !== next.hasBody) return null;
23680
+ entries.push(next);
23681
+ }
23682
+ return {
23683
+ args: response.args,
23684
+ result: response.result,
23685
+ rawResult: formatAgentResponseRaw(entries) || null,
23686
+ isError: entries.some((entry) => entry.isError),
23687
+ count: entries.length,
23688
+ completedCount: entries.length,
23689
+ completedAt: now,
23690
+ agentResponseEntries: entries,
23691
+ agentResponseKeys: entries.map((entry) => entry.key).filter(Boolean),
23692
+ agentResponseHasBody: entries.every((entry) => entry.hasBody),
23693
+ agentResponseAggregate: entries.length > 1
23694
+ };
23695
+ }
23696
+
23412
23697
  // src/tui/engine/tui-steering-persist.mjs
23413
23698
  import { randomBytes as randomBytes3 } from "crypto";
23414
23699
  import { join as join8 } from "path";
@@ -23778,6 +24063,10 @@ function createSessionFlow(bag) {
23778
24063
  skipSlashCommands: options.skipSlashCommands === true,
23779
24064
  displayText: mode === "task-notification" ? notificationDisplayText(displayText) : String(displayText || ""),
23780
24065
  suppressDisplay: options.suppressDisplay === true,
24066
+ // Completion resumes are consumed exactly once: Esc abandons their
24067
+ // uncommitted body instead of putting it back at the queue front.
24068
+ abortDiscardOnAbort: options.abortDiscardOnAbort === true,
24069
+ resumeCompletionKeys: Array.isArray(options.resumeCompletionKeys) ? options.resumeCompletionKeys.filter((key) => key != null && String(key).trim()) : [],
23781
24070
  steeringPersistId: options.steeringPersistId || null,
23782
24071
  steeringPersistRestored: options.steeringPersistRestored === true
23783
24072
  };
@@ -23893,6 +24182,13 @@ function createSessionFlow(bag) {
23893
24182
  pushUserOrSyntheticItem(entry.text, entry.id, isQueuedEntryEditable(entry) ? "user" : "injected");
23894
24183
  }
23895
24184
  const nonEditable = batch.filter((entry) => !isQueuedEntryEditable(entry));
24185
+ const discardOnAbort = nonEditable.filter(
24186
+ (entry) => entry?.abortDiscardOnAbort === true || entry?.mode === "pending-resume"
24187
+ );
24188
+ const requeueOnAbort = nonEditable.filter((entry) => !discardOnAbort.includes(entry));
24189
+ const discardExecutionPendingResumeKeys = discardOnAbort.flatMap(
24190
+ (entry) => Array.isArray(entry?.resumeCompletionKeys) ? entry.resumeCompletionKeys : []
24191
+ );
23896
24192
  const batchPastedImages = mergePastedImages(batch);
23897
24193
  const batchPastedTexts = mergePastedTexts(batch);
23898
24194
  const turnStatus = await bag.runTurn(merged, {
@@ -23902,7 +24198,8 @@ function createSessionFlow(bag) {
23902
24198
  onCommitted: () => commitSteeringQueueEntries(batch),
23903
24199
  submittedIds: [...ids],
23904
24200
  restorable: nonEditable.length === 0,
23905
- requeueOnAbort: nonEditable
24201
+ requeueOnAbort,
24202
+ discardExecutionPendingResumeKeys
23906
24203
  });
23907
24204
  if (flags.drainEpoch !== drainEpoch) return;
23908
24205
  flushDeferredClearedSessionUi();
@@ -24250,7 +24547,8 @@ function createRunTurn(bag) {
24250
24547
  submittedIds,
24251
24548
  reclaimed: false,
24252
24549
  committed: false,
24253
- requeueEntries: Array.isArray(options.requeueOnAbort) ? options.requeueOnAbort.slice() : []
24550
+ requeueEntries: Array.isArray(options.requeueOnAbort) ? options.requeueOnAbort.slice() : [],
24551
+ discardExecutionPendingResumeKeys: Array.isArray(options.discardExecutionPendingResumeKeys) ? options.discardExecutionPendingResumeKeys.slice() : []
24254
24552
  };
24255
24553
  set({ busy: true, lastTurn: null, spinner: { active: true, verb: pickVerb(turnIndex), startedAt, responseLength: 0, inputTokens: 0, outputTokens: 0, mode: "requesting" } });
24256
24554
  let assistantText = "";
@@ -24333,6 +24631,7 @@ function createRunTurn(bag) {
24333
24631
  };
24334
24632
  armWatchdog();
24335
24633
  let currentAssistantText = "";
24634
+ const committedSegments = [];
24336
24635
  let thinkingText = "";
24337
24636
  let thinkingStartedAt = 0;
24338
24637
  let thinkingSegmentStartedAt = 0;
@@ -24348,7 +24647,8 @@ function createRunTurn(bag) {
24348
24647
  const earlyResultBuffer = /* @__PURE__ */ new Map();
24349
24648
  const aggregateCards = [];
24350
24649
  let tailAggregate = null;
24351
- const TOOL_CARD_PUSH_DELAY_MS = 0;
24650
+ let providerToolBatch = 0;
24651
+ const TOOL_CARD_PUSH_DELAY_MS = 1e3;
24352
24652
  let deferredSeqCounter = 0;
24353
24653
  const deferredEntries = [];
24354
24654
  const flushDeferredUpTo = (entry) => {
@@ -24515,10 +24815,11 @@ function createRunTurn(bag) {
24515
24815
  aggregate.ensureVisible?.();
24516
24816
  const errors = allCalls.filter((r) => r.isError).length;
24517
24817
  const callErrors = allCalls.filter((r) => r.isCallError).length;
24818
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
24518
24819
  const completed = allCalls.filter((r) => r.resolved).length;
24519
- const succeeded = completed - errors;
24820
+ const succeeded = Math.max(0, completed - errors - exitErrors);
24520
24821
  const rawResult = aggregateRawResult(allCalls);
24521
- const displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
24822
+ const displayDetail = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
24522
24823
  patchItem(aggregate.itemId, {
24523
24824
  result: displayDetail,
24524
24825
  text: displayDetail,
@@ -24526,6 +24827,7 @@ function createRunTurn(bag) {
24526
24827
  isError: errors > 0,
24527
24828
  errorCount: errors,
24528
24829
  callErrorCount: callErrors,
24830
+ exitErrorCount: exitErrors,
24529
24831
  count: allCalls.length,
24530
24832
  completedCount: allCalls.length,
24531
24833
  doneCategories: aggregateDoneCategories(allCalls),
@@ -24616,6 +24918,7 @@ function createRunTurn(bag) {
24616
24918
  if (sealToolBlock) clearAggregateContinuation();
24617
24919
  const id = currentAssistantId || ensureAssistant(text);
24618
24920
  patchItem(id, { text, streaming: false });
24921
+ committedSegments.push(text);
24619
24922
  closeAssistantSegment();
24620
24923
  return true;
24621
24924
  };
@@ -24732,24 +25035,25 @@ function createRunTurn(bag) {
24732
25035
  if (!callRec || callRec.resolved || callRec.completedEarly) return;
24733
25036
  aggregate.ensureVisible?.();
24734
25037
  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;
25038
+ const { exitCode, isExitError, isCallError } = toolCallOutcome(message, rawText);
25039
+ const isError = isCallError;
24739
25040
  const text = isError ? toolErrorDisplay(rawText, callRec.name || "tool") : rawText;
24740
25041
  callRec.summary = !isError ? summarizeToolResult(callRec.name, callRec.args, rawText, isError) : null;
24741
25042
  assignAggregateSummaryOrder(aggregate, callRec);
24742
25043
  callRec.isError = isError;
24743
25044
  callRec.isCallError = isCallError;
25045
+ callRec.isExitError = isExitError;
25046
+ callRec.exitCode = exitCode;
24744
25047
  callRec.resultText = text;
24745
25048
  callRec.completedEarly = true;
24746
25049
  const allCalls = [...aggregate.calls.values()];
24747
25050
  const completedCount = allCalls.filter((r) => r.resolved || r.completedEarly).length;
24748
25051
  const errors = allCalls.filter((r) => r.isError).length;
24749
25052
  const callErrors = allCalls.filter((r) => r.isCallError).length;
24750
- const succeeded = completedCount - errors;
25053
+ const exitErrors = allCalls.filter((r) => r.isExitError).length;
25054
+ const succeeded = Math.max(0, completedCount - errors - exitErrors);
24751
25055
  const rawResult = aggregateRawResult(allCalls);
24752
- const displayDetail = errors > 0 ? succeeded > 0 ? `${succeeded} Ok \xB7 ${errors} Failed` : `${errors} Failed` : formatAggregateDetail(aggregateSummaries(aggregate));
25056
+ const displayDetail = errors > 0 || exitErrors > 0 ? failureDetailText({ succeeded, realErrors: callErrors, exitErrors, exitCode: allCalls.find((r) => r.isExitError)?.exitCode }) : formatAggregateDetail(aggregateSummaries(aggregate));
24753
25057
  const currentItem = getState().items.find((it) => it.id === card.itemId);
24754
25058
  const visualCompleted = Math.max(
24755
25059
  completedCount,
@@ -24761,6 +25065,7 @@ function createRunTurn(bag) {
24761
25065
  isError: errors > 0,
24762
25066
  errorCount: errors,
24763
25067
  callErrorCount: callErrors,
25068
+ exitErrorCount: exitErrors,
24764
25069
  count: allCalls.length,
24765
25070
  completedCount: visualCompleted
24766
25071
  };
@@ -24786,10 +25091,7 @@ function createRunTurn(bag) {
24786
25091
  if (!markTurnProgress("steer-message")) return;
24787
25092
  if (text === STEERING_SUPPRESSED_DISPLAY) return;
24788
25093
  flushStreamBatch();
24789
- if (currentAssistantId) {
24790
- patchItem(currentAssistantId, { text: currentAssistantText || assistantText, streaming: false });
24791
- closeAssistantSegment();
24792
- }
25094
+ commitAssistantSegment({ sealToolBlock: true });
24793
25095
  assistantText = "";
24794
25096
  const value = String(text || "").trim();
24795
25097
  if (value) {
@@ -24810,6 +25112,7 @@ function createRunTurn(bag) {
24810
25112
  }
24811
25113
  const batchCalls = (calls || []).filter(Boolean);
24812
25114
  if (batchCalls.length === 0) return;
25115
+ const agentBatch = ++providerToolBatch;
24813
25116
  const committedAssistantSegment = commitAssistantSegment({ sealToolBlock: true });
24814
25117
  if (committedAssistantSegment) {
24815
25118
  await yieldToRenderer({ frames: 2 });
@@ -24822,7 +25125,7 @@ function createRunTurn(bag) {
24822
25125
  const name = toolCallName(c);
24823
25126
  const args = toolCallArgs(c);
24824
25127
  const category = classifyToolCategory(name, args);
24825
- const bucket = category === "Agent" ? null : aggregateBucketForCategory(category);
25128
+ const bucket = aggregateBucketForCategory(category, { agentBatch });
24826
25129
  const callId = toolCallId(c);
24827
25130
  const callKey = callId || `__tool_${toolCards.length}_${i}`;
24828
25131
  const activeCount = Number(aggregateToolCategoryEntry(name, args, category)?.count || 1);
@@ -24865,7 +25168,7 @@ function createRunTurn(bag) {
24865
25168
  ...categoryEntry,
24866
25169
  count: Number(prevCategory?.count || 0) + Number(categoryEntry.count || 1)
24867
25170
  });
24868
- aggregateCard.calls.set(callKey, { name, args, category, summary: null, summarySeq: null, isError: false, isCallError: false, resultText: null, resolved: false, completedEarly: false });
25171
+ 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
25172
  touchedAggregates.add(aggregateCard);
24870
25173
  const card = { itemId: aggregateCard.itemId, callId: callKey, done: false, aggregate: aggregateCard };
24871
25174
  if (callId) {
@@ -25013,10 +25316,18 @@ function createRunTurn(bag) {
25013
25316
  flushStreamBatch();
25014
25317
  syncContextStats({ allowEstimated: true });
25015
25318
  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 });
25319
+ let finalRemainder = finalText;
25320
+ for (const seg of committedSegments) {
25321
+ const skipped = finalRemainder.replace(/^\s+/, "");
25322
+ const trimmedSeg = seg ? seg.replace(/^\s+/, "") : "";
25323
+ if (trimmedSeg && skipped.startsWith(trimmedSeg)) {
25324
+ finalRemainder = skipped.slice(trimmedSeg.length);
25325
+ }
25326
+ }
25327
+ if (finalRemainder.trim()) {
25328
+ const id = currentAssistantId || ensureAssistant(finalRemainder);
25329
+ currentAssistantText = finalRemainder;
25330
+ patchItem(id, { text: finalRemainder, streaming: false });
25020
25331
  } else if (currentAssistantId && (currentAssistantText.trim() || assistantText.trim())) {
25021
25332
  const streamedText = currentAssistantText || assistantText;
25022
25333
  patchItem(currentAssistantId, { text: streamedText, streaming: false });
@@ -25807,7 +26118,8 @@ function createEngineApiA(bag) {
25807
26118
  restoreQueued,
25808
26119
  resetStatsAndSyncContext,
25809
26120
  drain,
25810
- flushDeferredExecutionPendingResumeKick
26121
+ flushDeferredExecutionPendingResumeKick,
26122
+ discardExecutionPendingResume
25811
26123
  } = bag;
25812
26124
  return {
25813
26125
  getState: () => getState(),
@@ -26301,9 +26613,14 @@ function createEngineApiA(bag) {
26301
26613
  const restorePastedTexts = canRestore && restoreState?.pastedTexts ? restoreState.pastedTexts : null;
26302
26614
  const discardPastedImages = restoreState?.restorable && hasPendingSteering && restoreState?.pastedImages ? restoreState.pastedImages : null;
26303
26615
  const discardPastedTexts = restoreState?.restorable && hasPendingSteering && restoreState?.pastedTexts ? restoreState.pastedTexts : null;
26304
- const requeueEntries = restoreState && !restoreState.committed && Array.isArray(restoreState.requeueEntries) ? restoreState.requeueEntries.slice() : [];
26616
+ const requeueEntries = restoreState && !restoreState.committed && Array.isArray(restoreState.requeueEntries) ? restoreState.requeueEntries.filter(
26617
+ (entry) => entry?.abortDiscardOnAbort !== true && entry?.mode !== "pending-resume"
26618
+ ) : [];
26305
26619
  const aborted = runtime.abort("cli-react-abort");
26306
26620
  if (restoreState) {
26621
+ if (aborted !== false && Array.isArray(restoreState.discardExecutionPendingResumeKeys)) {
26622
+ discardExecutionPendingResume?.(restoreState.discardExecutionPendingResumeKeys);
26623
+ }
26307
26624
  if ((restoreText || requeueEntries.length > 0) && aborted !== false) {
26308
26625
  restoreState.reclaimed = true;
26309
26626
  const idSet = new Set((restoreState.submittedIds || []).filter((id) => id != null));
@@ -26319,6 +26636,7 @@ function createEngineApiA(bag) {
26319
26636
  }
26320
26637
  restoreState.restorable = false;
26321
26638
  restoreState.requeueEntries = [];
26639
+ restoreState.discardExecutionPendingResumeKeys = [];
26322
26640
  }
26323
26641
  const abortEpoch = flags.leadTurnEpoch;
26324
26642
  const recoveryMs = Number(flags.manualAbortRecoveryMs) > 0 ? Number(flags.manualAbortRecoveryMs) : MANUAL_ABORT_RECOVERY_MS;
@@ -26579,6 +26897,59 @@ async function createEngineSession({
26579
26897
  if (origin === "user") appendPromptHistory(state.cwd, text);
26580
26898
  pushItem({ kind: "user", id, text });
26581
26899
  };
26900
+ const pushAsyncAgentResponse = (text, id = nextId(), origin = "injected", metadata = {}) => {
26901
+ const synthetic = parseSyntheticAgentMessage(text);
26902
+ const isAgent = synthetic?.name === "agent";
26903
+ if (!isAgent) return pushUserOrSyntheticItem(text, id, origin);
26904
+ const responseHasBody = /\n\s*\n[\s\S]*\S/.test(String(text || ""));
26905
+ const rawResult = synthetic.rawResult ?? text;
26906
+ const args = {
26907
+ ...synthetic.args && typeof synthetic.args === "object" ? synthetic.args : {},
26908
+ type: "result"
26909
+ };
26910
+ const responseKey = String(metadata.responseKey || metadata.executionId || args.task_id || "").trim();
26911
+ const previous = state.items.at(-1);
26912
+ if (previous?.kind === "tool" && previous.agentDirection === "inbound") {
26913
+ const patch = appendAgentResponseTail(previous, {
26914
+ key: responseKey,
26915
+ args,
26916
+ result: synthetic.result,
26917
+ rawResult,
26918
+ hasBody: responseHasBody,
26919
+ isError: synthetic.isError === true
26920
+ });
26921
+ if (patch) {
26922
+ patchItem(previous.id, patch);
26923
+ return true;
26924
+ }
26925
+ }
26926
+ pushItem({
26927
+ kind: "tool",
26928
+ id,
26929
+ name: "agent",
26930
+ args,
26931
+ result: synthetic.result,
26932
+ rawResult,
26933
+ isError: synthetic.isError === true,
26934
+ expanded: false,
26935
+ count: 1,
26936
+ completedCount: 1,
26937
+ startedAt: Date.now(),
26938
+ completedAt: Date.now(),
26939
+ agentDirection: "inbound",
26940
+ agentResponseKey: responseKey,
26941
+ agentResponseHasBody: responseHasBody,
26942
+ agentResponseAggregate: false,
26943
+ agentResponseEntries: [{
26944
+ key: responseKey,
26945
+ raw: String(rawResult ?? "").trim(),
26946
+ result: synthetic.result,
26947
+ hasBody: responseHasBody,
26948
+ isError: synthetic.isError === true
26949
+ }]
26950
+ });
26951
+ return true;
26952
+ };
26582
26953
  const pushToast = (text, tone = "info", ttlMs = 3e3) => {
26583
26954
  const id = nextId();
26584
26955
  const value = String(text ?? "").trim();
@@ -26668,6 +27039,7 @@ async function createEngineSession({
26668
27039
  kickExecutionPendingResume,
26669
27040
  flushDeferredExecutionPendingResumeKick,
26670
27041
  scheduleExecutionPendingResumeKick,
27042
+ discardExecutionPendingResume,
26671
27043
  updateAgentJobCard,
26672
27044
  buildAgentJobCardPatch,
26673
27045
  subscribeRuntimeNotifications
@@ -26681,6 +27053,7 @@ async function createEngineSession({
26681
27053
  enqueue: (...args) => bag.enqueue(...args),
26682
27054
  drain: (...args) => bag.drain(...args),
26683
27055
  pushUserOrSyntheticItem,
27056
+ pushAsyncAgentResponse,
26684
27057
  makeQueueEntry: (...args) => bag.makeQueueEntry(...args),
26685
27058
  getPending: () => pending,
26686
27059
  agentStatusState,
@@ -26728,6 +27101,7 @@ async function createEngineSession({
26728
27101
  removeNotice,
26729
27102
  setProgressHint,
26730
27103
  pushUserOrSyntheticItem,
27104
+ pushAsyncAgentResponse,
26731
27105
  upsertSyntheticToolItem,
26732
27106
  markToolCallActive,
26733
27107
  markToolCallDone,
@@ -26747,6 +27121,7 @@ async function createEngineSession({
26747
27121
  kickExecutionPendingResume,
26748
27122
  flushDeferredExecutionPendingResumeKick,
26749
27123
  scheduleExecutionPendingResumeKick,
27124
+ discardExecutionPendingResume,
26750
27125
  updateAgentJobCard,
26751
27126
  subscribeRuntimeNotifications
26752
27127
  });