diff --git a/packages/agent/src/types.ts b/packages/agent/src/types.ts index 98b5c9917..6e7c07103 100644 --- a/packages/agent/src/types.ts +++ b/packages/agent/src/types.ts @@ -237,7 +237,7 @@ export interface AgentTool; /** Optional custom rendering for tool call display (returns UI component) */ - renderCall?: (args: Static, theme: TTheme) => unknown; + renderCall?: (args: Static, options: RenderResultOptions, theme: TTheme) => unknown; /** Optional custom rendering for tool result display (returns UI component) */ renderResult?: ( diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 9915b8fd1..f0eedaaac 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Changed + +- Unified `renderCall` signatures to `(args, options, theme)` across all tool renderers and extension types + ## [12.16.0] - 2026-02-21 ### Added diff --git a/packages/coding-agent/src/extensibility/custom-tools/types.ts b/packages/coding-agent/src/extensibility/custom-tools/types.ts index 7cfbc93de..5be0f3c9a 100644 --- a/packages/coding-agent/src/extensibility/custom-tools/types.ts +++ b/packages/coding-agent/src/extensibility/custom-tools/types.ts @@ -182,7 +182,7 @@ export interface CustomTool { /** Called on session lifecycle events - use to reconstruct state or cleanup resources */ onSession?: (event: CustomToolSessionEvent, ctx: CustomToolContext) => void | Promise; /** Custom rendering for tool call display - return a Component */ - renderCall?: (args: Static, theme: Theme) => Component; + renderCall?: (args: Static, options: RenderResultOptions, theme: Theme) => Component; /** Custom rendering for tool result display - return a Component */ renderResult?: ( diff --git a/packages/coding-agent/src/extensibility/extensions/types.ts b/packages/coding-agent/src/extensibility/extensions/types.ts index f7c84fa1f..5976ca3ce 100644 --- a/packages/coding-agent/src/extensibility/extensions/types.ts +++ b/packages/coding-agent/src/extensibility/extensions/types.ts @@ -304,7 +304,7 @@ export interface ToolDefinition void | Promise; /** Custom rendering for tool call display */ - renderCall?: (args: Static, theme: Theme) => Component; + renderCall?: (args: Static, options: ToolRenderResultOptions, theme: Theme) => Component; /** Custom rendering for tool result display */ renderResult?: ( diff --git a/packages/coding-agent/src/extensibility/extensions/wrapper.ts b/packages/coding-agent/src/extensibility/extensions/wrapper.ts index 88fdd6c56..6d404453d 100644 --- a/packages/coding-agent/src/extensibility/extensions/wrapper.ts +++ b/packages/coding-agent/src/extensibility/extensions/wrapper.ts @@ -18,7 +18,7 @@ export class RegisteredToolAdapter implements AgentTool { declare parameters: any; declare label: string; - renderCall?: (args: any, theme: any) => any; + renderCall?: (args: any, options: any, theme: any) => any; renderResult?: (result: any, options: any, theme: any, args?: any) => any; constructor( @@ -32,7 +32,8 @@ export class RegisteredToolAdapter implements AgentTool { // enters the custom-renderer path, gets undefined back, and silently // discards tool result text (extensions without renderers show blank). if (registeredTool.definition.renderCall) { - this.renderCall = (args: any, theme: any) => registeredTool.definition.renderCall!(args, theme as Theme); + this.renderCall = (args: any, options: any, theme: any) => + registeredTool.definition.renderCall!(args, options, theme as Theme); } if (registeredTool.definition.renderResult) { this.renderResult = (result: any, options: any, theme: any, args?: any) => diff --git a/packages/coding-agent/src/lsp/render.ts b/packages/coding-agent/src/lsp/render.ts index c45c1b788..c0c399f21 100644 --- a/packages/coding-agent/src/lsp/render.ts +++ b/packages/coding-agent/src/lsp/render.ts @@ -31,7 +31,7 @@ import type { LspParams, LspToolDetails } from "./types"; * Render the LSP tool call in the TUI. * Shows: "lsp " */ -export function renderCall(args: LspParams, theme: Theme): Text { +export function renderCall(args: LspParams, _options: RenderResultOptions, theme: Theme): Text { const actionLabel = (args.action ?? "request").replace(/_/g, " "); const queryPreview = args.query ? truncateToWidth(args.query, TRUNCATE_LENGTHS.SHORT) : undefined; diff --git a/packages/coding-agent/src/mcp/tool-bridge.ts b/packages/coding-agent/src/mcp/tool-bridge.ts index b8e8da6f5..b3782d158 100644 --- a/packages/coding-agent/src/mcp/tool-bridge.ts +++ b/packages/coding-agent/src/mcp/tool-bridge.ts @@ -198,7 +198,7 @@ export class MCPTool implements CustomTool { this.mcpServerName = connection.name; } - renderCall(args: unknown, theme: Theme) { + renderCall(args: unknown, _options: RenderResultOptions, theme: Theme) { return renderMCPCall((args ?? {}) as Record, theme, this.label); } @@ -304,7 +304,7 @@ export class DeferredMCPTool implements CustomTool { this.#fallbackProviderName = source?.providerName; } - renderCall(args: unknown, theme: Theme) { + renderCall(args: unknown, _options: RenderResultOptions, theme: Theme) { return renderMCPCall((args ?? {}) as Record, theme, this.label); } diff --git a/packages/coding-agent/src/modes/components/tool-execution.ts b/packages/coding-agent/src/modes/components/tool-execution.ts index 3fa9b6c30..bd151770c 100644 --- a/packages/coding-agent/src/modes/components/tool-execution.ts +++ b/packages/coding-agent/src/modes/components/tool-execution.ts @@ -391,7 +391,7 @@ export class ToolExecutionComponent extends Container { const shouldRenderCall = !this.#result || !mergeCallAndResult; if (shouldRenderCall && tool.renderCall) { try { - const callComponent = tool.renderCall(this.#getCallArgsForRender(), theme); + const callComponent = tool.renderCall(this.#getCallArgsForRender(), this.#renderState, theme); if (callComponent) { this.#contentBox.addChild(ensureInvalidate(callComponent)); } @@ -453,7 +453,7 @@ export class ToolExecutionComponent extends Container { if (shouldRenderCall) { // Render call component try { - const callComponent = renderer.renderCall(this.#getCallArgsForRender(), theme, this.#renderState); + const callComponent = renderer.renderCall(this.#getCallArgsForRender(), this.#renderState, theme); if (callComponent) { this.#contentBox.addChild(ensureInvalidate(callComponent)); } diff --git a/packages/coding-agent/src/patch/shared.ts b/packages/coding-agent/src/patch/shared.ts index 4f0579b1f..d64bd4faa 100644 --- a/packages/coding-agent/src/patch/shared.ts +++ b/packages/coding-agent/src/patch/shared.ts @@ -19,7 +19,6 @@ import { ToolUIKit, truncateDiffByHunk, } from "../tools/render-utils"; -import type { RenderCallOptions } from "../tools/renderers"; import { Ellipsis, Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui"; import type { DiffError, DiffResult, Operation } from "./types"; @@ -254,7 +253,7 @@ function renderDiffSection( export const editToolRenderer = { mergeCallAndResult: true, - renderCall(args: EditRenderArgs, uiTheme: Theme, options?: RenderCallOptions): Component { + renderCall(args: EditRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component { const ui = new ToolUIKit(uiTheme); const rawPath = args.file_path || args.path || ""; const filePath = shortenPath(rawPath); diff --git a/packages/coding-agent/src/task/render.ts b/packages/coding-agent/src/task/render.ts index 75d64532b..fb1d59377 100644 --- a/packages/coding-agent/src/task/render.ts +++ b/packages/coding-agent/src/task/render.ts @@ -445,7 +445,7 @@ function formatOutputInline(data: unknown, theme: Theme, maxWidth = 80): string /** * Render the tool call arguments. */ -export function renderCall(args: TaskParams, theme: Theme): Component { +export function renderCall(args: TaskParams, _options: RenderResultOptions, theme: Theme): Component { const lines: string[] = []; lines.push(renderStatusLine({ icon: "pending", title: "Task", description: args.agent }, theme)); diff --git a/packages/coding-agent/src/tools/ask.ts b/packages/coding-agent/src/tools/ask.ts index 850784e81..99bbca84e 100644 --- a/packages/coding-agent/src/tools/ask.ts +++ b/packages/coding-agent/src/tools/ask.ts @@ -380,7 +380,7 @@ interface AskRenderArgs { } export const askToolRenderer = { - renderCall(args: AskRenderArgs, uiTheme: Theme): Component { + renderCall(args: AskRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const ui = new ToolUIKit(uiTheme); const label = ui.title("Ask"); diff --git a/packages/coding-agent/src/tools/bash.ts b/packages/coding-agent/src/tools/bash.ts index 646a975c6..357bc9fe5 100644 --- a/packages/coding-agent/src/tools/bash.ts +++ b/packages/coding-agent/src/tools/bash.ts @@ -237,7 +237,7 @@ function formatBashCommand(args: BashRenderArgs, _uiTheme: Theme): string { export const BASH_PREVIEW_LINES = 10; export const bashToolRenderer = { - renderCall(args: BashRenderArgs, uiTheme: Theme): Component { + renderCall(args: BashRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const cmdText = formatBashCommand(args, uiTheme); const text = renderStatusLine({ icon: "pending", title: "Bash", description: cmdText }, uiTheme); return new Text(text, 0, 0); diff --git a/packages/coding-agent/src/tools/calculator.ts b/packages/coding-agent/src/tools/calculator.ts index a4b0b0a05..f255ffab3 100644 --- a/packages/coding-agent/src/tools/calculator.ts +++ b/packages/coding-agent/src/tools/calculator.ts @@ -444,7 +444,7 @@ export const calculatorToolRenderer = { * Render the tool call header showing the first expression and count. * Format: "Calc (N calcs)" */ - renderCall(args: CalculatorRenderArgs, uiTheme: Theme): Component { + renderCall(args: CalculatorRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const count = args.calculations?.length ?? 0; const firstExpression = args.calculations?.[0]?.expression; const description = firstExpression ? truncateToWidth(firstExpression, TRUNCATE_LENGTHS.TITLE) : undefined; diff --git a/packages/coding-agent/src/tools/fetch.ts b/packages/coding-agent/src/tools/fetch.ts index d2588fbf4..76654b1f0 100644 --- a/packages/coding-agent/src/tools/fetch.ts +++ b/packages/coding-agent/src/tools/fetch.ts @@ -967,6 +967,7 @@ function countNonEmptyLines(text: string): number { /** Render fetch call (URL preview) */ export function renderFetchCall( args: { url?: string; timeout?: number; raw?: boolean }, + _options: RenderResultOptions, uiTheme: Theme = theme, ): Component { const url = args.url ?? ""; diff --git a/packages/coding-agent/src/tools/find.ts b/packages/coding-agent/src/tools/find.ts index 874158ee6..9ab018399 100644 --- a/packages/coding-agent/src/tools/find.ts +++ b/packages/coding-agent/src/tools/find.ts @@ -407,7 +407,7 @@ const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS; export const findToolRenderer = { inline: true, - renderCall(args: FindRenderArgs, uiTheme: Theme): Component { + renderCall(args: FindRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const meta: string[] = []; if (args.limit !== undefined) meta.push(`limit:${args.limit}`); diff --git a/packages/coding-agent/src/tools/grep.ts b/packages/coding-agent/src/tools/grep.ts index 938170671..36c81555a 100644 --- a/packages/coding-agent/src/tools/grep.ts +++ b/packages/coding-agent/src/tools/grep.ts @@ -309,7 +309,7 @@ const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2; export const grepToolRenderer = { inline: true, - renderCall(args: GrepRenderArgs, uiTheme: Theme): Component { + renderCall(args: GrepRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const meta: string[] = []; if (args.path) meta.push(`in ${args.path}`); if (args.glob) meta.push(`glob:${args.glob}`); diff --git a/packages/coding-agent/src/tools/notebook.ts b/packages/coding-agent/src/tools/notebook.ts index aa667618e..3797ef3a0 100644 --- a/packages/coding-agent/src/tools/notebook.ts +++ b/packages/coding-agent/src/tools/notebook.ts @@ -203,7 +203,7 @@ interface NotebookRenderArgs { const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2; export const notebookToolRenderer = { - renderCall(args: NotebookRenderArgs, uiTheme: Theme): Component { + renderCall(args: NotebookRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const meta: string[] = []; const notebookPath = args.notebookPath ?? args.notebook_path; const cellNumber = args.cellNumber ?? args.cell_index; diff --git a/packages/coding-agent/src/tools/python.ts b/packages/coding-agent/src/tools/python.ts index 9a11bae99..bc2fbb186 100644 --- a/packages/coding-agent/src/tools/python.ts +++ b/packages/coding-agent/src/tools/python.ts @@ -834,7 +834,7 @@ function formatCellOutputLines( } export const pythonToolRenderer = { - renderCall(args: PythonRenderArgs, uiTheme: Theme): Component { + renderCall(args: PythonRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const ui = new ToolUIKit(uiTheme); const cells = args.cells ?? []; const cwd = getProjectDir(); diff --git a/packages/coding-agent/src/tools/read.ts b/packages/coding-agent/src/tools/read.ts index 6ca990035..f8c4fd664 100644 --- a/packages/coding-agent/src/tools/read.ts +++ b/packages/coding-agent/src/tools/read.ts @@ -1077,7 +1077,7 @@ interface ReadRenderArgs { } export const readToolRenderer = { - renderCall(args: ReadRenderArgs, uiTheme: Theme): Component { + renderCall(args: ReadRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component { const rawPath = args.file_path || args.path || ""; const filePath = shortenPath(rawPath); const offset = args.offset; diff --git a/packages/coding-agent/src/tools/renderers.ts b/packages/coding-agent/src/tools/renderers.ts index 0040d7b92..9e68a1a9a 100644 --- a/packages/coding-agent/src/tools/renderers.ts +++ b/packages/coding-agent/src/tools/renderers.ts @@ -28,7 +28,7 @@ export interface RenderCallOptions { } type ToolRenderer = { - renderCall: (args: unknown, theme: Theme, options?: RenderCallOptions) => Component; + renderCall: (args: unknown, options: RenderResultOptions, theme: Theme) => Component; renderResult: ( result: { content: Array<{ type: string; text?: string }>; details?: unknown; isError?: boolean }, options: RenderResultOptions & { renderContext?: Record }, diff --git a/packages/coding-agent/src/tools/review.ts b/packages/coding-agent/src/tools/review.ts index 3c91f5f2c..be65e5682 100644 --- a/packages/coding-agent/src/tools/review.ts +++ b/packages/coding-agent/src/tools/review.ts @@ -104,7 +104,7 @@ export const reportFindingTool: AgentTool 0 ? [`${count} items`] : ["empty"]; const text = renderStatusLine({ icon: "pending", title: "Todo Write", meta }, uiTheme); diff --git a/packages/coding-agent/src/tools/write.ts b/packages/coding-agent/src/tools/write.ts index 6822fc803..d1f51d0e7 100644 --- a/packages/coding-agent/src/tools/write.ts +++ b/packages/coding-agent/src/tools/write.ts @@ -28,7 +28,6 @@ import { shortenPath, ToolUIKit, } from "./render-utils"; -import type { RenderCallOptions } from "./renderers"; const writeSchema = Type.Object({ path: Type.String({ description: "Path to the file to write (relative or absolute)" }), @@ -189,7 +188,7 @@ function renderContentPreview(content: string, expanded: boolean, uiTheme: Theme } export const writeToolRenderer = { - renderCall(args: WriteRenderArgs, uiTheme: Theme, options?: RenderCallOptions): Component { + renderCall(args: WriteRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component { const ui = new ToolUIKit(uiTheme); const rawPath = args.file_path || args.path || ""; const filePath = shortenPath(rawPath); diff --git a/packages/coding-agent/src/web/search/index.ts b/packages/coding-agent/src/web/search/index.ts index 6a36a92bb..703297a11 100644 --- a/packages/coding-agent/src/web/search/index.ts +++ b/packages/coding-agent/src/web/search/index.ts @@ -285,8 +285,8 @@ export const webSearchCustomTool: CustomTool, "Deep Search", theme); }, @@ -436,7 +436,7 @@ Parameters: return executeExaTool("get_code_context_exa", params as Record, "web_search_code_context"); }, - renderCall(args, theme) { + renderCall(args, _options, theme) { return renderExaCall(args as Record, "Code Search", theme); }, @@ -464,7 +464,7 @@ Parameters: return executeExaTool("crawling", params as Record, "web_search_crawl"); }, - renderCall(args, theme) { + renderCall(args, _options, theme) { const url = (args as { url: string }).url; return renderExaCall({ query: url }, "Crawl URL", theme); }, @@ -495,7 +495,7 @@ Parameters: return executeExaTool("linkedin_search", params as Record, "web_search_linkedin"); }, - renderCall(args, theme) { + renderCall(args, _options, theme) { return renderExaCall(args as Record, "LinkedIn Search", theme); }, @@ -525,7 +525,7 @@ Parameters: return executeExaTool("company_research", params as Record, "web_search_company"); }, - renderCall(args, theme) { + renderCall(args, _options, theme) { const name = (args as { company_name: string }).company_name; return renderExaCall({ query: name }, "Company Research", theme); }, diff --git a/packages/coding-agent/src/web/search/render.ts b/packages/coding-agent/src/web/search/render.ts index f58a5ceab..b30c69fb5 100644 --- a/packages/coding-agent/src/web/search/render.ts +++ b/packages/coding-agent/src/web/search/render.ts @@ -283,6 +283,7 @@ export function renderSearchResult( /** Render web search call (query preview) */ export function renderSearchCall( args: { query?: string; provider?: string; [key: string]: unknown }, + _options: RenderResultOptions, theme: Theme, ): Component { const provider = args.provider ?? "auto";