diff options
| author | Aiden Cline <[email protected]> | 2026-04-14 11:37:33 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-04-14 11:37:33 -0500 |
| commit | b1312a3181bdd5f93ab2fcefdf4423ed464e33e3 (patch) | |
| tree | dec69b0c02f7eb2832440452277103880b9912ac /packages | |
| parent | a8f9f6b7059bc9edf8184f8ebddf32dd1c6030e8 (diff) | |
| download | opencode-b1312a3181bdd5f93ab2fcefdf4423ed464e33e3.tar.gz opencode-b1312a3181bdd5f93ab2fcefdf4423ed464e33e3.zip | |
core: prevent duplicate user messages in ACP clients (#22468)
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/opencode/src/acp/agent.ts | 6 | ||||
| -rw-r--r-- | packages/opencode/test/acp/event-subscription.test.ts | 40 |
2 files changed, 46 insertions, 0 deletions
diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 235280a0d..a22e6c462 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -453,6 +453,12 @@ export namespace ACP { return } } + + // ACP clients already know the prompt they just submitted, so replaying + // live user parts duplicates the message. We still replay user history in + // loadSession() and forkSession() via processMessage(). + if (part.type !== "text" && part.type !== "file") return + return } diff --git a/packages/opencode/test/acp/event-subscription.test.ts b/packages/opencode/test/acp/event-subscription.test.ts index a3ae01c5c..a0944e33b 100644 --- a/packages/opencode/test/acp/event-subscription.test.ts +++ b/packages/opencode/test/acp/event-subscription.test.ts @@ -295,6 +295,46 @@ describe("acp.agent event subscription", () => { }) }) + test("does not emit user_message_chunk for live prompt parts", async () => { + await using tmp = await tmpdir() + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const { agent, controller, sessionUpdates, stop } = createFakeAgent() + const cwd = "/tmp/opencode-acp-test" + const sessionId = await agent.newSession({ cwd, mcpServers: [] } as any).then((x) => x.sessionId) + + controller.push({ + directory: cwd, + payload: { + type: "message.part.updated", + properties: { + sessionID: sessionId, + time: Date.now(), + part: { + id: "part_1", + sessionID: sessionId, + messageID: "msg_user", + type: "text", + text: "hello", + }, + }, + }, + } as any) + + await new Promise((r) => setTimeout(r, 20)) + + expect( + sessionUpdates + .filter((u) => u.sessionId === sessionId) + .some((u) => u.update.sessionUpdate === "user_message_chunk"), + ).toBe(false) + + stop() + }, + }) + }) + test("keeps concurrent sessions isolated when message.part.delta events are interleaved", async () => { await using tmp = await tmpdir() await Instance.provide({ |
