Fix empty self-rendered tool rows

closes #5299
This commit is contained in:
Mario Zechner
2026-06-02 15:25:27 +02:00
parent 6e269edfd0
commit 0d38e17b68
3 changed files with 57 additions and 0 deletions

View File

@@ -13,6 +13,7 @@
- Fixed SDK embedding in bundled Node apps failing with `ENOENT` when `package.json` is not present next to the bundle entrypoint. The package metadata reader now gracefully handles missing `package.json` by using defaults, enabling `createAgentSession()` without requiring package-adjacent files at runtime ([#5226](https://github.com/earendil-works/pi/issues/5226)).
- Fixed HTTP timeout setting not being respected for non-Codex providers (e.g., llama.cpp via OpenAI-compatible API). The `httpIdleTimeoutMs` setting (set via `/settings` HTTP timeout) now applies as the default SDK request timeout for all providers that support it, not just OpenAI Codex Responses. Disabling the timeout (HTTP timeout = false) now correctly disables SDK timeouts for all supported providers by sending a maximum int32 value (effectively infinite) instead of 0, since SDKs treat timeout=0 as an immediate timeout ([#5294](https://github.com/earendil-works/pi/issues/5294)).
- Fixed opening and listing very large JSONL session files by reading session entries line-by-line instead of materializing the full file as one string ([#5231](https://github.com/earendil-works/pi/issues/5231)).
- Fixed `renderShell: "self"` tool renderers that emit no component lines leaving a blank chat row ([#5299](https://github.com/earendil-works/pi/issues/5299)).
## [0.78.0] - 2026-05-29

View File

@@ -222,6 +222,31 @@ export class ToolExecutionComponent extends Container {
if (this.hideComponent) {
return [];
}
if (this.hasRendererDefinition() && this.getRenderShell() === "self") {
const contentLines = this.selfRenderContainer.render(width);
if (contentLines.length === 0 && this.imageComponents.length === 0) {
return [];
}
const lines: string[] = [];
if (contentLines.length > 0) {
lines.push("");
lines.push(...contentLines);
}
for (let i = 0; i < this.imageComponents.length; i++) {
const spacer = this.imageSpacers[i];
if (spacer) {
lines.push(...spacer.render(width));
}
const imageComponent = this.imageComponents[i];
if (imageComponent) {
lines.push(...imageComponent.render(width));
}
}
return lines;
}
return super.render(width);
}

View File

@@ -67,6 +67,37 @@ describe("ToolExecutionComponent parity", () => {
expect(rendered).toContain("custom result");
});
test("self-rendered empty tool rows take no layout space", () => {
const toolDefinition: ToolDefinition = {
...createBaseToolDefinition(),
renderShell: "self",
renderCall: () => new Text("", 0, 0),
renderResult: () => new Text("", 0, 0),
};
const component = new ToolExecutionComponent(
"custom_tool",
"tool-empty-self-render",
{},
{},
toolDefinition,
createFakeTui(),
process.cwd(),
);
expect(component.render(120)).toEqual([]);
component.updateResult(
{
content: [],
details: {},
isError: false,
},
false,
);
expect(component.render(120)).toEqual([]);
});
test("uses built-in rendering for built-in overrides without custom renderers", () => {
const overrideDefinition: ToolDefinition = {
...createBaseToolDefinition("edit"),