From 49681e1b71c9c32cdfbf45c21f8e3cb3a8c8629d Mon Sep 17 00:00:00 2001 From: David Brailovsky Date: Fri, 25 Sep 2026 11:35:36 +0200 Subject: [PATCH] fix(coding-agent): hide line range for full-file read calls with null offset/limit Strict tool schemas make models send null for omitted optional fields. The read renderer only treated undefined as missing, so full-file reads rendered as "path:1". fixes #9996 --- packages/coding-agent/CHANGELOG.md | 1 + .../src/core/tools/renderers/read.ts | 5 +++-- .../test/tool-execution-component.test.ts | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 72c44137e..4ab640f42 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -16,6 +16,7 @@ - Prevented managed git packages from automatically installing Pi peer dependencies and added warnings for extension packages that list host-provided modules in `dependencies` ([#9863](https://github.com/earendil-works/pi/issues/9863)). - Fixed pinned git extensions loaded with `-e` continuing to use the first downloaded commit after the ref changes ([#9982](https://github.com/earendil-works/pi/issues/9982)). - Fixed `RpcClient` skipping the next event listener when a listener unsubscribes while handling an event, which could make `waitForIdle()` time out after `collectEvents()` ([#9990](https://github.com/earendil-works/pi/issues/9990)). +- Fixed full-file `read` calls rendering as `:1` when models send `null` for omitted `offset` and `limit` ([#9996](https://github.com/earendil-works/pi/issues/9996)). ## [0.87.1] - 2026-09-22 diff --git a/packages/coding-agent/src/core/tools/renderers/read.ts b/packages/coding-agent/src/core/tools/renderers/read.ts index 2f5af2103..ebf71f4a7 100644 --- a/packages/coding-agent/src/core/tools/renderers/read.ts +++ b/packages/coding-agent/src/core/tools/renderers/read.ts @@ -26,9 +26,10 @@ interface CompactReadClassification { const COMPACT_RESOURCE_FILE_NAMES = new Set(["AGENTS.override.md", "AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD"]); type ReadRenderArgs = { path?: string; file_path?: string; offset?: number; limit?: number }; function formatReadLineRange(args: ReadRenderArgs | undefined, theme: Theme): string { - if (args?.offset === undefined && args?.limit === undefined) return ""; + // Strict tool schemas make models send null for omitted optional fields. + if (args?.offset == null && args?.limit == null) return ""; const startLine = args.offset ?? 1; - const endLine = args.limit !== undefined ? startLine + args.limit - 1 : ""; + const endLine = args.limit != null ? startLine + args.limit - 1 : ""; return theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`); } function formatReadCall(args: ReadRenderArgs | undefined, theme: Theme, cwd: string): string { diff --git a/packages/coding-agent/test/tool-execution-component.test.ts b/packages/coding-agent/test/tool-execution-component.test.ts index a5fa09dda..089cff409 100644 --- a/packages/coding-agent/test/tool-execution-component.test.ts +++ b/packages/coding-agent/test/tool-execution-component.test.ts @@ -294,6 +294,22 @@ describe("ToolExecutionComponent parity", () => { expect(rendered.match(/\bread\b/g)?.length ?? 0).toBe(1); }); + // Issue #9996: strict tool schemas make models send null for omitted optional fields. + test("renders read calls with null offset and limit as full-file reads", () => { + const component = new ToolExecutionComponent( + "read", + "tool-read-null-range", + { path: "src/example.ts", offset: null, limit: null }, + {}, + createReadToolDefinition(process.cwd()), + createFakeTui(), + process.cwd(), + ); + const rendered = stripAnsi(component.render(120).join("\n")); + expect(rendered).toContain("read src/example.ts"); + expect(rendered).not.toContain("src/example.ts:"); + }); + test("inherits missing built-in result renderer slot from the built-in tool", () => { const overrideDefinition: ToolDefinition = { ...createBaseToolDefinition("read"),