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
This commit is contained in:
David Brailovsky committed 2026-09-25 11:36:43 +02:00
1 parent 92e8d4f02a
commit 49681e1b71
3 files changed
+20 -2

No files matched your search

+1
View File
@@ -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
@@ -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 {
@@ -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"),