diff --git a/src/tests/acp-v2.test.ts b/src/tests/acp-v2.test.ts index f88bd400..3b7bc673 100644 --- a/src/tests/acp-v2.test.ts +++ b/src/tests/acp-v2.test.ts @@ -740,6 +740,49 @@ describe("ACP v2 prompts", () => { ]); }); + it("replays a Write without the v1 diff that v2 cannot take", async () => { + const client = v2Client(); + await client.app.connectWith(connectRouter(), async (agent) => { + await initializeV2(agent); + const { sessionId } = await agent.request(v2.methods.agent.session.new, { cwd }); + const file = path.join(cwd, "a.ts"); + vi.mocked(getSessionMessages).mockResolvedValueOnce([ + transcriptEntry("assistant", randomUUID(), { + id: "msg_write", + role: "assistant", + content: [ + { + type: "tool_use", + id: "toolu_w", + name: "Write", + input: { file_path: file, content: "x\n" }, + }, + ], + }), + transcriptEntry("user", randomUUID(), { + role: "user", + content: [ + { + type: "tool_result", + tool_use_id: "toolu_w", + content: `File created successfully at: ${file}`, + }, + ], + }), + ] as unknown as Awaited>); + await agent.request(v2.methods.agent.session.resume, { + sessionId, + cwd, + replayFrom: { type: "start" }, + }); + await client.authUpdate(1); + }); + + expect(client.updates("tool_call_update")).toContainEqual( + expect.objectContaining({ toolCallId: "toolu_w", title: "Write a.ts", content: [] }), + ); + }); + it("replays the history before it answers session/resume, clearing each message first", async () => { const echoed = scriptTurns( async function* (options) { diff --git a/src/tool-calls/reporters/file-edit.ts b/src/tool-calls/reporters/file-edit.ts index 11fee8c2..4f55db20 100644 --- a/src/tool-calls/reporters/file-edit.ts +++ b/src/tool-calls/reporters/file-edit.ts @@ -37,8 +37,10 @@ export class WriteReporter implements ToolReporter { // whether the file exists, so the live tool call shows no diff: a diff // without the old text would claim a creation. The adapter reads the // file only for the preview. A replay has no preview and no hook, so it - // keeps the diff. - if (!(capabilities.diffPatch || capabilities.v2) || replay) { + // keeps the diff, except for a v2 client: a v2 diff names the operation, + // which the input does not tell. + const inputDiff = capabilities.v2 ? false : !capabilities.diffPatch || replay; + if (inputDiff) { facts.change = [ { type: "diff",