fix(coding-agent): honor custom renderers for built-in tool overrides closes #2595

This commit is contained in:
Mario Zechner
2026-03-27 03:10:13 +01:00
parent bc8eb74b82
commit 1ba899f6a6
3 changed files with 46 additions and 11 deletions

View File

@@ -5,6 +5,7 @@
### Fixed
- Fixed auto-compaction overflow recovery for Ollama models when the backend returns explicit `prompt too long; exceeded max context length ...` errors instead of silently truncating input ([#2626](https://github.com/badlogic/pi-mono/issues/2626))
- Fixed built-in tool overrides that reuse built-in parameter schemas to still honor custom `renderCall` and `renderResult` renderers in the interactive TUI, restoring the `minimal-mode` example ([#2595](https://github.com/badlogic/pi-mono/issues/2595))
## [0.63.0] - 2026-03-27

View File

@@ -72,19 +72,11 @@ export class ToolExecutionComponent extends Container {
this.updateDisplay();
}
private isBuiltInDefinition(definition: ToolDefinition<any, any> | undefined): boolean {
return (
definition !== undefined &&
this.builtInToolDefinition !== undefined &&
definition.parameters === this.builtInToolDefinition.parameters
);
}
private getCallRenderer(): ToolDefinition<any, any>["renderCall"] | undefined {
if (!this.builtInToolDefinition) {
return this.toolDefinition?.renderCall;
}
if (!this.toolDefinition || this.isBuiltInDefinition(this.toolDefinition)) {
if (!this.toolDefinition) {
return this.builtInToolDefinition.renderCall;
}
return this.toolDefinition.renderCall ?? this.builtInToolDefinition.renderCall;
@@ -94,7 +86,7 @@ export class ToolExecutionComponent extends Container {
if (!this.builtInToolDefinition) {
return this.toolDefinition?.renderResult;
}
if (!this.toolDefinition || this.isBuiltInDefinition(this.toolDefinition)) {
if (!this.toolDefinition) {
return this.builtInToolDefinition.renderResult;
}
return this.toolDefinition.renderResult ?? this.builtInToolDefinition.renderResult;

View File

@@ -4,7 +4,7 @@ import stripAnsi from "strip-ansi";
import { beforeAll, describe, expect, test } from "vitest";
import type { ToolDefinition } from "../src/core/extensions/types.js";
import { type BashOperations, createBashToolDefinition } from "../src/core/tools/bash.js";
import { createReadToolDefinition } from "../src/core/tools/read.js";
import { createReadTool, createReadToolDefinition } from "../src/core/tools/read.js";
import { createWriteToolDefinition } from "../src/core/tools/write.js";
import { ToolExecutionComponent } from "../src/modes/interactive/components/tool-execution.js";
import { initTheme } from "../src/modes/interactive/theme/theme.js";
@@ -166,6 +166,48 @@ describe("ToolExecutionComponent parity", () => {
expect(rendered).toContain("override result");
});
test("uses custom renderers for built-in overrides that reuse built-in definition parameters", () => {
const builtInDefinition = createReadToolDefinition(process.cwd());
const component = new ToolExecutionComponent(
"read",
"tool-4d",
{ path: "README.md" },
{},
{
...builtInDefinition,
renderCall: () => new Text("override call", 0, 0),
renderResult: () => new Text("override result", 0, 0),
},
createFakeTui(),
);
component.updateResult({ content: [{ type: "text", text: "hello" }], details: undefined, isError: false }, false);
const rendered = stripAnsi(component.render(120).join("\n"));
expect(rendered).toContain("override call");
expect(rendered).toContain("override result");
expect(rendered).not.toContain("read README.md");
});
test("uses custom renderers for built-in overrides that reuse wrapped built-in tool parameters", () => {
const builtInTool = createReadTool(process.cwd());
const component = new ToolExecutionComponent(
"read",
"tool-4e",
{ path: "README.md" },
{},
{
...createBaseToolDefinition("read"),
parameters: builtInTool.parameters,
renderCall: () => new Text("wrapped override call", 0, 0),
renderResult: () => new Text("wrapped override result", 0, 0),
},
createFakeTui(),
);
component.updateResult({ content: [{ type: "text", text: "hello" }], details: undefined, isError: false }, false);
const rendered = stripAnsi(component.render(120).join("\n"));
expect(rendered).toContain("wrapped override call");
expect(rendered).toContain("wrapped override result");
});
test("shares renderer state across custom call and result slots", () => {
type RenderState = { token?: string };
const toolDefinition: ToolDefinition<any, unknown, RenderState> = {