diff --git a/packages/core/src/tool/plugin/read.ts b/packages/core/src/tool/plugin/read.ts index b18ce6b5fad0..44da6ed002b9 100644 --- a/packages/core/src/tool/plugin/read.ts +++ b/packages/core/src/tool/plugin/read.ts @@ -19,6 +19,8 @@ const LocationInput = Schema.Struct({ path: Schema.String.annotate({ description: "File or directory to read" }), offset: ReadToolFileSystem.PageInput.fields.offset.annotate({ description: "The line or directory entry to start reading from (1-based)", + message: + "The offset must be a non-negative integer. Line and entry numbers are 1-based, so prefer omitting the offset or using 1.", }), limit: ReadToolFileSystem.PageInput.fields.limit.annotate({ description: "The maximum number of lines or directory entries to read (defaults to 2000)", diff --git a/packages/core/test/tool-read.test.ts b/packages/core/test/tool-read.test.ts index 776235c610b7..e0353941af78 100644 --- a/packages/core/test/tool-read.test.ts +++ b/packages/core/test/tool-read.test.ts @@ -213,7 +213,25 @@ describe("ReadTool", () => { page: { offset: undefined, limit: undefined }, }, ]) - expect(listCalls).toEqual([]) + }), + ) + + it.effect("surfaces 1-based guidance when the model hallucinates a negative offset", () => + Effect.gen(function* () { + const registry = yield* Tool.Service + const execution = yield* executeTool(registry, { + sessionID, + ...toolIdentity, + call: { + type: "tool-call", + id: "call-read-negative-offset", + name: "read", + input: { path: "README.md", offset: -1 }, + }, + }) + expect(execution.status).toBe("error") + if (execution.status !== "error") return + expect(JSON.stringify(execution.error)).toContain("1-based") }), )