Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/acp-bridge/src/bridgeClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,26 @@ describe('BridgeClient — BridgeFileSystem injection seam (F1 step 5)', () => {

expect(response.content).toBe('on-disk-content');
});

it('readTextFile rejects fractional positive limits before slicing inline content', async () => {
const client = makeClient(/* no fileSystem */);
const target = path.join(tmpDir, 'lines.txt');
await fsp.writeFile(target, 'a\nb\nc\n', 'utf8');

const err = await client
.readTextFile({
path: target,
sessionId: 'sess:test',
line: 1,
limit: 1.5,
})
.catch((e: unknown) => e);

expect(err).toBeInstanceOf(RequestError);
expect((err as Error).message).toContain(
'`limit` must be a positive integer.',
);
});
});
});

Expand Down
10 changes: 10 additions & 0 deletions packages/acp-bridge/src/bridgeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1879,6 +1879,16 @@ export class BridgeClient implements Client {
if (typeof params.limit === 'number' && params.limit <= 0) {
return { content: '' };
}
if (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, addressed in the latest push. I added the symmetric positive-integer guard for line in the inline fallback, so fractional line values now fail before reaching sliceLineRange(), matching the limit validation shape.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The guard condition params.limit > 0 && !Number.isSafeInteger(params.limit) has a NaN-shaped hole. NaN > 0 evaluates to false, so NaN falls through both the limit <= 0 early-return (since NaN <= 0 is also false) and this new guard, reaching sliceLineRange with NaN parameters. The sister validation in workspace-file-system.ts:456 uses !Number.isSafeInteger(opts.limit) || opts.limit < 1, which catches NaN because !Number.isSafeInteger(NaN) is true. Adopting the same pattern here would close the gap and align the two paths:

Suggested change
if (
if (typeof params.limit === 'number' && !Number.isSafeInteger(params.limit)) {
if (params.limit < 1) {
return { content: '' };
}
throw RequestError.invalidParams(
undefined,
`\`limit\` must be a positive integer, got ${params.limit}`,
);
}

— qwen3.7-max via Qwen Code /review

typeof params.limit === 'number' &&
params.limit > 0 &&
!Number.isSafeInteger(params.limit)
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The new inline guard correctly throws RequestError.invalidParams() (JSON-RPC -32602, Invalid Params). However, the adapter path (production qwen serve) wraps the same WorkspaceFileSystem parse_error as RequestError(-32603) (Internal Error) via preserveFsErrorOverAcp(). The same client error produces different JSON-RPC codes depending on the path: -32602 inline vs -32603 in production.

Consider having preserveFsErrorOverAcp map parse_error kind to -32602 (Invalid Params), or adding a fractional-limit guard in the adapter before wfs.readText so the error is thrown before the -32603 wrapping.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, addressed. I updated preserveFsErrorOverAcp() so FsError('parse_error') maps to JSON-RPC Invalid Params (-32602) while preserving the existing data.errorKind, data.status, and data.hint fields. Other filesystem error kinds still use the existing internal-error mapping.

throw RequestError.invalidParams(
undefined,
'`limit` must be a positive integer.',

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done. The inline fallback now includes the received value in both invalid-params messages: line and limit report the malformed value that was rejected.

);
}
// BSA0E: cap the file size we'll buffer into RSS at 100 MiB so a
// request like `{ line: 1, limit: 10 }` against a 500 MB log
// doesn't cost the daemon 500 MB of memory just to return 10
Expand Down
22 changes: 22 additions & 0 deletions packages/cli/src/serve/bridge-file-system-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,28 @@ describe('createBridgeFileSystemAdapter', () => {
});
expect(response.content).toBe('x\ny\n');
});

it('propagates parse_error for fractional positive limits', async () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, renamed. The adapter test now explicitly says it surfaces the existing WorkspaceFileSystem.readText() parse_error behavior, so it should no longer look like coverage for the new inline fallback guard.

const target = path.join(tmpDir, 'fractional-limit.txt');
await fsp.writeFile(target, 'x\ny\nz\n', 'utf8');
const adapter = createBridgeFileSystemAdapter(
buildFactory({ trusted: true }),
);

const err = await adapter
.readText({
path: target,
sessionId: 'sess:test',
line: 1,
limit: 1.5,
})
.catch((e: unknown) => e);

expect((err as { kind?: string }).kind).toBe('parse_error');
expect((err as Error).message).toContain(
'limit must be a positive integer',
);
});
});

describe('boundary enforcement', () => {
Expand Down
Loading