diff --git a/packages/core/src/tests/tool-handlers.test.ts b/packages/core/src/tests/tool-handlers.test.ts index 735c0274..a28e81a6 100644 --- a/packages/core/src/tests/tool-handlers.test.ts +++ b/packages/core/src/tests/tool-handlers.test.ts @@ -998,6 +998,22 @@ test("Read returns an acknowledgement for images and attaches the image as a fol ); }); +test("Read rejects fractional PDF page ranges", async () => { + const workspace = createTempWorkspace(); + const filePath = path.join(workspace, "sample.pdf"); + fs.writeFileSync(filePath, makePdfWithPages(4), "utf8"); + + for (const pages of ["1.9", "2.1-3.9"]) { + const readResult = await handleReadTool( + { file_path: filePath, pages }, + createContext("pdf-fractional-pages", workspace) + ); + + assert.equal(readResult.ok, false); + assert.match(readResult.error ?? "", /must be an integer/); + } +}); + function createContext( sessionId: string, projectRoot: string, @@ -1024,6 +1040,11 @@ function createTempWorkspace(): string { return dir; } +function makePdfWithPages(pageCount: number): string { + const pages = Array.from({ length: pageCount }, (_, index) => `${index + 1} 0 obj << /Type /Page >> endobj`); + return ["%PDF-1.4", ...pages, "%%EOF", ""].join("\n"); +} + async function readSnippet( filePath: string, sessionId: string, diff --git a/packages/core/src/tools/read-handler.ts b/packages/core/src/tools/read-handler.ts index 3771a7e0..1e2ba939 100644 --- a/packages/core/src/tools/read-handler.ts +++ b/packages/core/src/tools/read-handler.ts @@ -552,11 +552,13 @@ function parsePositiveInt(value: string, label: string): number { if (!Number.isFinite(numeric)) { throw new Error(`${label} must be a number.`); } - const integer = Math.trunc(numeric); - if (integer < 1) { + if (!Number.isInteger(numeric)) { + throw new Error(`${label} must be an integer.`); + } + if (numeric < 1) { throw new Error(`${label} must be >= 1.`); } - return integer; + return numeric; } function readNotebook(filePath: string): string {