merge: PR #1719 for local testing

This commit is contained in:
Mario Zechner 2026-03-02 22:54:58 +01:00
commit 7b7b967aef
5 changed files with 114 additions and 6 deletions

View file

@ -49,6 +49,9 @@ export function createToolHtmlRenderer(deps: ToolHtmlRendererDeps): ToolHtmlRend
}
const component = toolDef.renderCall(args, theme);
if (!component) {
return undefined;
}
const lines = component.render(width);
return ansiLinesToHtml(lines);
} catch {
@ -79,6 +82,9 @@ export function createToolHtmlRenderer(deps: ToolHtmlRendererDeps): ToolHtmlRend
// Always render expanded, client-side will apply truncation
const component = toolDef.renderResult(agentToolResult, { expanded: true, isPartial: false }, theme);
if (!component) {
return undefined;
}
const lines = component.render(width);
return ansiLinesToHtml(lines);
} catch {

View file

@ -356,10 +356,14 @@ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = un
): Promise<AgentToolResult<TDetails>>;
/** Custom rendering for tool call display */
renderCall?: (args: Static<TParams>, theme: Theme) => Component;
renderCall?: (args: Static<TParams>, theme: Theme) => Component | undefined;
/** Custom rendering for tool result display */
renderResult?: (result: AgentToolResult<TDetails>, options: ToolRenderResultOptions, theme: Theme) => Component;
renderResult?: (
result: AgentToolResult<TDetails>,
options: ToolRenderResultOptions,
theme: Theme,
) => Component | undefined;
}
// ============================================================================

View file

@ -95,6 +95,8 @@ export class ToolExecutionComponent extends Container {
private convertedImages: Map<number, { data: string; mimeType: string }> = new Map();
// Incremental syntax highlighting cache for write tool call args
private writeHighlightCache?: WriteHighlightCache;
// When true, this component intentionally renders no lines
private hideComponent = false;
constructor(
toolName: string,
@ -354,6 +356,13 @@ export class ToolExecutionComponent extends Container {
this.updateDisplay();
}
override render(width: number): string[] {
if (this.hideComponent) {
return [];
}
return super.render(width);
}
private updateDisplay(): void {
// Set background based on state
const bgFn = this.isPartial
@ -362,8 +371,12 @@ export class ToolExecutionComponent extends Container {
? (text: string) => theme.bg("toolErrorBg", text)
: (text: string) => theme.bg("toolSuccessBg", text);
const useBuiltInRenderer = this.shouldUseBuiltInRenderer();
let customRendererHasContent = false;
this.hideComponent = false;
// Use built-in rendering for built-in tools (or overrides without custom renderers)
if (this.shouldUseBuiltInRenderer()) {
if (useBuiltInRenderer) {
if (this.toolName === "bash") {
// Bash uses Box with visual line truncation
this.contentBox.setBgFn(bgFn);
@ -383,16 +396,19 @@ export class ToolExecutionComponent extends Container {
if (this.toolDefinition.renderCall) {
try {
const callComponent = this.toolDefinition.renderCall(this.args, theme);
if (callComponent) {
if (callComponent !== undefined) {
this.contentBox.addChild(callComponent);
customRendererHasContent = true;
}
} catch {
// Fall back to default on error
this.contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0));
customRendererHasContent = true;
}
} else {
// No custom renderCall, show tool name
this.contentBox.addChild(new Text(theme.fg("toolTitle", theme.bold(this.toolName)), 0, 0));
customRendererHasContent = true;
}
// Render result component if we have a result
@ -403,14 +419,16 @@ export class ToolExecutionComponent extends Container {
{ expanded: this.expanded, isPartial: this.isPartial },
theme,
);
if (resultComponent) {
if (resultComponent !== undefined) {
this.contentBox.addChild(resultComponent);
customRendererHasContent = true;
}
} catch {
// Fall back to showing raw output on error
const output = this.getTextOutput();
if (output) {
this.contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
customRendererHasContent = true;
}
}
} else if (this.result) {
@ -418,8 +436,13 @@ export class ToolExecutionComponent extends Container {
const output = this.getTextOutput();
if (output) {
this.contentBox.addChild(new Text(theme.fg("toolOutput", output), 0, 0));
customRendererHasContent = true;
}
}
} else {
// Unknown tool with no registered definition - show generic fallback
this.contentText.setCustomBgFn(bgFn);
this.contentText.setText(this.formatToolExecution());
}
// Handle images (same for both custom and built-in)
@ -463,6 +486,10 @@ export class ToolExecutionComponent extends Container {
}
}
}
if (!useBuiltInRenderer && this.toolDefinition) {
this.hideComponent = !customRendererHasContent && this.imageComponents.length === 0;
}
}
/**

View file

@ -2109,7 +2109,6 @@ export class InteractiveMode {
for (const content of this.streamingMessage.content) {
if (content.type === "toolCall") {
if (!this.pendingTools.has(content.id)) {
this.chatContainer.addChild(new Text("", 0, 0));
const component = new ToolExecutionComponent(
content.name,
content.arguments,