@nghyane/arcane 0.1.13 → 0.1.15

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.
Files changed (303) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/package.json +21 -70
  3. package/scripts/format-prompts.ts +1 -3
  4. package/src/cli/args.ts +2 -7
  5. package/src/cli/config-cli.ts +1 -1
  6. package/src/cli/plugin-cli.ts +1 -1
  7. package/src/cli/setup-cli.ts +1 -1
  8. package/src/cli/update-cli.ts +1 -1
  9. package/src/cli/web-search-cli.ts +1 -1
  10. package/src/cli.ts +0 -1
  11. package/src/commands/config.ts +1 -1
  12. package/src/commands/grep.ts +1 -1
  13. package/src/commands/jupyter.ts +1 -1
  14. package/src/commands/plugin.ts +1 -1
  15. package/src/commands/setup.ts +1 -1
  16. package/src/commands/shell.ts +1 -1
  17. package/src/commands/ssh.ts +1 -1
  18. package/src/commands/stats.ts +1 -1
  19. package/src/commands/update.ts +1 -1
  20. package/src/config/model-registry.ts +3 -4
  21. package/src/config/model-resolver.ts +36 -9
  22. package/src/config/prompt-templates.ts +1 -9
  23. package/src/config/settings-schema.ts +32 -88
  24. package/src/config/settings.ts +3 -4
  25. package/src/debug/index.ts +1 -1
  26. package/src/debug/log-formatting.ts +1 -1
  27. package/src/debug/log-viewer.ts +2 -2
  28. package/src/discovery/helpers.ts +13 -3
  29. package/src/exa/index.ts +1 -35
  30. package/src/exa/render.ts +30 -190
  31. package/src/export/html/index.ts +1 -1
  32. package/src/extensibility/custom-tools/loader.ts +1 -1
  33. package/src/extensibility/custom-tools/types.ts +5 -1
  34. package/src/extensibility/custom-tools/wrapper.ts +1 -1
  35. package/src/extensibility/extensions/runner.ts +1 -1
  36. package/src/extensibility/extensions/types.ts +1 -1
  37. package/src/extensibility/extensions/wrapper.ts +7 -15
  38. package/src/extensibility/hooks/runner.ts +1 -1
  39. package/src/extensibility/hooks/types.ts +1 -1
  40. package/src/extensibility/plugins/doctor.ts +1 -1
  41. package/src/index.ts +13 -13
  42. package/src/lsp/index.ts +77 -24
  43. package/src/lsp/render.ts +34 -583
  44. package/src/lsp/types.ts +3 -3
  45. package/src/lsp/utils.ts +1 -1
  46. package/src/main.ts +1 -1
  47. package/src/mcp/tool-bridge.ts +1 -24
  48. package/src/modes/components/assistant-message.ts +7 -7
  49. package/src/modes/components/bash-execution.ts +50 -112
  50. package/src/modes/components/bordered-loader.ts +1 -1
  51. package/src/modes/components/branch-summary-message.ts +16 -10
  52. package/src/modes/components/compaction-summary-message.ts +20 -12
  53. package/src/modes/components/context-group.ts +106 -0
  54. package/src/modes/components/custom-message.ts +4 -5
  55. package/src/modes/components/diff.ts +2 -2
  56. package/src/modes/components/dynamic-border.ts +1 -1
  57. package/src/modes/components/extensions/extension-dashboard.ts +1 -1
  58. package/src/modes/components/extensions/extension-list.ts +1 -1
  59. package/src/modes/components/extensions/inspector-panel.ts +1 -1
  60. package/src/modes/components/footer.ts +2 -2
  61. package/src/modes/components/history-search.ts +1 -1
  62. package/src/modes/components/hook-editor.ts +1 -1
  63. package/src/modes/components/hook-input.ts +1 -1
  64. package/src/modes/components/hook-message.ts +4 -5
  65. package/src/modes/components/hook-selector.ts +1 -1
  66. package/src/modes/components/index.ts +0 -2
  67. package/src/modes/components/keybinding-hints.ts +1 -1
  68. package/src/modes/components/login-dialog.ts +1 -1
  69. package/src/modes/components/mcp-add-wizard.ts +1 -1
  70. package/src/modes/components/model-selector.ts +1 -1
  71. package/src/modes/components/oauth-selector.ts +1 -1
  72. package/src/modes/components/plugin-settings.ts +1 -1
  73. package/src/modes/components/python-execution.ts +51 -91
  74. package/src/modes/components/queue-mode-selector.ts +1 -1
  75. package/src/modes/components/session-selector.ts +1 -1
  76. package/src/modes/components/settings-defs.ts +5 -10
  77. package/src/modes/components/settings-selector.ts +1 -1
  78. package/src/modes/components/show-images-selector.ts +1 -1
  79. package/src/modes/components/skill-message.ts +4 -4
  80. package/src/modes/components/status-line/segments.ts +2 -2
  81. package/src/modes/components/status-line/separators.ts +1 -1
  82. package/src/modes/components/status-line-segment-editor.ts +1 -1
  83. package/src/modes/components/status-line.ts +1 -1
  84. package/src/modes/components/theme-selector.ts +1 -1
  85. package/src/modes/components/thinking-selector.ts +1 -1
  86. package/src/modes/components/todo-display.ts +2 -4
  87. package/src/modes/components/todo-reminder.ts +4 -4
  88. package/src/modes/components/tool-execution.ts +118 -440
  89. package/src/modes/components/tool-image-display.ts +107 -0
  90. package/src/modes/components/tree-selector.ts +2 -2
  91. package/src/modes/components/ttsr-notification.ts +4 -17
  92. package/src/modes/components/user-message-selector.ts +1 -1
  93. package/src/modes/components/user-message.ts +9 -10
  94. package/src/modes/components/welcome.ts +1 -1
  95. package/src/modes/controllers/command-controller.ts +1 -1
  96. package/src/modes/controllers/event-controller.ts +58 -187
  97. package/src/modes/controllers/extension-ui-controller.ts +1 -1
  98. package/src/modes/controllers/input-controller.ts +3 -1
  99. package/src/modes/controllers/mcp-command-controller.ts +1 -1
  100. package/src/modes/controllers/selector-controller.ts +3 -26
  101. package/src/modes/controllers/ssh-command-controller.ts +1 -1
  102. package/src/modes/interactive-mode.ts +3 -7
  103. package/src/modes/print-mode.ts +5 -5
  104. package/src/modes/rpc/rpc-mode.ts +1 -1
  105. package/src/modes/types.ts +1 -2
  106. package/src/modes/utils/ui-helpers.ts +34 -32
  107. package/src/patch/edit-tool.ts +742 -0
  108. package/src/patch/index.ts +32 -898
  109. package/src/patch/schemas.ts +208 -0
  110. package/src/patch/shared.ts +83 -151
  111. package/src/prompts/agents/explore.md +22 -37
  112. package/src/prompts/agents/init.md +1 -1
  113. package/src/prompts/agents/librarian.md +29 -20
  114. package/src/prompts/agents/oracle.md +9 -2
  115. package/src/prompts/agents/reviewer.md +14 -48
  116. package/src/prompts/agents/task.md +16 -8
  117. package/src/prompts/compaction/branch-summary.md +4 -1
  118. package/src/prompts/compaction/compaction-summary.md +4 -1
  119. package/src/prompts/system/subagent-system-prompt.md +1 -1
  120. package/src/prompts/system/system-prompt.md +162 -178
  121. package/src/prompts/system/verification-reminder.md +6 -0
  122. package/src/sdk.ts +0 -9
  123. package/src/session/agent-session.ts +244 -1459
  124. package/src/session/model-controller.ts +406 -0
  125. package/src/session/retry-utils.ts +71 -0
  126. package/src/session/session-manager.ts +22 -186
  127. package/src/session/session-types.ts +312 -0
  128. package/src/session/stats.ts +387 -0
  129. package/src/session/streaming-edit.ts +258 -0
  130. package/src/session/ttsr.ts +213 -0
  131. package/src/slash-commands/builtin-registry.ts +0 -8
  132. package/src/stt/recorder.ts +2 -2
  133. package/src/system-prompt.ts +1 -14
  134. package/src/task/agents.ts +7 -33
  135. package/src/task/executor.ts +50 -438
  136. package/src/task/index.ts +104 -71
  137. package/src/task/progress-tracker.ts +390 -0
  138. package/src/task/render.ts +371 -187
  139. package/src/task/subprocess-tool-registry.ts +1 -1
  140. package/src/task/types.ts +14 -47
  141. package/src/tools/ask.ts +31 -42
  142. package/src/tools/bash-interactive.ts +2 -2
  143. package/src/tools/bash-interceptor.ts +2 -2
  144. package/src/tools/bash-normalize.ts +1 -1
  145. package/src/tools/bash-skill-urls.ts +2 -2
  146. package/src/tools/bash.ts +87 -136
  147. package/src/tools/browser.ts +54 -84
  148. package/src/tools/create-tools.ts +186 -0
  149. package/src/tools/default-renderer.ts +104 -0
  150. package/src/tools/explore.ts +11 -10
  151. package/src/tools/fetch.ts +24 -114
  152. package/src/tools/find.ts +48 -132
  153. package/src/tools/gemini-image.ts +5 -15
  154. package/src/tools/github.ts +450 -0
  155. package/src/tools/grep.ts +43 -179
  156. package/src/tools/index.ts +35 -198
  157. package/src/tools/json-tree.ts +3 -3
  158. package/src/tools/librarian.ts +18 -18
  159. package/src/tools/list-limit.ts +2 -2
  160. package/src/tools/notebook.ts +35 -87
  161. package/src/tools/oracle.ts +25 -25
  162. package/src/tools/output-meta.ts +89 -4
  163. package/src/tools/output-utils.ts +2 -2
  164. package/src/tools/python.ts +86 -637
  165. package/src/tools/read.ts +36 -119
  166. package/src/tools/reviewer-tool.ts +19 -21
  167. package/src/tools/search-code.ts +128 -0
  168. package/src/tools/ssh.ts +67 -126
  169. package/src/tools/subagent-tool.ts +197 -123
  170. package/src/tools/todo-write.ts +15 -31
  171. package/src/tools/tool-errors.ts +0 -30
  172. package/src/tools/undo-edit.ts +30 -67
  173. package/src/tools/write.ts +78 -127
  174. package/src/tui/code-cell.ts +4 -4
  175. package/src/tui/file-list.ts +2 -2
  176. package/src/tui/output-block.ts +1 -1
  177. package/src/tui/status-line.ts +1 -1
  178. package/src/tui/tree-list.ts +2 -2
  179. package/src/tui/types.ts +1 -1
  180. package/src/tui/utils.ts +1 -1
  181. package/src/{tools → ui}/render-utils.ts +87 -126
  182. package/src/utils/external-editor.ts +4 -4
  183. package/src/utils/file-mentions.ts +1 -1
  184. package/src/utils/index.ts +30 -0
  185. package/src/utils/tools-manager.ts +9 -19
  186. package/src/web/github-client.ts +290 -0
  187. package/src/web/scrapers/github.ts +11 -62
  188. package/src/web/search/auth.ts +1 -3
  189. package/src/web/search/index.ts +82 -46
  190. package/src/web/search/provider.ts +11 -16
  191. package/src/web/search/providers/grep.ts +160 -0
  192. package/src/web/search/render.ts +48 -235
  193. package/src/web/search/types.ts +1 -1
  194. package/src/commands/commit.ts +0 -36
  195. package/src/commit/agentic/agent.ts +0 -311
  196. package/src/commit/agentic/fallback.ts +0 -96
  197. package/src/commit/agentic/index.ts +0 -359
  198. package/src/commit/agentic/prompts/analyze-file.md +0 -22
  199. package/src/commit/agentic/prompts/session-user.md +0 -25
  200. package/src/commit/agentic/prompts/split-confirm.md +0 -1
  201. package/src/commit/agentic/prompts/system.md +0 -38
  202. package/src/commit/agentic/state.ts +0 -69
  203. package/src/commit/agentic/tools/analyze-file.ts +0 -118
  204. package/src/commit/agentic/tools/git-file-diff.ts +0 -194
  205. package/src/commit/agentic/tools/git-hunk.ts +0 -50
  206. package/src/commit/agentic/tools/git-overview.ts +0 -84
  207. package/src/commit/agentic/tools/index.ts +0 -56
  208. package/src/commit/agentic/tools/propose-changelog.ts +0 -128
  209. package/src/commit/agentic/tools/propose-commit.ts +0 -154
  210. package/src/commit/agentic/tools/recent-commits.ts +0 -81
  211. package/src/commit/agentic/tools/split-commit.ts +0 -280
  212. package/src/commit/agentic/topo-sort.ts +0 -44
  213. package/src/commit/agentic/trivial.ts +0 -51
  214. package/src/commit/agentic/validation.ts +0 -200
  215. package/src/commit/analysis/conventional.ts +0 -165
  216. package/src/commit/analysis/index.ts +0 -4
  217. package/src/commit/analysis/scope.ts +0 -242
  218. package/src/commit/analysis/summary.ts +0 -112
  219. package/src/commit/analysis/validation.ts +0 -66
  220. package/src/commit/changelog/detect.ts +0 -37
  221. package/src/commit/changelog/generate.ts +0 -110
  222. package/src/commit/changelog/index.ts +0 -234
  223. package/src/commit/changelog/parse.ts +0 -44
  224. package/src/commit/cli.ts +0 -93
  225. package/src/commit/git/diff.ts +0 -148
  226. package/src/commit/git/errors.ts +0 -9
  227. package/src/commit/git/index.ts +0 -211
  228. package/src/commit/git/operations.ts +0 -54
  229. package/src/commit/index.ts +0 -5
  230. package/src/commit/map-reduce/index.ts +0 -64
  231. package/src/commit/map-reduce/map-phase.ts +0 -178
  232. package/src/commit/map-reduce/reduce-phase.ts +0 -145
  233. package/src/commit/map-reduce/utils.ts +0 -9
  234. package/src/commit/message.ts +0 -11
  235. package/src/commit/model-selection.ts +0 -69
  236. package/src/commit/pipeline.ts +0 -243
  237. package/src/commit/prompts/analysis-system.md +0 -148
  238. package/src/commit/prompts/analysis-user.md +0 -38
  239. package/src/commit/prompts/changelog-system.md +0 -50
  240. package/src/commit/prompts/changelog-user.md +0 -18
  241. package/src/commit/prompts/file-observer-system.md +0 -24
  242. package/src/commit/prompts/file-observer-user.md +0 -8
  243. package/src/commit/prompts/reduce-system.md +0 -50
  244. package/src/commit/prompts/reduce-user.md +0 -17
  245. package/src/commit/prompts/summary-retry.md +0 -3
  246. package/src/commit/prompts/summary-system.md +0 -38
  247. package/src/commit/prompts/summary-user.md +0 -13
  248. package/src/commit/prompts/types-description.md +0 -2
  249. package/src/commit/types.ts +0 -109
  250. package/src/commit/utils/exclusions.ts +0 -42
  251. package/src/mcp/render.ts +0 -123
  252. package/src/modes/components/agent-dashboard.ts +0 -1130
  253. package/src/modes/components/codemode-group.ts +0 -369
  254. package/src/modes/components/read-tool-group.ts +0 -119
  255. package/src/modes/components/visual-truncate.ts +0 -63
  256. package/src/prompts/system/subagent-user-prompt.md +0 -8
  257. package/src/prompts/tools/ask.md +0 -44
  258. package/src/prompts/tools/bash.md +0 -24
  259. package/src/prompts/tools/browser.md +0 -33
  260. package/src/prompts/tools/calculator.md +0 -12
  261. package/src/prompts/tools/explore.md +0 -29
  262. package/src/prompts/tools/fetch.md +0 -16
  263. package/src/prompts/tools/find.md +0 -18
  264. package/src/prompts/tools/gemini-image.md +0 -23
  265. package/src/prompts/tools/grep.md +0 -28
  266. package/src/prompts/tools/hashline.md +0 -232
  267. package/src/prompts/tools/librarian.md +0 -24
  268. package/src/prompts/tools/lsp.md +0 -28
  269. package/src/prompts/tools/oracle.md +0 -26
  270. package/src/prompts/tools/patch.md +0 -74
  271. package/src/prompts/tools/python.md +0 -66
  272. package/src/prompts/tools/read.md +0 -36
  273. package/src/prompts/tools/replace.md +0 -38
  274. package/src/prompts/tools/reviewer.md +0 -41
  275. package/src/prompts/tools/ssh.md +0 -51
  276. package/src/prompts/tools/task-summary.md +0 -28
  277. package/src/prompts/tools/task.md +0 -146
  278. package/src/prompts/tools/todo-write.md +0 -65
  279. package/src/prompts/tools/undo-edit.md +0 -7
  280. package/src/prompts/tools/web-search.md +0 -19
  281. package/src/prompts/tools/write.md +0 -18
  282. package/src/task/batch.ts +0 -102
  283. package/src/task/discovery.ts +0 -126
  284. package/src/task/parallel.ts +0 -84
  285. package/src/task/template.ts +0 -32
  286. package/src/tools/calculator.ts +0 -537
  287. package/src/tools/jtd-to-typescript.ts +0 -198
  288. package/src/tools/renderers.ts +0 -60
  289. package/src/tools/tool-result.ts +0 -86
  290. /package/src/{modes/theme → theme}/dark.json +0 -0
  291. /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
  292. /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
  293. /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
  294. /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
  295. /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
  296. /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
  297. /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
  298. /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
  299. /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
  300. /package/src/{modes/theme → theme}/light.json +0 -0
  301. /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
  302. /package/src/{modes/theme → theme}/theme-schema.json +0 -0
  303. /package/src/{modes/theme → theme}/theme.ts +0 -0
@@ -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 "../modes/theme/theme";
5
- import { formatBadge, formatDuration, formatStatusIcon, replaceTabs, truncateToWidth } from "../tools/render-utils";
6
- import { Ellipsis, Hasher, type RenderCache, renderStatusLine } from "../tui";
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
- function renderToolLine(entry: ToolEntry, continuePrefix: string, theme: Theme): string {
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 ? ` ${theme.fg("dim", truncateToWidth(replaceTabs(entry.args), 50))}` : "";
50
- return `${continuePrefix}${icon} ${toolName}${args}`;
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
- // renderCall
58
+ // Unified subagent header
55
59
  // ═══════════════════════════════════════════════════════════════════════════
56
60
 
57
- export function renderCall(args: TaskParams, _options: RenderResultOptions, theme: Theme): Component {
58
- const lines: string[] = [];
59
- lines.push(renderStatusLine({ icon: "pending", title: "Task", description: args.id }, theme));
60
-
61
- const context = (args.context ?? "").trim();
62
- if (context) {
63
- const branch = theme.fg("dim", theme.tree.branch);
64
- const vertical = theme.fg("dim", theme.tree.vertical);
65
- const last = theme.fg("dim", theme.tree.last);
66
- lines.push(` ${branch} ${theme.fg("dim", "Context")}`);
67
- for (const line of context.split("\n")) {
68
- lines.push(` ${vertical} ${line ? theme.fg("muted", replaceTabs(line)) : ""}`);
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
- const assignmentPreview = truncateToWidth(replaceTabs(args.assignment.split("\n")[0] ?? ""), 60);
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
- // renderAgentProgress (streaming)
82
- // ═══════════════════════════════════════════════════════════════════════════
83
-
84
- /** Max tool history entries to show during streaming */
85
- const STREAMING_TOOL_LIMIT = 4;
86
-
87
- function renderAgentProgress(
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 lines: string[] = [];
95
- const prefix = isLast ? theme.fg("dim", theme.tree.last) : theme.fg("dim", theme.tree.branch);
96
- const continuePrefix = isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `;
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
- if (progress.durationMs > 0) {
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.push(statusLine);
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
- // Show recent completed (last N)
129
- const showCompleted = expanded ? completed : completed.slice(-STREAMING_TOOL_LIMIT);
130
- const skipped = completed.length - showCompleted.length;
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
- // renderAgentResult (final)
112
+ // Tool history rendering
148
113
  // ═══════════════════════════════════════════════════════════════════════════
149
114
 
150
- /** Max tool history entries when collapsed */
151
- const COLLAPSED_TOOL_LIMIT = 3;
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 renderAgentResult(result: SingleResult, isLast: boolean, expanded: boolean, theme: Theme): string[] {
135
+ function renderToolHistory(history: ToolEntry[], expanded: boolean, limit: number, theme: Theme): string[] {
154
136
  const lines: string[] = [];
155
- const prefix = isLast ? theme.fg("dim", theme.tree.last) : theme.fg("dim", theme.tree.branch);
156
- const continuePrefix = isLast ? " " : `${theme.fg("dim", theme.tree.vertical)} `;
137
+ const completed = history.filter(t => t.status !== "running");
138
+ const running = history.filter(t => t.status === "running");
157
139
 
158
- const aborted = result.aborted ?? false;
159
- const success = !aborted && result.exitCode === 0;
160
- const icon = aborted ? theme.status.aborted : success ? theme.status.success : theme.status.error;
161
- const iconColor: ThemeColor = success ? "success" : "error";
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
- lines.push(statusLine);
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
- // Error message for failed tasks
191
- if (result.error && !success) {
192
- lines.push(`${continuePrefix}${theme.fg("error", truncateToWidth(result.error, 70))}`);
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
- // renderResult (main entry point)
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
- const fallbackText = result.content.find(c => c.type === "text")?.text ?? "";
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, 100)), 0, 0);
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
- if (isPartial && details.progress) {
231
- details.progress.forEach((progress, i) => {
232
- const isLast = i === details.progress!.length - 1;
233
- lines.push(...renderAgentProgress(progress, isLast, expanded, theme, spinnerFrame));
234
- });
235
- } else if (details.results && details.results.length > 0) {
236
- details.results.forEach((res, i) => {
237
- const isLast = i === details.results.length - 1;
238
- lines.push(...renderAgentResult(res, isLast, expanded, theme));
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 text = fallbackText.trim() ? fallbackText : "No results";
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
- // Check for system notifications in fallback text
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.forEach((result, index) => {
309
- const isLast = index === details.results.length - 1;
310
- lines.push(...renderAgentResult(result, isLast, expanded, theme));
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
- };