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.
- package/package.json +9 -4
- package/scripts/abort-recovery-test.mjs +43 -2
- package/scripts/agent-tag-reuse-smoke.mjs +146 -6
- package/scripts/agent-terminal-reap-test.mjs +127 -0
- package/scripts/agent-trace-io-test.mjs +69 -0
- package/scripts/code-graph-disk-hit-test.mjs +224 -0
- package/scripts/execution-completion-dedup-test.mjs +157 -0
- package/scripts/execution-pending-resume-kick-test.mjs +57 -2
- package/scripts/execution-resume-esc-integration-test.mjs +176 -0
- package/scripts/explore-bench.mjs +38 -2
- package/scripts/explore-prompt-policy-test.mjs +152 -11
- package/scripts/find-fuzzy-hidden-test.mjs +122 -0
- package/scripts/internal-comms-bench-test.mjs +226 -0
- package/scripts/internal-comms-bench.mjs +185 -58
- package/scripts/internal-comms-smoke.mjs +171 -23
- package/scripts/live-worker-smoke.mjs +38 -2
- package/scripts/memory-cycle-routing-test.mjs +111 -0
- package/scripts/memory-rule-contract-test.mjs +93 -0
- package/scripts/output-style-smoke.mjs +2 -2
- package/scripts/rg-runner-test.mjs +240 -0
- package/scripts/routing-corpus-test.mjs +349 -0
- package/scripts/routing-corpus.mjs +211 -32
- package/scripts/session-orphan-sweep-test.mjs +83 -0
- package/scripts/steering-drain-buckets-test.mjs +316 -2
- package/scripts/tool-smoke.mjs +21 -13
- package/scripts/tool-tui-presentation-test.mjs +202 -0
- package/scripts/tui-transcript-perf-test.mjs +279 -0
- package/src/agents/heavy-worker/AGENT.md +10 -7
- package/src/agents/reviewer/AGENT.md +6 -4
- package/src/agents/worker/AGENT.md +7 -5
- package/src/rules/agent/00-common.md +4 -4
- package/src/rules/agent/00-core.md +11 -14
- package/src/rules/agent/20-skip-protocol.md +3 -3
- package/src/rules/agent/30-explorer.md +50 -60
- package/src/rules/agent/40-cycle1-agent.md +15 -24
- package/src/rules/agent/41-cycle2-agent.md +33 -57
- package/src/rules/agent/42-cycle3-agent.md +28 -42
- package/src/rules/lead/01-general.md +7 -10
- package/src/rules/lead/lead-brief.md +11 -14
- package/src/rules/lead/lead-tool.md +6 -5
- package/src/rules/shared/01-tool.md +44 -45
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +44 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +20 -5
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +21 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +17 -38
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +17 -8
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +270 -78
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/delivered-completions.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +67 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +232 -43
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +43 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +27 -13
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +25 -20
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +118 -53
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +106 -29
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +8 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +33 -2
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +151 -64
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +37 -13
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +84 -49
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +172 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +68 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +17 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +24 -10
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +4 -7
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -4
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +4 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +12 -2
- package/src/runtime/shared/buffered-appender.mjs +13 -2
- package/src/runtime/shared/tool-primitives.mjs +4 -1
- package/src/runtime/shared/tool-status.mjs +27 -0
- package/src/runtime/shared/tool-surface.mjs +6 -3
- package/src/session-runtime/config-helpers.mjs +14 -0
- package/src/session-runtime/context-status.mjs +1 -0
- package/src/session-runtime/effort.mjs +6 -2
- package/src/session-runtime/lifecycle-api.mjs +4 -0
- package/src/session-runtime/model-recency.mjs +5 -2
- package/src/session-runtime/runtime-core.mjs +36 -2
- package/src/session-runtime/session-turn-api.mjs +12 -0
- package/src/session-runtime/tool-catalog.mjs +34 -0
- package/src/standalone/agent-tool/notify.mjs +13 -0
- package/src/standalone/agent-tool.mjs +53 -70
- package/src/standalone/explore-tool.mjs +6 -7
- package/src/tui/App.jsx +33 -1
- package/src/tui/app/model-options.mjs +5 -3
- package/src/tui/app/model-picker.mjs +12 -24
- package/src/tui/app/transcript-window.mjs +10 -10
- package/src/tui/app/use-transcript-window.mjs +38 -56
- package/src/tui/components/ToolExecution.jsx +11 -6
- package/src/tui/components/TranscriptItem.jsx +1 -1
- package/src/tui/components/tool-execution/surface-detail.mjs +24 -10
- package/src/tui/components/tool-execution/text-format.mjs +10 -19
- package/src/tui/dist/index.mjs +866 -300
- package/src/tui/engine/agent-job-feed.mjs +216 -19
- package/src/tui/engine/agent-response-tail.mjs +68 -0
- package/src/tui/engine/notification-plan.mjs +16 -0
- package/src/tui/engine/session-api.mjs +14 -2
- package/src/tui/engine/session-flow.mjs +22 -2
- package/src/tui/engine/tool-card-results.mjs +64 -37
- package/src/tui/engine/tool-result-status.mjs +75 -21
- package/src/tui/engine/turn.mjs +172 -77
- package/src/tui/engine.mjs +199 -39
- package/src/tui/index.jsx +2 -2
- package/src/workflows/bench/WORKFLOW.md +25 -35
- package/src/workflows/default/WORKFLOW.md +38 -32
- package/src/workflows/solo/WORKFLOW.md +19 -22
- package/scripts/_jitter-fuzz.mjs +0 -44410
- package/scripts/_jitter-fuzz2.mjs +0 -44400
- package/scripts/_jitter-probe.mjs +0 -44397
- package/scripts/_jp2.mjs +0 -45614
package/src/tui/dist/index.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 &&
|
|
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
|
-
|
|
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 =
|
|
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
|
|
11779
|
-
const
|
|
11780
|
-
const
|
|
11781
|
-
|
|
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(
|
|
11741
|
+
const transcriptRowIndex = useMemo(() => buildTranscriptRowIndexIncremental(transcriptItems, {
|
|
11789
11742
|
columns: frameColumns,
|
|
11790
11743
|
toolOutputExpanded,
|
|
11791
11744
|
suppressMeasuredRowHeights,
|
|
11792
11745
|
measuredRowsVersion,
|
|
11793
11746
|
cacheRef: incrementalRowIndexCacheRef,
|
|
11794
|
-
|
|
11795
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps --
|
|
11796
|
-
}), [
|
|
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(
|
|
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(
|
|
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:
|
|
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 = (
|
|
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 =
|
|
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
|
-
|
|
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
|
|
16132
|
-
if (!isActiveModelPicker()) return;
|
|
16086
|
+
const adoptFreshModels = (freshModels) => {
|
|
16133
16087
|
if (!Array.isArray(freshModels) || freshModels.length === 0) return;
|
|
16134
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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 (
|
|
18970
|
-
if (
|
|
18971
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22911
|
-
|
|
22912
|
-
const
|
|
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 =
|
|
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 =
|
|
23097
|
+
const prev = itemById(id);
|
|
23071
23098
|
const ok = patchItem(id, patch);
|
|
23072
23099
|
if (!ok || !prev) return ok;
|
|
23073
|
-
const next =
|
|
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
|
|
23087
|
-
const
|
|
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
|
|
23109
|
-
const
|
|
23110
|
-
const
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
23278
|
-
if (
|
|
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
|
|
23291
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
23566
|
+
refreshAgentStatus(parsed);
|
|
23333
23567
|
return true;
|
|
23334
23568
|
}
|
|
23335
23569
|
if (delivery.action === "execution-ui") {
|
|
23336
|
-
const
|
|
23337
|
-
|
|
23338
|
-
|
|
23339
|
-
|
|
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
|
-
|
|
23587
|
+
refreshAgentStatus(parsed);
|
|
23342
23588
|
const resumeBody = String(delivery.modelContent || "").trim();
|
|
23343
23589
|
if (resumeBody) {
|
|
23344
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
24668
|
+
const now2 = Date.now();
|
|
24669
|
+
const idleMs = now2 - lastProgressAt;
|
|
24285
24670
|
if (idleMs < LEAD_TURN_TIMEOUT_MS2) {
|
|
24286
|
-
|
|
24287
|
-
|
|
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
|
-
},
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
24681
|
-
if (
|
|
24682
|
-
|
|
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.
|
|
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
|
|
24736
|
-
const
|
|
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
|
|
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
|
|
24753
|
-
const
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
26983
|
+
state = replaceEngineItemsState({
|
|
26984
|
+
state,
|
|
26494
26985
|
items: nextItems,
|
|
26495
|
-
|
|
26496
|
-
|
|
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()}
|
|
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: () => {
|