Compare commits

..

3 Commits

Author SHA1 Message Date
acp-release-bot[bot]
794aa84684 chore(main): release 0.47.0 (#784)
🤖 I have created a release *beep* *boop*
---


##
[0.47.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.46.0...v0.47.0)
(2026-06-17)


### Features

* Update to claude-agent-sdk 0.3.179
([#783](https://github.com/agentclientprotocol/claude-agent-acp/issues/783))
([59a098c](59a098c2b5))


### Bug Fixes

* Duplicate assistant messages in feed
([#785](https://github.com/agentclientprotocol/claude-agent-acp/issues/785))
([12d34e6](12d34e64e5))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
2026-06-17 12:17:10 +02:00
Ben Brandt
12d34e64e5 fix: Duplicate assistant messages in feed (#785) 2026-06-17 12:12:26 +02:00
Ben Brandt
59a098c2b5 feat: Update to claude-agent-sdk 0.3.179 (#783) 2026-06-17 09:04:56 +00:00
6 changed files with 114 additions and 46 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.46.0"
".": "0.47.0"
}

View File

@@ -1,5 +1,17 @@
# Changelog
## [0.47.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.46.0...v0.47.0) (2026-06-17)
### Features
* Update to claude-agent-sdk 0.3.179 ([#783](https://github.com/agentclientprotocol/claude-agent-acp/issues/783)) ([59a098c](https://github.com/agentclientprotocol/claude-agent-acp/commit/59a098c2b530bbae034e9a2dfbd31f8b4ef2a4d0))
### Bug Fixes
* Duplicate assistant messages in feed ([#785](https://github.com/agentclientprotocol/claude-agent-acp/issues/785)) ([12d34e6](https://github.com/agentclientprotocol/claude-agent-acp/commit/12d34e64e53564602ac1c38a30127e234c5c25ff))
## [0.46.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.45.1...v0.46.0) (2026-06-16)

76
package-lock.json generated
View File

@@ -1,16 +1,16 @@
{
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.46.0",
"version": "0.47.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.46.0",
"version": "0.47.0",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.26.0",
"@anthropic-ai/claude-agent-sdk": "0.3.178",
"@anthropic-ai/claude-agent-sdk": "0.3.179",
"zod": "^3.25.0 || ^4.0.0"
},
"bin": {
@@ -41,22 +41,22 @@
}
},
"node_modules/@anthropic-ai/claude-agent-sdk": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.178.tgz",
"integrity": "sha512-PNsz20jWuahDWq7OU+pjrgYzr2TnpC1oj5yCZDxGWJ8OvucXIdD2AYlf/vUo7oE2JJwGbMTBFqXErNrfPi6Ffw==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.179.tgz",
"integrity": "sha512-BlZULVW61ZCcho6mb026QoOQbGZnfxbsEtcoNaW5lF0sIEu7D+/nnQjHSMK8buS/h7HVmIx2RtGbHVX1y4V0uQ==",
"license": "SEE LICENSE IN README.md",
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.178",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.178",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.178",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.178",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.178",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.178",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.178",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.178"
"@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.179",
"@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.179",
"@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.179",
"@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.179",
"@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.179",
"@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.179",
"@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.179",
"@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.179"
},
"peerDependencies": {
"@anthropic-ai/sdk": ">=0.93.0",
@@ -65,9 +65,9 @@
}
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.178.tgz",
"integrity": "sha512-RmIJRoZfjwcrd7cHR3fJOL1d4L8SN7oB0REcQPbIuPM1vav2Ft3g5hBX7I86u52A4LLFKBc3SMom3+E6lR1YtQ==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.179.tgz",
"integrity": "sha512-2QoEl7p+RTkF4ARZ40N9OWWi+at8Iy9ZZg3UeP6sWoGrM0GL6NJSv8pbYUdoSRySbNeZshqWar5DF41265J5Sg==",
"cpu": [
"arm64"
],
@@ -78,9 +78,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.178.tgz",
"integrity": "sha512-wfNl7JoaUk9IzKWQPr+hyEOX8qnkM+e4GBZnKISh60xVhW9wOOp5RdfnYj5MoJzaLYivMSCbo5rb4ThWguhOMw==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.179.tgz",
"integrity": "sha512-U5683Hi4uP5Wkg9IhHayqx8co9rgeZaAJcutLAgJiAN9ZnL128+WT0K4DKaBVuMbeeoORodVs9IJgM38lBxUxg==",
"cpu": [
"x64"
],
@@ -91,9 +91,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.178.tgz",
"integrity": "sha512-ktBU6EdJoZivf67AxRXe2uSwj0g5tCe20So9QvWVKuEEtRA4sXW5EpgxSWT6LXkgfwUoeNsJK7VAOuVjZumKmg==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.179.tgz",
"integrity": "sha512-Io9X2xF5J3cHR20b0oflLe0sLHyT/KFMCA7sVJhwuSvcqeCHqXTxn6sG+4lArD9wNf+RtLVKgSvOt5Oz4j7mew==",
"cpu": [
"arm64"
],
@@ -107,9 +107,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.178.tgz",
"integrity": "sha512-Z3U0fNatVK3vkki3e5sjlLJMjros+cT6uq//tdohB2kjNK1CugUuPQFQxd6GU1Lq1DokmdSEPL4OGdKiHckNdQ==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.179.tgz",
"integrity": "sha512-ehqWR9bV0dJONkoKleKg/LJIGDVevLGLPVIQpol+Bqrgyv05DE1hx68g6mBnfp9IEKo2BMZCba3aHKhkHlZvnQ==",
"cpu": [
"arm64"
],
@@ -123,9 +123,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.178.tgz",
"integrity": "sha512-CPTivDz27LMn5m9iQnJSoWkztLo41e8QTbuYKlAC+CTr98gaYy3Mao0M2tirEK/nGno0xRJw/EpZ1G61yEM3yw==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.179.tgz",
"integrity": "sha512-Ck2pLG2GJ5lYULK0Rb6FvAUhkLSvIFgJ77MsbVhvBJHG3C31Fuk6FnkXEL614WSZZdI1ST0Y7s7wc9yLI2kmiQ==",
"cpu": [
"x64"
],
@@ -139,9 +139,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.178.tgz",
"integrity": "sha512-K84Ybyr0Olsslg2I1Tzd7KIcSkZgFqqDHn7q1Dfqi7bXVxjMjJ4SaV+LnEAeHSM2es7H8A7ejoTEl89xMZde4Q==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.179.tgz",
"integrity": "sha512-Q+clijh01m+Gg/tSNjHQWfPFRvXSVMIQJZqu6v28vFduaucL7ZL+p6o6VOSdlgLjhqtIFFjOO37aL+VkvEu9MQ==",
"cpu": [
"x64"
],
@@ -155,9 +155,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.178.tgz",
"integrity": "sha512-uKH3vgv7cQV3d9BPU4lrUKrFgoU/flmy/1QI62j9JzgE6uWVQKWC+BI4V7iceJ36tYVneRhjjuux+l+Q8egTHA==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.179.tgz",
"integrity": "sha512-7wc4j3vDE/TiuiCEPV024U/TDNKXUJ89V0N9WteYJ57YrIUm0RA51WiKCrEmSxSqstQ7Slq26e0gWGHRf7ljDQ==",
"cpu": [
"arm64"
],
@@ -168,9 +168,9 @@
]
},
"node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": {
"version": "0.3.178",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.178.tgz",
"integrity": "sha512-wSC5qG6UA1laWGylOU7axuC4NQxZJtibGQ79sYeOQCc05G7CfkUKSzszLOzsPP3eK63cKi/bX5PA6dd4nA5Wow==",
"version": "0.3.179",
"resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.179.tgz",
"integrity": "sha512-gFad1AVUZOXbEyS9lf3Pr0LDXrLuO6limZoZoUZQmjhjy0LnudnTU8P/VbCY0AHmT46YMZ/ieisFzeq8X5jTcg==",
"cpu": [
"x64"
],

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.46.0",
"version": "0.47.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -61,7 +61,7 @@
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.26.0",
"@anthropic-ai/claude-agent-sdk": "0.3.178",
"@anthropic-ai/claude-agent-sdk": "0.3.179",
"zod": "^3.25.0 || ^4.0.0"
},
"devDependencies": {

View File

@@ -959,9 +959,8 @@ export class ClaudeAcpAgent implements Agent {
// via `stream_event` deltas, split by block type, so the consolidated
// `assistant` message can drop the duplicate blocks that already reached the
// client as chunks while still forwarding any that a non-streaming gateway
// delivered only as an assembled block.
let streamedTextIds = new Set<string>();
let streamedThinkingIds = new Set<string>();
const streamedTextIds = new Set<string>();
const streamedThinkingIds = new Set<string>();
// Stop reason accumulated for the active turn (result subtype, refusal,
// max_tokens, …). Reset per turn; read when the turn settles at idle.
let stopReason: StopReason = "end_turn";
@@ -974,8 +973,6 @@ export class ClaudeAcpAgent implements Agent {
lastRefusalExplanation = null;
compactionInProgress = false;
currentStreamMessageId = undefined;
streamedTextIds = new Set<string>();
streamedThinkingIds = new Set<string>();
stopReason = "end_turn";
session.accumulatedUsage = {
inputTokens: 0,
@@ -1864,6 +1861,13 @@ export class ClaudeAcpAgent implements Agent {
}
return true;
});
// The consolidated message is the last place this id is needed for
// dedupe — drop it so the streamed-id sets stay bounded to in-flight
// streams rather than growing for the session's whole life.
if (id !== undefined) {
streamedTextIds.delete(id);
streamedThinkingIds.delete(id);
}
} else if (message.type === "assistant") {
// Subagent assistant message (`parent_tool_use_id !== null`). It is
// never streamed live and its text/thinking is internal to the tool

View File

@@ -3679,6 +3679,36 @@ describe("assembled assistant text fallback", () => {
});
}
// Like injectSession, but the user-message echo is yielded at the position of
// the "ECHO" sentinel in `messages` rather than always first — so a test can
// reproduce the production ordering where the assistant stream arrives before
// the SDK replays the user message.
function injectSessionEchoAt(agent: ClaudeAcpAgent, messages: any[]) {
const input = new Pushable<any>();
async function* messageGenerator() {
const iter = input[Symbol.asyncIterator]();
const { value: userMessage } = await iter.next();
for (const m of messages) {
if (m === "ECHO") {
yield {
type: "user",
message: userMessage.message,
parent_tool_use_id: null,
uuid: userMessage.uuid,
session_id: "test-session",
isReplay: true,
};
} else {
yield m;
}
}
}
agent.sessions["test-session"] = mockSessionState({
query: wrapQuery(messageGenerator()),
input,
});
}
function messageChunkTexts(updates: any[]): string[] {
return updates
.filter((u) => u.update?.sessionUpdate === "agent_message_chunk")
@@ -3723,6 +3753,28 @@ describe("assembled assistant text fallback", () => {
expect(messageChunkTexts(updates)).toEqual(["hello ", "world"]);
});
it("dedupes streamed text even when the stream arrives before the user echo", async () => {
const { agent, updates } = createMockAgentWithCapture();
// Production ordering: the SDK emits the assistant's stream events before it
// replays the user message that activates the turn. The streamed-id tracking
// must survive activation, or the consolidated block is re-emitted as a
// duplicate (regression from the persistent-consumer rework).
injectSessionEchoAt(agent, [
messageStart("msg-streamed"),
textDelta("hello "),
textDelta("world"),
"ECHO",
assistantMessage("msg-streamed", [{ type: "text", text: "hello world" }]),
result(),
idle,
]);
await agent.prompt({ sessionId: "test-session", prompt: [{ type: "text", text: "hi" }] });
// Still just the two streamed deltas — no duplicated assembled block.
expect(messageChunkTexts(updates)).toEqual(["hello ", "world"]);
});
it("dedupes per block type: streamed text is dropped but an un-streamed thinking block in the same message is forwarded", async () => {
const { agent, updates } = createMockAgentWithCapture();
// Gateway streams the text live but delivers the thinking block only in the