mixdog 0.9.46 → 0.9.49
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 +14 -4
- package/scripts/agent-parallel-smoke.mjs +4 -1
- package/scripts/agent-route-batch-test.mjs +40 -0
- package/scripts/code-graph-aggregate-cwd-test.mjs +154 -0
- package/scripts/code-graph-description-contract.mjs +185 -0
- package/scripts/code-graph-disk-hit-test.mjs +55 -0
- package/scripts/code-graph-dispatch-test.mjs +96 -0
- package/scripts/compact-pressure-test.mjs +40 -0
- package/scripts/deferred-tool-loading-test.mjs +233 -0
- package/scripts/execution-completion-dedup-test.mjs +48 -0
- package/scripts/explore-prompt-policy-test.mjs +88 -3
- package/scripts/live-worker-smoke.mjs +68 -16
- package/scripts/memory-core-input-test.mjs +33 -13
- package/scripts/native-edit-wire-test.mjs +152 -0
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +294 -16
- package/scripts/patch-binary-cache-test.mjs +181 -0
- package/scripts/prompt-immediate-render-test.mjs +89 -0
- package/scripts/provider-toolcall-test.mjs +280 -15
- package/scripts/shell-failure-diagnostics-test.mjs +211 -0
- package/scripts/statusline-quota-hysteresis-test.mjs +26 -1
- package/scripts/streaming-tail-window-test.mjs +29 -0
- package/scripts/tool-failures.mjs +21 -3
- package/scripts/tool-smoke.mjs +263 -38
- package/scripts/tool-tui-presentation-test.mjs +17 -1
- package/scripts/tui-perf-run.ps1 +26 -0
- package/scripts/tui-transcript-perf-test.mjs +7 -1
- package/scripts/verify-release-assets-test.mjs +647 -0
- package/scripts/verify-release-assets.mjs +293 -0
- package/scripts/windows-hide-spawn-options-test.mjs +19 -0
- package/src/cli.mjs +1 -0
- package/src/defaults/cycle3-review-prompt.md +10 -4
- package/src/defaults/memory-promote-prompt.md +4 -3
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +16 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +35 -11
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +35 -11
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +34 -34
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +45 -38
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +36 -11
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +10 -6
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +13 -8
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +30 -16
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +25 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +69 -4
- package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +12 -12
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +12 -7
- package/src/runtime/agent/orchestrator/tools/patch-binary-fetcher.mjs +77 -18
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +99 -24
- package/src/runtime/memory/lib/memory-action-handlers.mjs +21 -11
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +13 -5
- package/src/runtime/memory/lib/memory-cycle2.mjs +8 -5
- package/src/runtime/memory/lib/memory-cycle3.mjs +41 -6
- package/src/runtime/memory/lib/query-handlers.mjs +49 -6
- package/src/runtime/memory/lib/recall-format.mjs +21 -6
- package/src/runtime/memory/tool-defs.mjs +2 -3
- package/src/session-runtime/context-status.mjs +25 -16
- package/src/session-runtime/model-route-api.mjs +2 -0
- package/src/session-runtime/runtime-core.mjs +2 -2
- package/src/session-runtime/session-turn-api.mjs +4 -1
- package/src/session-runtime/tool-catalog.mjs +113 -19
- package/src/session-runtime/tool-defs.mjs +1 -0
- package/src/standalone/agent-tool/helpers.mjs +25 -6
- package/src/standalone/agent-tool.mjs +75 -41
- package/src/tui/App.jsx +4 -0
- package/src/tui/app/input-parsers.mjs +8 -9
- package/src/tui/components/Markdown.jsx +6 -1
- package/src/tui/components/Message.jsx +11 -2
- package/src/tui/components/PromptInput.jsx +19 -21
- package/src/tui/components/Spinner.jsx +4 -4
- package/src/tui/components/StatusLine.jsx +6 -6
- package/src/tui/components/TranscriptItem.jsx +2 -2
- package/src/tui/components/prompt-input/immediate-render.mjs +47 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +14 -9
- package/src/tui/dist/index.mjs +130 -45
- package/src/tui/engine/agent-job-feed.mjs +21 -2
- package/src/tui/engine/turn.mjs +12 -1
- package/src/tui/markdown/measure-rendered-rows.mjs +1 -1
- package/src/tui/markdown/streaming-markdown.mjs +20 -0
- package/src/ui/statusline.mjs +5 -5
- package/src/vendor/statusline/src/gateway/session-routes.mjs +22 -10
|
@@ -28,6 +28,7 @@ export {
|
|
|
28
28
|
resetStreamingMarkdownStablePrefix,
|
|
29
29
|
resetAllStreamingMarkdownStablePrefixes,
|
|
30
30
|
streamingLayoutText,
|
|
31
|
+
windowPlainStreamingText,
|
|
31
32
|
} from '../markdown/streaming-markdown.mjs';
|
|
32
33
|
export {
|
|
33
34
|
measureMarkdownRenderedRows,
|
|
@@ -82,7 +83,11 @@ export function Markdown({ children, themeEpoch = 0, trimPartialFences = false,
|
|
|
82
83
|
export function StreamingMarkdown({ children, themeEpoch = 0, columns, streamKey }) {
|
|
83
84
|
const parts = resolveStreamingMarkdownParts(children, streamKey);
|
|
84
85
|
if (parts.plain) {
|
|
85
|
-
|
|
86
|
+
// Plain streaming text has no markdown tokens to style. Sending a growing
|
|
87
|
+
// multi-line tail through renderTokenAnsiSegments/marked on every delta
|
|
88
|
+
// reparses the whole response and dominates frame time; Ink can wrap the
|
|
89
|
+
// identical visible text directly.
|
|
90
|
+
return <Text color={theme.text} wrap="wrap">{parts.unstableForRender}</Text>;
|
|
86
91
|
}
|
|
87
92
|
return (
|
|
88
93
|
<Box flexDirection="column" gap={1}>
|
|
@@ -15,7 +15,12 @@
|
|
|
15
15
|
import React from 'react';
|
|
16
16
|
import { Box, Text } from 'ink';
|
|
17
17
|
import { theme, TURN_MARKER } from '../theme.mjs';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Markdown,
|
|
20
|
+
StreamingMarkdown,
|
|
21
|
+
resetStreamingMarkdownStablePrefix,
|
|
22
|
+
windowPlainStreamingText,
|
|
23
|
+
} from './Markdown.jsx';
|
|
19
24
|
import { assistantBodyWidth } from '../markdown/table-layout.mjs';
|
|
20
25
|
|
|
21
26
|
// `themeEpoch` is a memo-busting prop (threaded from App): the active theme
|
|
@@ -28,6 +33,7 @@ export const AssistantMessage = React.memo(function AssistantMessage({
|
|
|
28
33
|
columns = 80,
|
|
29
34
|
themeEpoch = 0,
|
|
30
35
|
assistantId,
|
|
36
|
+
streamingWindowRows = 0,
|
|
31
37
|
}) {
|
|
32
38
|
// The body column needs an EXPLICIT numeric width. Without it, ink/Yoga
|
|
33
39
|
// measures the wrapped markdown body before the row's available width is
|
|
@@ -43,6 +49,9 @@ export const AssistantMessage = React.memo(function AssistantMessage({
|
|
|
43
49
|
}, [streaming, assistantId]);
|
|
44
50
|
|
|
45
51
|
const bodyWidth = assistantBodyWidth(columns);
|
|
52
|
+
const renderText = streaming && streamingWindowRows > 0
|
|
53
|
+
? windowPlainStreamingText(text, bodyWidth, streamingWindowRows)
|
|
54
|
+
: text;
|
|
46
55
|
return (
|
|
47
56
|
<Box flexDirection="row" marginTop={1}>
|
|
48
57
|
<Box flexShrink={0} minWidth={2}>
|
|
@@ -50,7 +59,7 @@ export const AssistantMessage = React.memo(function AssistantMessage({
|
|
|
50
59
|
</Box>
|
|
51
60
|
<Box flexDirection="column" flexShrink={0} width={bodyWidth}>
|
|
52
61
|
{streaming
|
|
53
|
-
? <StreamingMarkdown themeEpoch={themeEpoch} columns={bodyWidth} streamKey={assistantId}>{
|
|
62
|
+
? <StreamingMarkdown themeEpoch={themeEpoch} columns={bodyWidth} streamKey={assistantId}>{renderText}</StreamingMarkdown>
|
|
54
63
|
: <Markdown themeEpoch={themeEpoch} columns={bodyWidth}>{text}</Markdown>}
|
|
55
64
|
</Box>
|
|
56
65
|
</Box>
|
|
@@ -49,6 +49,10 @@ import {
|
|
|
49
49
|
isModifiedEnterSequence,
|
|
50
50
|
isAnyModifiedEnterSequence,
|
|
51
51
|
} from './prompt-input/edit-helpers.mjs';
|
|
52
|
+
import {
|
|
53
|
+
cancelPromptImmediateFlush,
|
|
54
|
+
schedulePromptImmediateFlush,
|
|
55
|
+
} from './prompt-input/immediate-render.mjs';
|
|
52
56
|
|
|
53
57
|
// Windows Terminal IME composition can clip a glyph that starts exactly at the
|
|
54
58
|
// left edge of the editable text node. The rounded prompt box already adds a
|
|
@@ -108,6 +112,7 @@ export function PromptInput({
|
|
|
108
112
|
});
|
|
109
113
|
const [, bumpCursorAnchorEpoch] = useState(0);
|
|
110
114
|
const draftRef = useRef(draft);
|
|
115
|
+
const interruptActiveRef = useRef(interruptActive);
|
|
111
116
|
const lastReportedValueRef = useRef(draft.value);
|
|
112
117
|
// Bumped on every submit/draftOverride replace/unmount so an async paste
|
|
113
118
|
// (clipboard read or onPasteText promise) that resolves after the draft
|
|
@@ -132,6 +137,7 @@ export function PromptInput({
|
|
|
132
137
|
const undoRef = useRef({ past: [], future: [], lastPushAt: 0, lastValue: null });
|
|
133
138
|
const { value, cursor } = draft;
|
|
134
139
|
draftRef.current = draft;
|
|
140
|
+
interruptActiveRef.current = interruptActive;
|
|
135
141
|
if (selectionRef) {
|
|
136
142
|
const range = selectionRange(draft);
|
|
137
143
|
selectionRef.current = range
|
|
@@ -158,30 +164,22 @@ export function PromptInput({
|
|
|
158
164
|
}
|
|
159
165
|
};
|
|
160
166
|
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
// batch the rest to one flush per ~16ms, with a trailing flush so the final
|
|
166
|
-
// char of a burst always lands.
|
|
167
|
-
const FLUSH_INTERVAL_MS = 16;
|
|
167
|
+
// During an active turn, ink is already painting streaming updates at its
|
|
168
|
+
// 60fps budget. A second unthrottled input render competes with those frames
|
|
169
|
+
// and makes key echo slower, so busy input stays on ink's normal render path.
|
|
170
|
+
// Idle input keeps the leading+trailing immediate flush for crisp caret echo.
|
|
168
171
|
const scheduleImmediateFlush = () => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
t.lastAt = now;
|
|
175
|
-
queueMicrotask(flushImmediate);
|
|
176
|
-
} else if (t.timer === null) {
|
|
177
|
-
t.timer = setTimeout(() => {
|
|
178
|
-
t.timer = null;
|
|
179
|
-
t.lastAt = Date.now();
|
|
180
|
-
flushImmediate();
|
|
181
|
-
}, FLUSH_INTERVAL_MS - elapsed);
|
|
182
|
-
}
|
|
172
|
+
schedulePromptImmediateFlush({
|
|
173
|
+
throttle: flushThrottleRef.current,
|
|
174
|
+
isSuppressed: () => interruptActiveRef.current,
|
|
175
|
+
flush: flushImmediate,
|
|
176
|
+
});
|
|
183
177
|
};
|
|
184
178
|
|
|
179
|
+
useEffect(() => () => {
|
|
180
|
+
cancelPromptImmediateFlush(flushThrottleRef.current);
|
|
181
|
+
}, []);
|
|
182
|
+
|
|
185
183
|
const commitDraft = (next, options = {}) => {
|
|
186
184
|
const sameDraft = draftStateEqual(draftRef.current, next);
|
|
187
185
|
if (!options.keepPreferredColumn) preferredColumnRef.current = null;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* is mode-driven: up while requesting, down otherwise. Input totals hidden.
|
|
16
16
|
* - elided duration formatting ("0:25" after 60s).
|
|
17
17
|
* - mode prop: 'responding' | 'thinking' | 'tool-use' | 'tool-input' |
|
|
18
|
-
* 'requesting' | 'compacting' | 'resuming' (default 'responding').
|
|
18
|
+
* 'requesting' | 'reconnecting' | 'compacting' | 'resuming' (default 'responding').
|
|
19
19
|
*/
|
|
20
20
|
import React, { useRef } from 'react';
|
|
21
21
|
import { Box, Text } from 'ink';
|
|
@@ -40,7 +40,7 @@ const SHOW_TOKENS_AFTER_MS = 30_000;
|
|
|
40
40
|
const THINKING_DELAY_MS = 3000;
|
|
41
41
|
|
|
42
42
|
// One-way shimmer. The tail runs past the final character before restarting.
|
|
43
|
-
const GLIMMER_SPEED_MS = { requesting: 70, compacting: 120, 'auto-clear': 120, resuming: 120, 'tool-use': 120, responding: 120, thinking: 120, 'tool-input': 120 };
|
|
43
|
+
const GLIMMER_SPEED_MS = { requesting: 70, reconnecting: 70, compacting: 120, 'auto-clear': 120, resuming: 120, 'tool-use': 120, responding: 120, thinking: 120, 'tool-input': 120 };
|
|
44
44
|
const GLIMMER_TRAIL = 4;
|
|
45
45
|
const THINKING_GLIMMER_SPEED_MS = 120;
|
|
46
46
|
const THINKING_GLIMMER_TRAIL = 4;
|
|
@@ -230,7 +230,7 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
|
|
|
230
230
|
: theme.spinnerGlyph;
|
|
231
231
|
|
|
232
232
|
// --- Verb shimmer (traveling highlight) ---
|
|
233
|
-
if (!displayVerbRef.current) {
|
|
233
|
+
if (!displayVerbRef.current || displayVerbModeRef.current !== mode) {
|
|
234
234
|
displayVerbRef.current = stableModeVerb(mode, verb);
|
|
235
235
|
nextVerbCheckRef.current = nextVerbCheckAt(now);
|
|
236
236
|
}
|
|
@@ -240,7 +240,7 @@ export function Spinner({ verb = 'Working', startedAt, outputTokens = 0, tokens
|
|
|
240
240
|
nextVerbCheckRef.current = nextVerbCheckAt(now);
|
|
241
241
|
}
|
|
242
242
|
const displayVerb = displayVerbRef.current;
|
|
243
|
-
const messageText = `${displayVerb}…`;
|
|
243
|
+
const messageText = mode === 'reconnecting' ? displayVerb : `${displayVerb}…`;
|
|
244
244
|
const messageLen = messageText.length;
|
|
245
245
|
|
|
246
246
|
// Glimmer speed per mode.
|
|
@@ -152,18 +152,18 @@ function localContextPct({
|
|
|
152
152
|
compactBoundaryTokens = 0,
|
|
153
153
|
autoCompactTokenLimit = 0,
|
|
154
154
|
} = {}) {
|
|
155
|
-
const
|
|
155
|
+
const boundary = localNum(compactBoundaryTokens) > 0
|
|
156
156
|
? localNum(compactBoundaryTokens)
|
|
157
157
|
: (localNum(displayContextWindow) > 0
|
|
158
158
|
? localNum(displayContextWindow)
|
|
159
159
|
: (localNum(contextWindow) > 0
|
|
160
160
|
? localNum(contextWindow)
|
|
161
161
|
: (localNum(rawContextWindow) > 0 ? localNum(rawContextWindow) : 200_000)));
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
162
|
+
// The trigger is the denominator used by the pre-send auto-compact decision,
|
|
163
|
+
// so the local boot gauge reaches 100% at the exact same pressure.
|
|
164
|
+
const window = localNum(autoCompactTokenLimit) > 0
|
|
165
|
+
? localNum(autoCompactTokenLimit)
|
|
166
|
+
: boundary;
|
|
167
167
|
const s = stats && typeof stats === 'object' ? stats : {};
|
|
168
168
|
const source = String(s.currentContextSource || '').toLowerCase();
|
|
169
169
|
const estimated = localNum(s.currentEstimatedContextTokens);
|
|
@@ -61,7 +61,7 @@ export function ToolHookDenialCard({ item, columns = 80 }) {
|
|
|
61
61
|
// transcript row (which reads theme.* directly) to re-render. Threading the
|
|
62
62
|
// epoch through Item → AssistantMessage/UserMessage/ToolExecution breaks
|
|
63
63
|
// React.memo's shallow equality on a theme change without a broad refactor.
|
|
64
|
-
export const Item = React.memo(function Item({ item, prevKind, columns, toolOutputExpanded, rightMessage = '', rightTone = 'info', rightMessageWidth = 24, themeEpoch = 0 }) {
|
|
64
|
+
export const Item = React.memo(function Item({ item, prevKind, columns, toolOutputExpanded, rightMessage = '', rightTone = 'info', rightMessageWidth = 24, themeEpoch = 0, streamingWindowRows = 0 }) {
|
|
65
65
|
const hintOnTurnDoneRow = item.kind === 'turndone' || item.kind === 'statusdone';
|
|
66
66
|
let node = null;
|
|
67
67
|
switch (item.kind) {
|
|
@@ -69,7 +69,7 @@ export const Item = React.memo(function Item({ item, prevKind, columns, toolOutp
|
|
|
69
69
|
node = <UserMessage text={item.text} attached={prevKind === 'user'} columns={columns} themeEpoch={themeEpoch} />;
|
|
70
70
|
break;
|
|
71
71
|
case 'assistant':
|
|
72
|
-
node = <AssistantMessage text={item.text} streaming={item.streaming} columns={columns} themeEpoch={themeEpoch} assistantId={item.id} />;
|
|
72
|
+
node = <AssistantMessage text={item.text} streaming={item.streaming} columns={columns} themeEpoch={themeEpoch} assistantId={item.id} streamingWindowRows={streamingWindowRows} />;
|
|
73
73
|
break;
|
|
74
74
|
case 'tool': {
|
|
75
75
|
if (shouldSuppressFullyFailedToolItem(item)) return null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function suppressed(value) {
|
|
2
|
+
return typeof value === 'function' ? value() === true : value === true;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function cancelPromptImmediateFlush(throttle, clearTimer = clearTimeout) {
|
|
6
|
+
if (!throttle || throttle.timer === null) return false;
|
|
7
|
+
clearTimer(throttle.timer);
|
|
8
|
+
throttle.timer = null;
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function schedulePromptImmediateFlush({
|
|
13
|
+
throttle,
|
|
14
|
+
isSuppressed = false,
|
|
15
|
+
flush,
|
|
16
|
+
intervalMs = 16,
|
|
17
|
+
now = Date.now,
|
|
18
|
+
enqueue = queueMicrotask,
|
|
19
|
+
setTimer = setTimeout,
|
|
20
|
+
clearTimer = clearTimeout,
|
|
21
|
+
}) {
|
|
22
|
+
if (!throttle || typeof flush !== 'function') {
|
|
23
|
+
throw new TypeError('schedulePromptImmediateFlush requires throttle state and a flush function');
|
|
24
|
+
}
|
|
25
|
+
if (suppressed(isSuppressed)) {
|
|
26
|
+
cancelPromptImmediateFlush(throttle, clearTimer);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const current = now();
|
|
31
|
+
const elapsed = current - throttle.lastAt;
|
|
32
|
+
if (elapsed >= intervalMs) {
|
|
33
|
+
cancelPromptImmediateFlush(throttle, clearTimer);
|
|
34
|
+
throttle.lastAt = current;
|
|
35
|
+
enqueue(flush);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (throttle.timer !== null) return false;
|
|
39
|
+
|
|
40
|
+
throttle.timer = setTimer(() => {
|
|
41
|
+
throttle.timer = null;
|
|
42
|
+
if (suppressed(isSuppressed)) return;
|
|
43
|
+
throttle.lastAt = now();
|
|
44
|
+
flush();
|
|
45
|
+
}, intervalMs - elapsed);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
@@ -353,19 +353,24 @@ export function toolSearchLoadedSummary(resultText) {
|
|
|
353
353
|
parsed = JSON.parse(String(resultText || ''));
|
|
354
354
|
} catch {
|
|
355
355
|
const text = String(resultText || '');
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
return [
|
|
356
|
+
const loaded = /^Loaded deferred tools:\s*(.+)$/m.exec(text)?.[1] || '';
|
|
357
|
+
const already = /^Already active:\s*(.+)$/m.exec(text)?.[1] || '';
|
|
358
|
+
return [
|
|
359
|
+
...(loaded ? [`Loaded: ${loaded}`] : []),
|
|
360
|
+
...(already ? [`Already active: ${already}`] : []),
|
|
361
|
+
].join(' · ');
|
|
359
362
|
}
|
|
360
363
|
const tools = parsed?.selected?.tools;
|
|
361
364
|
if (!tools || typeof tools !== 'object') return '';
|
|
362
|
-
const
|
|
363
|
-
...(Array.isArray(tools.added) ? tools.added : []),
|
|
364
|
-
...(Array.isArray(tools.already) ? tools.already : []),
|
|
365
|
-
]
|
|
365
|
+
const uniqueNames = (names) => [...new Set((Array.isArray(names) ? names : [])
|
|
366
366
|
.map((name) => String(name || '').trim())
|
|
367
|
-
.filter(Boolean);
|
|
368
|
-
|
|
367
|
+
.filter(Boolean))];
|
|
368
|
+
const loaded = uniqueNames(tools.added);
|
|
369
|
+
const already = uniqueNames(tools.already);
|
|
370
|
+
return [
|
|
371
|
+
...(loaded.length ? [`Loaded: ${loaded.join(', ')}`] : []),
|
|
372
|
+
...(already.length ? [`Already active: ${already.join(', ')}`] : []),
|
|
373
|
+
].join(' · ');
|
|
369
374
|
}
|
|
370
375
|
|
|
371
376
|
export function agentTerminalDetail(status, isError, elapsed, error = '') {
|
package/src/tui/dist/index.mjs
CHANGED
|
@@ -3512,7 +3512,7 @@ var STALL_TIMEOUT_MS = 3e3;
|
|
|
3512
3512
|
var STALL_FADE_MS = 2e3;
|
|
3513
3513
|
var SHOW_TOKENS_AFTER_MS = 3e4;
|
|
3514
3514
|
var THINKING_DELAY_MS = 3e3;
|
|
3515
|
-
var GLIMMER_SPEED_MS = { requesting: 70, compacting: 120, "auto-clear": 120, resuming: 120, "tool-use": 120, responding: 120, thinking: 120, "tool-input": 120 };
|
|
3515
|
+
var GLIMMER_SPEED_MS = { requesting: 70, reconnecting: 70, compacting: 120, "auto-clear": 120, resuming: 120, "tool-use": 120, responding: 120, thinking: 120, "tool-input": 120 };
|
|
3516
3516
|
var GLIMMER_TRAIL = 4;
|
|
3517
3517
|
var THINKING_GLIMMER_SPEED_MS = 120;
|
|
3518
3518
|
var THINKING_GLIMMER_TRAIL = 4;
|
|
@@ -3649,7 +3649,7 @@ function Spinner({ verb = "Working", startedAt, outputTokens = 0, tokens = 0, th
|
|
|
3649
3649
|
STALL_RGB,
|
|
3650
3650
|
stalledIntensity
|
|
3651
3651
|
)) : theme.spinnerGlyph;
|
|
3652
|
-
if (!displayVerbRef.current) {
|
|
3652
|
+
if (!displayVerbRef.current || displayVerbModeRef.current !== mode) {
|
|
3653
3653
|
displayVerbRef.current = stableModeVerb(mode, verb);
|
|
3654
3654
|
nextVerbCheckRef.current = nextVerbCheckAt(now);
|
|
3655
3655
|
}
|
|
@@ -3659,7 +3659,7 @@ function Spinner({ verb = "Working", startedAt, outputTokens = 0, tokens = 0, th
|
|
|
3659
3659
|
nextVerbCheckRef.current = nextVerbCheckAt(now);
|
|
3660
3660
|
}
|
|
3661
3661
|
const displayVerb = displayVerbRef.current;
|
|
3662
|
-
const messageText = `${displayVerb}\u2026`;
|
|
3662
|
+
const messageText = mode === "reconnecting" ? displayVerb : `${displayVerb}\u2026`;
|
|
3663
3663
|
const messageLen = messageText.length;
|
|
3664
3664
|
const glimmerSpeed = GLIMMER_SPEED_MS[mode] ?? 200;
|
|
3665
3665
|
const shimmerSpan = Math.max(1, messageLen + GLIMMER_TRAIL);
|
|
@@ -4057,8 +4057,8 @@ function localContextPct({
|
|
|
4057
4057
|
compactBoundaryTokens = 0,
|
|
4058
4058
|
autoCompactTokenLimit = 0
|
|
4059
4059
|
} = {}) {
|
|
4060
|
-
const
|
|
4061
|
-
const window =
|
|
4060
|
+
const boundary = localNum(compactBoundaryTokens) > 0 ? localNum(compactBoundaryTokens) : localNum(displayContextWindow) > 0 ? localNum(displayContextWindow) : localNum(contextWindow) > 0 ? localNum(contextWindow) : localNum(rawContextWindow) > 0 ? localNum(rawContextWindow) : 2e5;
|
|
4061
|
+
const window = localNum(autoCompactTokenLimit) > 0 ? localNum(autoCompactTokenLimit) : boundary;
|
|
4062
4062
|
const s = stats && typeof stats === "object" ? stats : {};
|
|
4063
4063
|
const source = String(s.currentContextSource || "").toLowerCase();
|
|
4064
4064
|
const estimated = localNum(s.currentEstimatedContextTokens);
|
|
@@ -4784,6 +4784,51 @@ function isAnyModifiedEnterSequence(input) {
|
|
|
4784
4784
|
return Boolean(modifyOtherKeys && Number(modifyOtherKeys[1]) - 1 !== 0);
|
|
4785
4785
|
}
|
|
4786
4786
|
|
|
4787
|
+
// src/tui/components/prompt-input/immediate-render.mjs
|
|
4788
|
+
function suppressed(value) {
|
|
4789
|
+
return typeof value === "function" ? value() === true : value === true;
|
|
4790
|
+
}
|
|
4791
|
+
function cancelPromptImmediateFlush(throttle, clearTimer = clearTimeout) {
|
|
4792
|
+
if (!throttle || throttle.timer === null) return false;
|
|
4793
|
+
clearTimer(throttle.timer);
|
|
4794
|
+
throttle.timer = null;
|
|
4795
|
+
return true;
|
|
4796
|
+
}
|
|
4797
|
+
function schedulePromptImmediateFlush({
|
|
4798
|
+
throttle,
|
|
4799
|
+
isSuppressed = false,
|
|
4800
|
+
flush,
|
|
4801
|
+
intervalMs = 16,
|
|
4802
|
+
now = Date.now,
|
|
4803
|
+
enqueue = queueMicrotask,
|
|
4804
|
+
setTimer = setTimeout,
|
|
4805
|
+
clearTimer = clearTimeout
|
|
4806
|
+
}) {
|
|
4807
|
+
if (!throttle || typeof flush !== "function") {
|
|
4808
|
+
throw new TypeError("schedulePromptImmediateFlush requires throttle state and a flush function");
|
|
4809
|
+
}
|
|
4810
|
+
if (suppressed(isSuppressed)) {
|
|
4811
|
+
cancelPromptImmediateFlush(throttle, clearTimer);
|
|
4812
|
+
return false;
|
|
4813
|
+
}
|
|
4814
|
+
const current = now();
|
|
4815
|
+
const elapsed = current - throttle.lastAt;
|
|
4816
|
+
if (elapsed >= intervalMs) {
|
|
4817
|
+
cancelPromptImmediateFlush(throttle, clearTimer);
|
|
4818
|
+
throttle.lastAt = current;
|
|
4819
|
+
enqueue(flush);
|
|
4820
|
+
return true;
|
|
4821
|
+
}
|
|
4822
|
+
if (throttle.timer !== null) return false;
|
|
4823
|
+
throttle.timer = setTimer(() => {
|
|
4824
|
+
throttle.timer = null;
|
|
4825
|
+
if (suppressed(isSuppressed)) return;
|
|
4826
|
+
throttle.lastAt = now();
|
|
4827
|
+
flush();
|
|
4828
|
+
}, intervalMs - elapsed);
|
|
4829
|
+
return true;
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4787
4832
|
// src/tui/components/PromptInput.jsx
|
|
4788
4833
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
4789
4834
|
var IME_LEFT_GUARD_COLUMNS = 0;
|
|
@@ -4832,6 +4877,7 @@ function PromptInput({
|
|
|
4832
4877
|
});
|
|
4833
4878
|
const [, bumpCursorAnchorEpoch] = useState3(0);
|
|
4834
4879
|
const draftRef = useRef4(draft);
|
|
4880
|
+
const interruptActiveRef = useRef4(interruptActive);
|
|
4835
4881
|
const lastReportedValueRef = useRef4(draft.value);
|
|
4836
4882
|
const pasteGenerationRef = useRef4(0);
|
|
4837
4883
|
if (valueRef) valueRef.current = draftRef.current.value;
|
|
@@ -4844,6 +4890,7 @@ function PromptInput({
|
|
|
4844
4890
|
const undoRef = useRef4({ past: [], future: [], lastPushAt: 0, lastValue: null });
|
|
4845
4891
|
const { value, cursor } = draft;
|
|
4846
4892
|
draftRef.current = draft;
|
|
4893
|
+
interruptActiveRef.current = interruptActive;
|
|
4847
4894
|
if (selectionRef) {
|
|
4848
4895
|
const range = selectionRange(draft);
|
|
4849
4896
|
selectionRef.current = range ? { range, text: mask ? "" : draft.value.slice(range.start, range.end) } : null;
|
|
@@ -4859,26 +4906,16 @@ function PromptInput({
|
|
|
4859
4906
|
node = node.parentNode;
|
|
4860
4907
|
}
|
|
4861
4908
|
};
|
|
4862
|
-
const FLUSH_INTERVAL_MS = 16;
|
|
4863
4909
|
const scheduleImmediateFlush = () => {
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
clearTimeout(t.timer);
|
|
4870
|
-
t.timer = null;
|
|
4871
|
-
}
|
|
4872
|
-
t.lastAt = now;
|
|
4873
|
-
queueMicrotask(flushImmediate);
|
|
4874
|
-
} else if (t.timer === null) {
|
|
4875
|
-
t.timer = setTimeout(() => {
|
|
4876
|
-
t.timer = null;
|
|
4877
|
-
t.lastAt = Date.now();
|
|
4878
|
-
flushImmediate();
|
|
4879
|
-
}, FLUSH_INTERVAL_MS - elapsed);
|
|
4880
|
-
}
|
|
4910
|
+
schedulePromptImmediateFlush({
|
|
4911
|
+
throttle: flushThrottleRef.current,
|
|
4912
|
+
isSuppressed: () => interruptActiveRef.current,
|
|
4913
|
+
flush: flushImmediate
|
|
4914
|
+
});
|
|
4881
4915
|
};
|
|
4916
|
+
useEffect3(() => () => {
|
|
4917
|
+
cancelPromptImmediateFlush(flushThrottleRef.current);
|
|
4918
|
+
}, []);
|
|
4882
4919
|
const commitDraft = (next, options = {}) => {
|
|
4883
4920
|
const sameDraft = draftStateEqual(draftRef.current, next);
|
|
4884
4921
|
if (!options.keepPreferredColumn) preferredColumnRef.current = null;
|
|
@@ -8114,14 +8151,14 @@ function parseMemoryCoreRows(text) {
|
|
|
8114
8151
|
currentProjectId = label === "COMMON" ? null : label;
|
|
8115
8152
|
return null;
|
|
8116
8153
|
}
|
|
8117
|
-
const match = raw.match(/^id=(\d+)\s
|
|
8154
|
+
const match = raw.match(/^id=(\d+)\s+(.+?)(?:\s+—\s+(.+))?$/);
|
|
8118
8155
|
if (match) {
|
|
8119
|
-
const [, id,
|
|
8156
|
+
const [, id, element, summary = ""] = match;
|
|
8120
8157
|
return {
|
|
8121
8158
|
value: `core-${id}`,
|
|
8122
8159
|
// Display is summary-first: session injection only ever uses the
|
|
8123
|
-
// summary sentence (buildSessionCoreMemoryPayload), so id/
|
|
8124
|
-
//
|
|
8160
|
+
// summary sentence (buildSessionCoreMemoryPayload), so id/element
|
|
8161
|
+
// are UI noise. The element stays in the hidden fields for
|
|
8125
8162
|
// edit/delete plumbing.
|
|
8126
8163
|
label: `#${id}`,
|
|
8127
8164
|
meta: currentProjectId || "",
|
|
@@ -8129,7 +8166,6 @@ function parseMemoryCoreRows(text) {
|
|
|
8129
8166
|
_line: raw,
|
|
8130
8167
|
_action: "core-entry",
|
|
8131
8168
|
_id: Number(id),
|
|
8132
|
-
_category: category,
|
|
8133
8169
|
_element: element,
|
|
8134
8170
|
_summary: summary || element,
|
|
8135
8171
|
_projectId: currentProjectId,
|
|
@@ -9293,6 +9329,24 @@ var STABLE_PREFIX_LRU_MAX = 32;
|
|
|
9293
9329
|
function streamingLayoutText(text) {
|
|
9294
9330
|
return String(text ?? "").replace(/^\n+|\n+$/g, "");
|
|
9295
9331
|
}
|
|
9332
|
+
function windowPlainStreamingText(text, columns, maxRows) {
|
|
9333
|
+
const value = streamingLayoutText(text);
|
|
9334
|
+
const rowBudget = Math.max(0, Math.floor(Number(maxRows) || 0));
|
|
9335
|
+
if (!value || rowBudget <= 0 || hasMarkdownSyntax(value)) return value;
|
|
9336
|
+
const lines = value.split("\n");
|
|
9337
|
+
const width = Math.max(1, Math.floor(Number(columns) || 80));
|
|
9338
|
+
let rows = 0;
|
|
9339
|
+
let start = lines.length;
|
|
9340
|
+
while (start > 0) {
|
|
9341
|
+
const line = lines[start - 1];
|
|
9342
|
+
const lineRows = Math.max(1, Math.ceil(displayWidth(line) / width));
|
|
9343
|
+
if (rows > 0 && rows + lineRows > rowBudget) break;
|
|
9344
|
+
rows += lineRows;
|
|
9345
|
+
start -= 1;
|
|
9346
|
+
if (rows >= rowBudget) break;
|
|
9347
|
+
}
|
|
9348
|
+
return start > 0 ? lines.slice(start).join("\n") : value;
|
|
9349
|
+
}
|
|
9296
9350
|
function isWhitespaceOnlyText(text) {
|
|
9297
9351
|
return !String(text ?? "").trim();
|
|
9298
9352
|
}
|
|
@@ -9532,7 +9586,7 @@ function measureStreamingMarkdownRenderedRows(text, columns, streamKey) {
|
|
|
9532
9586
|
if (!value) return 1;
|
|
9533
9587
|
const parts = resolveStreamingMarkdownParts(value, streamKey);
|
|
9534
9588
|
if (parts.plain) {
|
|
9535
|
-
return
|
|
9589
|
+
return estimateWrappedRowsFallback(parts.unstableForRender, columns, 3);
|
|
9536
9590
|
}
|
|
9537
9591
|
let rows = 0;
|
|
9538
9592
|
let childCount = 0;
|
|
@@ -18532,7 +18586,7 @@ function Markdown({ children, themeEpoch = 0, trimPartialFences = false, columns
|
|
|
18532
18586
|
function StreamingMarkdown({ children, themeEpoch = 0, columns, streamKey }) {
|
|
18533
18587
|
const parts = resolveStreamingMarkdownParts(children, streamKey);
|
|
18534
18588
|
if (parts.plain) {
|
|
18535
|
-
return /* @__PURE__ */ jsx13(
|
|
18589
|
+
return /* @__PURE__ */ jsx13(Text13, { color: theme.text, wrap: "wrap", children: parts.unstableForRender });
|
|
18536
18590
|
}
|
|
18537
18591
|
return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", gap: 1, children: [
|
|
18538
18592
|
parts.stablePrefix ? /* @__PURE__ */ jsx13(Markdown, { themeEpoch, columns, children: parts.stablePrefix }) : null,
|
|
@@ -18547,15 +18601,17 @@ var AssistantMessage = React14.memo(function AssistantMessage2({
|
|
|
18547
18601
|
streaming = false,
|
|
18548
18602
|
columns = 80,
|
|
18549
18603
|
themeEpoch = 0,
|
|
18550
|
-
assistantId
|
|
18604
|
+
assistantId,
|
|
18605
|
+
streamingWindowRows = 0
|
|
18551
18606
|
}) {
|
|
18552
18607
|
React14.useEffect(() => {
|
|
18553
18608
|
if (!streaming && assistantId) resetStreamingMarkdownStablePrefix(assistantId);
|
|
18554
18609
|
}, [streaming, assistantId]);
|
|
18555
18610
|
const bodyWidth = assistantBodyWidth(columns);
|
|
18611
|
+
const renderText = streaming && streamingWindowRows > 0 ? windowPlainStreamingText(text, bodyWidth, streamingWindowRows) : text;
|
|
18556
18612
|
return /* @__PURE__ */ jsxs10(Box12, { flexDirection: "row", marginTop: 1, children: [
|
|
18557
18613
|
/* @__PURE__ */ jsx14(Box12, { flexShrink: 0, minWidth: 2, children: /* @__PURE__ */ jsx14(Text14, { color: theme.text, children: TURN_MARKER }) }),
|
|
18558
|
-
/* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexShrink: 0, width: bodyWidth, children: streaming ? /* @__PURE__ */ jsx14(StreamingMarkdown, { themeEpoch, columns: bodyWidth, streamKey: assistantId, children:
|
|
18614
|
+
/* @__PURE__ */ jsx14(Box12, { flexDirection: "column", flexShrink: 0, width: bodyWidth, children: streaming ? /* @__PURE__ */ jsx14(StreamingMarkdown, { themeEpoch, columns: bodyWidth, streamKey: assistantId, children: renderText }) : /* @__PURE__ */ jsx14(Markdown, { themeEpoch, columns: bodyWidth, children: text }) })
|
|
18559
18615
|
] });
|
|
18560
18616
|
});
|
|
18561
18617
|
var UserMessage = React14.memo(function UserMessage2({ text, attached = false, columns, themeEpoch = 0 }) {
|
|
@@ -19009,17 +19065,22 @@ function toolSearchLoadedSummary(resultText) {
|
|
|
19009
19065
|
parsed = JSON.parse(String(resultText || ""));
|
|
19010
19066
|
} catch {
|
|
19011
19067
|
const text = String(resultText || "");
|
|
19012
|
-
const
|
|
19013
|
-
|
|
19014
|
-
return [
|
|
19068
|
+
const loaded2 = /^Loaded deferred tools:\s*(.+)$/m.exec(text)?.[1] || "";
|
|
19069
|
+
const already2 = /^Already active:\s*(.+)$/m.exec(text)?.[1] || "";
|
|
19070
|
+
return [
|
|
19071
|
+
...loaded2 ? [`Loaded: ${loaded2}`] : [],
|
|
19072
|
+
...already2 ? [`Already active: ${already2}`] : []
|
|
19073
|
+
].join(" \xB7 ");
|
|
19015
19074
|
}
|
|
19016
19075
|
const tools = parsed?.selected?.tools;
|
|
19017
19076
|
if (!tools || typeof tools !== "object") return "";
|
|
19018
|
-
const
|
|
19019
|
-
|
|
19020
|
-
|
|
19021
|
-
|
|
19022
|
-
|
|
19077
|
+
const uniqueNames = (names) => [...new Set((Array.isArray(names) ? names : []).map((name) => String(name || "").trim()).filter(Boolean))];
|
|
19078
|
+
const loaded = uniqueNames(tools.added);
|
|
19079
|
+
const already = uniqueNames(tools.already);
|
|
19080
|
+
return [
|
|
19081
|
+
...loaded.length ? [`Loaded: ${loaded.join(", ")}`] : [],
|
|
19082
|
+
...already.length ? [`Already active: ${already.join(", ")}`] : []
|
|
19083
|
+
].join(" \xB7 ");
|
|
19023
19084
|
}
|
|
19024
19085
|
function agentTerminalDetail(status, isError, elapsed, error = "") {
|
|
19025
19086
|
const failureDetail = isError && error ? backgroundTaskFailureStatusLabel(status, error, { surface: "agent" }) : "";
|
|
@@ -19424,7 +19485,7 @@ function ToolHookDenialCard({ item, columns = 80 }) {
|
|
|
19424
19485
|
] }) : null
|
|
19425
19486
|
] });
|
|
19426
19487
|
}
|
|
19427
|
-
var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpanded, rightMessage = "", rightTone = "info", rightMessageWidth = 24, themeEpoch = 0 }) {
|
|
19488
|
+
var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpanded, rightMessage = "", rightTone = "info", rightMessageWidth = 24, themeEpoch = 0, streamingWindowRows = 0 }) {
|
|
19428
19489
|
const hintOnTurnDoneRow = item.kind === "turndone" || item.kind === "statusdone";
|
|
19429
19490
|
let node = null;
|
|
19430
19491
|
switch (item.kind) {
|
|
@@ -19432,7 +19493,7 @@ var Item = React19.memo(function Item2({ item, prevKind, columns, toolOutputExpa
|
|
|
19432
19493
|
node = /* @__PURE__ */ jsx19(UserMessage, { text: item.text, attached: prevKind === "user", columns, themeEpoch });
|
|
19433
19494
|
break;
|
|
19434
19495
|
case "assistant":
|
|
19435
|
-
node = /* @__PURE__ */ jsx19(AssistantMessage, { text: item.text, streaming: item.streaming, columns, themeEpoch, assistantId: item.id });
|
|
19496
|
+
node = /* @__PURE__ */ jsx19(AssistantMessage, { text: item.text, streaming: item.streaming, columns, themeEpoch, assistantId: item.id, streamingWindowRows });
|
|
19436
19497
|
break;
|
|
19437
19498
|
case "tool": {
|
|
19438
19499
|
if (shouldSuppressFullyFailedToolItem(item)) return null;
|
|
@@ -21712,7 +21773,8 @@ function App({ store, initialStatusLine = "", forceOnboarding = false }) {
|
|
|
21712
21773
|
rightMessage: attachOverlayHint ? inputHint : "",
|
|
21713
21774
|
rightTone: attachOverlayHint ? inputHintTone : "info",
|
|
21714
21775
|
rightMessageWidth: attachOverlayHint ? guardHintWidth || transientStatusWidth || 24 : 24,
|
|
21715
|
-
themeEpoch: state.themeEpoch || 0
|
|
21776
|
+
themeEpoch: state.themeEpoch || 0,
|
|
21777
|
+
streamingWindowRows: transcriptTailPinned && item.id === state.streamingTail?.id ? transcriptContentHeight + TRANSCRIPT_WINDOW_TAIL_OVERSCAN_ROWS : 0
|
|
21716
21778
|
}
|
|
21717
21779
|
);
|
|
21718
21780
|
return measureRef ? /* @__PURE__ */ jsx20(Box18, { ref: measureRef, flexDirection: "column", flexShrink: 0, children: itemNode }, item.id) : /* @__PURE__ */ jsx20(React20.Fragment, { children: itemNode }, item.id);
|
|
@@ -23520,6 +23582,9 @@ function createAgentJobFeed({
|
|
|
23520
23582
|
const terminalExecutionNotificationKeys = /* @__PURE__ */ new Set();
|
|
23521
23583
|
const terminalExecutionResponseKeys = /* @__PURE__ */ new Set();
|
|
23522
23584
|
const executionNotificationKeys = /* @__PURE__ */ new Map();
|
|
23585
|
+
const AGENT_STATUS_COALESCE_MS = 16;
|
|
23586
|
+
let agentStatusRefreshTimer = null;
|
|
23587
|
+
let agentStatusRefreshForce = false;
|
|
23523
23588
|
const clearExecutionDedupState = () => {
|
|
23524
23589
|
displayedExecutionNotificationKeys.clear();
|
|
23525
23590
|
displayedExecutionResponseStates.clear();
|
|
@@ -23668,7 +23733,16 @@ function createAgentJobFeed({
|
|
|
23668
23733
|
if (!parsed?.taskId) return;
|
|
23669
23734
|
const status = String(parsed.status || "").toLowerCase();
|
|
23670
23735
|
const terminal = /^(completed|complete|done|success|succeeded|ok|failed|error|timeout|killed|cancelled|canceled|denied)$/.test(status);
|
|
23671
|
-
|
|
23736
|
+
agentStatusRefreshForce = agentStatusRefreshForce || terminal;
|
|
23737
|
+
if (agentStatusRefreshTimer) return;
|
|
23738
|
+
agentStatusRefreshTimer = setTimeout(() => {
|
|
23739
|
+
agentStatusRefreshTimer = null;
|
|
23740
|
+
if (getDisposed()) return;
|
|
23741
|
+
const force = agentStatusRefreshForce;
|
|
23742
|
+
agentStatusRefreshForce = false;
|
|
23743
|
+
set(agentStatusState(force ? { force: true } : void 0));
|
|
23744
|
+
}, AGENT_STATUS_COALESCE_MS);
|
|
23745
|
+
agentStatusRefreshTimer.unref?.();
|
|
23672
23746
|
}
|
|
23673
23747
|
function subscribeRuntimeNotifications() {
|
|
23674
23748
|
if (typeof runtime.onNotification !== "function") return null;
|
|
@@ -23780,6 +23854,9 @@ function createAgentJobFeed({
|
|
|
23780
23854
|
try {
|
|
23781
23855
|
unsubscribe?.();
|
|
23782
23856
|
} finally {
|
|
23857
|
+
if (agentStatusRefreshTimer) clearTimeout(agentStatusRefreshTimer);
|
|
23858
|
+
agentStatusRefreshTimer = null;
|
|
23859
|
+
agentStatusRefreshForce = false;
|
|
23783
23860
|
clearExecutionDedupState();
|
|
23784
23861
|
}
|
|
23785
23862
|
};
|
|
@@ -25460,8 +25537,9 @@ function createRunTurn(bag) {
|
|
|
25460
25537
|
label: compactEventLabel(event),
|
|
25461
25538
|
detail: compactEventDetail(event)
|
|
25462
25539
|
});
|
|
25540
|
+
syncContextStats({ allowEstimated: true });
|
|
25463
25541
|
},
|
|
25464
|
-
onStageChange: async (stage) => {
|
|
25542
|
+
onStageChange: async (stage, detail = null) => {
|
|
25465
25543
|
if (!markTurnProgress(`stage:${String(stage || "")}`)) return;
|
|
25466
25544
|
if (!getState().spinner) return;
|
|
25467
25545
|
const value = String(stage || "");
|
|
@@ -25484,6 +25562,13 @@ function createRunTurn(bag) {
|
|
|
25484
25562
|
await yieldToRenderer();
|
|
25485
25563
|
return;
|
|
25486
25564
|
}
|
|
25565
|
+
if (value === "reconnecting") {
|
|
25566
|
+
compactingActive = false;
|
|
25567
|
+
const retryVerb = String(detail?.message || "Reconnecting");
|
|
25568
|
+
set({ spinner: { ...getState().spinner, mode: "reconnecting", verb: retryVerb } });
|
|
25569
|
+
await yieldToRenderer();
|
|
25570
|
+
return;
|
|
25571
|
+
}
|
|
25487
25572
|
if (value === "requesting" || value === "streaming") compactingActive = false;
|
|
25488
25573
|
const mode = value === "requesting" ? "requesting" : value === "streaming" ? getState().spinner.thinking ? "thinking" : "responding" : null;
|
|
25489
25574
|
if (!mode || getState().spinner.mode === mode) return;
|