@yuaone/cli 0.9.8 → 1.0.0
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/README.md +111 -2
- package/dist/cli.js +0 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +264 -94
- package/dist/commands/index.js.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -0
- package/dist/config.js.map +1 -1
- package/dist/interactive.d.ts +11 -0
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +129 -1
- package/dist/interactive.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +5 -21
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/agent-bridge.d.ts +2 -21
- package/dist/tui/agent-bridge.d.ts.map +1 -1
- package/dist/tui/agent-bridge.js +16 -82
- package/dist/tui/agent-bridge.js.map +1 -1
- package/dist/tui/components/CompactBar.d.ts +25 -0
- package/dist/tui/components/CompactBar.d.ts.map +1 -0
- package/dist/tui/components/CompactBar.js +42 -0
- package/dist/tui/components/CompactBar.js.map +1 -0
- package/dist/tui/components/FooterBar.d.ts +0 -4
- package/dist/tui/components/FooterBar.d.ts.map +1 -1
- package/dist/tui/components/FooterBar.js +44 -15
- package/dist/tui/components/FooterBar.js.map +1 -1
- package/dist/tui/components/InputBox.d.ts.map +1 -1
- package/dist/tui/components/InputBox.js +15 -12
- package/dist/tui/components/InputBox.js.map +1 -1
- package/dist/tui/components/MarkdownRenderer.d.ts +0 -4
- package/dist/tui/components/MarkdownRenderer.d.ts.map +1 -1
- package/dist/tui/components/MarkdownRenderer.js +3 -3
- package/dist/tui/components/MarkdownRenderer.js.map +1 -1
- package/dist/tui/components/MessageBubble.d.ts.map +1 -1
- package/dist/tui/components/MessageBubble.js +44 -4
- package/dist/tui/components/MessageBubble.js.map +1 -1
- package/dist/tui/components/StatusBadge.d.ts +18 -0
- package/dist/tui/components/StatusBadge.d.ts.map +1 -0
- package/dist/tui/components/StatusBadge.js +37 -0
- package/dist/tui/components/StatusBadge.js.map +1 -0
- package/dist/tui/components/StatusBar.d.ts.map +1 -1
- package/dist/tui/components/StatusBar.js +4 -2
- package/dist/tui/components/StatusBar.js.map +1 -1
- package/dist/tui/hooks/useAgentStream.d.ts +0 -4
- package/dist/tui/hooks/useAgentStream.d.ts.map +1 -1
- package/dist/tui/hooks/useAgentStream.js +321 -51
- package/dist/tui/hooks/useAgentStream.js.map +1 -1
- package/dist/tui/hooks/useMouseScroll.d.ts +10 -19
- package/dist/tui/hooks/useMouseScroll.d.ts.map +1 -1
- package/dist/tui/hooks/useMouseScroll.js +86 -83
- package/dist/tui/hooks/useMouseScroll.js.map +1 -1
- package/dist/tui/types.d.ts +22 -2
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StatusBadge — one-line agent status indicator shown above the InputBox.
|
|
3
|
+
*
|
|
4
|
+
* Shows the current agent activity inferred by ReasoningProgressAdapter:
|
|
5
|
+
* ● coding ──────────────────────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* Disappears when status is null (idle).
|
|
8
|
+
*/
|
|
9
|
+
import React from "react";
|
|
10
|
+
export type AgentStatus = "planning" | "searching" | "reading" | "analyzing" | "coding" | "fixing" | "reviewing" | "testing" | "running" | "waiting" | null;
|
|
11
|
+
export interface StatusBadgeProps {
|
|
12
|
+
status: AgentStatus;
|
|
13
|
+
detail?: string;
|
|
14
|
+
/** Terminal column width — falls back to useTerminalSize if omitted */
|
|
15
|
+
columns?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function StatusBadge({ status, detail, columns: columnsProp }: StatusBadgeProps): React.JSX.Element | null;
|
|
18
|
+
//# sourceMappingURL=StatusBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/tui/components/StatusBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,WAAW,GACX,SAAS,GACT,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,SAAS,GACT,SAAS,GACT,SAAS,GACT,IAAI,CAAC;AAqBT,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,gBAAgB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CA+BhH"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { useTerminalSize } from "../hooks/useTerminalSize.js";
|
|
4
|
+
const STATUS_CONFIG = {
|
|
5
|
+
planning: { icon: "◈", label: "planning", color: "#60a5fa" }, // blue
|
|
6
|
+
searching: { icon: "◎", label: "searching", color: "#c084fc" }, // purple
|
|
7
|
+
reading: { icon: "◷", label: "reading", color: "#94a3b8" }, // slate
|
|
8
|
+
analyzing: { icon: "◉", label: "analyzing", color: "#22d3ee" }, // cyan
|
|
9
|
+
coding: { icon: "●", label: "coding", color: "#4ade80" }, // green
|
|
10
|
+
fixing: { icon: "◆", label: "fixing", color: "#fb923c" }, // orange
|
|
11
|
+
reviewing: { icon: "◈", label: "reviewing", color: "#7dd3fc" }, // light blue
|
|
12
|
+
testing: { icon: "◎", label: "testing", color: "#facc15" }, // yellow
|
|
13
|
+
running: { icon: "▶", label: "running", color: "#fde047" }, // bright yellow
|
|
14
|
+
waiting: { icon: "◌", label: "waiting", color: "#64748b" }, // dim
|
|
15
|
+
};
|
|
16
|
+
export function StatusBadge({ status, detail, columns: columnsProp }) {
|
|
17
|
+
const { columns: termColumns } = useTerminalSize();
|
|
18
|
+
const columns = columnsProp ?? termColumns;
|
|
19
|
+
if (!status)
|
|
20
|
+
return null;
|
|
21
|
+
const cfg = STATUS_CONFIG[status];
|
|
22
|
+
// icon + space + label + 2 padding chars (leading spaces before detail) + 1 trailing
|
|
23
|
+
const iconLabelWidth = cfg.icon.length + 1 + cfg.label.length;
|
|
24
|
+
const maxDetailWidth = Math.max(0, columns - iconLabelWidth - 4);
|
|
25
|
+
let safeDetail = "";
|
|
26
|
+
if (detail) {
|
|
27
|
+
// Truncate with ellipsis if the detail text is too long
|
|
28
|
+
safeDetail = detail.length > maxDetailWidth
|
|
29
|
+
? detail.slice(0, Math.max(0, maxDetailWidth - 1)) + "…"
|
|
30
|
+
: detail;
|
|
31
|
+
}
|
|
32
|
+
const detailPart = safeDetail ? ` ${safeDetail}` : "";
|
|
33
|
+
const textLen = iconLabelWidth + detailPart.length + 2;
|
|
34
|
+
const padLen = Math.max(0, columns - textLen - 1);
|
|
35
|
+
return (_jsxs(Box, { width: columns, children: [_jsxs(Text, { color: cfg.color, children: [cfg.icon, " "] }), _jsx(Text, { color: cfg.color, bold: true, children: cfg.label }), detailPart ? _jsx(Text, { dimColor: true, children: detailPart }) : null, _jsx(Text, { dimColor: true, children: "─".repeat(padLen) })] }));
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=StatusBadge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusBadge.js","sourceRoot":"","sources":["../../../src/tui/components/StatusBadge.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../src/tui/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC;CACV;AAED,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,WAAe,EACf,YAAgB,EAChB,eAAsB,GACvB,EAAE,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../src/tui/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,IAAI,CAAC;CACV;AAED,wBAAgB,SAAS,CAAC,EACxB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,WAAe,EACf,YAAgB,EAChB,eAAsB,GACvB,EAAE,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAkCpC"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { useTerminalSize } from "../hooks/useTerminalSize.js";
|
|
4
|
+
import { TOKENS } from "../lib/tokens.js";
|
|
4
5
|
export function StatusBar({ version, model, provider, tokensPerSec, isRunning, inputTokens = 0, outputTokens = 0, updateAvailable = null, }) {
|
|
5
6
|
const { columns, tier } = useTerminalSize();
|
|
7
|
+
const left = `${TOKENS.brand.prefix} YUAN v${version}`;
|
|
6
8
|
const status = isRunning ? "●" : "○";
|
|
7
9
|
const tps = tokensPerSec != null ? `${tokensPerSec} tok/s` : "";
|
|
8
10
|
const total = inputTokens + outputTokens;
|
|
@@ -18,6 +20,6 @@ export function StatusBar({ version, model, provider, tokensPerSec, isRunning, i
|
|
|
18
20
|
const right = tier === "compact"
|
|
19
21
|
? `${status} ${model}${updateLabel}`
|
|
20
22
|
: `${provider}/${model} ${tokenInfo} ${status} ${tps}${updateLabel}`;
|
|
21
|
-
return (
|
|
23
|
+
return (_jsxs(Box, { width: columns, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: left }), _jsx(Text, { dimColor: !updateAvailable, color: updateAvailable ? "yellow" : undefined, children: right })] }));
|
|
22
24
|
}
|
|
23
25
|
//# sourceMappingURL=StatusBar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../../src/tui/components/StatusBar.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"StatusBar.js","sourceRoot":"","sources":["../../../src/tui/components/StatusBar.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAgB1C,MAAM,UAAU,SAAS,CAAC,EACxB,OAAO,EACP,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,WAAW,GAAG,CAAC,EACf,YAAY,GAAG,CAAC,EAChB,eAAe,GAAG,IAAI,GACP;IACf,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,eAAe,EAAE,CAAC;IAE5C,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,UAAU,OAAO,EAAE,CAAC;IACvD,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACrC,MAAM,GAAG,GAAG,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,YAAY,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,MAAM,KAAK,GAAG,WAAW,GAAG,YAAY,CAAC;IAEzC,MAAM,UAAU,GAAG,EAAE,CAAC;IACtB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,KAAK,GACT,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC;IAEpE,MAAM,SAAS,GACb,KAAK,GAAG,CAAC;QACP,CAAC,CAAC,IAAI,WAAW,KAAK,YAAY,IAAI,KAAK,EAAE;QAC7C,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,WAAW,GAAG,eAAe;QACjC,CAAC,CAAC,MAAM,eAAe,CAAC,OAAO,IAAI,eAAe,CAAC,MAAM,EAAE;QAC3D,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,KAAK,GACT,IAAI,KAAK,SAAS;QAChB,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,GAAG,WAAW,EAAE;QACpC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,GAAG,WAAW,EAAE,CAAC;IAEzE,OAAO,CACL,MAAC,GAAG,IAAC,KAAK,EAAE,OAAO,EAAE,cAAc,EAAC,eAAe,aACjD,KAAC,IAAI,IAAC,QAAQ,kBAAE,IAAI,GAAQ,EAC5B,KAAC,IAAI,IAAC,QAAQ,EAAE,CAAC,eAAe,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,YAC5E,KAAK,GACD,IACH,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -6,10 +6,6 @@
|
|
|
6
6
|
import type { AgentStreamState } from "../types.js";
|
|
7
7
|
export interface UseAgentStreamReturn {
|
|
8
8
|
state: AgentStreamState;
|
|
9
|
-
/** Real-time elapsed ms — separated from state to avoid App-wide 100ms re-renders */
|
|
10
|
-
elapsedMs: number;
|
|
11
|
-
/** ms since last event while stalled — separated from state */
|
|
12
|
-
stalledMs: number;
|
|
13
9
|
handleEvent: (event: AgentEventLike) => void;
|
|
14
10
|
addUserMessage: (content: string) => void;
|
|
15
11
|
addSystemMessage: (content: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/tui/hooks/useAgentStream.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"useAgentStream.d.ts","sourceRoot":"","sources":["../../../src/tui/hooks/useAgentStream.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAIV,gBAAgB,EAKjB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,gBAAgB,CAAC;IACxB,WAAW,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,cAAc,IAAI,oBAAoB,CAu3BrD"}
|
|
@@ -18,8 +18,11 @@ export function useAgentStream() {
|
|
|
18
18
|
const [filesChangedCount, setFilesChangedCount] = useState(0);
|
|
19
19
|
const [reasoningTree, setReasoningTree] = useState(undefined);
|
|
20
20
|
const [backgroundTasks, setBackgroundTasks] = useState([]);
|
|
21
|
+
const [progressLabel, setProgressLabel] = useState(undefined);
|
|
22
|
+
const [currentPhase, setCurrentPhase] = useState(undefined);
|
|
21
23
|
const currentMsgIdRef = useRef(null);
|
|
22
24
|
const tokenWindowRef = useRef([]);
|
|
25
|
+
const currentThinkingMsgIdRef = useRef(null);
|
|
23
26
|
const activeToolBatchIdRef = useRef(null);
|
|
24
27
|
const lastToolCallAtRef = useRef(0);
|
|
25
28
|
const startTimeRef = useRef(null);
|
|
@@ -52,7 +55,7 @@ export function useAgentStream() {
|
|
|
52
55
|
if (startTimeRef.current) {
|
|
53
56
|
setElapsedMs(Date.now() - startTimeRef.current);
|
|
54
57
|
}
|
|
55
|
-
},
|
|
58
|
+
}, 1000);
|
|
56
59
|
}, []);
|
|
57
60
|
const stopTimer = useCallback(() => {
|
|
58
61
|
if (timerRef.current) {
|
|
@@ -81,6 +84,10 @@ export function useAgentStream() {
|
|
|
81
84
|
const pendingTextRef = useRef("");
|
|
82
85
|
const flushTimerRef = useRef(null);
|
|
83
86
|
const FLUSH_INTERVAL = 120;
|
|
87
|
+
// Debounce buffer for thinking/reasoning lines to avoid per-token re-renders
|
|
88
|
+
const pendingThinkingRef = useRef([]);
|
|
89
|
+
const thinkingFlushTimerRef = useRef(null);
|
|
90
|
+
const THINKING_FLUSH_INTERVAL = 80;
|
|
84
91
|
const flushPendingText = useCallback(() => {
|
|
85
92
|
if (flushTimerRef.current) {
|
|
86
93
|
clearTimeout(flushTimerRef.current);
|
|
@@ -95,53 +102,61 @@ export function useAgentStream() {
|
|
|
95
102
|
content: msg.content + text,
|
|
96
103
|
}));
|
|
97
104
|
}, [updateCurrentMessage]);
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.filter((line) => {
|
|
105
|
-
if (/^iteration \d+: (preparing context|calling model)$/.test(line))
|
|
106
|
-
return false;
|
|
107
|
-
if (line === "starting agent loop")
|
|
108
|
-
return false;
|
|
109
|
-
if (/^\[planner:(start|done)\]/.test(line))
|
|
110
|
-
return false;
|
|
111
|
-
if (/^\[.*:(start|done)\]/.test(line))
|
|
112
|
-
return false;
|
|
113
|
-
if (/^success: /.test(line))
|
|
114
|
-
return false;
|
|
115
|
-
if (/^⚡ running .+ in parallel$/.test(line))
|
|
116
|
-
return false;
|
|
117
|
-
return true;
|
|
118
|
-
})
|
|
119
|
-
.map((line) => (line.startsWith("· ") ? line : `· ${line}`));
|
|
105
|
+
const flushThinkingLines = useCallback(() => {
|
|
106
|
+
if (thinkingFlushTimerRef.current) {
|
|
107
|
+
clearTimeout(thinkingFlushTimerRef.current);
|
|
108
|
+
thinkingFlushTimerRef.current = null;
|
|
109
|
+
}
|
|
110
|
+
const lines = pendingThinkingRef.current;
|
|
120
111
|
if (lines.length === 0)
|
|
121
112
|
return;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
113
|
+
pendingThinkingRef.current = [];
|
|
114
|
+
const currentId = currentThinkingMsgIdRef.current;
|
|
115
|
+
if (!currentId) {
|
|
116
|
+
const id = `thinking-${Date.now()}`;
|
|
117
|
+
currentThinkingMsgIdRef.current = id;
|
|
118
|
+
const msg = {
|
|
119
|
+
id,
|
|
120
|
+
role: "system",
|
|
121
|
+
content: lines.join("\n"),
|
|
122
|
+
timestamp: Date.now(),
|
|
123
|
+
};
|
|
124
|
+
setMessages((prev) => [...prev, msg]);
|
|
127
125
|
return;
|
|
126
|
+
}
|
|
128
127
|
setMessages((prev) => prev.map((m) => {
|
|
129
|
-
if (m.id !==
|
|
128
|
+
if (m.id !== currentId)
|
|
130
129
|
return m;
|
|
131
|
-
const existing = new Set(
|
|
130
|
+
const existing = new Set(m.content.split("\n"));
|
|
132
131
|
const deduped = lines.filter((line) => !existing.has(line));
|
|
133
132
|
if (deduped.length === 0)
|
|
134
133
|
return m;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
: deduped.join("\n")
|
|
138
|
-
|
|
134
|
+
return {
|
|
135
|
+
...m,
|
|
136
|
+
content: `${m.content}\n${deduped.join("\n")}`,
|
|
137
|
+
};
|
|
139
138
|
}));
|
|
140
139
|
}, []);
|
|
140
|
+
const appendThinkingLines = useCallback((raw) => {
|
|
141
|
+
const lines = raw
|
|
142
|
+
.split("\n")
|
|
143
|
+
.map((line) => line.trim())
|
|
144
|
+
.filter(Boolean)
|
|
145
|
+
.map((line) => (line.startsWith("· ") ? line : `· ${line}`));
|
|
146
|
+
if (lines.length === 0)
|
|
147
|
+
return;
|
|
148
|
+
// Buffer lines and flush after a short debounce window to reduce re-renders
|
|
149
|
+
pendingThinkingRef.current.push(...lines);
|
|
150
|
+
if (thinkingFlushTimerRef.current)
|
|
151
|
+
clearTimeout(thinkingFlushTimerRef.current);
|
|
152
|
+
thinkingFlushTimerRef.current = setTimeout(flushThinkingLines, THINKING_FLUSH_INTERVAL);
|
|
153
|
+
}, [flushThinkingLines]);
|
|
141
154
|
useEffect(() => {
|
|
142
155
|
return () => {
|
|
143
156
|
if (flushTimerRef.current)
|
|
144
157
|
clearTimeout(flushTimerRef.current);
|
|
158
|
+
if (thinkingFlushTimerRef.current)
|
|
159
|
+
clearTimeout(thinkingFlushTimerRef.current);
|
|
145
160
|
};
|
|
146
161
|
}, []);
|
|
147
162
|
const addUserMessage = useCallback((content) => {
|
|
@@ -173,7 +188,9 @@ export function useAgentStream() {
|
|
|
173
188
|
setLastError(null);
|
|
174
189
|
setFilesChangedCount(0);
|
|
175
190
|
setTokensPerSecond(0);
|
|
191
|
+
setCurrentPhase("explore");
|
|
176
192
|
tokenWindowRef.current = [];
|
|
193
|
+
currentThinkingMsgIdRef.current = null;
|
|
177
194
|
activeToolBatchIdRef.current = null;
|
|
178
195
|
lastToolCallAtRef.current = 0;
|
|
179
196
|
startTimer();
|
|
@@ -206,11 +223,21 @@ export function useAgentStream() {
|
|
|
206
223
|
appendThinkingLines(String(event.content ?? ""));
|
|
207
224
|
break;
|
|
208
225
|
}
|
|
226
|
+
case "progress:status": {
|
|
227
|
+
// Fine-grained status from ReasoningProgressAdapter: analyzing/searching/coding/etc.
|
|
228
|
+
const label = String(event.status ?? "");
|
|
229
|
+
if (label)
|
|
230
|
+
setProgressLabel(label);
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
209
233
|
case "agent:text_delta": {
|
|
210
234
|
const text = event.text;
|
|
211
235
|
// Only update status/tool state on the FIRST token of a stream — not every token
|
|
212
236
|
if (!isStreamingRef.current) {
|
|
213
237
|
isStreamingRef.current = true;
|
|
238
|
+
// Reset thinking message ID so post-stream thinking events create a NEW message
|
|
239
|
+
// rather than appending to the old thinking block (prevents reasoning overlap)
|
|
240
|
+
currentThinkingMsgIdRef.current = null;
|
|
214
241
|
statusRef.current = "streaming";
|
|
215
242
|
setStatus("streaming");
|
|
216
243
|
setCurrentToolName(null);
|
|
@@ -325,6 +352,7 @@ export function useAgentStream() {
|
|
|
325
352
|
isStreaming: false,
|
|
326
353
|
}));
|
|
327
354
|
currentMsgIdRef.current = null;
|
|
355
|
+
currentThinkingMsgIdRef.current = null;
|
|
328
356
|
activeToolBatchIdRef.current = null;
|
|
329
357
|
setTimeout(() => {
|
|
330
358
|
setStatus("idle");
|
|
@@ -333,20 +361,28 @@ export function useAgentStream() {
|
|
|
333
361
|
}
|
|
334
362
|
case "agent:completed": {
|
|
335
363
|
flushPendingText();
|
|
364
|
+
flushThinkingLines();
|
|
336
365
|
stopTimer();
|
|
337
366
|
statusRef.current = "completed";
|
|
338
367
|
setStatus("completed");
|
|
339
368
|
setStalledMs(0);
|
|
340
369
|
setCurrentToolName(null);
|
|
341
370
|
setCurrentToolArgs(null);
|
|
342
|
-
const summary = event.summary;
|
|
343
|
-
updateCurrentMessage((msg) =>
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
371
|
+
const summary = event.summary ?? "";
|
|
372
|
+
updateCurrentMessage((msg) => {
|
|
373
|
+
// When the agent only made tool calls (no text output), content is "".
|
|
374
|
+
// Emit a synthetic "Done." so the user sees a completion indication.
|
|
375
|
+
const hasText = msg.content && msg.content.trim().length > 0;
|
|
376
|
+
const hasSummary = summary && summary.trim().length > 0;
|
|
377
|
+
return {
|
|
378
|
+
...msg,
|
|
379
|
+
content: hasText ? msg.content : hasSummary ? summary : "Done.",
|
|
380
|
+
isStreaming: false,
|
|
381
|
+
};
|
|
382
|
+
});
|
|
348
383
|
isStreamingRef.current = false;
|
|
349
384
|
currentMsgIdRef.current = null;
|
|
385
|
+
currentThinkingMsgIdRef.current = null;
|
|
350
386
|
activeToolBatchIdRef.current = null;
|
|
351
387
|
setTimeout(() => {
|
|
352
388
|
setStatus("idle");
|
|
@@ -386,9 +422,70 @@ export function useAgentStream() {
|
|
|
386
422
|
const passed = event.passed;
|
|
387
423
|
const stage = event.stage;
|
|
388
424
|
const issues = event.issues;
|
|
389
|
-
const statusLabel = passed ? "passed" :
|
|
390
|
-
const
|
|
391
|
-
|
|
425
|
+
const statusLabel = passed ? "✓ passed" : `✗ ${issues.length} issue(s)`;
|
|
426
|
+
const stepType = passed ? "success" : "warning";
|
|
427
|
+
const summary = issues.length > 0
|
|
428
|
+
? `${statusLabel}: ${issues[0]?.slice(0, 60) ?? ""}${issues.length > 1 ? ` (+${issues.length - 1} more)` : ""}`
|
|
429
|
+
: statusLabel;
|
|
430
|
+
const now = Date.now();
|
|
431
|
+
const step = {
|
|
432
|
+
id: `qa-${now}-${Math.random().toString(36).slice(2, 6)}`,
|
|
433
|
+
label: `[QA ${stage}] ${summary}`,
|
|
434
|
+
type: stepType,
|
|
435
|
+
timestamp: now,
|
|
436
|
+
};
|
|
437
|
+
setBackgroundTasks((prev) => {
|
|
438
|
+
const existing = prev.find((t) => t.id === "qa-pipeline");
|
|
439
|
+
const newSteps = existing
|
|
440
|
+
? [...existing.steps, step].slice(-20)
|
|
441
|
+
: [step];
|
|
442
|
+
if (existing) {
|
|
443
|
+
return prev.map((t) => t.id === "qa-pipeline"
|
|
444
|
+
? { ...t, status: passed ? "idle" : "running", steps: newSteps, lastUpdatedAt: now }
|
|
445
|
+
: t);
|
|
446
|
+
}
|
|
447
|
+
return [...prev, {
|
|
448
|
+
id: "qa-pipeline",
|
|
449
|
+
label: "QA Pipeline",
|
|
450
|
+
status: passed ? "idle" : "running",
|
|
451
|
+
steps: newSteps,
|
|
452
|
+
lastUpdatedAt: now,
|
|
453
|
+
}];
|
|
454
|
+
});
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case "agent:evidence_report": {
|
|
458
|
+
const ev = event;
|
|
459
|
+
const { filePath, syntax, diffStats } = ev;
|
|
460
|
+
const fileName = filePath.split("/").pop() ?? filePath;
|
|
461
|
+
const syntaxLabel = syntax === "ok" ? "✓ syntax" : syntax === "error" ? "✗ syntax err" : "";
|
|
462
|
+
const diffLabel = diffStats ? `+${diffStats.added}/-${diffStats.removed}` : "";
|
|
463
|
+
const parts = [syntaxLabel, diffLabel].filter(Boolean).join(" ");
|
|
464
|
+
const now = Date.now();
|
|
465
|
+
const step = {
|
|
466
|
+
id: `ev-${now}-${Math.random().toString(36).slice(2, 6)}`,
|
|
467
|
+
label: `[evidence] ${fileName}${parts ? " " + parts : ""}`,
|
|
468
|
+
type: syntax === "error" ? "warning" : "success",
|
|
469
|
+
timestamp: now,
|
|
470
|
+
};
|
|
471
|
+
setBackgroundTasks((prev) => {
|
|
472
|
+
const existing = prev.find((t) => t.id === "evidence");
|
|
473
|
+
const newSteps = existing
|
|
474
|
+
? [...existing.steps, step].slice(-20)
|
|
475
|
+
: [step];
|
|
476
|
+
if (existing) {
|
|
477
|
+
return prev.map((t) => t.id === "evidence"
|
|
478
|
+
? { ...t, steps: newSteps, lastUpdatedAt: now }
|
|
479
|
+
: t);
|
|
480
|
+
}
|
|
481
|
+
return [...prev, {
|
|
482
|
+
id: "evidence",
|
|
483
|
+
label: "Evidence",
|
|
484
|
+
status: "idle",
|
|
485
|
+
steps: newSteps,
|
|
486
|
+
lastUpdatedAt: now,
|
|
487
|
+
}];
|
|
488
|
+
});
|
|
392
489
|
break;
|
|
393
490
|
}
|
|
394
491
|
case "agent:bg_update": {
|
|
@@ -424,24 +521,194 @@ export function useAgentStream() {
|
|
|
424
521
|
});
|
|
425
522
|
break;
|
|
426
523
|
}
|
|
524
|
+
case "agent:phase_transition": {
|
|
525
|
+
const to = event.to;
|
|
526
|
+
setCurrentPhase(to);
|
|
527
|
+
// Append as a dim thinking line — no new message bubble, no status overlap
|
|
528
|
+
appendThinkingLines(`phase: ${event.from} → ${to} (${event.trigger})`);
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case "agent:subagent_phase": {
|
|
532
|
+
// Route to task panel, not main message stream
|
|
533
|
+
const taskId = event.taskId;
|
|
534
|
+
const phase = event.phase;
|
|
535
|
+
const now = Date.now();
|
|
536
|
+
const step = {
|
|
537
|
+
id: `step-${now}-${Math.random().toString(36).slice(2, 6)}`,
|
|
538
|
+
label: phase.length > 60 ? phase.slice(0, 57) + "…" : phase,
|
|
539
|
+
type: "info",
|
|
540
|
+
timestamp: now,
|
|
541
|
+
};
|
|
542
|
+
setBackgroundTasks((prev) => {
|
|
543
|
+
const existing = prev.find((t) => t.id === taskId);
|
|
544
|
+
if (existing) {
|
|
545
|
+
return prev.map((t) => t.id === taskId
|
|
546
|
+
? { ...t, status: "running", steps: [...t.steps, step].slice(-20), lastUpdatedAt: now }
|
|
547
|
+
: t);
|
|
548
|
+
}
|
|
549
|
+
const newTask = {
|
|
550
|
+
id: taskId,
|
|
551
|
+
label: taskId,
|
|
552
|
+
status: "running",
|
|
553
|
+
steps: [step],
|
|
554
|
+
lastUpdatedAt: now,
|
|
555
|
+
};
|
|
556
|
+
return [...prev, newTask];
|
|
557
|
+
});
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
case "agent:subagent_done": {
|
|
561
|
+
const taskId = event.taskId;
|
|
562
|
+
const success = event.success;
|
|
563
|
+
const now = Date.now();
|
|
564
|
+
const step = {
|
|
565
|
+
id: `step-${now}-${Math.random().toString(36).slice(2, 6)}`,
|
|
566
|
+
label: success ? "done" : "failed",
|
|
567
|
+
type: success ? "success" : "error",
|
|
568
|
+
timestamp: now,
|
|
569
|
+
};
|
|
570
|
+
setBackgroundTasks((prev) => prev.map((t) => t.id === taskId
|
|
571
|
+
? { ...t, status: success ? "idle" : "error", steps: [...t.steps, step].slice(-20), lastUpdatedAt: now }
|
|
572
|
+
: t));
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
// ─── Phase 3: Autonomous Engineering Loop ───────────────────────
|
|
576
|
+
case "agent:research_result": {
|
|
577
|
+
const ev = event;
|
|
578
|
+
const repoCount = ev.sources.filter(s => s.source === "repo").length;
|
|
579
|
+
const webCount = ev.sources.filter(s => s.source !== "repo").length;
|
|
580
|
+
const pct = Math.round(ev.confidence * 100);
|
|
581
|
+
const phaseEvent = {
|
|
582
|
+
id: `research-${ev.timestamp}`,
|
|
583
|
+
kind: "research",
|
|
584
|
+
title: `Research confidence:${pct}% ${ev.sources.length} sources`,
|
|
585
|
+
summary: ev.summary.split("\n")[0] ?? "",
|
|
586
|
+
items: [
|
|
587
|
+
`${repoCount} repo ${webCount} web`,
|
|
588
|
+
...ev.sources.slice(0, 5).map(s => `${s.title.slice(0, 40)}: ${s.snippet.slice(0, 60)}`),
|
|
589
|
+
],
|
|
590
|
+
status: "done",
|
|
591
|
+
timestamp: ev.timestamp,
|
|
592
|
+
};
|
|
593
|
+
updateCurrentMessage((msg) => ({
|
|
594
|
+
...msg,
|
|
595
|
+
phaseEvents: [...(msg.phaseEvents ?? []), phaseEvent],
|
|
596
|
+
}));
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
case "agent:plan_generated": {
|
|
600
|
+
const ev = event;
|
|
601
|
+
const phaseEvent = {
|
|
602
|
+
id: `plan-${ev.timestamp}`,
|
|
603
|
+
kind: "plan",
|
|
604
|
+
title: `Plan ${ev.steps.length} steps`,
|
|
605
|
+
summary: ev.steps[0]?.description ?? "",
|
|
606
|
+
items: ev.steps.map((s, i) => {
|
|
607
|
+
const dep = s.dependsOn.length > 0 ? ` (after ${s.dependsOn.map(d => d + 1).join(",")})` : "";
|
|
608
|
+
return `${i + 1}. ${s.description.slice(0, 70)}${dep}`;
|
|
609
|
+
}),
|
|
610
|
+
status: "done",
|
|
611
|
+
timestamp: ev.timestamp,
|
|
612
|
+
};
|
|
613
|
+
updateCurrentMessage((msg) => ({
|
|
614
|
+
...msg,
|
|
615
|
+
phaseEvents: [...(msg.phaseEvents ?? []), phaseEvent],
|
|
616
|
+
}));
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
case "agent:tournament_result": {
|
|
620
|
+
const ev = event;
|
|
621
|
+
const pct = Math.round(ev.qualityScore * 100);
|
|
622
|
+
const phaseEvent = {
|
|
623
|
+
id: `tournament-${ev.timestamp}`,
|
|
624
|
+
kind: "tournament",
|
|
625
|
+
title: `Tournament winner:patch${ev.winner + 1} score:${pct}%`,
|
|
626
|
+
summary: `${ev.candidates} candidates evaluated`,
|
|
627
|
+
items: [
|
|
628
|
+
`Candidates: ${ev.candidates}`,
|
|
629
|
+
`Winner: patch ${ev.winner + 1} of ${ev.candidates}`,
|
|
630
|
+
`Quality score: ${pct}%`,
|
|
631
|
+
],
|
|
632
|
+
status: ev.qualityScore > 0 ? "done" : "error",
|
|
633
|
+
timestamp: ev.timestamp,
|
|
634
|
+
};
|
|
635
|
+
updateCurrentMessage((msg) => ({
|
|
636
|
+
...msg,
|
|
637
|
+
phaseEvents: [...(msg.phaseEvents ?? []), phaseEvent],
|
|
638
|
+
}));
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
case "agent:task_memory_update": {
|
|
642
|
+
const ev = event;
|
|
643
|
+
const statusIcon = ev.status === "completed" ? "✓" : ev.status === "failed" ? "✗" : "●";
|
|
644
|
+
const phaseEvent = {
|
|
645
|
+
id: `task-${ev.timestamp}`,
|
|
646
|
+
kind: "task",
|
|
647
|
+
title: `Task ${statusIcon} ${ev.status} phase:${ev.phase}`,
|
|
648
|
+
summary: `${ev.taskId.slice(0, 16)}… ${ev.phase}`,
|
|
649
|
+
items: [
|
|
650
|
+
`Task: ${ev.taskId.slice(0, 32)}`,
|
|
651
|
+
`Phase: ${ev.phase}`,
|
|
652
|
+
`Status: ${ev.status}`,
|
|
653
|
+
],
|
|
654
|
+
status: ev.status === "completed" ? "done" : ev.status === "failed" ? "error" : "running",
|
|
655
|
+
timestamp: ev.timestamp,
|
|
656
|
+
};
|
|
657
|
+
updateCurrentMessage((msg) => ({
|
|
658
|
+
...msg,
|
|
659
|
+
phaseEvents: [...(msg.phaseEvents ?? []), phaseEvent],
|
|
660
|
+
}));
|
|
661
|
+
break;
|
|
662
|
+
}
|
|
663
|
+
case "agent:debug_report": {
|
|
664
|
+
const ev = event;
|
|
665
|
+
const pct = Math.round(ev.confidence * 100);
|
|
666
|
+
const phaseEvent = {
|
|
667
|
+
id: `debug-${ev.timestamp}`,
|
|
668
|
+
kind: "debug",
|
|
669
|
+
title: `Debug Report confidence:${pct}%`,
|
|
670
|
+
summary: ev.rootCause.slice(0, 80),
|
|
671
|
+
items: [
|
|
672
|
+
`Root cause: ${ev.rootCause.slice(0, 80)}`,
|
|
673
|
+
`Suspected: ${ev.suspectedFiles.slice(0, 3).join(", ") || "none"}`,
|
|
674
|
+
`Fix: ${ev.fixStrategy.slice(0, 80)}`,
|
|
675
|
+
],
|
|
676
|
+
status: ev.confidence > 0.3 ? "done" : "error",
|
|
677
|
+
timestamp: ev.timestamp,
|
|
678
|
+
};
|
|
679
|
+
updateCurrentMessage((msg) => ({
|
|
680
|
+
...msg,
|
|
681
|
+
phaseEvents: [...(msg.phaseEvents ?? []), phaseEvent],
|
|
682
|
+
}));
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
427
685
|
default:
|
|
428
686
|
break;
|
|
429
687
|
}
|
|
430
|
-
|
|
688
|
+
// Clear progressLabel whenever agent goes to a terminal/non-thinking state
|
|
689
|
+
const k = event.kind;
|
|
690
|
+
if (k === "agent:done" || k === "agent:completed" || k === "agent:error" ||
|
|
691
|
+
k === "agent:tool_call" || k === "agent:text_delta") {
|
|
692
|
+
setProgressLabel(undefined);
|
|
693
|
+
}
|
|
694
|
+
}, [appendThinkingLines, updateCurrentMessage, flushPendingText, flushThinkingLines, stopTimer]);
|
|
431
695
|
const interrupt = useCallback(() => {
|
|
432
696
|
flushPendingText();
|
|
697
|
+
flushThinkingLines();
|
|
433
698
|
stopTimer();
|
|
434
699
|
statusRef.current = "interrupted";
|
|
435
700
|
setStatus("interrupted");
|
|
436
701
|
setStalledMs(0);
|
|
437
702
|
setCurrentToolName(null);
|
|
438
703
|
setCurrentToolArgs(null);
|
|
704
|
+
setProgressLabel(undefined);
|
|
439
705
|
updateCurrentMessage((msg) => ({
|
|
440
706
|
...msg,
|
|
441
707
|
isStreaming: false,
|
|
442
708
|
content: msg.content + "\n\n[Interrupted]",
|
|
443
709
|
}));
|
|
444
710
|
currentMsgIdRef.current = null;
|
|
711
|
+
currentThinkingMsgIdRef.current = null;
|
|
445
712
|
activeToolBatchIdRef.current = null;
|
|
446
713
|
const sysMsg = {
|
|
447
714
|
id: `sys-${Date.now()}`,
|
|
@@ -459,11 +726,16 @@ export function useAgentStream() {
|
|
|
459
726
|
clearTimeout(flushTimerRef.current);
|
|
460
727
|
flushTimerRef.current = null;
|
|
461
728
|
}
|
|
729
|
+
if (thinkingFlushTimerRef.current) {
|
|
730
|
+
clearTimeout(thinkingFlushTimerRef.current);
|
|
731
|
+
thinkingFlushTimerRef.current = null;
|
|
732
|
+
}
|
|
462
733
|
if (timerRef.current) {
|
|
463
734
|
clearInterval(timerRef.current);
|
|
464
735
|
timerRef.current = null;
|
|
465
736
|
}
|
|
466
737
|
pendingTextRef.current = "";
|
|
738
|
+
pendingThinkingRef.current = [];
|
|
467
739
|
startTimeRef.current = null;
|
|
468
740
|
setMessages([]);
|
|
469
741
|
isStreamingRef.current = false;
|
|
@@ -474,31 +746,29 @@ export function useAgentStream() {
|
|
|
474
746
|
setCurrentToolArgs(null);
|
|
475
747
|
setLastError(null);
|
|
476
748
|
setFilesChangedCount(0);
|
|
749
|
+
currentThinkingMsgIdRef.current = null;
|
|
477
750
|
activeToolBatchIdRef.current = null;
|
|
478
751
|
currentMsgIdRef.current = null;
|
|
479
752
|
}, []);
|
|
480
|
-
// elapsedMs and stalledMs are intentionally excluded from state to prevent
|
|
481
|
-
// 100ms timer ticks from causing App-wide re-renders (which shakes InputBox).
|
|
482
|
-
// FooterBar receives them as separate props via agentStream.elapsedMs / .stalledMs.
|
|
483
753
|
const state = {
|
|
484
754
|
status,
|
|
485
755
|
messages,
|
|
486
756
|
tokensPerSecond,
|
|
487
757
|
totalTokensUsed,
|
|
488
|
-
elapsedMs
|
|
758
|
+
elapsedMs,
|
|
489
759
|
lastElapsedMs,
|
|
490
760
|
currentToolName,
|
|
491
761
|
currentToolArgs,
|
|
492
762
|
lastError,
|
|
493
763
|
filesChangedCount,
|
|
494
764
|
reasoningTree,
|
|
495
|
-
stalledMs
|
|
765
|
+
stalledMs,
|
|
496
766
|
backgroundTasks,
|
|
767
|
+
progressLabel,
|
|
768
|
+
currentPhase,
|
|
497
769
|
};
|
|
498
770
|
return {
|
|
499
771
|
state,
|
|
500
|
-
elapsedMs,
|
|
501
|
-
stalledMs,
|
|
502
772
|
handleEvent,
|
|
503
773
|
addUserMessage,
|
|
504
774
|
addSystemMessage,
|