@bastani/atomic 0.8.4-0 → 0.8.5-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/CHANGELOG.md +16 -0
- package/README.md +24 -23
- package/dist/builtin/intercom/README.md +5 -5
- package/dist/builtin/intercom/index.ts +1 -1
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/ui/compose.ts +19 -1
- package/dist/builtin/intercom/ui/session-list.ts +19 -1
- package/dist/builtin/mcp/README.md +3 -3
- package/dist/builtin/mcp/commands.ts +1 -1
- package/dist/builtin/mcp/host-html-template.ts +1 -1
- package/dist/builtin/mcp/mcp-panel.ts +14 -14
- package/dist/builtin/mcp/mcp-setup-panel.ts +4 -4
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/tool-result-renderer.ts +1 -1
- package/dist/builtin/subagents/README.md +3 -3
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/tui/render.ts +1844 -1062
- package/dist/builtin/web-access/README.md +1 -1
- package/dist/builtin/web-access/curator-page.ts +2 -2
- package/dist/builtin/web-access/index.ts +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/README.md +34 -7
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +23 -4
- package/dist/builtin/workflows/builtin/ralph.ts +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/workflow/SKILL.md +75 -16
- package/dist/builtin/workflows/skills/workflow/references/running-workflows.md +34 -11
- package/dist/builtin/workflows/skills/workflow/references/sdk-authoring.md +111 -20
- package/dist/builtin/workflows/src/extension/discovery.ts +32 -4
- package/dist/builtin/workflows/src/extension/index.ts +347 -63
- package/dist/builtin/workflows/src/extension/render-call.ts +3 -1
- package/dist/builtin/workflows/src/extension/render-result.ts +7 -0
- package/dist/builtin/workflows/src/extension/runtime.ts +4 -2
- package/dist/builtin/workflows/src/extension/wiring.ts +32 -8
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +36 -14
- package/dist/builtin/workflows/src/runs/background/runner.ts +2 -2
- package/dist/builtin/workflows/src/runs/background/status.ts +89 -0
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +338 -78
- package/dist/builtin/workflows/src/runs/foreground/stage-control-registry.ts +2 -0
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +55 -7
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +146 -10
- package/dist/builtin/workflows/src/shared/store.ts +29 -0
- package/dist/builtin/workflows/src/shared/types.ts +25 -4
- package/dist/builtin/workflows/src/tui/graph-canvas.ts +69 -2
- package/dist/builtin/workflows/src/tui/graph-view.ts +97 -182
- package/dist/builtin/workflows/src/tui/header.ts +36 -20
- package/dist/builtin/workflows/src/tui/inline-form-card.ts +129 -46
- package/dist/builtin/workflows/src/tui/inline-form-editor.ts +111 -36
- package/dist/builtin/workflows/src/tui/inputs-picker.ts +311 -91
- package/dist/builtin/workflows/src/tui/layout.ts +1 -1
- package/dist/builtin/workflows/src/tui/node-card.ts +66 -37
- package/dist/builtin/workflows/src/tui/overlay-adapter.ts +20 -6
- package/dist/builtin/workflows/src/tui/prompt-card.ts +262 -85
- package/dist/builtin/workflows/src/tui/run-detail.ts +50 -31
- package/dist/builtin/workflows/src/tui/session-confirm.ts +21 -14
- package/dist/builtin/workflows/src/tui/session-picker.ts +35 -26
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +531 -960
- package/dist/builtin/workflows/src/tui/status-helpers.ts +6 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +8 -4
- package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -2
- package/dist/builtin/workflows/src/tui/switcher.ts +55 -25
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +33 -1
- package/dist/builtin/workflows/src/tui/workflow-list.ts +10 -6
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +20 -6
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +3 -3
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +3 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +3 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +24 -12
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +6 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +28 -17
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +65 -28
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +13 -5
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +5 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js +1 -1
- package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts +8 -8
- package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/view/dialog-builder.js +6 -6
- package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +7 -4
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +3 -2
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +3 -2
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -2
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/render-utils.d.ts +2 -1
- package/dist/core/tools/render-utils.d.ts.map +1 -1
- package/dist/core/tools/render-utils.js.map +1 -1
- package/dist/core/tools/todos.d.ts.map +1 -1
- package/dist/core/tools/todos.js +1 -1
- package/dist/core/tools/todos.js.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.d.ts +4 -3
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +3 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +3 -3
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +2 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +3 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +13 -3
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +2 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +2 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +47 -5
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +5 -5
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +3 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +2 -2
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +7 -7
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +8 -8
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +3 -3
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +2 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +10 -12
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +3 -3
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/working-status.d.ts +25 -0
- package/dist/modes/interactive/components/working-status.d.ts.map +1 -0
- package/dist/modes/interactive/components/working-status.js +28 -0
- package/dist/modes/interactive/components/working-status.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +8 -7
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +5 -5
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/development.md +2 -2
- package/docs/extensions.md +7 -7
- package/docs/packages.md +11 -8
- package/docs/quickstart.md +2 -2
- package/docs/rpc.md +1 -1
- package/docs/sdk.md +14 -11
- package/docs/session-format.md +1 -1
- package/docs/sessions.md +10 -10
- package/docs/settings.md +1 -1
- package/docs/terminal-setup.md +9 -9
- package/docs/tmux.md +10 -10
- package/docs/tui.md +2 -2
- package/docs/usage.md +9 -9
- package/package.json +6 -1
|
@@ -5,83 +5,106 @@
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
7
7
|
import { getMarkdownTheme, type ExtensionContext } from "@bastani/atomic";
|
|
8
|
-
import { Container, Markdown, Spacer, Text, visibleWidth, type Component } from "@earendil-works/pi-tui";
|
|
9
8
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
Container,
|
|
10
|
+
Markdown,
|
|
11
|
+
Spacer,
|
|
12
|
+
Text,
|
|
13
|
+
visibleWidth,
|
|
14
|
+
type Component,
|
|
15
|
+
} from "@earendil-works/pi-tui";
|
|
16
|
+
import {
|
|
17
|
+
type AgentProgress,
|
|
18
|
+
type AsyncJobState,
|
|
19
|
+
type AsyncJobStep,
|
|
20
|
+
type AsyncParallelGroupStatus,
|
|
21
|
+
type Details,
|
|
22
|
+
MAX_WIDGET_JOBS,
|
|
23
|
+
WIDGET_KEY,
|
|
17
24
|
} from "../shared/types.ts";
|
|
18
|
-
import {
|
|
19
|
-
|
|
25
|
+
import {
|
|
26
|
+
formatTokens,
|
|
27
|
+
formatUsage,
|
|
28
|
+
formatDuration,
|
|
29
|
+
formatModelThinking,
|
|
30
|
+
formatToolCall,
|
|
31
|
+
shortenPath,
|
|
32
|
+
} from "../shared/formatters.ts";
|
|
33
|
+
import {
|
|
34
|
+
getDisplayItems,
|
|
35
|
+
getLastActivity,
|
|
36
|
+
getSingleResultOutput,
|
|
37
|
+
} from "../shared/utils.ts";
|
|
20
38
|
import { flatToLogicalStepIndex } from "../runs/background/parallel-groups.ts";
|
|
21
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
aggregateStepStatus,
|
|
41
|
+
formatActivityLabel,
|
|
42
|
+
formatAgentRunningLabel,
|
|
43
|
+
formatParallelOutcome,
|
|
44
|
+
} from "../shared/status-format.ts";
|
|
22
45
|
|
|
23
46
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
24
47
|
|
|
25
48
|
function getTermWidth(): number {
|
|
26
|
-
|
|
49
|
+
return process.stdout.columns || 120;
|
|
27
50
|
}
|
|
28
51
|
|
|
29
52
|
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
30
53
|
|
|
31
54
|
/**
|
|
32
55
|
* Truncate a line to maxWidth, preserving ANSI styling through the ellipsis.
|
|
33
|
-
*
|
|
56
|
+
*
|
|
34
57
|
* pi-tui's truncateToWidth adds \x1b[0m before ellipsis which resets all styling,
|
|
35
58
|
* causing background color bleed in the TUI. This implementation tracks active
|
|
36
59
|
* ANSI styles and re-applies them before the ellipsis.
|
|
37
|
-
*
|
|
60
|
+
*
|
|
38
61
|
* Uses Intl.Segmenter for proper Unicode/emoji handling (not char-by-char).
|
|
39
62
|
*/
|
|
40
63
|
function truncLine(text: string, maxWidth: number): string {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
if (visibleWidth(text) <= maxWidth) return text;
|
|
65
|
+
|
|
66
|
+
const targetWidth = maxWidth - 1;
|
|
67
|
+
let result = "";
|
|
68
|
+
let currentWidth = 0;
|
|
69
|
+
let activeStyles: string[] = [];
|
|
70
|
+
let i = 0;
|
|
71
|
+
|
|
72
|
+
while (i < text.length) {
|
|
73
|
+
const ansiMatch = text.slice(i).match(/^\x1b\[[0-9;]*m/);
|
|
74
|
+
if (ansiMatch) {
|
|
75
|
+
const code = ansiMatch[0];
|
|
76
|
+
result += code;
|
|
77
|
+
|
|
78
|
+
if (code === "\x1b[0m" || code === "\x1b[m") {
|
|
79
|
+
activeStyles = [];
|
|
80
|
+
} else {
|
|
81
|
+
activeStyles.push(code);
|
|
82
|
+
}
|
|
83
|
+
i += code.length;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let end = i;
|
|
88
|
+
while (end < text.length && !text.slice(end).match(/^\x1b\[[0-9;]*m/)) {
|
|
89
|
+
end++;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const textPortion = text.slice(i, end);
|
|
93
|
+
for (const seg of segmenter.segment(textPortion)) {
|
|
94
|
+
const grapheme = seg.segment;
|
|
95
|
+
const graphemeWidth = visibleWidth(grapheme);
|
|
96
|
+
|
|
97
|
+
if (currentWidth + graphemeWidth > targetWidth) {
|
|
98
|
+
return result + activeStyles.join("") + "…";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
result += grapheme;
|
|
102
|
+
currentWidth += graphemeWidth;
|
|
103
|
+
}
|
|
104
|
+
i = end;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return result + activeStyles.join("") + "…";
|
|
85
108
|
}
|
|
86
109
|
|
|
87
110
|
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
@@ -91,1167 +114,1926 @@ let widgetTimer: ReturnType<typeof setInterval> | undefined;
|
|
|
91
114
|
let latestWidgetCtx: ExtensionContext | undefined;
|
|
92
115
|
let latestWidgetJobs: AsyncJobState[] = [];
|
|
93
116
|
|
|
94
|
-
const resultAnimationTimers = new Map<
|
|
95
|
-
|
|
96
|
-
|
|
117
|
+
const resultAnimationTimers = new Map<
|
|
118
|
+
ReturnType<typeof setInterval>,
|
|
119
|
+
ResultAnimationContext["state"]
|
|
120
|
+
>();
|
|
121
|
+
const outputActivityCache = new Map<
|
|
122
|
+
string,
|
|
123
|
+
{ checkedAt: number; text: string }
|
|
124
|
+
>();
|
|
125
|
+
const STALE_EXTENSION_CONTEXT_MESSAGE =
|
|
126
|
+
"This extension ctx is stale after session replacement or reload";
|
|
97
127
|
|
|
98
128
|
interface ResultAnimationContext {
|
|
99
|
-
|
|
100
|
-
|
|
129
|
+
state: { subagentResultAnimationTimer?: ReturnType<typeof setInterval> };
|
|
130
|
+
invalidate: () => void;
|
|
101
131
|
}
|
|
102
132
|
|
|
103
133
|
function spinnerFrame(): string {
|
|
104
|
-
|
|
134
|
+
return SPINNER[
|
|
135
|
+
Math.floor(Date.now() / WIDGET_ANIMATION_MS) % SPINNER.length
|
|
136
|
+
]!;
|
|
105
137
|
}
|
|
106
138
|
|
|
107
139
|
function isStaleExtensionContextError(error: unknown): boolean {
|
|
108
|
-
|
|
109
|
-
|
|
140
|
+
if (!(error instanceof Error)) return false;
|
|
141
|
+
return error.message.includes(STALE_EXTENSION_CONTEXT_MESSAGE);
|
|
110
142
|
}
|
|
111
143
|
|
|
112
144
|
function resultIsRunning(result: AgentToolResult<Details>): boolean {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
145
|
+
return (
|
|
146
|
+
result.details?.progress?.some((entry) => entry.status === "running") ||
|
|
147
|
+
result.details?.results.some(
|
|
148
|
+
(entry) => entry.progress?.status === "running",
|
|
149
|
+
) ||
|
|
150
|
+
false
|
|
151
|
+
);
|
|
116
152
|
}
|
|
117
153
|
|
|
118
154
|
function stopResultAnimation(context: ResultAnimationContext): void {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
155
|
+
const timer = context.state.subagentResultAnimationTimer;
|
|
156
|
+
if (!timer) return;
|
|
157
|
+
clearInterval(timer);
|
|
158
|
+
resultAnimationTimers.delete(timer);
|
|
159
|
+
context.state.subagentResultAnimationTimer = undefined;
|
|
124
160
|
}
|
|
125
161
|
|
|
126
|
-
export function syncResultAnimation(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
162
|
+
export function syncResultAnimation(
|
|
163
|
+
result: AgentToolResult<Details>,
|
|
164
|
+
context: ResultAnimationContext,
|
|
165
|
+
): void {
|
|
166
|
+
if (!resultIsRunning(result)) {
|
|
167
|
+
stopResultAnimation(context);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (context.state.subagentResultAnimationTimer) return;
|
|
171
|
+
const timer = setInterval(() => {
|
|
172
|
+
try {
|
|
173
|
+
context.invalidate();
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (!isStaleExtensionContextError(error)) throw error;
|
|
176
|
+
stopResultAnimation(context);
|
|
177
|
+
}
|
|
178
|
+
}, WIDGET_ANIMATION_MS);
|
|
179
|
+
timer.unref?.();
|
|
180
|
+
context.state.subagentResultAnimationTimer = timer;
|
|
181
|
+
resultAnimationTimers.set(timer, context.state);
|
|
143
182
|
}
|
|
144
183
|
|
|
145
184
|
function extractOutputTarget(task: string): string | undefined {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
185
|
+
const writeToMatch = task.match(/\[Write to:\s*([^\]\n]+)\]/i);
|
|
186
|
+
if (writeToMatch?.[1]?.trim()) return writeToMatch[1].trim();
|
|
187
|
+
const findingsMatch = task.match(/Write your findings to:\s*(\S+)/i);
|
|
188
|
+
if (findingsMatch?.[1]?.trim()) return findingsMatch[1].trim();
|
|
189
|
+
const outputMatch = task.match(/[Oo]utput(?:\s+to)?\s*:\s*(\S+)/i);
|
|
190
|
+
if (outputMatch?.[1]?.trim()) return outputMatch[1].trim();
|
|
191
|
+
return undefined;
|
|
153
192
|
}
|
|
154
193
|
|
|
155
|
-
function hasEmptyTextOutputWithoutOutputTarget(
|
|
156
|
-
|
|
157
|
-
|
|
194
|
+
function hasEmptyTextOutputWithoutOutputTarget(
|
|
195
|
+
task: string,
|
|
196
|
+
output: string,
|
|
197
|
+
): boolean {
|
|
198
|
+
if (output.trim()) return false;
|
|
199
|
+
return !extractOutputTarget(task);
|
|
158
200
|
}
|
|
159
201
|
|
|
160
202
|
function getToolCallLines(
|
|
161
|
-
|
|
162
|
-
|
|
203
|
+
result: Pick<Details["results"][number], "messages" | "toolCalls">,
|
|
204
|
+
expanded: boolean,
|
|
163
205
|
): string[] {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
206
|
+
if (result.messages) {
|
|
207
|
+
return getDisplayItems(result.messages)
|
|
208
|
+
.filter(
|
|
209
|
+
(
|
|
210
|
+
item,
|
|
211
|
+
): item is {
|
|
212
|
+
type: "tool";
|
|
213
|
+
name: string;
|
|
214
|
+
args: Record<string, unknown>;
|
|
215
|
+
} => item.type === "tool",
|
|
216
|
+
)
|
|
217
|
+
.map((item) => formatToolCall(item.name, item.args, expanded));
|
|
218
|
+
}
|
|
219
|
+
return (
|
|
220
|
+
result.toolCalls?.map((toolCall) =>
|
|
221
|
+
expanded ? toolCall.expandedText : toolCall.text,
|
|
222
|
+
) ?? []
|
|
223
|
+
);
|
|
170
224
|
}
|
|
171
225
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
226
|
+
function formatCurrentToolLine(
|
|
227
|
+
progress: Pick<
|
|
228
|
+
AgentProgress,
|
|
229
|
+
"currentTool" | "currentToolArgs" | "currentToolStartedAt"
|
|
230
|
+
>,
|
|
231
|
+
availableWidth: number,
|
|
232
|
+
expanded: boolean,
|
|
233
|
+
): string | undefined {
|
|
234
|
+
if (!progress.currentTool) return undefined;
|
|
235
|
+
const maxToolArgsLen = Math.max(50, availableWidth - 20);
|
|
236
|
+
const toolArgsPreview = progress.currentToolArgs
|
|
237
|
+
? expanded || progress.currentToolArgs.length <= maxToolArgsLen
|
|
238
|
+
? progress.currentToolArgs
|
|
239
|
+
: `${progress.currentToolArgs.slice(0, maxToolArgsLen)}...`
|
|
240
|
+
: "";
|
|
241
|
+
const durationSuffix =
|
|
242
|
+
progress.currentToolStartedAt !== undefined
|
|
243
|
+
? ` | ${formatDuration(Math.max(0, Date.now() - progress.currentToolStartedAt))}`
|
|
244
|
+
: "";
|
|
245
|
+
return toolArgsPreview
|
|
246
|
+
? `${progress.currentTool}: ${toolArgsPreview}${durationSuffix}`
|
|
247
|
+
: `${progress.currentTool}${durationSuffix}`;
|
|
187
248
|
}
|
|
188
249
|
|
|
189
|
-
function buildLiveStatusLine(
|
|
190
|
-
|
|
250
|
+
function buildLiveStatusLine(
|
|
251
|
+
progress: Pick<AgentProgress, "activityState" | "lastActivityAt">,
|
|
252
|
+
): string | undefined {
|
|
253
|
+
return formatActivityLabel(progress.lastActivityAt, progress.activityState);
|
|
191
254
|
}
|
|
192
255
|
|
|
193
256
|
function themeBold(theme: Theme, text: string): string {
|
|
194
|
-
|
|
257
|
+
return (theme as { bold?: (value: string) => string }).bold?.(text) ?? text;
|
|
195
258
|
}
|
|
196
259
|
|
|
197
260
|
function statJoin(theme: Theme, parts: string[]): string {
|
|
198
|
-
|
|
261
|
+
return parts
|
|
262
|
+
.filter(Boolean)
|
|
263
|
+
.map((part) => theme.fg("dim", part))
|
|
264
|
+
.join(` ${theme.fg("dim", "·")} `);
|
|
199
265
|
}
|
|
200
266
|
|
|
201
267
|
function formatTokenStat(tokens: number): string {
|
|
202
|
-
|
|
268
|
+
return `${formatTokens(tokens)} token`;
|
|
203
269
|
}
|
|
204
270
|
|
|
205
271
|
function formatToolUseStat(count: number): string {
|
|
206
|
-
|
|
272
|
+
return `${count} tool use${count === 1 ? "" : "s"}`;
|
|
207
273
|
}
|
|
208
274
|
|
|
209
|
-
function formatProgressStats(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
275
|
+
function formatProgressStats(
|
|
276
|
+
theme: Theme,
|
|
277
|
+
progress:
|
|
278
|
+
| Pick<AgentProgress, "toolCount" | "tokens" | "durationMs">
|
|
279
|
+
| undefined,
|
|
280
|
+
includeDuration = true,
|
|
281
|
+
): string {
|
|
282
|
+
if (!progress) return "";
|
|
283
|
+
const parts: string[] = [];
|
|
284
|
+
if (progress.toolCount > 0) parts.push(formatToolUseStat(progress.toolCount));
|
|
285
|
+
if (progress.tokens > 0) parts.push(formatTokenStat(progress.tokens));
|
|
286
|
+
if (includeDuration && progress.durationMs > 0)
|
|
287
|
+
parts.push(formatDuration(progress.durationMs));
|
|
288
|
+
return statJoin(theme, parts);
|
|
216
289
|
}
|
|
217
290
|
|
|
218
291
|
function firstOutputLine(text: string): string {
|
|
219
|
-
|
|
292
|
+
return (
|
|
293
|
+
text
|
|
294
|
+
.split("\n")
|
|
295
|
+
.find((line) => line.trim())
|
|
296
|
+
?.trim() ?? ""
|
|
297
|
+
);
|
|
220
298
|
}
|
|
221
299
|
|
|
222
|
-
function resultStatusLine(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
300
|
+
function resultStatusLine(
|
|
301
|
+
result: Details["results"][number],
|
|
302
|
+
output: string,
|
|
303
|
+
): string {
|
|
304
|
+
if (result.detached)
|
|
305
|
+
return result.detachedReason
|
|
306
|
+
? `Detached: ${result.detachedReason}`
|
|
307
|
+
: "Detached";
|
|
308
|
+
if (result.interrupted) return "Paused";
|
|
309
|
+
if (result.exitCode !== 0)
|
|
310
|
+
return `Error: ${result.error ?? (firstOutputLine(output) || `exit ${result.exitCode}`)}`;
|
|
311
|
+
if (hasEmptyTextOutputWithoutOutputTarget(result.task, output))
|
|
312
|
+
return "Done (no text output)";
|
|
313
|
+
return "Done";
|
|
228
314
|
}
|
|
229
315
|
|
|
230
|
-
function resultGlyph(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
316
|
+
function resultGlyph(
|
|
317
|
+
result: Details["results"][number],
|
|
318
|
+
output: string,
|
|
319
|
+
theme: Theme,
|
|
320
|
+
running = result.progress?.status === "running",
|
|
321
|
+
): string {
|
|
322
|
+
if (running) return theme.fg("accent", spinnerFrame());
|
|
323
|
+
if (result.detached) return theme.fg("warning", "■");
|
|
324
|
+
if (result.interrupted) return theme.fg("warning", "■");
|
|
325
|
+
if (result.exitCode !== 0) return theme.fg("error", "✗");
|
|
326
|
+
if (hasEmptyTextOutputWithoutOutputTarget(result.task, output))
|
|
327
|
+
return theme.fg("warning", "✓");
|
|
328
|
+
return theme.fg("success", "✓");
|
|
237
329
|
}
|
|
238
330
|
|
|
239
331
|
function compactCurrentActivity(progress: AgentProgress): string {
|
|
240
|
-
|
|
332
|
+
return (
|
|
333
|
+
formatCurrentToolLine(progress, getTermWidth() - 4, false) ??
|
|
334
|
+
buildLiveStatusLine(progress) ??
|
|
335
|
+
"thinking…"
|
|
336
|
+
);
|
|
241
337
|
}
|
|
242
338
|
|
|
243
339
|
function hasAnimatedWidgetJobs(jobs: AsyncJobState[]): boolean {
|
|
244
|
-
|
|
340
|
+
return jobs.some((job) => job.status === "running");
|
|
245
341
|
}
|
|
246
342
|
|
|
247
343
|
function formatWidgetAgents(agents: string[]): string {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
344
|
+
const distinct = [...new Set(agents)];
|
|
345
|
+
if (distinct.length === 1 && agents.length > 1)
|
|
346
|
+
return `${distinct[0]} ×${agents.length}`;
|
|
347
|
+
if (agents.length > 3)
|
|
348
|
+
return `${agents.slice(0, 2).join(", ")} +${agents.length - 2} more`;
|
|
349
|
+
return agents.join(", ");
|
|
252
350
|
}
|
|
253
351
|
|
|
254
352
|
function widgetJobName(job: AsyncJobState): string {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
353
|
+
if (job.mode === "parallel") return "parallel";
|
|
354
|
+
if (job.mode === "chain") return "chain";
|
|
355
|
+
if (job.mode === "single" && job.agents?.length === 1) return job.agents[0]!;
|
|
356
|
+
if (job.agents?.length) return formatWidgetAgents(job.agents);
|
|
357
|
+
return job.mode ?? "subagent";
|
|
260
358
|
}
|
|
261
359
|
|
|
262
360
|
function getCachedLastActivity(outputFile: string | undefined): string {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
361
|
+
if (!outputFile) return "";
|
|
362
|
+
const now = Date.now();
|
|
363
|
+
const cached = outputActivityCache.get(outputFile);
|
|
364
|
+
if (cached && now - cached.checkedAt < 1000) return cached.text;
|
|
365
|
+
const text = getLastActivity(outputFile);
|
|
366
|
+
outputActivityCache.set(outputFile, { checkedAt: now, text });
|
|
367
|
+
return text;
|
|
270
368
|
}
|
|
271
369
|
|
|
272
370
|
function widgetActivity(job: AsyncJobState): string {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
371
|
+
const facts: string[] = [];
|
|
372
|
+
if (job.currentTool && job.currentToolStartedAt !== undefined)
|
|
373
|
+
facts.push(
|
|
374
|
+
`${job.currentTool} ${formatDuration(Math.max(0, Date.now() - job.currentToolStartedAt))}`,
|
|
375
|
+
);
|
|
376
|
+
else if (job.currentTool) facts.push(job.currentTool);
|
|
377
|
+
if (job.currentPath) facts.push(shortenPath(job.currentPath));
|
|
378
|
+
if (job.turnCount !== undefined) facts.push(`${job.turnCount} turns`);
|
|
379
|
+
if (job.toolCount !== undefined) facts.push(`${job.toolCount} tools`);
|
|
380
|
+
const activity =
|
|
381
|
+
formatActivityLabel(job.lastActivityAt, job.activityState) ??
|
|
382
|
+
(job.status === "running" ? getCachedLastActivity(job.outputFile) : "");
|
|
383
|
+
if (activity && facts.length) return `${activity} · ${facts.join(" · ")}`;
|
|
384
|
+
if (activity) return activity;
|
|
385
|
+
if (facts.length) return facts.join(" · ");
|
|
386
|
+
if (job.status === "running") return "thinking…";
|
|
387
|
+
if (job.status === "queued") return "queued…";
|
|
388
|
+
if (job.status === "paused") return "Paused";
|
|
389
|
+
if (job.status === "failed") return "Failed";
|
|
390
|
+
return "Done";
|
|
289
391
|
}
|
|
290
392
|
|
|
291
393
|
function widgetStatusGlyph(job: AsyncJobState, theme: Theme): string {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
394
|
+
if (job.status === "running") return theme.fg("accent", spinnerFrame());
|
|
395
|
+
if (job.status === "queued") return theme.fg("muted", "◦");
|
|
396
|
+
if (job.status === "complete") return theme.fg("success", "✓");
|
|
397
|
+
if (job.status === "paused") return theme.fg("warning", "■");
|
|
398
|
+
return theme.fg("error", "✗");
|
|
297
399
|
}
|
|
298
400
|
|
|
299
401
|
function widgetStepGlyph(status: AsyncJobStep["status"], theme: Theme): string {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
402
|
+
if (status === "running") return theme.fg("accent", spinnerFrame());
|
|
403
|
+
if (status === "complete" || status === "completed")
|
|
404
|
+
return theme.fg("success", "✓");
|
|
405
|
+
if (status === "failed") return theme.fg("error", "✗");
|
|
406
|
+
if (status === "paused") return theme.fg("warning", "■");
|
|
407
|
+
return theme.fg("muted", "◦");
|
|
305
408
|
}
|
|
306
409
|
|
|
307
|
-
function widgetStepStatus(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
410
|
+
function widgetStepStatus(
|
|
411
|
+
status: AsyncJobStep["status"],
|
|
412
|
+
theme: Theme,
|
|
413
|
+
): string {
|
|
414
|
+
if (status === "running") return theme.fg("accent", "running");
|
|
415
|
+
if (status === "complete" || status === "completed")
|
|
416
|
+
return theme.fg("success", "complete");
|
|
417
|
+
if (status === "failed") return theme.fg("error", "failed");
|
|
418
|
+
if (status === "paused") return theme.fg("warning", "paused");
|
|
419
|
+
return theme.fg("dim", status);
|
|
313
420
|
}
|
|
314
421
|
|
|
315
|
-
function widgetStepActivity(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
422
|
+
function widgetStepActivity(
|
|
423
|
+
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
424
|
+
): string {
|
|
425
|
+
const facts: string[] = [];
|
|
426
|
+
if (step.currentTool && step.currentToolStartedAt !== undefined)
|
|
427
|
+
facts.push(
|
|
428
|
+
`${step.currentTool} ${formatDuration(Math.max(0, Date.now() - step.currentToolStartedAt))}`,
|
|
429
|
+
);
|
|
430
|
+
else if (step.currentTool) facts.push(step.currentTool);
|
|
431
|
+
if (step.currentPath) facts.push(shortenPath(step.currentPath));
|
|
432
|
+
if (step.turnCount !== undefined) facts.push(`${step.turnCount} turns`);
|
|
433
|
+
if (step.toolCount !== undefined) facts.push(`${step.toolCount} tools`);
|
|
434
|
+
if (step.tokens?.total) facts.push(formatTokenStat(step.tokens.total));
|
|
435
|
+
const activity = formatActivityLabel(step.lastActivityAt, step.activityState);
|
|
436
|
+
if (activity && facts.length) return `${activity} · ${facts.join(" · ")}`;
|
|
437
|
+
if (activity) return activity;
|
|
438
|
+
return facts.join(" · ");
|
|
327
439
|
}
|
|
328
440
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
441
|
+
function widgetChainDetails(
|
|
442
|
+
job: AsyncJobState,
|
|
443
|
+
theme: Theme,
|
|
444
|
+
expanded = false,
|
|
445
|
+
width = getTermWidth(),
|
|
446
|
+
): string[] {
|
|
447
|
+
if (!job.steps?.length) return [];
|
|
448
|
+
const total = job.chainStepCount ?? job.steps.length;
|
|
449
|
+
const lines: string[] = [];
|
|
450
|
+
for (const span of buildAsyncChainStepSpans(
|
|
451
|
+
total,
|
|
452
|
+
job.steps.length,
|
|
453
|
+
job.parallelGroups,
|
|
454
|
+
)) {
|
|
455
|
+
const steps = job.steps.slice(span.start, span.start + span.count);
|
|
456
|
+
if (span.isParallel) {
|
|
457
|
+
const status = aggregateStepStatus(steps);
|
|
458
|
+
lines.push(
|
|
459
|
+
` ${widgetStepGlyph(status, theme)} Step ${span.stepIndex + 1}/${total}: ${themeBold(theme, "parallel group")} ${theme.fg("dim", "·")} ${theme.fg("dim", formatParallelOutcome(steps, span.count))}`,
|
|
460
|
+
);
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const step = steps[0];
|
|
464
|
+
if (!step) {
|
|
465
|
+
lines.push(
|
|
466
|
+
` ${theme.fg("dim", `◦ Step ${span.stepIndex + 1}/${total}: pending`)}`,
|
|
467
|
+
);
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
lines.push(
|
|
471
|
+
...foregroundStyleWidgetStepLines(
|
|
472
|
+
job,
|
|
473
|
+
theme,
|
|
474
|
+
step,
|
|
475
|
+
"Step",
|
|
476
|
+
span.stepIndex + 1,
|
|
477
|
+
total,
|
|
478
|
+
expanded,
|
|
479
|
+
width,
|
|
480
|
+
),
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
return lines;
|
|
349
484
|
}
|
|
350
485
|
|
|
351
|
-
function widgetParallelAgentDetails(
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
486
|
+
function widgetParallelAgentDetails(
|
|
487
|
+
job: AsyncJobState,
|
|
488
|
+
theme: Theme,
|
|
489
|
+
): string[] {
|
|
490
|
+
if (!job.steps?.length) return [];
|
|
491
|
+
if (job.mode !== "parallel" && job.mode !== "chain") return [];
|
|
492
|
+
if (
|
|
493
|
+
job.mode === "chain" &&
|
|
494
|
+
!job.activeParallelGroup &&
|
|
495
|
+
job.parallelGroups?.length
|
|
496
|
+
)
|
|
497
|
+
return widgetChainDetails(job, theme);
|
|
498
|
+
const total = job.stepsTotal ?? job.steps.length;
|
|
499
|
+
return job.steps.map((step, index) => {
|
|
500
|
+
const marker = index === job.steps!.length - 1 ? "└" : "├";
|
|
501
|
+
const activity = widgetStepActivity(step);
|
|
502
|
+
const itemTitle =
|
|
503
|
+
job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
504
|
+
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
505
|
+
return ` ${theme.fg("dim", `${marker} ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index + 1}/${total}: ${step.agent} · ${widgetStepStatus(step.status, theme)}${modelDisplay}${activity ? ` · ${activity}` : ""}`)}`;
|
|
506
|
+
});
|
|
363
507
|
}
|
|
364
508
|
|
|
365
|
-
function parseParallelGroupAgentCount(
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
509
|
+
function parseParallelGroupAgentCount(
|
|
510
|
+
label: string | undefined,
|
|
511
|
+
): number | undefined {
|
|
512
|
+
if (!label || !label.startsWith("[") || !label.endsWith("]"))
|
|
513
|
+
return undefined;
|
|
514
|
+
const inner = label.slice(1, -1).trim();
|
|
515
|
+
if (!inner) return 0;
|
|
516
|
+
return inner
|
|
517
|
+
.split("+")
|
|
518
|
+
.map((part) => part.trim())
|
|
519
|
+
.filter(Boolean).length;
|
|
370
520
|
}
|
|
371
521
|
|
|
372
|
-
function isChainParallelGroupActive(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
522
|
+
function isChainParallelGroupActive(
|
|
523
|
+
details: Pick<Details, "mode" | "chainAgents" | "currentStepIndex">,
|
|
524
|
+
): boolean {
|
|
525
|
+
if (details.mode !== "chain") return false;
|
|
526
|
+
if (details.currentStepIndex === undefined) return false;
|
|
527
|
+
const currentLabel = details.chainAgents?.[details.currentStepIndex];
|
|
528
|
+
return parseParallelGroupAgentCount(currentLabel) !== undefined;
|
|
377
529
|
}
|
|
378
530
|
|
|
379
531
|
interface ChainStepSpan {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
532
|
+
stepIndex: number;
|
|
533
|
+
start: number;
|
|
534
|
+
count: number;
|
|
535
|
+
isParallel: boolean;
|
|
384
536
|
}
|
|
385
537
|
|
|
386
|
-
function buildChainStepSpans(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
538
|
+
function buildChainStepSpans(
|
|
539
|
+
chainAgents: string[] | undefined,
|
|
540
|
+
): ChainStepSpan[] {
|
|
541
|
+
if (!chainAgents?.length) return [];
|
|
542
|
+
const spans: ChainStepSpan[] = [];
|
|
543
|
+
let start = 0;
|
|
544
|
+
for (let stepIndex = 0; stepIndex < chainAgents.length; stepIndex++) {
|
|
545
|
+
const label = chainAgents[stepIndex]!;
|
|
546
|
+
const parsedCount = parseParallelGroupAgentCount(label);
|
|
547
|
+
const count = parsedCount ?? 1;
|
|
548
|
+
spans.push({
|
|
549
|
+
stepIndex,
|
|
550
|
+
start,
|
|
551
|
+
count,
|
|
552
|
+
isParallel: parsedCount !== undefined,
|
|
553
|
+
});
|
|
554
|
+
start += count;
|
|
555
|
+
}
|
|
556
|
+
return spans;
|
|
398
557
|
}
|
|
399
558
|
|
|
400
|
-
function buildAsyncChainStepSpans(
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
559
|
+
function buildAsyncChainStepSpans(
|
|
560
|
+
total: number,
|
|
561
|
+
stepCount: number,
|
|
562
|
+
parallelGroups: AsyncParallelGroupStatus[] = [],
|
|
563
|
+
): ChainStepSpan[] {
|
|
564
|
+
const spans: ChainStepSpan[] = [];
|
|
565
|
+
let flatIndex = 0;
|
|
566
|
+
for (let stepIndex = 0; stepIndex < total; stepIndex++) {
|
|
567
|
+
const group = parallelGroups.find(
|
|
568
|
+
(candidate) => candidate.stepIndex === stepIndex,
|
|
569
|
+
);
|
|
570
|
+
if (group) {
|
|
571
|
+
spans.push({
|
|
572
|
+
stepIndex,
|
|
573
|
+
start: group.start,
|
|
574
|
+
count: group.count,
|
|
575
|
+
isParallel: true,
|
|
576
|
+
});
|
|
577
|
+
flatIndex = Math.max(flatIndex, group.start + group.count);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
spans.push({
|
|
581
|
+
stepIndex,
|
|
582
|
+
start: flatIndex,
|
|
583
|
+
count: flatIndex < stepCount ? 1 : 0,
|
|
584
|
+
isParallel: false,
|
|
585
|
+
});
|
|
586
|
+
flatIndex++;
|
|
587
|
+
}
|
|
588
|
+
return spans;
|
|
414
589
|
}
|
|
415
590
|
|
|
416
591
|
function isDoneResult(result: Details["results"][number]): boolean {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
592
|
+
const status = result.progress?.status;
|
|
593
|
+
if (status === "completed") return true;
|
|
594
|
+
if (status === "running" || status === "pending") return false;
|
|
595
|
+
if (result.interrupted || result.detached) return false;
|
|
596
|
+
return result.exitCode === 0;
|
|
422
597
|
}
|
|
423
598
|
|
|
424
599
|
interface MultiProgressLabel {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
600
|
+
headerLabel: string;
|
|
601
|
+
itemTitle: "Step" | "Agent";
|
|
602
|
+
totalCount: number;
|
|
603
|
+
hasParallelInChain: boolean;
|
|
604
|
+
activeParallelGroup: boolean;
|
|
605
|
+
groupStartIndex: number;
|
|
606
|
+
groupEndIndex: number;
|
|
607
|
+
showActiveGroupOnly: boolean;
|
|
433
608
|
}
|
|
434
609
|
|
|
435
|
-
function buildMultiProgressLabel(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
610
|
+
function buildMultiProgressLabel(
|
|
611
|
+
details: Pick<
|
|
612
|
+
Details,
|
|
613
|
+
| "mode"
|
|
614
|
+
| "results"
|
|
615
|
+
| "progress"
|
|
616
|
+
| "totalSteps"
|
|
617
|
+
| "currentStepIndex"
|
|
618
|
+
| "chainAgents"
|
|
619
|
+
>,
|
|
620
|
+
hasRunning: boolean,
|
|
621
|
+
): MultiProgressLabel {
|
|
622
|
+
const stepSpans = buildChainStepSpans(details.chainAgents);
|
|
623
|
+
const hasParallelInChain =
|
|
624
|
+
details.mode === "chain" && stepSpans.some((span) => span.isParallel);
|
|
625
|
+
const activeParallelGroup = isChainParallelGroupActive(details);
|
|
626
|
+
const itemTitle: "Step" | "Agent" =
|
|
627
|
+
details.mode === "parallel" || activeParallelGroup ? "Agent" : "Step";
|
|
628
|
+
|
|
629
|
+
if (details.mode === "parallel") {
|
|
630
|
+
const totalCount = details.totalSteps ?? details.results.length;
|
|
631
|
+
const statuses = new Array(totalCount).fill("pending") as Array<
|
|
632
|
+
"pending" | "running" | "completed" | "failed" | "detached"
|
|
633
|
+
>;
|
|
634
|
+
for (const progress of details.progress ?? []) {
|
|
635
|
+
if (progress.index >= 0 && progress.index < totalCount)
|
|
636
|
+
statuses[progress.index] = progress.status;
|
|
637
|
+
}
|
|
638
|
+
for (let i = 0; i < details.results.length; i++) {
|
|
639
|
+
const result = details.results[i]!;
|
|
640
|
+
const progressFromArray =
|
|
641
|
+
details.progress?.find((progress) => progress.index === i) ||
|
|
642
|
+
details.progress?.find(
|
|
643
|
+
(progress) =>
|
|
644
|
+
progress.agent === result.agent && progress.status === "running",
|
|
645
|
+
);
|
|
646
|
+
const index = result.progress?.index ?? progressFromArray?.index ?? i;
|
|
647
|
+
if (index < 0 || index >= totalCount) continue;
|
|
648
|
+
const status =
|
|
649
|
+
result.progress?.status ??
|
|
650
|
+
(result.interrupted || result.detached
|
|
651
|
+
? "detached"
|
|
652
|
+
: result.exitCode === 0
|
|
653
|
+
? "completed"
|
|
654
|
+
: "failed");
|
|
655
|
+
statuses[index] = status;
|
|
656
|
+
}
|
|
657
|
+
const running = statuses.filter((status) => status === "running").length;
|
|
658
|
+
const done = statuses.filter((status) => status === "completed").length;
|
|
659
|
+
const headerLabel = hasRunning
|
|
660
|
+
? `${formatAgentRunningLabel(running)} · ${done}/${totalCount} done`
|
|
661
|
+
: `${done}/${totalCount} done`;
|
|
662
|
+
return {
|
|
663
|
+
headerLabel,
|
|
664
|
+
itemTitle,
|
|
665
|
+
totalCount,
|
|
666
|
+
hasParallelInChain,
|
|
667
|
+
activeParallelGroup,
|
|
668
|
+
groupStartIndex: 0,
|
|
669
|
+
groupEndIndex: totalCount,
|
|
670
|
+
showActiveGroupOnly: false,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (activeParallelGroup) {
|
|
675
|
+
const currentStepIndex = details.currentStepIndex!;
|
|
676
|
+
const span = stepSpans[currentStepIndex];
|
|
677
|
+
const groupSize = span?.count ?? 1;
|
|
678
|
+
const groupStart = span?.start ?? 0;
|
|
679
|
+
const groupEnd = groupStart + groupSize;
|
|
680
|
+
let running = 0;
|
|
681
|
+
let done = 0;
|
|
682
|
+
for (let index = groupStart; index < groupEnd; index++) {
|
|
683
|
+
const progressEntry = details.progress?.find(
|
|
684
|
+
(progress) => progress.index === index,
|
|
685
|
+
);
|
|
686
|
+
const resultEntry = details.results.find(
|
|
687
|
+
(result) => result.progress?.index === index,
|
|
688
|
+
);
|
|
689
|
+
if (progressEntry?.status === "running") {
|
|
690
|
+
running++;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
if (progressEntry?.status === "completed") {
|
|
694
|
+
done++;
|
|
695
|
+
continue;
|
|
696
|
+
}
|
|
697
|
+
if (resultEntry && isDoneResult(resultEntry)) done++;
|
|
698
|
+
}
|
|
699
|
+
const totalSteps = details.totalSteps ?? details.chainAgents?.length ?? 1;
|
|
700
|
+
const headerLabel = hasRunning
|
|
701
|
+
? `step ${currentStepIndex + 1}/${totalSteps} · parallel group: ${formatAgentRunningLabel(running)} · ${done}/${groupSize} done`
|
|
702
|
+
: `step ${currentStepIndex + 1}/${totalSteps} · parallel group: ${done}/${groupSize} done`;
|
|
703
|
+
return {
|
|
704
|
+
headerLabel,
|
|
705
|
+
itemTitle,
|
|
706
|
+
totalCount: groupSize,
|
|
707
|
+
hasParallelInChain,
|
|
708
|
+
activeParallelGroup,
|
|
709
|
+
groupStartIndex: groupStart,
|
|
710
|
+
groupEndIndex: groupEnd,
|
|
711
|
+
showActiveGroupOnly: true,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (details.mode === "chain" && details.chainAgents?.length) {
|
|
716
|
+
const totalCount = details.totalSteps ?? details.chainAgents.length;
|
|
717
|
+
const doneLogical = stepSpans.filter((span) => {
|
|
718
|
+
for (let index = span.start; index < span.start + span.count; index++) {
|
|
719
|
+
const progressEntry = details.progress?.find(
|
|
720
|
+
(progress) => progress.index === index,
|
|
721
|
+
);
|
|
722
|
+
const resultEntry =
|
|
723
|
+
details.results.find((result) => result.progress?.index === index) ??
|
|
724
|
+
details.results[index];
|
|
725
|
+
if (
|
|
726
|
+
progressEntry?.status === "running" ||
|
|
727
|
+
progressEntry?.status === "pending"
|
|
728
|
+
)
|
|
729
|
+
return false;
|
|
730
|
+
if (resultEntry && !isDoneResult(resultEntry)) return false;
|
|
731
|
+
}
|
|
732
|
+
return true;
|
|
733
|
+
}).length;
|
|
734
|
+
const currentStep =
|
|
735
|
+
details.currentStepIndex !== undefined
|
|
736
|
+
? details.currentStepIndex + 1
|
|
737
|
+
: Math.min(totalCount, doneLogical + (hasRunning ? 1 : 0));
|
|
738
|
+
const headerLabel = hasRunning
|
|
739
|
+
? `step ${currentStep}/${totalCount}`
|
|
740
|
+
: `step ${doneLogical}/${totalCount}`;
|
|
741
|
+
return {
|
|
742
|
+
headerLabel,
|
|
743
|
+
itemTitle,
|
|
744
|
+
totalCount,
|
|
745
|
+
hasParallelInChain,
|
|
746
|
+
activeParallelGroup,
|
|
747
|
+
groupStartIndex: 0,
|
|
748
|
+
groupEndIndex: details.results.length,
|
|
749
|
+
showActiveGroupOnly: false,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const totalCount = details.totalSteps ?? details.results.length;
|
|
754
|
+
const currentStep =
|
|
755
|
+
details.currentStepIndex !== undefined
|
|
756
|
+
? details.currentStepIndex + 1
|
|
757
|
+
: Math.min(
|
|
758
|
+
totalCount,
|
|
759
|
+
details.results.filter(isDoneResult).length + (hasRunning ? 1 : 0),
|
|
760
|
+
);
|
|
761
|
+
const done = details.results.filter(isDoneResult).length;
|
|
762
|
+
const headerLabel = hasRunning
|
|
763
|
+
? `step ${currentStep}/${totalCount}`
|
|
764
|
+
: `step ${done}/${totalCount}`;
|
|
765
|
+
return {
|
|
766
|
+
headerLabel,
|
|
767
|
+
itemTitle,
|
|
768
|
+
totalCount,
|
|
769
|
+
hasParallelInChain,
|
|
770
|
+
activeParallelGroup,
|
|
771
|
+
groupStartIndex: 0,
|
|
772
|
+
groupEndIndex: details.results.length,
|
|
773
|
+
showActiveGroupOnly: false,
|
|
774
|
+
};
|
|
518
775
|
}
|
|
519
776
|
|
|
520
|
-
function resultRowLabel(
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
777
|
+
function resultRowLabel(
|
|
778
|
+
details: Pick<Details, "mode" | "chainAgents">,
|
|
779
|
+
label: MultiProgressLabel,
|
|
780
|
+
resultIndex: number,
|
|
781
|
+
stepNumber: number,
|
|
782
|
+
): string {
|
|
783
|
+
if (details.mode === "chain" && label.hasParallelInChain) {
|
|
784
|
+
const span = buildChainStepSpans(details.chainAgents).find(
|
|
785
|
+
(candidate) =>
|
|
786
|
+
resultIndex >= candidate.start &&
|
|
787
|
+
resultIndex < candidate.start + candidate.count,
|
|
788
|
+
);
|
|
789
|
+
if (span?.isParallel)
|
|
790
|
+
return `Agent ${resultIndex - span.start + 1}/${span.count}`;
|
|
791
|
+
if (span) return `Step ${span.stepIndex + 1}`;
|
|
792
|
+
}
|
|
793
|
+
if (label.itemTitle === "Agent") {
|
|
794
|
+
const localStepNumber = label.activeParallelGroup
|
|
795
|
+
? Math.max(1, stepNumber - label.groupStartIndex)
|
|
796
|
+
: stepNumber;
|
|
797
|
+
return `Agent ${localStepNumber}/${label.totalCount}`;
|
|
798
|
+
}
|
|
799
|
+
return `Step ${stepNumber}`;
|
|
533
800
|
}
|
|
534
801
|
|
|
535
802
|
function widgetStats(job: AsyncJobState, theme: Theme): string {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
803
|
+
const parts: string[] = [];
|
|
804
|
+
const stepsTotal = job.stepsTotal ?? job.agents?.length ?? 1;
|
|
805
|
+
if (job.activeParallelGroup) {
|
|
806
|
+
const running = job.runningSteps ?? (job.status === "running" ? 1 : 0);
|
|
807
|
+
const done =
|
|
808
|
+
job.completedSteps ?? (job.status === "complete" ? stepsTotal : 0);
|
|
809
|
+
if (job.mode === "parallel") {
|
|
810
|
+
if (job.status === "running" && running > 0)
|
|
811
|
+
parts.push(formatAgentRunningLabel(running));
|
|
812
|
+
if (stepsTotal > 0) parts.push(`${done}/${stepsTotal} done`);
|
|
813
|
+
} else {
|
|
814
|
+
const activeGroup =
|
|
815
|
+
job.currentStep !== undefined
|
|
816
|
+
? job.parallelGroups?.find(
|
|
817
|
+
(group) =>
|
|
818
|
+
job.currentStep! >= group.start &&
|
|
819
|
+
job.currentStep! < group.start + group.count,
|
|
820
|
+
)
|
|
821
|
+
: job.parallelGroups?.find((group) => group.start === 0);
|
|
822
|
+
const logicalStep = activeGroup?.stepIndex ?? job.currentStep ?? 0;
|
|
823
|
+
const total = job.chainStepCount ?? stepsTotal;
|
|
824
|
+
const groupParts = [`${done}/${stepsTotal} done`];
|
|
825
|
+
if (job.status === "running" && running > 0)
|
|
826
|
+
groupParts.unshift(formatAgentRunningLabel(running));
|
|
827
|
+
parts.push(
|
|
828
|
+
`step ${logicalStep + 1}/${total} · parallel group: ${groupParts.join(" · ")}`,
|
|
829
|
+
);
|
|
830
|
+
}
|
|
831
|
+
} else if (job.currentStep !== undefined) {
|
|
832
|
+
if (job.mode === "chain" && job.parallelGroups?.length) {
|
|
833
|
+
const total = job.chainStepCount ?? stepsTotal;
|
|
834
|
+
parts.push(
|
|
835
|
+
`step ${flatToLogicalStepIndex(job.currentStep, total, job.parallelGroups) + 1}/${total}`,
|
|
836
|
+
);
|
|
837
|
+
} else {
|
|
838
|
+
parts.push(`step ${job.currentStep + 1}/${stepsTotal}`);
|
|
839
|
+
}
|
|
840
|
+
} else if (stepsTotal > 1) {
|
|
841
|
+
parts.push(`steps ${stepsTotal}`);
|
|
842
|
+
}
|
|
843
|
+
if (job.toolCount !== undefined) parts.push(formatToolUseStat(job.toolCount));
|
|
844
|
+
if (job.totalTokens?.total)
|
|
845
|
+
parts.push(formatTokenStat(job.totalTokens.total));
|
|
846
|
+
const endTime =
|
|
847
|
+
job.status === "complete" ||
|
|
848
|
+
job.status === "failed" ||
|
|
849
|
+
job.status === "paused"
|
|
850
|
+
? (job.updatedAt ?? Date.now())
|
|
851
|
+
: Date.now();
|
|
852
|
+
if (job.startedAt)
|
|
853
|
+
parts.push(formatDuration(Math.max(0, endTime - job.startedAt)));
|
|
854
|
+
return statJoin(theme, parts);
|
|
569
855
|
}
|
|
570
856
|
|
|
571
|
-
function widgetStepStats(
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
857
|
+
function widgetStepStats(
|
|
858
|
+
theme: Theme,
|
|
859
|
+
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
860
|
+
): string {
|
|
861
|
+
return statJoin(theme, [
|
|
862
|
+
step.turnCount !== undefined ? `${step.turnCount} turns` : "",
|
|
863
|
+
step.toolCount !== undefined ? formatToolUseStat(step.toolCount) : "",
|
|
864
|
+
step.tokens?.total ? formatTokenStat(step.tokens.total) : "",
|
|
865
|
+
step.durationMs !== undefined ? formatDuration(step.durationMs) : "",
|
|
866
|
+
]);
|
|
578
867
|
}
|
|
579
868
|
|
|
580
|
-
function modelThinkingBadge(
|
|
581
|
-
|
|
582
|
-
|
|
869
|
+
function modelThinkingBadge(
|
|
870
|
+
theme: Theme,
|
|
871
|
+
model?: string,
|
|
872
|
+
thinking?: string,
|
|
873
|
+
): string {
|
|
874
|
+
const label = formatModelThinking(model, thinking);
|
|
875
|
+
return label ? theme.fg("dim", ` (${label})`) : "";
|
|
583
876
|
}
|
|
584
877
|
|
|
585
|
-
function widgetStepActivityLine(
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
878
|
+
function widgetStepActivityLine(
|
|
879
|
+
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
880
|
+
width: number,
|
|
881
|
+
expanded: boolean,
|
|
882
|
+
): string {
|
|
883
|
+
const toolLine = formatCurrentToolLine(step, width, expanded);
|
|
884
|
+
if (toolLine) return toolLine;
|
|
885
|
+
const activity = formatActivityLabel(step.lastActivityAt, step.activityState);
|
|
886
|
+
if (activity) return activity;
|
|
887
|
+
if (step.status === "running") return "thinking…";
|
|
888
|
+
return "";
|
|
592
889
|
}
|
|
593
890
|
|
|
594
|
-
function widgetOutputPath(
|
|
595
|
-
|
|
596
|
-
|
|
891
|
+
function widgetOutputPath(
|
|
892
|
+
job: AsyncJobState,
|
|
893
|
+
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
894
|
+
): string | undefined {
|
|
895
|
+
if (typeof step.index !== "number") return undefined;
|
|
896
|
+
return path.join(job.asyncDir, `output-${step.index}.log`);
|
|
597
897
|
}
|
|
598
898
|
|
|
599
899
|
function foregroundStyleWidgetStepLines(
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
900
|
+
job: AsyncJobState,
|
|
901
|
+
theme: Theme,
|
|
902
|
+
step: NonNullable<AsyncJobState["steps"]>[number],
|
|
903
|
+
itemTitle: "Agent" | "Step",
|
|
904
|
+
index: number,
|
|
905
|
+
total: number,
|
|
906
|
+
expanded: boolean,
|
|
907
|
+
width: number,
|
|
608
908
|
): string[] {
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
909
|
+
const status = widgetStepStatus(step.status, theme);
|
|
910
|
+
const stats = widgetStepStats(theme, step);
|
|
911
|
+
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
912
|
+
const lines = [
|
|
913
|
+
` ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
914
|
+
];
|
|
915
|
+
const activity = widgetStepActivityLine(step, width, expanded);
|
|
916
|
+
if (activity) lines.push(` ${theme.fg("dim", `⎿ ${activity}`)}`);
|
|
917
|
+
if (step.status === "running") {
|
|
918
|
+
if (!expanded)
|
|
919
|
+
lines.push(` ${theme.fg("accent", "Press ctrl+o for live detail")}`);
|
|
920
|
+
const output = widgetOutputPath(job, step);
|
|
921
|
+
if (output)
|
|
922
|
+
lines.push(` ${theme.fg("dim", `output: ${shortenPath(output)}`)}`);
|
|
923
|
+
if (expanded) {
|
|
924
|
+
const liveStatus = buildLiveStatusLine(step);
|
|
925
|
+
if (liveStatus && liveStatus !== activity)
|
|
926
|
+
lines.push(` ${theme.fg("accent", liveStatus)}`);
|
|
927
|
+
for (const tool of step.recentTools?.slice(-3) ?? []) {
|
|
928
|
+
const maxArgsLen = Math.max(40, width - 30);
|
|
929
|
+
const argsPreview =
|
|
930
|
+
tool.args.length <= maxArgsLen
|
|
931
|
+
? tool.args
|
|
932
|
+
: `${tool.args.slice(0, maxArgsLen)}...`;
|
|
933
|
+
lines.push(
|
|
934
|
+
` ${theme.fg("dim", `${tool.tool}${argsPreview ? `: ${argsPreview}` : ""}`)}`,
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
for (const line of step.recentOutput?.slice(-5) ?? []) {
|
|
938
|
+
lines.push(` ${theme.fg("dim", line)}`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return lines;
|
|
633
943
|
}
|
|
634
944
|
|
|
635
|
-
function foregroundStyleWidgetDetails(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
945
|
+
function foregroundStyleWidgetDetails(
|
|
946
|
+
job: AsyncJobState,
|
|
947
|
+
theme: Theme,
|
|
948
|
+
expanded: boolean,
|
|
949
|
+
width: number,
|
|
950
|
+
): string[] {
|
|
951
|
+
if (!job.steps?.length)
|
|
952
|
+
return [` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`];
|
|
953
|
+
if (
|
|
954
|
+
job.mode === "chain" &&
|
|
955
|
+
!job.activeParallelGroup &&
|
|
956
|
+
job.parallelGroups?.length
|
|
957
|
+
)
|
|
958
|
+
return widgetChainDetails(job, theme, expanded, width);
|
|
959
|
+
const total = job.stepsTotal ?? job.steps.length;
|
|
960
|
+
const itemTitle =
|
|
961
|
+
job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
962
|
+
const lines: string[] = [];
|
|
963
|
+
for (const [index, step] of job.steps.entries()) {
|
|
964
|
+
lines.push(
|
|
965
|
+
...foregroundStyleWidgetStepLines(
|
|
966
|
+
job,
|
|
967
|
+
theme,
|
|
968
|
+
step,
|
|
969
|
+
itemTitle,
|
|
970
|
+
index + 1,
|
|
971
|
+
total,
|
|
972
|
+
expanded,
|
|
973
|
+
width,
|
|
974
|
+
),
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
return lines;
|
|
645
978
|
}
|
|
646
979
|
|
|
647
|
-
function buildSingleWidgetLines(
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
980
|
+
function buildSingleWidgetLines(
|
|
981
|
+
job: AsyncJobState,
|
|
982
|
+
theme: Theme,
|
|
983
|
+
width: number,
|
|
984
|
+
expanded: boolean,
|
|
985
|
+
): string[] {
|
|
986
|
+
const stats = widgetStats(job, theme);
|
|
987
|
+
const count =
|
|
988
|
+
job.mode === "chain"
|
|
989
|
+
? job.chainStepCount
|
|
990
|
+
: (job.stepsTotal ?? job.agents?.length ?? job.steps?.length);
|
|
991
|
+
const mode = widgetJobName(job);
|
|
992
|
+
const title = `async subagent ${mode}${count && count > 1 ? ` (${count})` : ""}`;
|
|
993
|
+
return [
|
|
994
|
+
`${theme.fg("toolTitle", themeBold(theme, title))} ${theme.fg("dim", "· background")}`,
|
|
995
|
+
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, mode)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
996
|
+
...foregroundStyleWidgetDetails(job, theme, expanded, width),
|
|
997
|
+
].map((line) => truncLine(line, width));
|
|
657
998
|
}
|
|
658
999
|
|
|
659
|
-
function compactSingleWidgetLines(
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1000
|
+
function compactSingleWidgetLines(
|
|
1001
|
+
job: AsyncJobState,
|
|
1002
|
+
theme: Theme,
|
|
1003
|
+
width: number,
|
|
1004
|
+
): string[] {
|
|
1005
|
+
const fullLines = buildSingleWidgetLines(job, theme, width, false);
|
|
1006
|
+
if (
|
|
1007
|
+
fullLines.length <= 10 ||
|
|
1008
|
+
!job.steps?.length ||
|
|
1009
|
+
(job.mode !== "parallel" && !job.activeParallelGroup)
|
|
1010
|
+
)
|
|
1011
|
+
return fullLines;
|
|
1012
|
+
|
|
1013
|
+
const total = job.stepsTotal ?? job.steps.length;
|
|
1014
|
+
const itemTitle =
|
|
1015
|
+
job.mode === "parallel" || job.activeParallelGroup ? "Agent" : "Step";
|
|
1016
|
+
const lines = fullLines.slice(0, 2);
|
|
1017
|
+
for (const [index, step] of job.steps.entries()) {
|
|
1018
|
+
const status = widgetStepStatus(step.status, theme);
|
|
1019
|
+
const activity = widgetStepActivityLine(step, width, false);
|
|
1020
|
+
const stepStats = widgetStepStats(theme, step);
|
|
1021
|
+
const activitySuffix = activity
|
|
1022
|
+
? ` ${theme.fg("dim", "·")} ${theme.fg("dim", activity)}`
|
|
1023
|
+
: "";
|
|
1024
|
+
const modelDisplay = modelThinkingBadge(theme, step.model, step.thinking);
|
|
1025
|
+
lines.push(
|
|
1026
|
+
` ${widgetStepGlyph(step.status, theme)} ${itemTitle} ${index + 1}/${total}: ${themeBold(theme, step.agent)} ${theme.fg("dim", "·")} ${status}${modelDisplay}${activitySuffix}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}`,
|
|
1027
|
+
);
|
|
1028
|
+
}
|
|
1029
|
+
if (job.steps.some((step) => step.status === "running"))
|
|
1030
|
+
lines.push(theme.fg("accent", " Press ctrl+o for live detail"));
|
|
1031
|
+
return lines.map((line) => truncLine(line, width));
|
|
676
1032
|
}
|
|
677
1033
|
|
|
678
|
-
function fitWidgetLineBudget(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
1034
|
+
function fitWidgetLineBudget(
|
|
1035
|
+
lines: string[],
|
|
1036
|
+
theme: Theme,
|
|
1037
|
+
width: number,
|
|
1038
|
+
expanded: boolean,
|
|
1039
|
+
): string[] {
|
|
1040
|
+
const rows = process.stdout.rows || 30;
|
|
1041
|
+
const budget = expanded
|
|
1042
|
+
? Math.max(12, Math.min(24, Math.floor(rows * 0.55)))
|
|
1043
|
+
: Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
|
|
1044
|
+
if (lines.length <= budget) return lines;
|
|
1045
|
+
const visibleLines = Math.max(1, budget - 1);
|
|
1046
|
+
const hiddenCount = lines.length - visibleLines;
|
|
1047
|
+
const hint = expanded
|
|
1048
|
+
? `… ${hiddenCount} live-detail lines hidden`
|
|
1049
|
+
: `… ${hiddenCount} lines hidden · Ctrl+O expands`;
|
|
1050
|
+
return [
|
|
1051
|
+
...lines.slice(0, visibleLines),
|
|
1052
|
+
truncLine(theme.fg("dim", hint), width),
|
|
1053
|
+
];
|
|
690
1054
|
}
|
|
691
1055
|
|
|
692
|
-
function buildWidgetComponent(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
1056
|
+
function buildWidgetComponent(
|
|
1057
|
+
jobs: AsyncJobState[],
|
|
1058
|
+
expanded: boolean,
|
|
1059
|
+
): (_tui: unknown, theme: Theme) => Component {
|
|
1060
|
+
return (_tui, theme) => {
|
|
1061
|
+
const width = getTermWidth();
|
|
1062
|
+
const lines = expanded
|
|
1063
|
+
? buildWidgetLines(jobs, theme, width, true)
|
|
1064
|
+
: jobs.length === 1
|
|
1065
|
+
? compactSingleWidgetLines(jobs[0]!, theme, width)
|
|
1066
|
+
: buildWidgetLines(jobs, theme, width, false);
|
|
1067
|
+
const container = new Container();
|
|
1068
|
+
for (const line of fitWidgetLineBudget(lines, theme, width, expanded))
|
|
1069
|
+
container.addChild(new Text(line, 1, 0));
|
|
1070
|
+
return container;
|
|
1071
|
+
};
|
|
704
1072
|
}
|
|
705
1073
|
|
|
706
|
-
export function buildWidgetLines(
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
1074
|
+
export function buildWidgetLines(
|
|
1075
|
+
jobs: AsyncJobState[],
|
|
1076
|
+
theme: Theme,
|
|
1077
|
+
width = getTermWidth(),
|
|
1078
|
+
expanded = false,
|
|
1079
|
+
): string[] {
|
|
1080
|
+
if (jobs.length === 0) return [];
|
|
1081
|
+
if (jobs.length === 1)
|
|
1082
|
+
return buildSingleWidgetLines(jobs[0]!, theme, width, expanded);
|
|
1083
|
+
const running = jobs.filter((job) => job.status === "running");
|
|
1084
|
+
const queued = jobs.filter((job) => job.status === "queued");
|
|
1085
|
+
const finished = jobs.filter(
|
|
1086
|
+
(job) => job.status !== "running" && job.status !== "queued",
|
|
1087
|
+
);
|
|
1088
|
+
|
|
1089
|
+
const lines: string[] = [];
|
|
1090
|
+
const hasActive = running.length > 0 || queued.length > 0;
|
|
1091
|
+
lines.push(
|
|
1092
|
+
truncLine(
|
|
1093
|
+
`${theme.fg(hasActive ? "accent" : "dim", hasActive ? "●" : "○")} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "· background")}`,
|
|
1094
|
+
width,
|
|
1095
|
+
),
|
|
1096
|
+
);
|
|
1097
|
+
|
|
1098
|
+
const items: string[][] = [];
|
|
1099
|
+
let hiddenRunning = 0;
|
|
1100
|
+
let hiddenFinished = 0;
|
|
1101
|
+
let queuedSummaryShown = false;
|
|
1102
|
+
let slots = MAX_WIDGET_JOBS;
|
|
1103
|
+
|
|
1104
|
+
for (const job of running) {
|
|
1105
|
+
if (slots <= 0) {
|
|
1106
|
+
hiddenRunning++;
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
const stats = widgetStats(job, theme);
|
|
1110
|
+
items.push([
|
|
1111
|
+
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1112
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1113
|
+
...widgetParallelAgentDetails(job, theme),
|
|
1114
|
+
]);
|
|
1115
|
+
slots--;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
if (queued.length > 0 && slots > 0) {
|
|
1119
|
+
items.push([
|
|
1120
|
+
`${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`,
|
|
1121
|
+
]);
|
|
1122
|
+
queuedSummaryShown = true;
|
|
1123
|
+
slots--;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
for (const job of finished) {
|
|
1127
|
+
if (slots <= 0) {
|
|
1128
|
+
hiddenFinished++;
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
const stats = widgetStats(job, theme);
|
|
1132
|
+
items.push([
|
|
1133
|
+
`${widgetStatusGlyph(job, theme)} ${themeBold(theme, widgetJobName(job))}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1134
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
1135
|
+
...widgetParallelAgentDetails(job, theme),
|
|
1136
|
+
]);
|
|
1137
|
+
slots--;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
const hiddenQueued =
|
|
1141
|
+
queued.length > 0 && !queuedSummaryShown ? queued.length : 0;
|
|
1142
|
+
const hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
|
|
1143
|
+
if (hiddenTotal > 0) {
|
|
1144
|
+
const parts: string[] = [];
|
|
1145
|
+
if (hiddenRunning > 0) parts.push(`${hiddenRunning} running`);
|
|
1146
|
+
if (hiddenQueued > 0) parts.push(`${hiddenQueued} queued`);
|
|
1147
|
+
if (hiddenFinished > 0) parts.push(`${hiddenFinished} finished`);
|
|
1148
|
+
items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
for (let i = 0; i < items.length; i++) {
|
|
1152
|
+
const item = items[i]!;
|
|
1153
|
+
const last = i === items.length - 1;
|
|
1154
|
+
const branch = last ? "└─" : "├─";
|
|
1155
|
+
const continuation = last ? " " : "│ ";
|
|
1156
|
+
lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
|
|
1157
|
+
for (const detail of item.slice(1)) {
|
|
1158
|
+
lines.push(
|
|
1159
|
+
truncLine(`${theme.fg("dim", continuation)} ${detail}`, width),
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return lines;
|
|
773
1165
|
}
|
|
774
1166
|
|
|
775
1167
|
function refreshAnimatedWidget(): void {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1168
|
+
try {
|
|
1169
|
+
if (!latestWidgetCtx?.hasUI || latestWidgetJobs.length === 0) return;
|
|
1170
|
+
latestWidgetCtx.ui.setWidget(
|
|
1171
|
+
WIDGET_KEY,
|
|
1172
|
+
buildWidgetComponent(
|
|
1173
|
+
latestWidgetJobs,
|
|
1174
|
+
latestWidgetCtx.ui.getToolsExpanded?.() ?? false,
|
|
1175
|
+
),
|
|
1176
|
+
);
|
|
1177
|
+
latestWidgetCtx.ui.requestRender?.();
|
|
1178
|
+
} catch (error) {
|
|
1179
|
+
if (!isStaleExtensionContextError(error)) throw error;
|
|
1180
|
+
stopWidgetAnimation();
|
|
1181
|
+
}
|
|
784
1182
|
}
|
|
785
1183
|
|
|
786
1184
|
function ensureWidgetAnimation(): void {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1185
|
+
if (widgetTimer) return;
|
|
1186
|
+
widgetTimer = setInterval(() => {
|
|
1187
|
+
if (!hasAnimatedWidgetJobs(latestWidgetJobs)) {
|
|
1188
|
+
stopWidgetAnimation();
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
refreshAnimatedWidget();
|
|
1192
|
+
}, WIDGET_ANIMATION_MS);
|
|
1193
|
+
widgetTimer.unref?.();
|
|
796
1194
|
}
|
|
797
1195
|
|
|
798
1196
|
export function stopWidgetAnimation(): void {
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1197
|
+
if (widgetTimer) {
|
|
1198
|
+
clearInterval(widgetTimer);
|
|
1199
|
+
widgetTimer = undefined;
|
|
1200
|
+
}
|
|
1201
|
+
latestWidgetCtx = undefined;
|
|
1202
|
+
latestWidgetJobs = [];
|
|
1203
|
+
outputActivityCache.clear();
|
|
806
1204
|
}
|
|
807
1205
|
|
|
808
1206
|
export function stopResultAnimations(): void {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1207
|
+
for (const [timer, state] of resultAnimationTimers) {
|
|
1208
|
+
clearInterval(timer);
|
|
1209
|
+
state.subagentResultAnimationTimer = undefined;
|
|
1210
|
+
}
|
|
1211
|
+
resultAnimationTimers.clear();
|
|
814
1212
|
}
|
|
815
1213
|
|
|
816
1214
|
/**
|
|
817
1215
|
* Render the async jobs widget
|
|
818
1216
|
*/
|
|
819
|
-
export function renderWidget(
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1217
|
+
export function renderWidget(
|
|
1218
|
+
ctx: ExtensionContext,
|
|
1219
|
+
jobs: AsyncJobState[],
|
|
1220
|
+
): void {
|
|
1221
|
+
if (jobs.length === 0) {
|
|
1222
|
+
stopWidgetAnimation();
|
|
1223
|
+
if (ctx.hasUI) ctx.ui.setWidget(WIDGET_KEY, undefined);
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
if (!ctx.hasUI) {
|
|
1227
|
+
stopWidgetAnimation();
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
latestWidgetCtx = ctx;
|
|
1231
|
+
latestWidgetJobs = [...jobs];
|
|
1232
|
+
|
|
1233
|
+
ctx.ui.setWidget(
|
|
1234
|
+
WIDGET_KEY,
|
|
1235
|
+
buildWidgetComponent(jobs, ctx.ui.getToolsExpanded?.() ?? false),
|
|
1236
|
+
);
|
|
1237
|
+
if (hasAnimatedWidgetJobs(jobs)) ensureWidgetAnimation();
|
|
1238
|
+
else stopWidgetAnimation();
|
|
835
1239
|
}
|
|
836
1240
|
|
|
837
|
-
function renderSingleCompact(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1241
|
+
function renderSingleCompact(
|
|
1242
|
+
d: Details,
|
|
1243
|
+
r: Details["results"][number],
|
|
1244
|
+
theme: Theme,
|
|
1245
|
+
): Component {
|
|
1246
|
+
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
1247
|
+
const progress = r.progress || r.progressSummary;
|
|
1248
|
+
const isRunning = r.progress?.status === "running";
|
|
1249
|
+
const contextBadge =
|
|
1250
|
+
d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1251
|
+
const stats = statJoin(theme, [
|
|
1252
|
+
r.usage?.turns ? `⟳${r.usage.turns}` : "",
|
|
1253
|
+
formatProgressStats(theme, progress),
|
|
1254
|
+
]);
|
|
1255
|
+
const c = new Container();
|
|
1256
|
+
const width = getTermWidth() - 4;
|
|
1257
|
+
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1258
|
+
c.addChild(
|
|
1259
|
+
new Text(
|
|
1260
|
+
truncLine(
|
|
1261
|
+
`${resultGlyph(r, output, theme, isRunning)} ${theme.fg("toolTitle", theme.bold(r.agent))}${modelDisplay}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1262
|
+
width,
|
|
1263
|
+
),
|
|
1264
|
+
0,
|
|
1265
|
+
0,
|
|
1266
|
+
),
|
|
1267
|
+
);
|
|
1268
|
+
|
|
1269
|
+
if (isRunning && r.progress) {
|
|
1270
|
+
const activity = compactCurrentActivity(r.progress);
|
|
1271
|
+
c.addChild(
|
|
1272
|
+
new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0),
|
|
1273
|
+
);
|
|
1274
|
+
const liveStatus = buildLiveStatusLine(r.progress);
|
|
1275
|
+
if (liveStatus && liveStatus !== activity)
|
|
1276
|
+
c.addChild(
|
|
1277
|
+
new Text(truncLine(theme.fg("dim", ` ${liveStatus}`), width), 0, 0),
|
|
1278
|
+
);
|
|
1279
|
+
c.addChild(
|
|
1280
|
+
new Text(
|
|
1281
|
+
truncLine(theme.fg("accent", " Press ctrl+o for live detail"), width),
|
|
1282
|
+
0,
|
|
1283
|
+
0,
|
|
1284
|
+
),
|
|
1285
|
+
);
|
|
1286
|
+
if (r.artifactPaths)
|
|
1287
|
+
c.addChild(
|
|
1288
|
+
new Text(
|
|
1289
|
+
truncLine(
|
|
1290
|
+
theme.fg(
|
|
1291
|
+
"dim",
|
|
1292
|
+
` output: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1293
|
+
),
|
|
1294
|
+
width,
|
|
1295
|
+
),
|
|
1296
|
+
0,
|
|
1297
|
+
0,
|
|
1298
|
+
),
|
|
1299
|
+
);
|
|
1300
|
+
return c;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
c.addChild(
|
|
1304
|
+
new Text(
|
|
1305
|
+
truncLine(theme.fg("dim", ` ⎿ ${resultStatusLine(r, output)}`), width),
|
|
1306
|
+
0,
|
|
1307
|
+
0,
|
|
1308
|
+
),
|
|
1309
|
+
);
|
|
1310
|
+
const preview = firstOutputLine(output);
|
|
1311
|
+
if (
|
|
1312
|
+
preview &&
|
|
1313
|
+
r.exitCode === 0 &&
|
|
1314
|
+
!hasEmptyTextOutputWithoutOutputTarget(r.task, output)
|
|
1315
|
+
) {
|
|
1316
|
+
c.addChild(
|
|
1317
|
+
new Text(truncLine(theme.fg("dim", ` ${preview}`), width), 0, 0),
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
if (r.sessionFile)
|
|
1321
|
+
c.addChild(
|
|
1322
|
+
new Text(
|
|
1323
|
+
truncLine(
|
|
1324
|
+
theme.fg("dim", ` session: ${shortenPath(r.sessionFile)}`),
|
|
1325
|
+
width,
|
|
1326
|
+
),
|
|
1327
|
+
0,
|
|
1328
|
+
0,
|
|
1329
|
+
),
|
|
1330
|
+
);
|
|
1331
|
+
if (r.artifactPaths)
|
|
1332
|
+
c.addChild(
|
|
1333
|
+
new Text(
|
|
1334
|
+
truncLine(
|
|
1335
|
+
theme.fg(
|
|
1336
|
+
"dim",
|
|
1337
|
+
` output: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1338
|
+
),
|
|
1339
|
+
width,
|
|
1340
|
+
),
|
|
1341
|
+
0,
|
|
1342
|
+
0,
|
|
1343
|
+
),
|
|
1344
|
+
);
|
|
1345
|
+
if (r.truncation?.artifactPath)
|
|
1346
|
+
c.addChild(
|
|
1347
|
+
new Text(
|
|
1348
|
+
truncLine(
|
|
1349
|
+
theme.fg(
|
|
1350
|
+
"dim",
|
|
1351
|
+
` full output: ${shortenPath(r.truncation.artifactPath)}`,
|
|
1352
|
+
),
|
|
1353
|
+
width,
|
|
1354
|
+
),
|
|
1355
|
+
0,
|
|
1356
|
+
0,
|
|
1357
|
+
),
|
|
1358
|
+
);
|
|
1359
|
+
return c;
|
|
870
1360
|
}
|
|
871
1361
|
|
|
872
1362
|
function renderMultiCompact(d: Details, theme: Theme): Component {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1363
|
+
const hasRunning =
|
|
1364
|
+
d.progress?.some((p) => p.status === "running") ||
|
|
1365
|
+
d.results.some((r) => r.progress?.status === "running");
|
|
1366
|
+
const failed = d.results.some(
|
|
1367
|
+
(r) => r.exitCode !== 0 && r.progress?.status !== "running",
|
|
1368
|
+
);
|
|
1369
|
+
const paused = d.results.some(
|
|
1370
|
+
(r) => (r.interrupted || r.detached) && r.progress?.status !== "running",
|
|
1371
|
+
);
|
|
1372
|
+
let totalSummary = d.progressSummary;
|
|
1373
|
+
if (!totalSummary) {
|
|
1374
|
+
let sawProgress = false;
|
|
1375
|
+
const summary = { toolCount: 0, tokens: 0, durationMs: 0 };
|
|
1376
|
+
for (const r of d.results) {
|
|
1377
|
+
const prog = r.progress || r.progressSummary;
|
|
1378
|
+
if (!prog) continue;
|
|
1379
|
+
sawProgress = true;
|
|
1380
|
+
summary.toolCount += prog.toolCount;
|
|
1381
|
+
summary.tokens += prog.tokens;
|
|
1382
|
+
summary.durationMs =
|
|
1383
|
+
d.mode === "chain"
|
|
1384
|
+
? summary.durationMs + prog.durationMs
|
|
1385
|
+
: Math.max(summary.durationMs, prog.durationMs);
|
|
1386
|
+
}
|
|
1387
|
+
if (sawProgress) totalSummary = summary;
|
|
1388
|
+
}
|
|
1389
|
+
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
1390
|
+
const itemTitle = multiLabel.itemTitle;
|
|
1391
|
+
const stats = statJoin(theme, [
|
|
1392
|
+
multiLabel.headerLabel,
|
|
1393
|
+
formatProgressStats(theme, totalSummary),
|
|
1394
|
+
]);
|
|
1395
|
+
const glyph = hasRunning
|
|
1396
|
+
? theme.fg("accent", spinnerFrame())
|
|
1397
|
+
: failed
|
|
1398
|
+
? theme.fg("error", "✗")
|
|
1399
|
+
: paused
|
|
1400
|
+
? theme.fg("warning", "■")
|
|
1401
|
+
: theme.fg("success", "✓");
|
|
1402
|
+
const contextBadge =
|
|
1403
|
+
d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1404
|
+
const c = new Container();
|
|
1405
|
+
const width = getTermWidth() - 4;
|
|
1406
|
+
c.addChild(
|
|
1407
|
+
new Text(
|
|
1408
|
+
truncLine(
|
|
1409
|
+
`${glyph} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
1410
|
+
width,
|
|
1411
|
+
),
|
|
1412
|
+
0,
|
|
1413
|
+
0,
|
|
1414
|
+
),
|
|
1415
|
+
);
|
|
1416
|
+
|
|
1417
|
+
const useResultsDirectly =
|
|
1418
|
+
multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
1419
|
+
const displayStart = multiLabel.showActiveGroupOnly
|
|
1420
|
+
? multiLabel.groupStartIndex
|
|
1421
|
+
: 0;
|
|
1422
|
+
const displayEnd = multiLabel.showActiveGroupOnly
|
|
1423
|
+
? multiLabel.groupEndIndex
|
|
1424
|
+
: useResultsDirectly
|
|
1425
|
+
? d.results.length
|
|
1426
|
+
: d.chainAgents!.length;
|
|
1427
|
+
for (let i = displayStart; i < displayEnd; i++) {
|
|
1428
|
+
const r = d.results[i];
|
|
1429
|
+
const fallbackLabel = itemTitle.toLowerCase();
|
|
1430
|
+
const rowNumber = multiLabel.showActiveGroupOnly
|
|
1431
|
+
? i - multiLabel.groupStartIndex + 1
|
|
1432
|
+
: i + 1;
|
|
1433
|
+
const agentName = useResultsDirectly
|
|
1434
|
+
? r?.agent || `${fallbackLabel}-${rowNumber}`
|
|
1435
|
+
: d.chainAgents![i] || r?.agent || `${fallbackLabel}-${rowNumber}`;
|
|
1436
|
+
if (!r) {
|
|
1437
|
+
c.addChild(
|
|
1438
|
+
new Text(
|
|
1439
|
+
truncLine(
|
|
1440
|
+
theme.fg(
|
|
1441
|
+
"dim",
|
|
1442
|
+
` ◦ ${itemTitle} ${rowNumber}: ${agentName} · pending`,
|
|
1443
|
+
),
|
|
1444
|
+
width,
|
|
1445
|
+
),
|
|
1446
|
+
0,
|
|
1447
|
+
0,
|
|
1448
|
+
),
|
|
1449
|
+
);
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
const output = getSingleResultOutput(r);
|
|
1453
|
+
const progressFromArray =
|
|
1454
|
+
d.progress?.find((p) => p.index === i) ||
|
|
1455
|
+
d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1456
|
+
const rProg = r.progress || progressFromArray || r.progressSummary;
|
|
1457
|
+
const rRunning = rProg && "status" in rProg && rProg.status === "running";
|
|
1458
|
+
const rPending = rProg && "status" in rProg && rProg.status === "pending";
|
|
1459
|
+
const stepNumber =
|
|
1460
|
+
r.progress?.index !== undefined
|
|
1461
|
+
? r.progress.index + 1
|
|
1462
|
+
: progressFromArray?.index !== undefined
|
|
1463
|
+
? progressFromArray.index + 1
|
|
1464
|
+
: i + 1;
|
|
1465
|
+
const stepStats = formatProgressStats(theme, rProg);
|
|
1466
|
+
const glyph = rPending
|
|
1467
|
+
? theme.fg("dim", "◦")
|
|
1468
|
+
: resultGlyph(r, output, theme, rRunning);
|
|
1469
|
+
const pendingLabel = rPending ? ` ${theme.fg("dim", "· pending")}` : "";
|
|
1470
|
+
const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
|
|
1471
|
+
const line = `${glyph} ${stepLabel}: ${themeBold(theme, agentName)}${stepStats ? ` ${theme.fg("dim", "·")} ${stepStats}` : ""}${pendingLabel}`;
|
|
1472
|
+
c.addChild(new Text(truncLine(` ${line}`, width), 0, 0));
|
|
1473
|
+
if (rRunning && rProg && "status" in rProg) {
|
|
1474
|
+
const activity = compactCurrentActivity(rProg);
|
|
1475
|
+
c.addChild(
|
|
1476
|
+
new Text(truncLine(theme.fg("dim", ` ⎿ ${activity}`), width), 0, 0),
|
|
1477
|
+
);
|
|
1478
|
+
c.addChild(
|
|
1479
|
+
new Text(
|
|
1480
|
+
truncLine(
|
|
1481
|
+
theme.fg("accent", " Press ctrl+o for live detail"),
|
|
1482
|
+
width,
|
|
1483
|
+
),
|
|
1484
|
+
0,
|
|
1485
|
+
0,
|
|
1486
|
+
),
|
|
1487
|
+
);
|
|
1488
|
+
} else if (
|
|
1489
|
+
!rPending &&
|
|
1490
|
+
(r.exitCode !== 0 ||
|
|
1491
|
+
r.interrupted ||
|
|
1492
|
+
r.detached ||
|
|
1493
|
+
hasEmptyTextOutputWithoutOutputTarget(r.task, output))
|
|
1494
|
+
) {
|
|
1495
|
+
c.addChild(
|
|
1496
|
+
new Text(
|
|
1497
|
+
truncLine(
|
|
1498
|
+
theme.fg(
|
|
1499
|
+
r.exitCode !== 0 ? "error" : "dim",
|
|
1500
|
+
` ⎿ ${resultStatusLine(r, output)}`,
|
|
1501
|
+
),
|
|
1502
|
+
width,
|
|
1503
|
+
),
|
|
1504
|
+
0,
|
|
1505
|
+
0,
|
|
1506
|
+
),
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1509
|
+
const outputTarget = extractOutputTarget(r.task);
|
|
1510
|
+
if (outputTarget)
|
|
1511
|
+
c.addChild(
|
|
1512
|
+
new Text(
|
|
1513
|
+
truncLine(theme.fg("dim", ` output: ${outputTarget}`), width),
|
|
1514
|
+
0,
|
|
1515
|
+
0,
|
|
1516
|
+
),
|
|
1517
|
+
);
|
|
1518
|
+
if (r.artifactPaths)
|
|
1519
|
+
c.addChild(
|
|
1520
|
+
new Text(
|
|
1521
|
+
truncLine(
|
|
1522
|
+
theme.fg(
|
|
1523
|
+
"dim",
|
|
1524
|
+
` output: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1525
|
+
),
|
|
1526
|
+
width,
|
|
1527
|
+
),
|
|
1528
|
+
0,
|
|
1529
|
+
0,
|
|
1530
|
+
),
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
if (d.artifacts)
|
|
1534
|
+
c.addChild(
|
|
1535
|
+
new Text(
|
|
1536
|
+
truncLine(
|
|
1537
|
+
theme.fg("dim", ` artifacts: ${shortenPath(d.artifacts.dir)}`),
|
|
1538
|
+
width,
|
|
1539
|
+
),
|
|
1540
|
+
0,
|
|
1541
|
+
0,
|
|
1542
|
+
),
|
|
1543
|
+
);
|
|
1544
|
+
return c;
|
|
943
1545
|
}
|
|
944
1546
|
|
|
945
1547
|
/**
|
|
946
1548
|
* Render a subagent result
|
|
947
1549
|
*/
|
|
948
1550
|
export function renderSubagentResult(
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1551
|
+
result: AgentToolResult<Details>,
|
|
1552
|
+
options: { expanded: boolean },
|
|
1553
|
+
theme: Theme,
|
|
952
1554
|
): Component {
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1555
|
+
const d = result.details;
|
|
1556
|
+
if (!d || !d.results.length) {
|
|
1557
|
+
const t = result.content[0];
|
|
1558
|
+
const text = t?.type === "text" ? t.text : "(no output)";
|
|
1559
|
+
const contextPrefix =
|
|
1560
|
+
d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "";
|
|
1561
|
+
return new Text(
|
|
1562
|
+
truncLine(`${contextPrefix}${text}`, getTermWidth() - 4),
|
|
1563
|
+
0,
|
|
1564
|
+
0,
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
const expanded = options.expanded;
|
|
1569
|
+
const mdTheme = getMarkdownTheme();
|
|
1570
|
+
|
|
1571
|
+
if (d.mode === "single" && d.results.length === 1) {
|
|
1572
|
+
const r = d.results[0];
|
|
1573
|
+
if (!expanded) return renderSingleCompact(d, r, theme);
|
|
1574
|
+
const isRunning = r.progress?.status === "running";
|
|
1575
|
+
const icon = isRunning
|
|
1576
|
+
? theme.fg("warning", "running")
|
|
1577
|
+
: r.detached
|
|
1578
|
+
? theme.fg("warning", "detached")
|
|
1579
|
+
: r.exitCode === 0
|
|
1580
|
+
? theme.fg("success", "ok")
|
|
1581
|
+
: theme.fg("error", "failed");
|
|
1582
|
+
const contextBadge =
|
|
1583
|
+
d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1584
|
+
const output = r.truncation?.text || getSingleResultOutput(r);
|
|
1585
|
+
|
|
1586
|
+
const progressInfo =
|
|
1587
|
+
isRunning && r.progress
|
|
1588
|
+
? ` | ${r.progress.toolCount} tools, ${formatTokens(r.progress.tokens)} tok, ${formatDuration(r.progress.durationMs)}`
|
|
1589
|
+
: r.progressSummary
|
|
1590
|
+
? ` | ${r.progressSummary.toolCount} tools, ${formatTokens(r.progressSummary.tokens)} tok, ${formatDuration(r.progressSummary.durationMs)}`
|
|
1591
|
+
: "";
|
|
1592
|
+
|
|
1593
|
+
const w = getTermWidth() - 4;
|
|
1594
|
+
const fit = (text: string) => (expanded ? text : truncLine(text, w));
|
|
1595
|
+
const toolCallLines = getToolCallLines(r, expanded);
|
|
1596
|
+
const c = new Container();
|
|
1597
|
+
c.addChild(
|
|
1598
|
+
new Text(
|
|
1599
|
+
fit(
|
|
1600
|
+
`${icon} ${theme.fg("toolTitle", theme.bold(r.agent))}${contextBadge}${progressInfo}`,
|
|
1601
|
+
),
|
|
1602
|
+
0,
|
|
1603
|
+
0,
|
|
1604
|
+
),
|
|
1605
|
+
);
|
|
1606
|
+
c.addChild(new Spacer(1));
|
|
1607
|
+
const taskMaxLen = Math.max(20, w - 8);
|
|
1608
|
+
const taskPreview =
|
|
1609
|
+
expanded || r.task.length <= taskMaxLen
|
|
1610
|
+
? r.task
|
|
1611
|
+
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
1612
|
+
c.addChild(new Text(fit(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0));
|
|
1613
|
+
c.addChild(new Spacer(1));
|
|
1614
|
+
|
|
1615
|
+
if (isRunning && r.progress) {
|
|
1616
|
+
const toolLine = formatCurrentToolLine(r.progress, w, expanded);
|
|
1617
|
+
if (toolLine) {
|
|
1618
|
+
c.addChild(new Text(fit(theme.fg("warning", `> ${toolLine}`)), 0, 0));
|
|
1619
|
+
}
|
|
1620
|
+
const liveStatusLine = buildLiveStatusLine(r.progress);
|
|
1621
|
+
if (liveStatusLine) {
|
|
1622
|
+
c.addChild(new Text(fit(theme.fg("accent", liveStatusLine)), 0, 0));
|
|
1623
|
+
}
|
|
1624
|
+
c.addChild(
|
|
1625
|
+
new Text(fit(theme.fg("accent", "Press ctrl+o for live detail")), 0, 0),
|
|
1626
|
+
);
|
|
1627
|
+
if (r.artifactPaths) {
|
|
1628
|
+
c.addChild(
|
|
1629
|
+
new Text(
|
|
1630
|
+
fit(
|
|
1631
|
+
theme.fg(
|
|
1632
|
+
"dim",
|
|
1633
|
+
`Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1634
|
+
),
|
|
1635
|
+
),
|
|
1636
|
+
0,
|
|
1637
|
+
0,
|
|
1638
|
+
),
|
|
1639
|
+
);
|
|
1640
|
+
}
|
|
1641
|
+
if (r.progress.recentTools?.length) {
|
|
1642
|
+
for (const t of r.progress.recentTools.slice(-3)) {
|
|
1643
|
+
const maxArgsLen = Math.max(40, w - 24);
|
|
1644
|
+
const argsPreview =
|
|
1645
|
+
expanded || t.args.length <= maxArgsLen
|
|
1646
|
+
? t.args
|
|
1647
|
+
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
1648
|
+
c.addChild(
|
|
1649
|
+
new Text(fit(theme.fg("dim", `${t.tool}: ${argsPreview}`)), 0, 0),
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
for (const line of (r.progress.recentOutput ?? []).slice(-5)) {
|
|
1654
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
1655
|
+
}
|
|
1656
|
+
if (
|
|
1657
|
+
toolLine ||
|
|
1658
|
+
liveStatusLine ||
|
|
1659
|
+
r.progress.recentTools?.length ||
|
|
1660
|
+
r.progress.recentOutput?.length ||
|
|
1661
|
+
r.artifactPaths
|
|
1662
|
+
) {
|
|
1663
|
+
c.addChild(new Spacer(1));
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
if (expanded) {
|
|
1668
|
+
for (const line of toolCallLines) {
|
|
1669
|
+
c.addChild(new Text(fit(theme.fg("muted", line)), 0, 0));
|
|
1670
|
+
}
|
|
1671
|
+
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
if (output) c.addChild(new Markdown(output, 0, 0, mdTheme));
|
|
1675
|
+
c.addChild(new Spacer(1));
|
|
1676
|
+
if (r.skills?.length) {
|
|
1677
|
+
c.addChild(
|
|
1678
|
+
new Text(fit(theme.fg("dim", `Skills: ${r.skills.join(", ")}`)), 0, 0),
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
if (r.skillsWarning) {
|
|
1682
|
+
c.addChild(
|
|
1683
|
+
new Text(fit(theme.fg("warning", `Warning: ${r.skillsWarning}`)), 0, 0),
|
|
1684
|
+
);
|
|
1685
|
+
}
|
|
1686
|
+
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
1687
|
+
c.addChild(
|
|
1688
|
+
new Text(
|
|
1689
|
+
fit(theme.fg("dim", `Fallbacks: ${r.attemptedModels.join(" → ")}`)),
|
|
1690
|
+
0,
|
|
1691
|
+
0,
|
|
1692
|
+
),
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1695
|
+
c.addChild(
|
|
1696
|
+
new Text(fit(theme.fg("dim", formatUsage(r.usage, r.model))), 0, 0),
|
|
1697
|
+
);
|
|
1698
|
+
if (r.sessionFile) {
|
|
1699
|
+
c.addChild(
|
|
1700
|
+
new Text(
|
|
1701
|
+
fit(theme.fg("dim", `Session: ${shortenPath(r.sessionFile)}`)),
|
|
1702
|
+
0,
|
|
1703
|
+
0,
|
|
1704
|
+
),
|
|
1705
|
+
);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
if (!isRunning && r.artifactPaths) {
|
|
1709
|
+
c.addChild(new Spacer(1));
|
|
1710
|
+
c.addChild(
|
|
1711
|
+
new Text(
|
|
1712
|
+
fit(
|
|
1713
|
+
theme.fg(
|
|
1714
|
+
"dim",
|
|
1715
|
+
`Artifacts: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1716
|
+
),
|
|
1717
|
+
),
|
|
1718
|
+
0,
|
|
1719
|
+
0,
|
|
1720
|
+
),
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
1723
|
+
return c;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
if (!expanded) return renderMultiCompact(d, theme);
|
|
1727
|
+
|
|
1728
|
+
const hasRunning =
|
|
1729
|
+
d.progress?.some((p) => p.status === "running") ||
|
|
1730
|
+
d.results.some((r) => r.progress?.status === "running");
|
|
1731
|
+
const ok = d.results.filter(
|
|
1732
|
+
(r) =>
|
|
1733
|
+
r.progress?.status === "completed" ||
|
|
1734
|
+
(r.exitCode === 0 && r.progress?.status !== "running"),
|
|
1735
|
+
).length;
|
|
1736
|
+
const hasEmptyWithoutTarget = d.results.some(
|
|
1737
|
+
(r) =>
|
|
1738
|
+
r.exitCode === 0 &&
|
|
1739
|
+
r.progress?.status !== "running" &&
|
|
1740
|
+
hasEmptyTextOutputWithoutOutputTarget(r.task, getSingleResultOutput(r)),
|
|
1741
|
+
);
|
|
1742
|
+
const icon = hasRunning
|
|
1743
|
+
? theme.fg("warning", "running")
|
|
1744
|
+
: hasEmptyWithoutTarget
|
|
1745
|
+
? theme.fg("warning", "warning")
|
|
1746
|
+
: ok === d.results.length
|
|
1747
|
+
? theme.fg("success", "ok")
|
|
1748
|
+
: theme.fg("error", "failed");
|
|
1749
|
+
|
|
1750
|
+
const totalSummary =
|
|
1751
|
+
d.progressSummary ||
|
|
1752
|
+
d.results.reduce(
|
|
1753
|
+
(acc, r) => {
|
|
1754
|
+
const prog = r.progress || r.progressSummary;
|
|
1755
|
+
if (prog) {
|
|
1756
|
+
acc.toolCount += prog.toolCount;
|
|
1757
|
+
acc.tokens += prog.tokens;
|
|
1758
|
+
acc.durationMs =
|
|
1759
|
+
d.mode === "chain"
|
|
1760
|
+
? acc.durationMs + prog.durationMs
|
|
1761
|
+
: Math.max(acc.durationMs, prog.durationMs);
|
|
1762
|
+
}
|
|
1763
|
+
return acc;
|
|
1764
|
+
},
|
|
1765
|
+
{ toolCount: 0, tokens: 0, durationMs: 0 },
|
|
1766
|
+
);
|
|
1767
|
+
|
|
1768
|
+
const summaryStr =
|
|
1769
|
+
totalSummary.toolCount || totalSummary.tokens
|
|
1770
|
+
? ` | ${totalSummary.toolCount} tools, ${formatTokens(totalSummary.tokens)} tok, ${formatDuration(totalSummary.durationMs)}`
|
|
1771
|
+
: "";
|
|
1772
|
+
|
|
1773
|
+
const modeLabel = d.mode;
|
|
1774
|
+
const contextBadge =
|
|
1775
|
+
d.context === "fork" ? theme.fg("warning", " [fork]") : "";
|
|
1776
|
+
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
1777
|
+
const itemTitle = multiLabel.itemTitle;
|
|
1778
|
+
|
|
1779
|
+
const chainVis =
|
|
1780
|
+
d.chainAgents?.length && !multiLabel.hasParallelInChain
|
|
1781
|
+
? d.chainAgents
|
|
1782
|
+
.map((agent, i) => {
|
|
1783
|
+
const result = d.results[i];
|
|
1784
|
+
const isFailed =
|
|
1785
|
+
result &&
|
|
1786
|
+
result.exitCode !== 0 &&
|
|
1787
|
+
result.progress?.status !== "running";
|
|
1788
|
+
const isComplete =
|
|
1789
|
+
result &&
|
|
1790
|
+
result.exitCode === 0 &&
|
|
1791
|
+
result.progress?.status !== "running";
|
|
1792
|
+
const isEmptyWithoutTarget =
|
|
1793
|
+
Boolean(result) &&
|
|
1794
|
+
Boolean(isComplete) &&
|
|
1795
|
+
hasEmptyTextOutputWithoutOutputTarget(
|
|
1796
|
+
result.task,
|
|
1797
|
+
getSingleResultOutput(result),
|
|
1798
|
+
);
|
|
1799
|
+
const isCurrent = i === (d.currentStepIndex ?? d.results.length);
|
|
1800
|
+
const stepIcon = isFailed
|
|
1801
|
+
? theme.fg("error", "failed")
|
|
1802
|
+
: isEmptyWithoutTarget
|
|
1803
|
+
? theme.fg("warning", "warning")
|
|
1804
|
+
: isComplete
|
|
1805
|
+
? theme.fg("success", "done")
|
|
1806
|
+
: isCurrent && hasRunning
|
|
1807
|
+
? theme.fg("warning", "running")
|
|
1808
|
+
: theme.fg("dim", "pending");
|
|
1809
|
+
return `${stepIcon} ${agent}`;
|
|
1810
|
+
})
|
|
1811
|
+
.join(theme.fg("dim", " → "))
|
|
1812
|
+
: null;
|
|
1813
|
+
|
|
1814
|
+
const w = getTermWidth() - 4;
|
|
1815
|
+
const fit = (text: string) => (expanded ? text : truncLine(text, w));
|
|
1816
|
+
const c = new Container();
|
|
1817
|
+
c.addChild(
|
|
1818
|
+
new Text(
|
|
1819
|
+
fit(
|
|
1820
|
+
`${icon} ${theme.fg("toolTitle", theme.bold(modeLabel))}${contextBadge} · ${multiLabel.headerLabel}${summaryStr}`,
|
|
1821
|
+
),
|
|
1822
|
+
0,
|
|
1823
|
+
0,
|
|
1824
|
+
),
|
|
1825
|
+
);
|
|
1826
|
+
if (chainVis) {
|
|
1827
|
+
c.addChild(new Text(fit(` ${chainVis}`), 0, 0));
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
const useResultsDirectly =
|
|
1831
|
+
multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
1832
|
+
const displayStart = multiLabel.showActiveGroupOnly
|
|
1833
|
+
? multiLabel.groupStartIndex
|
|
1834
|
+
: 0;
|
|
1835
|
+
const displayEnd = multiLabel.showActiveGroupOnly
|
|
1836
|
+
? multiLabel.groupEndIndex
|
|
1837
|
+
: useResultsDirectly
|
|
1838
|
+
? d.results.length
|
|
1839
|
+
: d.chainAgents!.length;
|
|
1840
|
+
|
|
1841
|
+
c.addChild(new Spacer(1));
|
|
1842
|
+
|
|
1843
|
+
for (let i = displayStart; i < displayEnd; i++) {
|
|
1844
|
+
const r = d.results[i];
|
|
1845
|
+
const rowNumber = multiLabel.showActiveGroupOnly
|
|
1846
|
+
? i - multiLabel.groupStartIndex + 1
|
|
1847
|
+
: i + 1;
|
|
1848
|
+
const agentName = useResultsDirectly
|
|
1849
|
+
? r?.agent || `step-${rowNumber}`
|
|
1850
|
+
: d.chainAgents![i] || r?.agent || `step-${rowNumber}`;
|
|
1851
|
+
|
|
1852
|
+
if (!r) {
|
|
1853
|
+
c.addChild(
|
|
1854
|
+
new Text(
|
|
1855
|
+
fit(theme.fg("dim", ` ${itemTitle} ${rowNumber}: ${agentName}`)),
|
|
1856
|
+
0,
|
|
1857
|
+
0,
|
|
1858
|
+
),
|
|
1859
|
+
);
|
|
1860
|
+
c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
|
|
1861
|
+
c.addChild(new Spacer(1));
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
const progressFromArray =
|
|
1866
|
+
d.progress?.find((p) => p.index === i) ||
|
|
1867
|
+
d.progress?.find((p) => p.agent === r.agent && p.status === "running");
|
|
1868
|
+
const rProg = r.progress || progressFromArray || r.progressSummary;
|
|
1869
|
+
const rRunning = rProg?.status === "running";
|
|
1870
|
+
const stepNumber =
|
|
1871
|
+
typeof rProg?.index === "number" ? rProg.index + 1 : i + 1;
|
|
1872
|
+
|
|
1873
|
+
const resultOutput = getSingleResultOutput(r);
|
|
1874
|
+
const statusIcon = rRunning
|
|
1875
|
+
? theme.fg("warning", "running")
|
|
1876
|
+
: r.exitCode !== 0
|
|
1877
|
+
? theme.fg("error", "failed")
|
|
1878
|
+
: hasEmptyTextOutputWithoutOutputTarget(r.task, resultOutput)
|
|
1879
|
+
? theme.fg("warning", "warning")
|
|
1880
|
+
: theme.fg("success", "done");
|
|
1881
|
+
const stats = rProg
|
|
1882
|
+
? ` | ${rProg.toolCount} tools, ${formatDuration(rProg.durationMs)}`
|
|
1883
|
+
: "";
|
|
1884
|
+
const modelDisplay = modelThinkingBadge(theme, r.model);
|
|
1885
|
+
const stepLabel = resultRowLabel(d, multiLabel, i, stepNumber);
|
|
1886
|
+
const stepHeader = rRunning
|
|
1887
|
+
? `${statusIcon} ${stepLabel}: ${theme.bold(theme.fg("warning", r.agent))}${modelDisplay}${stats}`
|
|
1888
|
+
: `${statusIcon} ${stepLabel}: ${theme.bold(r.agent)}${modelDisplay}${stats}`;
|
|
1889
|
+
const toolCallLines = getToolCallLines(r, expanded);
|
|
1890
|
+
c.addChild(new Text(fit(stepHeader), 0, 0));
|
|
1891
|
+
|
|
1892
|
+
const taskMaxLen = Math.max(20, w - 12);
|
|
1893
|
+
const taskPreview =
|
|
1894
|
+
expanded || r.task.length <= taskMaxLen
|
|
1895
|
+
? r.task
|
|
1896
|
+
: `${r.task.slice(0, taskMaxLen)}...`;
|
|
1897
|
+
c.addChild(
|
|
1898
|
+
new Text(fit(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0),
|
|
1899
|
+
);
|
|
1900
|
+
|
|
1901
|
+
const outputTarget = extractOutputTarget(r.task);
|
|
1902
|
+
if (outputTarget) {
|
|
1903
|
+
c.addChild(
|
|
1904
|
+
new Text(fit(theme.fg("dim", ` output: ${outputTarget}`)), 0, 0),
|
|
1905
|
+
);
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
if (r.skills?.length) {
|
|
1909
|
+
c.addChild(
|
|
1910
|
+
new Text(
|
|
1911
|
+
fit(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)),
|
|
1912
|
+
0,
|
|
1913
|
+
0,
|
|
1914
|
+
),
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
if (r.skillsWarning) {
|
|
1918
|
+
c.addChild(
|
|
1919
|
+
new Text(
|
|
1920
|
+
fit(theme.fg("warning", ` Warning: ${r.skillsWarning}`)),
|
|
1921
|
+
0,
|
|
1922
|
+
0,
|
|
1923
|
+
),
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
if (r.attemptedModels && r.attemptedModels.length > 1) {
|
|
1927
|
+
c.addChild(
|
|
1928
|
+
new Text(
|
|
1929
|
+
fit(
|
|
1930
|
+
theme.fg("dim", ` fallbacks: ${r.attemptedModels.join(" → ")}`),
|
|
1931
|
+
),
|
|
1932
|
+
0,
|
|
1933
|
+
0,
|
|
1934
|
+
),
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
if (rRunning && rProg) {
|
|
1939
|
+
if (rProg.skills?.length) {
|
|
1940
|
+
c.addChild(
|
|
1941
|
+
new Text(
|
|
1942
|
+
fit(theme.fg("accent", ` skills: ${rProg.skills.join(", ")}`)),
|
|
1943
|
+
0,
|
|
1944
|
+
0,
|
|
1945
|
+
),
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
const toolLine = formatCurrentToolLine(rProg, w, expanded);
|
|
1949
|
+
if (toolLine) {
|
|
1950
|
+
c.addChild(
|
|
1951
|
+
new Text(fit(theme.fg("warning", ` > ${toolLine}`)), 0, 0),
|
|
1952
|
+
);
|
|
1953
|
+
}
|
|
1954
|
+
const liveStatusLine = buildLiveStatusLine(rProg);
|
|
1955
|
+
if (liveStatusLine) {
|
|
1956
|
+
c.addChild(
|
|
1957
|
+
new Text(fit(theme.fg("accent", ` ${liveStatusLine}`)), 0, 0),
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
c.addChild(
|
|
1961
|
+
new Text(
|
|
1962
|
+
fit(theme.fg("accent", " Press ctrl+o for live detail")),
|
|
1963
|
+
0,
|
|
1964
|
+
0,
|
|
1965
|
+
),
|
|
1966
|
+
);
|
|
1967
|
+
if (r.artifactPaths) {
|
|
1968
|
+
c.addChild(
|
|
1969
|
+
new Text(
|
|
1970
|
+
fit(
|
|
1971
|
+
theme.fg(
|
|
1972
|
+
"dim",
|
|
1973
|
+
` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
1974
|
+
),
|
|
1975
|
+
),
|
|
1976
|
+
0,
|
|
1977
|
+
0,
|
|
1978
|
+
),
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1981
|
+
if (rProg.recentTools?.length) {
|
|
1982
|
+
for (const t of rProg.recentTools.slice(-3)) {
|
|
1983
|
+
const maxArgsLen = Math.max(40, w - 30);
|
|
1984
|
+
const argsPreview =
|
|
1985
|
+
expanded || t.args.length <= maxArgsLen
|
|
1986
|
+
? t.args
|
|
1987
|
+
: `${t.args.slice(0, maxArgsLen)}...`;
|
|
1988
|
+
c.addChild(
|
|
1989
|
+
new Text(
|
|
1990
|
+
fit(theme.fg("dim", ` ${t.tool}: ${argsPreview}`)),
|
|
1991
|
+
0,
|
|
1992
|
+
0,
|
|
1993
|
+
),
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
const recentLines = (rProg.recentOutput ?? []).slice(-5);
|
|
1998
|
+
for (const line of recentLines) {
|
|
1999
|
+
c.addChild(new Text(fit(theme.fg("dim", ` ${line}`)), 0, 0));
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
if (!rRunning && r.artifactPaths) {
|
|
2004
|
+
c.addChild(
|
|
2005
|
+
new Text(
|
|
2006
|
+
fit(
|
|
2007
|
+
theme.fg(
|
|
2008
|
+
"dim",
|
|
2009
|
+
` artifacts: ${shortenPath(r.artifactPaths.outputPath)}`,
|
|
2010
|
+
),
|
|
2011
|
+
),
|
|
2012
|
+
0,
|
|
2013
|
+
0,
|
|
2014
|
+
),
|
|
2015
|
+
);
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
if (expanded && !rRunning) {
|
|
2019
|
+
for (const line of toolCallLines) {
|
|
2020
|
+
c.addChild(new Text(fit(theme.fg("muted", ` ${line}`)), 0, 0));
|
|
2021
|
+
}
|
|
2022
|
+
if (toolCallLines.length) c.addChild(new Spacer(1));
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
c.addChild(new Spacer(1));
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
if (d.artifacts) {
|
|
2029
|
+
c.addChild(new Spacer(1));
|
|
2030
|
+
c.addChild(
|
|
2031
|
+
new Text(
|
|
2032
|
+
fit(theme.fg("dim", `Artifacts dir: ${shortenPath(d.artifacts.dir)}`)),
|
|
2033
|
+
0,
|
|
2034
|
+
0,
|
|
2035
|
+
),
|
|
2036
|
+
);
|
|
2037
|
+
}
|
|
2038
|
+
return c;
|
|
1257
2039
|
}
|