@nghyane/arcane 0.1.13 → 0.1.14
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 +28 -0
- package/package.json +21 -70
- package/scripts/format-prompts.ts +1 -3
- package/src/cli/args.ts +2 -7
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +0 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/grep.ts +1 -1
- package/src/commands/jupyter.ts +1 -1
- package/src/commands/plugin.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/shell.ts +1 -1
- package/src/commands/ssh.ts +1 -1
- package/src/commands/stats.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config/model-registry.ts +3 -4
- package/src/config/model-resolver.ts +36 -9
- package/src/config/prompt-templates.ts +1 -9
- package/src/config/settings-schema.ts +32 -88
- package/src/config/settings.ts +3 -4
- package/src/debug/index.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +2 -2
- package/src/discovery/helpers.ts +13 -3
- package/src/exa/index.ts +1 -35
- package/src/exa/render.ts +30 -190
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +5 -1
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/extensions/wrapper.ts +7 -15
- package/src/extensibility/hooks/runner.ts +1 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/index.ts +13 -13
- package/src/lsp/index.ts +77 -24
- package/src/lsp/render.ts +34 -583
- package/src/lsp/types.ts +3 -3
- package/src/lsp/utils.ts +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -24
- package/src/modes/components/assistant-message.ts +7 -7
- package/src/modes/components/bash-execution.ts +48 -113
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/branch-summary-message.ts +13 -10
- package/src/modes/components/compaction-summary-message.ts +14 -13
- package/src/modes/components/context-group.ts +106 -0
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +2 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/index.ts +0 -2
- package/src/modes/components/keybinding-hints.ts +1 -1
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/oauth-selector.ts +1 -1
- package/src/modes/components/plugin-settings.ts +1 -1
- package/src/modes/components/python-execution.ts +49 -92
- package/src/modes/components/queue-mode-selector.ts +1 -1
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +5 -10
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +1 -1
- package/src/modes/components/skill-message.ts +4 -4
- package/src/modes/components/status-line/segments.ts +2 -2
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line-segment-editor.ts +1 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/theme-selector.ts +1 -1
- package/src/modes/components/thinking-selector.ts +1 -1
- package/src/modes/components/todo-display.ts +2 -4
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +118 -440
- package/src/modes/components/tool-image-display.ts +107 -0
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/components/ttsr-notification.ts +4 -17
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +9 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +58 -187
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +3 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +3 -26
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +3 -7
- package/src/modes/print-mode.ts +5 -5
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/types.ts +1 -2
- package/src/modes/utils/ui-helpers.ts +34 -32
- package/src/patch/edit-tool.ts +742 -0
- package/src/patch/index.ts +32 -898
- package/src/patch/schemas.ts +208 -0
- package/src/patch/shared.ts +83 -151
- package/src/prompts/agents/explore.md +22 -37
- package/src/prompts/agents/frontmatter.md +1 -1
- package/src/prompts/agents/init.md +2 -2
- package/src/prompts/agents/librarian.md +30 -21
- package/src/prompts/agents/oracle.md +9 -2
- package/src/prompts/agents/reviewer.md +15 -49
- package/src/prompts/agents/task.md +17 -9
- package/src/prompts/compaction/branch-summary-context.md +1 -1
- package/src/prompts/compaction/branch-summary-preamble.md +1 -1
- package/src/prompts/compaction/branch-summary.md +4 -1
- package/src/prompts/compaction/compaction-short-summary.md +1 -1
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +4 -1
- package/src/prompts/compaction/compaction-turn-prefix.md +1 -1
- package/src/prompts/compaction/compaction-update-summary.md +1 -1
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read_path.md +1 -1
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +1 -1
- package/src/prompts/system/agent-creation-user.md +1 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/file-operations.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +2 -2
- package/src/prompts/system/summarization-system.md +1 -1
- package/src/prompts/system/system-prompt.md +163 -178
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/system/verification-reminder.md +6 -0
- package/src/prompts/system/web-search.md +1 -1
- package/src/sdk.ts +0 -9
- package/src/session/agent-session.ts +244 -1459
- package/src/session/model-controller.ts +406 -0
- package/src/session/retry-utils.ts +71 -0
- package/src/session/session-manager.ts +22 -186
- package/src/session/session-types.ts +312 -0
- package/src/session/stats.ts +387 -0
- package/src/session/streaming-edit.ts +258 -0
- package/src/session/ttsr.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +0 -8
- package/src/stt/recorder.ts +2 -2
- package/src/system-prompt.ts +1 -14
- package/src/task/agents.ts +7 -33
- package/src/task/executor.ts +50 -438
- package/src/task/index.ts +104 -71
- package/src/task/progress-tracker.ts +390 -0
- package/src/task/render.ts +371 -187
- package/src/task/subprocess-tool-registry.ts +1 -1
- package/src/task/types.ts +14 -47
- package/src/tools/ask.ts +31 -42
- package/src/tools/bash-interactive.ts +2 -2
- package/src/tools/bash-interceptor.ts +2 -2
- package/src/tools/bash-normalize.ts +1 -1
- package/src/tools/bash-skill-urls.ts +2 -2
- package/src/tools/bash.ts +87 -136
- package/src/tools/browser.ts +54 -84
- package/src/tools/create-tools.ts +186 -0
- package/src/tools/default-renderer.ts +104 -0
- package/src/tools/explore.ts +11 -10
- package/src/tools/fetch.ts +24 -114
- package/src/tools/find.ts +48 -132
- package/src/tools/gemini-image.ts +5 -15
- package/src/tools/github.ts +450 -0
- package/src/tools/grep.ts +43 -179
- package/src/tools/index.ts +35 -198
- package/src/tools/json-tree.ts +3 -3
- package/src/tools/librarian.ts +18 -18
- package/src/tools/list-limit.ts +2 -2
- package/src/tools/notebook.ts +35 -87
- package/src/tools/oracle.ts +25 -25
- package/src/tools/output-meta.ts +89 -4
- package/src/tools/output-utils.ts +2 -2
- package/src/tools/python.ts +86 -637
- package/src/tools/read.ts +36 -119
- package/src/tools/reviewer-tool.ts +19 -21
- package/src/tools/search-code.ts +128 -0
- package/src/tools/ssh.ts +67 -126
- package/src/tools/subagent-tool.ts +197 -123
- package/src/tools/todo-write.ts +15 -31
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/undo-edit.ts +30 -67
- package/src/tools/write.ts +78 -127
- package/src/tui/code-cell.ts +4 -4
- package/src/tui/file-list.ts +2 -2
- package/src/tui/output-block.ts +1 -1
- package/src/tui/status-line.ts +1 -1
- package/src/tui/tree-list.ts +2 -2
- package/src/tui/types.ts +1 -1
- package/src/tui/utils.ts +1 -1
- package/src/{tools → ui}/render-utils.ts +87 -126
- package/src/utils/external-editor.ts +4 -4
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/index.ts +30 -0
- package/src/utils/tools-manager.ts +9 -19
- package/src/web/github-client.ts +290 -0
- package/src/web/scrapers/github.ts +11 -62
- package/src/web/search/auth.ts +1 -3
- package/src/web/search/index.ts +82 -46
- package/src/web/search/provider.ts +11 -16
- package/src/web/search/providers/grep.ts +160 -0
- package/src/web/search/render.ts +48 -235
- package/src/web/search/types.ts +1 -1
- package/src/commands/commit.ts +0 -36
- package/src/commit/agentic/agent.ts +0 -311
- package/src/commit/agentic/fallback.ts +0 -96
- package/src/commit/agentic/index.ts +0 -359
- package/src/commit/agentic/prompts/analyze-file.md +0 -22
- package/src/commit/agentic/prompts/session-user.md +0 -25
- package/src/commit/agentic/prompts/split-confirm.md +0 -1
- package/src/commit/agentic/prompts/system.md +0 -38
- package/src/commit/agentic/state.ts +0 -69
- package/src/commit/agentic/tools/analyze-file.ts +0 -118
- package/src/commit/agentic/tools/git-file-diff.ts +0 -194
- package/src/commit/agentic/tools/git-hunk.ts +0 -50
- package/src/commit/agentic/tools/git-overview.ts +0 -84
- package/src/commit/agentic/tools/index.ts +0 -56
- package/src/commit/agentic/tools/propose-changelog.ts +0 -128
- package/src/commit/agentic/tools/propose-commit.ts +0 -154
- package/src/commit/agentic/tools/recent-commits.ts +0 -81
- package/src/commit/agentic/tools/split-commit.ts +0 -280
- package/src/commit/agentic/topo-sort.ts +0 -44
- package/src/commit/agentic/trivial.ts +0 -51
- package/src/commit/agentic/validation.ts +0 -200
- package/src/commit/analysis/conventional.ts +0 -165
- package/src/commit/analysis/index.ts +0 -4
- package/src/commit/analysis/scope.ts +0 -242
- package/src/commit/analysis/summary.ts +0 -112
- package/src/commit/analysis/validation.ts +0 -66
- package/src/commit/changelog/detect.ts +0 -37
- package/src/commit/changelog/generate.ts +0 -110
- package/src/commit/changelog/index.ts +0 -234
- package/src/commit/changelog/parse.ts +0 -44
- package/src/commit/cli.ts +0 -93
- package/src/commit/git/diff.ts +0 -148
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -211
- package/src/commit/git/operations.ts +0 -54
- package/src/commit/index.ts +0 -5
- package/src/commit/map-reduce/index.ts +0 -64
- package/src/commit/map-reduce/map-phase.ts +0 -178
- package/src/commit/map-reduce/reduce-phase.ts +0 -145
- package/src/commit/map-reduce/utils.ts +0 -9
- package/src/commit/message.ts +0 -11
- package/src/commit/model-selection.ts +0 -69
- package/src/commit/pipeline.ts +0 -243
- package/src/commit/prompts/analysis-system.md +0 -148
- package/src/commit/prompts/analysis-user.md +0 -38
- package/src/commit/prompts/changelog-system.md +0 -50
- package/src/commit/prompts/changelog-user.md +0 -18
- package/src/commit/prompts/file-observer-system.md +0 -24
- package/src/commit/prompts/file-observer-user.md +0 -8
- package/src/commit/prompts/reduce-system.md +0 -50
- package/src/commit/prompts/reduce-user.md +0 -17
- package/src/commit/prompts/summary-retry.md +0 -3
- package/src/commit/prompts/summary-system.md +0 -38
- package/src/commit/prompts/summary-user.md +0 -13
- package/src/commit/prompts/types-description.md +0 -2
- package/src/commit/types.ts +0 -109
- package/src/commit/utils/exclusions.ts +0 -42
- package/src/mcp/render.ts +0 -123
- package/src/modes/components/agent-dashboard.ts +0 -1130
- package/src/modes/components/codemode-group.ts +0 -369
- package/src/modes/components/read-tool-group.ts +0 -119
- package/src/modes/components/visual-truncate.ts +0 -63
- package/src/prompts/system/subagent-user-prompt.md +0 -8
- package/src/prompts/tools/ask.md +0 -44
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/browser.md +0 -33
- package/src/prompts/tools/calculator.md +0 -12
- package/src/prompts/tools/explore.md +0 -29
- package/src/prompts/tools/fetch.md +0 -16
- package/src/prompts/tools/find.md +0 -18
- package/src/prompts/tools/gemini-image.md +0 -23
- package/src/prompts/tools/grep.md +0 -28
- package/src/prompts/tools/hashline.md +0 -232
- package/src/prompts/tools/librarian.md +0 -24
- package/src/prompts/tools/lsp.md +0 -28
- package/src/prompts/tools/oracle.md +0 -26
- package/src/prompts/tools/patch.md +0 -74
- package/src/prompts/tools/python.md +0 -66
- package/src/prompts/tools/read.md +0 -36
- package/src/prompts/tools/replace.md +0 -38
- package/src/prompts/tools/reviewer.md +0 -41
- package/src/prompts/tools/ssh.md +0 -51
- package/src/prompts/tools/task-summary.md +0 -28
- package/src/prompts/tools/task.md +0 -146
- package/src/prompts/tools/todo-write.md +0 -65
- package/src/prompts/tools/undo-edit.md +0 -7
- package/src/prompts/tools/web-search.md +0 -19
- package/src/prompts/tools/write.md +0 -18
- package/src/task/batch.ts +0 -102
- package/src/task/discovery.ts +0 -126
- package/src/task/parallel.ts +0 -84
- package/src/task/template.ts +0 -32
- package/src/tools/calculator.ts +0 -537
- package/src/tools/jtd-to-typescript.ts +0 -198
- package/src/tools/renderers.ts +0 -60
- package/src/tools/tool-result.ts +0 -86
- /package/src/{modes/theme → theme}/dark.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
- /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/{modes/theme → theme}/light.json +0 -0
- /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
- /package/src/{modes/theme → theme}/theme-schema.json +0 -0
- /package/src/{modes/theme → theme}/theme.ts +0 -0
package/src/task/render.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import type { Component } from "@nghyane/arcane-tui";
|
|
2
|
-
import { Text } from "@nghyane/arcane-tui";
|
|
2
|
+
import { Markdown, Text } from "@nghyane/arcane-tui";
|
|
3
3
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
4
|
-
import type { Theme, ThemeColor } from "../
|
|
5
|
-
import {
|
|
6
|
-
import { Ellipsis, Hasher, type RenderCache
|
|
4
|
+
import type { Theme, ThemeColor } from "../theme/theme";
|
|
5
|
+
import { getMarkdownTheme } from "../theme/theme";
|
|
6
|
+
import { Ellipsis, Hasher, type RenderCache } from "../tui";
|
|
7
|
+
import {
|
|
8
|
+
formatBadge,
|
|
9
|
+
formatDuration,
|
|
10
|
+
formatStatusIcon,
|
|
11
|
+
PREVIEW_LIMITS,
|
|
12
|
+
replaceTabs,
|
|
13
|
+
type ToolUIColor,
|
|
14
|
+
TRUNCATE_LENGTHS,
|
|
15
|
+
truncateToWidth,
|
|
16
|
+
} from "../ui/render-utils";
|
|
7
17
|
import { subprocessToolRegistry } from "./subprocess-tool-registry";
|
|
8
18
|
import type { AgentProgress, SingleResult, TaskParams, TaskToolDetails } from "./types";
|
|
9
19
|
|
|
@@ -26,19 +36,11 @@ function getStatusIcon(status: AgentProgress["status"], theme: Theme, spinnerFra
|
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
38
|
|
|
29
|
-
function formatTaskId(id: string): string {
|
|
30
|
-
const segments = id.split(".");
|
|
31
|
-
if (segments.length < 2) return id;
|
|
32
|
-
const parsed = segments.map(segment => segment.match(/^(\d+)-(.+)$/));
|
|
33
|
-
if (parsed.some(match => !match)) return id;
|
|
34
|
-
const indices = parsed.map(match => match![1]).join(".");
|
|
35
|
-
const labels = parsed.map(match => match![2]).join(">");
|
|
36
|
-
return `${indices} ${labels}`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
39
|
type ToolEntry = { tool: string; args: string; status: "success" | "error" | "running" };
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
const INDENT = " ";
|
|
42
|
+
|
|
43
|
+
function renderToolLine(entry: ToolEntry, theme: Theme): string {
|
|
42
44
|
const icon =
|
|
43
45
|
entry.status === "running"
|
|
44
46
|
? theme.fg("accent", theme.status.running)
|
|
@@ -46,234 +48,243 @@ function renderToolLine(entry: ToolEntry, continuePrefix: string, theme: Theme):
|
|
|
46
48
|
? theme.fg("error", theme.status.error)
|
|
47
49
|
: theme.fg("dim", theme.status.success);
|
|
48
50
|
const toolName = entry.status === "running" ? theme.fg("muted", entry.tool) : theme.fg("dim", entry.tool);
|
|
49
|
-
const args = entry.args
|
|
50
|
-
|
|
51
|
+
const args = entry.args
|
|
52
|
+
? ` ${theme.fg("dim", truncateToWidth(replaceTabs(entry.args), TRUNCATE_LENGTHS.TOOL_ARGS))}`
|
|
53
|
+
: "";
|
|
54
|
+
return `${INDENT}${icon} ${toolName}${args}`;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
54
|
-
//
|
|
58
|
+
// Unified subagent header
|
|
55
59
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
/** Config for rendering any subagent (task, explore, oracle, etc.) */
|
|
62
|
+
export interface SubagentRenderConfig {
|
|
63
|
+
label: string;
|
|
64
|
+
getDescription: (args: Record<string, unknown>) => string;
|
|
65
|
+
getContextLine?: (args: Record<string, unknown>) => string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Cheap fingerprint of tool state for cache invalidation. */
|
|
69
|
+
function toolStateFingerprint(details: TaskToolDetails): number {
|
|
70
|
+
let fp = 0;
|
|
71
|
+
if (details.progress) {
|
|
72
|
+
for (const p of details.progress) {
|
|
73
|
+
fp = (fp * 31 + p.toolHistory.length) | 0;
|
|
74
|
+
const last = p.toolHistory[p.toolHistory.length - 1];
|
|
75
|
+
if (last) fp = (fp * 31 + (last.status === "running" ? 1 : 2)) | 0;
|
|
69
76
|
}
|
|
70
|
-
const assignmentPreview = truncateToWidth(replaceTabs(args.assignment.split("\n")[0] ?? ""), 60);
|
|
71
|
-
lines.push(` ${last} ${theme.fg("dim", "Assignment")}: ${theme.fg("muted", assignmentPreview)}`);
|
|
72
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
73
77
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
lines.push(` ${theme.fg("dim", "Assignment")}: ${theme.fg("muted", assignmentPreview)}`);
|
|
77
|
-
return new Text(lines.join("\n"), 0, 0);
|
|
78
|
+
if (details.results) fp = (fp * 31 + details.results.length) | 0;
|
|
79
|
+
return fp >>> 0;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
progress: AgentProgress,
|
|
89
|
-
isLast: boolean,
|
|
90
|
-
expanded: boolean,
|
|
82
|
+
/**
|
|
83
|
+
* Render the stable header block for any subagent.
|
|
84
|
+
* Returns [headerLine, contextLine?] — structure never changes across states.
|
|
85
|
+
*/
|
|
86
|
+
function renderSubagentHeader(
|
|
87
|
+
config: SubagentRenderConfig,
|
|
88
|
+
args: Record<string, unknown>,
|
|
89
|
+
state: { icon: string; duration?: number; badge?: { text: string; color: ToolUIColor } },
|
|
91
90
|
theme: Theme,
|
|
92
|
-
spinnerFrame?: number,
|
|
93
91
|
): string[] {
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const icon = getStatusIcon(progress.status, theme, spinnerFrame);
|
|
99
|
-
const iconColor: ThemeColor =
|
|
100
|
-
progress.status === "completed"
|
|
101
|
-
? "success"
|
|
102
|
-
: progress.status === "failed" || progress.status === "aborted"
|
|
103
|
-
? "error"
|
|
104
|
-
: "accent";
|
|
105
|
-
|
|
106
|
-
// Main status line
|
|
107
|
-
const description = progress.description?.trim();
|
|
108
|
-
const displayId = formatTaskId(progress.id);
|
|
109
|
-
const titlePart = description ? `${theme.bold(displayId)}: ${description}` : displayId;
|
|
110
|
-
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)}`;
|
|
111
|
-
|
|
112
|
-
if (progress.status === "failed" || progress.status === "aborted") {
|
|
113
|
-
statusLine += ` ${formatBadge(progress.status, iconColor, theme)}`;
|
|
92
|
+
const desc = truncateToWidth(replaceTabs(config.getDescription(args)), TRUNCATE_LENGTHS.CONTENT);
|
|
93
|
+
let header = `${state.icon} ${theme.fg("accent", theme.bold(config.label))} ${theme.fg("muted", desc)}`;
|
|
94
|
+
if (state.duration && state.duration > 0) {
|
|
95
|
+
header += `${theme.sep.dot}${theme.fg("dim", formatDuration(state.duration))}`;
|
|
114
96
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
statusLine += `${theme.sep.dot}${theme.fg("dim", formatDuration(progress.durationMs))}`;
|
|
97
|
+
if (state.badge) {
|
|
98
|
+
header += ` ${formatBadge(state.badge.text, state.badge.color, theme)}`;
|
|
118
99
|
}
|
|
119
100
|
|
|
120
|
-
lines
|
|
121
|
-
|
|
122
|
-
// Tool history — show last N completed + current running
|
|
123
|
-
if (progress.status === "running" || progress.status === "completed" || progress.status === "failed") {
|
|
124
|
-
const history = progress.toolHistory;
|
|
125
|
-
const completed = history.filter(t => t.status !== "running");
|
|
126
|
-
const running = history.filter(t => t.status === "running");
|
|
101
|
+
const lines = [header];
|
|
127
102
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (skipped > 0) {
|
|
132
|
-
lines.push(`${continuePrefix}${theme.fg("dim", `… ${skipped} more`)}`);
|
|
133
|
-
}
|
|
134
|
-
for (const entry of showCompleted) {
|
|
135
|
-
lines.push(renderToolLine(entry, continuePrefix, theme));
|
|
136
|
-
}
|
|
137
|
-
// Show currently running tool
|
|
138
|
-
for (const entry of running) {
|
|
139
|
-
lines.push(renderToolLine(entry, continuePrefix, theme));
|
|
140
|
-
}
|
|
103
|
+
const contextLine = config.getContextLine?.(args);
|
|
104
|
+
if (contextLine) {
|
|
105
|
+
lines.push(`${INDENT}${theme.fg("dim", "↳")} ${theme.fg("dim", contextLine)}`);
|
|
141
106
|
}
|
|
142
107
|
|
|
143
108
|
return lines;
|
|
144
109
|
}
|
|
145
110
|
|
|
146
111
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
147
|
-
//
|
|
112
|
+
// Tool history rendering
|
|
148
113
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
149
114
|
|
|
150
|
-
|
|
151
|
-
const COLLAPSED_TOOL_LIMIT =
|
|
115
|
+
const STREAMING_TOOL_LIMIT = PREVIEW_LIMITS.SUBAGENT_STREAMING_TOOLS;
|
|
116
|
+
const COLLAPSED_TOOL_LIMIT = PREVIEW_LIMITS.SUBAGENT_COLLAPSED_TOOLS;
|
|
117
|
+
const COLLAPSED_CONCLUSION_LINES = PREVIEW_LIMITS.SUBAGENT_CONCLUSION;
|
|
118
|
+
|
|
119
|
+
/** Render conclusion text as markdown, returning indented lines with collapsed/expanded limit. */
|
|
120
|
+
function renderConclusionMarkdown(text: string, width: number, expanded: boolean, theme: Theme): string[] {
|
|
121
|
+
const md = new Markdown(text.trim(), INDENT.length, 0, getMarkdownTheme());
|
|
122
|
+
const mdLines = md.render(width);
|
|
123
|
+
if (mdLines.length === 0) return [];
|
|
124
|
+
|
|
125
|
+
const maxLines = expanded ? mdLines.length : COLLAPSED_CONCLUSION_LINES;
|
|
126
|
+
const show = mdLines.slice(0, maxLines);
|
|
127
|
+
const remaining = mdLines.length - maxLines;
|
|
128
|
+
const lines = ["", ...show];
|
|
129
|
+
if (remaining > 0) {
|
|
130
|
+
lines.push(`${INDENT}${theme.fg("dim", `… ${remaining} more lines`)}`);
|
|
131
|
+
}
|
|
132
|
+
return lines;
|
|
133
|
+
}
|
|
152
134
|
|
|
153
|
-
function
|
|
135
|
+
function renderToolHistory(history: ToolEntry[], expanded: boolean, limit: number, theme: Theme): string[] {
|
|
154
136
|
const lines: string[] = [];
|
|
155
|
-
const
|
|
156
|
-
const
|
|
137
|
+
const completed = history.filter(t => t.status !== "running");
|
|
138
|
+
const running = history.filter(t => t.status === "running");
|
|
157
139
|
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const statusText = aborted ? "aborted" : success ? "done" : "failed";
|
|
163
|
-
|
|
164
|
-
// Main status line
|
|
165
|
-
const description = result.description?.trim();
|
|
166
|
-
const displayId = formatTaskId(result.id);
|
|
167
|
-
const titlePart = description ? `${theme.bold(displayId)}: ${description}` : displayId;
|
|
168
|
-
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)} ${formatBadge(statusText, iconColor, theme)}`;
|
|
169
|
-
statusLine += `${theme.sep.dot}${theme.fg("dim", formatDuration(result.durationMs))}`;
|
|
170
|
-
|
|
171
|
-
if (result.truncated) {
|
|
172
|
-
statusLine += ` ${theme.fg("warning", "[truncated]")}`;
|
|
140
|
+
const showCompleted = expanded ? completed : completed.slice(-limit);
|
|
141
|
+
const skipped = completed.length - showCompleted.length;
|
|
142
|
+
if (skipped > 0) {
|
|
143
|
+
lines.push(`${INDENT}${theme.fg("dim", `… ${skipped} more`)}`);
|
|
173
144
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
// Tool history
|
|
178
|
-
const history = result.toolHistory ?? [];
|
|
179
|
-
if (history.length > 0) {
|
|
180
|
-
const show = expanded ? history : history.slice(-COLLAPSED_TOOL_LIMIT);
|
|
181
|
-
const skipped = history.length - show.length;
|
|
182
|
-
if (skipped > 0) {
|
|
183
|
-
lines.push(`${continuePrefix}${theme.fg("dim", `… ${skipped} more`)}`);
|
|
184
|
-
}
|
|
185
|
-
for (const entry of show) {
|
|
186
|
-
lines.push(renderToolLine(entry, continuePrefix, theme));
|
|
187
|
-
}
|
|
145
|
+
for (const entry of showCompleted) {
|
|
146
|
+
lines.push(renderToolLine(entry, theme));
|
|
188
147
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
148
|
+
if (running.length > 0) {
|
|
149
|
+
for (const entry of running) {
|
|
150
|
+
lines.push(renderToolLine(entry, theme));
|
|
151
|
+
}
|
|
152
|
+
} else if (completed.length > 0) {
|
|
153
|
+
// Between tool calls — reserve empty line to prevent layout shift
|
|
154
|
+
lines.push(INDENT);
|
|
193
155
|
}
|
|
194
|
-
|
|
195
156
|
return lines;
|
|
196
157
|
}
|
|
197
158
|
|
|
198
159
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
199
|
-
//
|
|
160
|
+
// Task tool renderCall / renderResult
|
|
200
161
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
201
162
|
|
|
163
|
+
const taskRenderConfig: SubagentRenderConfig = {
|
|
164
|
+
label: "Task",
|
|
165
|
+
getDescription: args => String(args.description ?? ""),
|
|
166
|
+
getContextLine: args => {
|
|
167
|
+
const prompt = String(args.prompt ?? "").trim();
|
|
168
|
+
if (!prompt) return null;
|
|
169
|
+
return `Prompt: ${truncateToWidth(replaceTabs(prompt.split("\n")[0] ?? ""), TRUNCATE_LENGTHS.SUBAGENT_ERROR)}`;
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export function renderCall(args: TaskParams, options: RenderResultOptions, theme: Theme): Component {
|
|
174
|
+
const params = args as Record<string, unknown>;
|
|
175
|
+
let cached: RenderCache | undefined;
|
|
176
|
+
return {
|
|
177
|
+
render() {
|
|
178
|
+
const frame = options.spinnerFrame ?? 0;
|
|
179
|
+
const key = new Hasher().u32(frame).digest();
|
|
180
|
+
if (cached?.key === key) return cached.lines;
|
|
181
|
+
const icon = formatStatusIcon("running", theme, frame);
|
|
182
|
+
const lines = renderSubagentHeader(taskRenderConfig, params, { icon }, theme);
|
|
183
|
+
cached = { key, lines };
|
|
184
|
+
return lines;
|
|
185
|
+
},
|
|
186
|
+
invalidate() {
|
|
187
|
+
cached = undefined;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
202
192
|
export function renderResult(
|
|
203
193
|
result: { content: Array<{ type: string; text?: string }>; details?: TaskToolDetails },
|
|
204
194
|
options: RenderResultOptions,
|
|
205
195
|
theme: Theme,
|
|
206
196
|
): Component {
|
|
207
|
-
|
|
208
|
-
const details = result.details;
|
|
209
|
-
|
|
210
|
-
if (!details) {
|
|
197
|
+
if (!result.details) {
|
|
211
198
|
const text = result.content.find(c => c.type === "text")?.text || "";
|
|
212
|
-
return new Text(theme.fg("dim", truncateToWidth(text,
|
|
199
|
+
return new Text(theme.fg("dim", truncateToWidth(text, TRUNCATE_LENGTHS.LONG)), 0, 0);
|
|
213
200
|
}
|
|
214
201
|
|
|
215
202
|
let cached: RenderCache | undefined;
|
|
216
203
|
|
|
217
204
|
return {
|
|
218
205
|
render(width) {
|
|
206
|
+
// Read from result ref each render to pick up mutable updates
|
|
207
|
+
const details = result.details!;
|
|
208
|
+
const fallbackText = result.content.find(c => c.type === "text")?.text ?? "";
|
|
219
209
|
const { expanded, isPartial, spinnerFrame } = options;
|
|
220
210
|
const key = new Hasher()
|
|
221
211
|
.bool(expanded)
|
|
222
212
|
.bool(isPartial)
|
|
223
213
|
.u32(spinnerFrame ?? 0)
|
|
224
214
|
.u32(width)
|
|
215
|
+
.u32(toolStateFingerprint(details))
|
|
225
216
|
.digest();
|
|
226
217
|
if (cached?.key === key) return cached.lines;
|
|
227
218
|
|
|
228
219
|
const lines: string[] = [];
|
|
220
|
+
const args = {} as Record<string, unknown>;
|
|
221
|
+
|
|
222
|
+
if (isPartial && details.progress?.length) {
|
|
223
|
+
const p = details.progress[0];
|
|
224
|
+
const icon = getStatusIcon(p.status, theme, spinnerFrame);
|
|
225
|
+
const duration = p.durationMs > 0 ? p.durationMs : undefined;
|
|
226
|
+
const errorColor = "error" as const;
|
|
227
|
+
const badge =
|
|
228
|
+
p.status === "failed" || p.status === "aborted" ? { text: p.status, color: errorColor } : undefined;
|
|
229
|
+
// Use description from progress for header
|
|
230
|
+
const headerArgs = { description: p.description ?? p.id, prompt: p.task };
|
|
231
|
+
lines.push(
|
|
232
|
+
...renderSubagentHeader(
|
|
233
|
+
taskRenderConfig,
|
|
234
|
+
headerArgs as Record<string, unknown>,
|
|
235
|
+
{ icon, duration, badge },
|
|
236
|
+
theme,
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
lines.push(...renderToolHistory(p.toolHistory, expanded, STREAMING_TOOL_LIMIT, theme));
|
|
240
|
+
} else if (details.results?.length) {
|
|
241
|
+
const r = details.results[0];
|
|
242
|
+
const aborted = r.aborted ?? false;
|
|
243
|
+
const success = !aborted && r.exitCode === 0;
|
|
244
|
+
const statusText = aborted ? "aborted" : success ? "done" : "failed";
|
|
245
|
+
const iconColor: ToolUIColor = success ? "success" : "error";
|
|
246
|
+
const icon = formatStatusIcon(success ? "success" : "error", theme);
|
|
247
|
+
const headerArgs = { description: r.description ?? r.id, prompt: r.task };
|
|
248
|
+
lines.push(
|
|
249
|
+
...renderSubagentHeader(
|
|
250
|
+
taskRenderConfig,
|
|
251
|
+
headerArgs as Record<string, unknown>,
|
|
252
|
+
{ icon, duration: r.durationMs, badge: { text: statusText, color: iconColor } },
|
|
253
|
+
theme,
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// Tool history
|
|
258
|
+
const history = r.toolHistory ?? [];
|
|
259
|
+
if (history.length > 0) {
|
|
260
|
+
const show = expanded ? history : history.slice(-COLLAPSED_TOOL_LIMIT);
|
|
261
|
+
const skipped = history.length - show.length;
|
|
262
|
+
if (skipped > 0) {
|
|
263
|
+
lines.push(`${INDENT}${theme.fg("dim", `… ${skipped} more`)}`);
|
|
264
|
+
}
|
|
265
|
+
for (const entry of show) {
|
|
266
|
+
lines.push(renderToolLine(entry, theme));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
229
269
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Summary line
|
|
242
|
-
const abortedCount = details.results.filter(r => r.aborted).length;
|
|
243
|
-
const successCount = details.results.filter(r => !r.aborted && r.exitCode === 0).length;
|
|
244
|
-
const failCount = details.results.length - successCount - abortedCount;
|
|
245
|
-
const parts: string[] = [];
|
|
246
|
-
if (successCount > 0) parts.push(theme.fg("success", `${successCount} succeeded`));
|
|
247
|
-
if (failCount > 0) parts.push(theme.fg("error", `${failCount} failed`));
|
|
248
|
-
if (abortedCount > 0) parts.push(theme.fg("error", `${abortedCount} aborted`));
|
|
249
|
-
parts.push(theme.fg("dim", formatDuration(details.totalDurationMs)));
|
|
250
|
-
lines.push(parts.join(theme.sep.dot));
|
|
270
|
+
if (r.error && !success) {
|
|
271
|
+
lines.push(`${INDENT}${theme.fg("error", truncateToWidth(r.error, TRUNCATE_LENGTHS.SUBAGENT_ERROR))}`);
|
|
272
|
+
}
|
|
273
|
+
if (success && fallbackText.trim()) {
|
|
274
|
+
lines.push(...renderConclusionMarkdown(fallbackText, width, expanded, theme));
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
const icon = formatStatusIcon("pending", theme);
|
|
278
|
+
lines.push(...renderSubagentHeader(taskRenderConfig, args, { icon }, theme));
|
|
251
279
|
}
|
|
252
280
|
|
|
253
281
|
if (lines.length === 0) {
|
|
254
|
-
const
|
|
255
|
-
const result = [theme.fg("dim", truncateToWidth(text, width))];
|
|
282
|
+
const result = [truncateToWidth(fallbackText.trim() || theme.fg("dim", "No results"), width)];
|
|
256
283
|
cached = { key, lines: result };
|
|
257
284
|
return result;
|
|
258
285
|
}
|
|
259
286
|
|
|
260
|
-
|
|
261
|
-
if (fallbackText.trim()) {
|
|
262
|
-
const summaryLines = fallbackText.split("\n");
|
|
263
|
-
const markerIndex = summaryLines.findIndex(
|
|
264
|
-
line => line.includes("<system-notification>") || line.startsWith("Applied patches:"),
|
|
265
|
-
);
|
|
266
|
-
if (markerIndex >= 0) {
|
|
267
|
-
for (const line of summaryLines.slice(markerIndex)) {
|
|
268
|
-
if (!line.trim()) continue;
|
|
269
|
-
lines.push(theme.fg("dim", line));
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const indented = lines.map(line =>
|
|
275
|
-
line.length > 0 ? truncateToWidth(` ${line}`, width, Ellipsis.Omit) : "",
|
|
276
|
-
);
|
|
287
|
+
const indented = lines.map(line => (line.length > 0 ? truncateToWidth(line, width, Ellipsis.Omit) : ""));
|
|
277
288
|
cached = { key, lines: indented };
|
|
278
289
|
return indented;
|
|
279
290
|
},
|
|
@@ -283,10 +294,189 @@ export function renderResult(
|
|
|
283
294
|
};
|
|
284
295
|
}
|
|
285
296
|
|
|
297
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
298
|
+
// Unified subagent renderer factory
|
|
299
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Create a renderer for any subagent tool (explore, oracle, librarian, code_review).
|
|
303
|
+
*
|
|
304
|
+
* Renders call and result with unified header + tool history + conclusion.
|
|
305
|
+
*/
|
|
306
|
+
export function createUnifiedSubagentRenderer(config: SubagentRenderConfig): {
|
|
307
|
+
renderCall: (args: unknown, options: RenderResultOptions, theme: Theme) => Component;
|
|
308
|
+
renderResult: (
|
|
309
|
+
result: { content: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean },
|
|
310
|
+
options: RenderResultOptions,
|
|
311
|
+
theme: Theme,
|
|
312
|
+
args?: unknown,
|
|
313
|
+
) => Component;
|
|
314
|
+
} {
|
|
315
|
+
return {
|
|
316
|
+
renderCall(args: unknown, options: RenderResultOptions, theme: Theme): Component {
|
|
317
|
+
const params = (args ?? {}) as Record<string, unknown>;
|
|
318
|
+
let cached: RenderCache | undefined;
|
|
319
|
+
return {
|
|
320
|
+
render() {
|
|
321
|
+
const frame = options.spinnerFrame ?? 0;
|
|
322
|
+
const key = new Hasher().u32(frame).digest();
|
|
323
|
+
if (cached?.key === key) return cached.lines;
|
|
324
|
+
const icon = formatStatusIcon("running", theme, frame);
|
|
325
|
+
const lines = renderSubagentHeader(config, params, { icon }, theme);
|
|
326
|
+
cached = { key, lines };
|
|
327
|
+
return lines;
|
|
328
|
+
},
|
|
329
|
+
invalidate() {
|
|
330
|
+
cached = undefined;
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
renderResult(
|
|
336
|
+
result: { content: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean },
|
|
337
|
+
options: RenderResultOptions,
|
|
338
|
+
theme: Theme,
|
|
339
|
+
args?: unknown,
|
|
340
|
+
): Component {
|
|
341
|
+
const params = (args ?? {}) as Record<string, unknown>;
|
|
342
|
+
|
|
343
|
+
if (!result.details) {
|
|
344
|
+
const text = result.content.find(c => c.type === "text")?.text || "No results";
|
|
345
|
+
return new Text(theme.fg("dim", truncateToWidth(text, TRUNCATE_LENGTHS.LONG)), 0, 0);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let cached: RenderCache | undefined;
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
render(width) {
|
|
352
|
+
// Read from result ref each render to pick up mutable updates
|
|
353
|
+
const details = result.details as TaskToolDetails;
|
|
354
|
+
const fallbackText = result.content.find(c => c.type === "text")?.text ?? "";
|
|
355
|
+
const { expanded, isPartial, spinnerFrame } = options;
|
|
356
|
+
const key = new Hasher()
|
|
357
|
+
.bool(expanded)
|
|
358
|
+
.bool(isPartial)
|
|
359
|
+
.u32(spinnerFrame ?? 0)
|
|
360
|
+
.u32(width)
|
|
361
|
+
.u32(toolStateFingerprint(details))
|
|
362
|
+
.digest();
|
|
363
|
+
if (cached?.key === key) return cached.lines;
|
|
364
|
+
|
|
365
|
+
const lines: string[] = [];
|
|
366
|
+
|
|
367
|
+
// Stable header — same structure as renderCall
|
|
368
|
+
if (isPartial && details.progress?.length) {
|
|
369
|
+
const p = details.progress[0];
|
|
370
|
+
const icon = getStatusIcon(p.status, theme, spinnerFrame);
|
|
371
|
+
const duration = p.durationMs > 0 ? p.durationMs : undefined;
|
|
372
|
+
const errorColor = "error" as const;
|
|
373
|
+
const badge =
|
|
374
|
+
p.status === "failed" || p.status === "aborted"
|
|
375
|
+
? { text: p.status, color: errorColor }
|
|
376
|
+
: undefined;
|
|
377
|
+
lines.push(...renderSubagentHeader(config, params, { icon, duration, badge }, theme));
|
|
378
|
+
lines.push(...renderToolHistory(p.toolHistory, expanded, STREAMING_TOOL_LIMIT, theme));
|
|
379
|
+
} else if (details.results?.length) {
|
|
380
|
+
const r = details.results[0];
|
|
381
|
+
const aborted = r.aborted ?? false;
|
|
382
|
+
const success = !aborted && r.exitCode === 0;
|
|
383
|
+
const statusText = aborted ? "aborted" : success ? "done" : "failed";
|
|
384
|
+
const iconColor: ToolUIColor = success ? "success" : "error";
|
|
385
|
+
const icon = formatStatusIcon(success ? "success" : "error", theme);
|
|
386
|
+
lines.push(
|
|
387
|
+
...renderSubagentHeader(
|
|
388
|
+
config,
|
|
389
|
+
params,
|
|
390
|
+
{
|
|
391
|
+
icon,
|
|
392
|
+
duration: r.durationMs,
|
|
393
|
+
badge: { text: statusText, color: iconColor },
|
|
394
|
+
},
|
|
395
|
+
theme,
|
|
396
|
+
),
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
const history = r.toolHistory ?? [];
|
|
400
|
+
if (history.length > 0) {
|
|
401
|
+
const show = expanded ? history : history.slice(-COLLAPSED_TOOL_LIMIT);
|
|
402
|
+
const skipped = history.length - show.length;
|
|
403
|
+
if (skipped > 0) {
|
|
404
|
+
lines.push(`${INDENT}${theme.fg("dim", `… ${skipped} more`)}`);
|
|
405
|
+
}
|
|
406
|
+
for (const entry of show) {
|
|
407
|
+
lines.push(renderToolLine(entry, theme));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (r.error && !success) {
|
|
412
|
+
lines.push(
|
|
413
|
+
`${INDENT}${theme.fg("error", truncateToWidth(r.error, TRUNCATE_LENGTHS.SUBAGENT_ERROR))}`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
if (success && fallbackText.trim()) {
|
|
417
|
+
lines.push(...renderConclusionMarkdown(fallbackText, width, expanded, theme));
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
const icon = formatStatusIcon("pending", theme);
|
|
421
|
+
lines.push(...renderSubagentHeader(config, params, { icon }, theme));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (lines.length === 0) {
|
|
425
|
+
const text = fallbackText.trim() ? fallbackText : "No results";
|
|
426
|
+
const result = [theme.fg("dim", truncateToWidth(text, width))];
|
|
427
|
+
cached = { key, lines: result };
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const indented = lines.map(line => (line.length > 0 ? truncateToWidth(line, width, Ellipsis.Omit) : ""));
|
|
432
|
+
cached = { key, lines: indented };
|
|
433
|
+
return indented;
|
|
434
|
+
},
|
|
435
|
+
invalidate() {
|
|
436
|
+
cached = undefined;
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
286
443
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
287
444
|
// Subprocess tool registry
|
|
288
445
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
289
446
|
|
|
447
|
+
function renderAgentResult(result: SingleResult, expanded: boolean, theme: Theme): string[] {
|
|
448
|
+
const lines: string[] = [];
|
|
449
|
+
const aborted = result.aborted ?? false;
|
|
450
|
+
const success = !aborted && result.exitCode === 0;
|
|
451
|
+
const icon = aborted ? theme.status.aborted : success ? theme.status.success : theme.status.error;
|
|
452
|
+
const iconColor: ThemeColor = success ? "success" : "error";
|
|
453
|
+
const statusText = aborted ? "aborted" : success ? "done" : "failed";
|
|
454
|
+
|
|
455
|
+
const description = result.description?.trim();
|
|
456
|
+
const titlePart = description || result.id;
|
|
457
|
+
let statusLine = `${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)} ${formatBadge(statusText, iconColor, theme)}`;
|
|
458
|
+
statusLine += `${theme.sep.dot}${theme.fg("dim", formatDuration(result.durationMs))}`;
|
|
459
|
+
lines.push(statusLine);
|
|
460
|
+
|
|
461
|
+
const history = result.toolHistory ?? [];
|
|
462
|
+
if (history.length > 0) {
|
|
463
|
+
const show = expanded ? history : history.slice(-COLLAPSED_TOOL_LIMIT);
|
|
464
|
+
const skipped = history.length - show.length;
|
|
465
|
+
if (skipped > 0) {
|
|
466
|
+
lines.push(`${INDENT}${theme.fg("dim", `… ${skipped} more`)}`);
|
|
467
|
+
}
|
|
468
|
+
for (const entry of show) {
|
|
469
|
+
lines.push(renderToolLine(entry, theme));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (result.error && !success) {
|
|
474
|
+
lines.push(`${INDENT}${theme.fg("error", truncateToWidth(result.error, TRUNCATE_LENGTHS.SUBAGENT_ERROR))}`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return lines;
|
|
478
|
+
}
|
|
479
|
+
|
|
290
480
|
function isTaskToolDetails(value: unknown): value is TaskToolDetails {
|
|
291
481
|
return (
|
|
292
482
|
Boolean(value) &&
|
|
@@ -305,10 +495,9 @@ const taskSubprocessHandler = {
|
|
|
305
495
|
const lines: string[] = [];
|
|
306
496
|
for (const details of allData) {
|
|
307
497
|
if (!details.results || details.results.length === 0) continue;
|
|
308
|
-
details.results
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
});
|
|
498
|
+
for (const result of details.results) {
|
|
499
|
+
lines.push(...renderAgentResult(result, expanded, theme));
|
|
500
|
+
}
|
|
312
501
|
}
|
|
313
502
|
return new Text(lines.join("\n"), 0, 0);
|
|
314
503
|
},
|
|
@@ -319,8 +508,3 @@ subprocessToolRegistry.register<TaskToolDetails>("explore", taskSubprocessHandle
|
|
|
319
508
|
subprocessToolRegistry.register<TaskToolDetails>("librarian", taskSubprocessHandler);
|
|
320
509
|
subprocessToolRegistry.register<TaskToolDetails>("oracle", taskSubprocessHandler);
|
|
321
510
|
subprocessToolRegistry.register<TaskToolDetails>("code_review", taskSubprocessHandler);
|
|
322
|
-
|
|
323
|
-
export const taskToolRenderer = {
|
|
324
|
-
renderCall,
|
|
325
|
-
renderResult,
|
|
326
|
-
};
|