fix(cursor): read pi_ls's entry cap from the shape read actually sets
The bridge looked for a flat `details.resultLimitReached`. `glob` sets that alongside the structured meta, so `pi_find` worked; `read` - the tool serving `pi_ls` - records the cap only through `OutputMeta`, at `details.meta.limits.resultLimit.reached`. Every capped listing therefore reached Cursor with `entry_limit_reached` unset, which reads as a complete listing. Both shapes are now checked, mirroring how `piTruncation` already handles its two producers. Covered by running the real `ReadTool` against a directory that trips the per-directory cap and asserting the wire field, so a move in the producer's shape fails here instead of silently sending clipped output as whole. (cherry picked from commit cbfe7ca9d59faa2f967e2724744b58a96a9a1839)
This commit is contained in:
committed by
can1357
parent
0601ee7324
commit
6d42f6e52e
@@ -5,7 +5,7 @@ import * as path from "node:path";
|
||||
import { create, fromBinary } from "@bufbuild/protobuf";
|
||||
import type { AgentEvent, AgentTool, AgentToolContext } from "@oh-my-pi/pi-agent-core";
|
||||
import { type BlockState, handleServerMessage, type ToolCallState } from "@oh-my-pi/pi-ai/providers/cursor";
|
||||
import { piTruncation } from "@oh-my-pi/pi-ai/providers/cursor/exec-modern";
|
||||
import { buildPiLsResult, piTruncation } from "@oh-my-pi/pi-ai/providers/cursor/exec-modern";
|
||||
import type { AssistantMessage } from "@oh-my-pi/pi-ai/types";
|
||||
import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
|
||||
import {
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { EditTool } from "@oh-my-pi/pi-coding-agent/edit";
|
||||
import type { ExtensionRunner } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
|
||||
import { ExtensionToolWrapper } from "@oh-my-pi/pi-coding-agent/extensibility/extensions";
|
||||
import { GrepTool, type Tool, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { GrepTool, ReadTool, type Tool, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
|
||||
import { BashTool } from "@oh-my-pi/pi-coding-agent/tools/bash";
|
||||
import type { TruncationMeta } from "@oh-my-pi/pi-coding-agent/tools/output-meta";
|
||||
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
||||
@@ -210,6 +210,45 @@ describe("pi_bash truncation reaches the wire from a real BashTool result", () =
|
||||
expect(wire?.truncatedBy).toBe(details.meta?.truncation?.truncatedBy);
|
||||
});
|
||||
|
||||
it("reports the entry cap ReadTool actually records for a large listing", async () => {
|
||||
// Same producer/consumer contract for the listing cap. `glob` records it
|
||||
// twice — a flat `details.resultLimitReached` and the structured meta —
|
||||
// but `read`, which serves `pi_ls`, records it only through `OutputMeta`.
|
||||
// Reading just the flat field dropped `entry_limit_reached` for every
|
||||
// real listing, so Cursor got clipped output with no signal it was cut.
|
||||
//
|
||||
// The root listing is uncapped; the depth-2 tree caps each child
|
||||
// directory, so the entries have to sit one level down to trip it.
|
||||
const listing = path.join(cwd, "many");
|
||||
const child = path.join(listing, "child");
|
||||
await fs.mkdir(child, { recursive: true });
|
||||
await Promise.all(Array.from({ length: 40 }, (_, i) => Bun.write(path.join(child, `f${i}.txt`), "x")));
|
||||
const read = new ReadTool(createTestSession(cwd));
|
||||
const result = await read.execute("l1", { path: listing });
|
||||
|
||||
// Guard the assumption the bridge encodes: the cap lives in the nested
|
||||
// meta and nowhere flat. If the producer's shape moves, this fails here
|
||||
// rather than silently sending a clipped listing as if it were whole.
|
||||
const details = result.details as {
|
||||
resultLimitReached?: number;
|
||||
meta?: { limits?: { resultLimit?: { reached: number } } };
|
||||
};
|
||||
expect(details.resultLimitReached).toBeUndefined();
|
||||
expect(details.meta?.limits?.resultLimit?.reached).toBeGreaterThan(0);
|
||||
|
||||
const wire = buildPiLsResult({
|
||||
role: "toolResult",
|
||||
toolCallId: "l1",
|
||||
toolName: "read",
|
||||
content: result.content,
|
||||
isError: false,
|
||||
timestamp: Date.now(),
|
||||
details: result.details,
|
||||
});
|
||||
if (wire.result.case !== "success") throw new Error(`expected success, got ${wire.result.case}`);
|
||||
expect(wire.result.value.entryLimitReached).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("sends no truncation summary for output that fit", async () => {
|
||||
const bash = new BashTool(createTestSession(cwd));
|
||||
const result = await bash.execute("t2", { command: "echo hi" });
|
||||
|
||||
Reference in New Issue
Block a user