Compare commits

..

2 Commits

Author SHA1 Message Date
acp-release-bot[bot]
e9163f8553 chore(main): release 0.52.0 (#811)
🤖 I have created a release *beep* *boop*
---


##
[0.52.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.51.0...v0.52.0)
(2026-06-25)


### Features

* Add version flag handling
([#813](https://github.com/agentclientprotocol/claude-agent-acp/issues/813))
([9616bda](9616bdac47)),
closes
[#809](https://github.com/agentclientprotocol/claude-agent-acp/issues/809)
* **deps-dev:** bump expect-type from 1.3.0 to 1.4.0 in the minor group
([#814](https://github.com/agentclientprotocol/claude-agent-acp/issues/814))
([61272ac](61272acb30))
* **deps:** Update @anthropic-ai/claude-agent-sdk to 0.3.191
([#810](https://github.com/agentclientprotocol/claude-agent-acp/issues/810))
([228f02e](228f02ecfb))
* Push session title updates at turn end
([#812](https://github.com/agentclientprotocol/claude-agent-acp/issues/812))
([1fe7ec0](1fe7ec09a3))

---
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-25 17:11:07 +02:00
dependabot[bot]
61272acb30 feat(deps-dev): bump expect-type from 1.3.0 to 1.4.0 in the minor group (#814)
Bumps the minor group with 1 update:
[expect-type](https://github.com/mmkal/expect-type).

Updates `expect-type` from 1.3.0 to 1.4.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mmkal/expect-type/releases">expect-type's
releases</a>.</em></p>
<blockquote>
<h2>v1.4.0</h2>
<h2>What's Changed</h2>
<ul>
<li>test: lock non-callable thisParameter fallback by <a
href="https://github.com/mmkal"><code>@​mmkal</code></a> in <a
href="https://redirect.github.com/mmkal/expect-type/pull/214">mmkal/expect-type#214</a></li>
<li>feat: support overloads in <code>thisParameter</code> by <a
href="https://github.com/aryaemami59"><code>@​aryaemami59</code></a> in
<a
href="https://redirect.github.com/mmkal/expect-type/pull/204">mmkal/expect-type#204</a></li>
<li>feat: <code>.branded.inspect</code> to view deep prop types by <a
href="https://github.com/mmkal"><code>@​mmkal</code></a> in <a
href="https://redirect.github.com/mmkal/expect-type/pull/113">mmkal/expect-type#113</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/mmkal/expect-type/compare/v1.3.0...v1.4.0">https://github.com/mmkal/expect-type/compare/v1.3.0...v1.4.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="48743118a1"><code>4874311</code></a>
1.4.0</li>
<li><a
href="27b0e579fd"><code>27b0e57</code></a>
idea: <code>.branded.inspect</code> to view deep prop types (<a
href="https://redirect.github.com/mmkal/expect-type/issues/113">#113</a>)</li>
<li><a
href="2794969582"><code>2794969</code></a>
renovate: update 🌲 (<a
href="https://redirect.github.com/mmkal/expect-type/issues/215">#215</a>)</li>
<li><a
href="29130cdef2"><code>29130cd</code></a>
feat: support overloads in <code>thisParameter</code> (<a
href="https://redirect.github.com/mmkal/expect-type/issues/204">#204</a>)</li>
<li><a
href="658ea8784a"><code>658ea87</code></a>
test: lock non-callable thisParameter fallback (<a
href="https://redirect.github.com/mmkal/expect-type/issues/214">#214</a>)</li>
<li><a
href="22eb69e931"><code>22eb69e</code></a>
renovate: update 🌲 (<a
href="https://redirect.github.com/mmkal/expect-type/issues/212">#212</a>)</li>
<li><a
href="f6ec417af1"><code>f6ec417</code></a>
chore: gate Renovate dependency PRs (<a
href="https://redirect.github.com/mmkal/expect-type/issues/213">#213</a>)</li>
<li><a
href="25403eb822"><code>25403eb</code></a>
renovate: update np, <code>@​types/node</code> and 12 more 🌲 (<a
href="https://redirect.github.com/mmkal/expect-type/issues/133">#133</a>)</li>
<li><a
href="459a1e116b"><code>459a1e1</code></a>
Update <code>@typescript/native-preview</code> as it's stable enough (<a
href="https://redirect.github.com/mmkal/expect-type/issues/186">#186</a>)</li>
<li><a
href="c84e6164c1"><code>c84e616</code></a>
Update pnpm to <code>10.25.0</code> (<a
href="https://redirect.github.com/mmkal/expect-type/issues/187">#187</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/mmkal/expect-type/compare/v1.3.0...v1.4.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=expect-type&package-manager=npm_and_yarn&previous-version=1.3.0&new-version=1.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 17:08:42 +02:00
8 changed files with 31 additions and 237 deletions

View File

@@ -1,3 +1,3 @@
{
".": "0.51.0"
".": "0.52.0"
}

View File

@@ -1,5 +1,15 @@
# Changelog
## [0.52.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.51.0...v0.52.0) (2026-06-25)
### Features
* Add version flag handling ([#813](https://github.com/agentclientprotocol/claude-agent-acp/issues/813)) ([9616bda](https://github.com/agentclientprotocol/claude-agent-acp/commit/9616bdac47505e4a14c36d667fcffc9ae97e1f2a)), closes [#809](https://github.com/agentclientprotocol/claude-agent-acp/issues/809)
* **deps-dev:** bump expect-type from 1.3.0 to 1.4.0 in the minor group ([#814](https://github.com/agentclientprotocol/claude-agent-acp/issues/814)) ([61272ac](https://github.com/agentclientprotocol/claude-agent-acp/commit/61272acb30dcafaa2455d334b11ce2ad97339707))
* **deps:** Update @anthropic-ai/claude-agent-sdk to 0.3.191 ([#810](https://github.com/agentclientprotocol/claude-agent-acp/issues/810)) ([228f02e](https://github.com/agentclientprotocol/claude-agent-acp/commit/228f02ecfb23be16e59e121c6b42c0f2b2f40a4e))
* Push session title updates at turn end ([#812](https://github.com/agentclientprotocol/claude-agent-acp/issues/812)) ([1fe7ec0](https://github.com/agentclientprotocol/claude-agent-acp/commit/1fe7ec09a3a7bcb7501231dae4b0ffe6ef9b70a4))
## [0.51.0](https://github.com/agentclientprotocol/claude-agent-acp/compare/v0.50.0...v0.51.0) (2026-06-24)

10
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.51.0",
"version": "0.52.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@agentclientprotocol/claude-agent-acp",
"version": "0.51.0",
"version": "0.52.0",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "1.0.0",
@@ -2015,9 +2015,9 @@
}
},
"node_modules/expect-type": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
"integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
"dev": true,
"license": "Apache-2.0",
"engines": {

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.51.0",
"version": "0.52.0",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",

View File

@@ -275,11 +275,6 @@ type Session = {
* `tool_result` time so a long-running session doesn't accumulate every
* tool call for its whole lifetime. */
toolUseCache: ToolUseCache;
/**
* This lets canUseTool wait for the first tool call message from the SDK
* to ensure it emits a tool_call_update only after the first tool_call message.
*/
waitingForToolCall: WaitingForToolCall;
/** Maps the ACP `messageId` we expose to clients (see `messageIdForGrouping`)
* to the SDK message uuid that the Agent SDK's rewind/resume APIs key on
* (`Query.rewindFiles` takes a user-message uuid; `resumeSessionAt` takes an
@@ -394,8 +389,6 @@ export type ToolUpdateMeta = {
};
};
export type WaitingForToolCall = Map<string, () => void>;
export type ToolUseCache = {
[key: string]: {
type: "tool_use" | "server_tool_use" | "mcp_tool_use";
@@ -1696,7 +1689,6 @@ export class ClaudeAcpAgent {
"assistant",
params.sessionId,
session.toolUseCache,
session.waitingForToolCall,
this.client,
this.logger,
)) {
@@ -1867,7 +1859,6 @@ export class ClaudeAcpAgent {
message,
params.sessionId,
session.toolUseCache,
session.waitingForToolCall,
this.client,
this.logger,
{
@@ -1968,7 +1959,6 @@ export class ClaudeAcpAgent {
message.message.role,
params.sessionId,
session.toolUseCache,
session.waitingForToolCall,
this.client,
this.logger,
{
@@ -2099,7 +2089,6 @@ export class ClaudeAcpAgent {
message.message.role,
params.sessionId,
session.toolUseCache,
session.waitingForToolCall,
this.client,
this.logger,
{
@@ -2467,7 +2456,6 @@ export class ClaudeAcpAgent {
private async replaySessionHistory(sessionId: string): Promise<void> {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const messages = await getSessionMessages(sessionId);
for (const message of messages) {
@@ -2496,7 +2484,6 @@ export class ClaudeAcpAgent {
message.message.role,
sessionId,
toolUseCache,
waitingForToolCall,
this.client,
this.logger,
{
@@ -2545,6 +2532,8 @@ export class ClaudeAcpAgent {
canUseTool(sessionId: string): CanUseTool {
return async (toolName, toolInput, { signal, suggestions, toolUseID }) => {
const alwaysAllowLabel = describeAlwaysAllow(suggestions, toolName);
const supportsTerminalOutput = this.clientCapabilities?._meta?.["terminal_output"] === true;
const session = this.sessions[sessionId];
if (!session) {
return {
@@ -2552,13 +2541,6 @@ export class ClaudeAcpAgent {
message: "Session not found",
};
}
if (!session.toolUseCache[toolUseID]) {
const { promise, resolve } = Promise.withResolvers<void>();
session.waitingForToolCall.set(toolUseID, resolve);
await promise;
}
const alwaysAllowLabel = describeAlwaysAllow(suggestions, toolName);
const supportsTerminalOutput = this.clientCapabilities?._meta?.["terminal_output"] === true;
// AskUserQuestion is surfaced to us as a normal permission check (the SDK
// routes it through canUseTool whenever a callback is registered, rather
@@ -3419,7 +3401,6 @@ export class ClaudeAcpAgent {
) ?? DEFAULT_CONTEXT_WINDOW,
taskState,
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
@@ -4145,7 +4126,6 @@ export function toAcpNotifications(
role: "assistant" | "user",
sessionId: string,
toolUseCache: ToolUseCache,
waitingForToolCall: WaitingForToolCall,
client: AcpClient,
logger: Logger,
options?: {
@@ -4315,13 +4295,6 @@ export function toAcpNotifications(
...toolInfoFromToolUse(chunk, supportsTerminalOutput, options?.cwd),
};
} else {
if (waitingForToolCall.has(chunk.id)) {
// If a canUseTool is waiting for the first tool_use chunk,
// we can resolve it now.
const resolve = waitingForToolCall.get(chunk.id)!;
resolve();
waitingForToolCall.delete(chunk.id);
}
// First encounter (streaming content_block_start or replay) —
// send as tool_call with terminal_info for Bash tools.
update = {
@@ -4479,7 +4452,6 @@ export function streamEventToAcpNotifications(
message: SDKPartialAssistantMessage,
sessionId: string,
toolUseCache: ToolUseCache,
waitingForToolCall: WaitingForToolCall,
client: AcpClient,
logger: Logger,
options?: {
@@ -4497,7 +4469,6 @@ export function streamEventToAcpNotifications(
"assistant",
sessionId,
toolUseCache,
waitingForToolCall,
client,
logger,
{
@@ -4514,7 +4485,6 @@ export function streamEventToAcpNotifications(
"assistant",
sessionId,
toolUseCache,
waitingForToolCall,
client,
logger,
{

View File

@@ -118,7 +118,6 @@ function mockSessionState(overrides: Record<string, any> = {}) {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
...overrides,
} as any;
@@ -873,7 +872,6 @@ describe("tool conversions", () => {
received.message.role,
"test",
{},
new Map(),
{} as AcpClient,
console,
),
@@ -1816,51 +1814,11 @@ describe("permission request cancellation", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
} as any;
return agent.sessions[sessionId]!;
}
it("waits for the first tool_call before requesting permission", async () => {
const requestPermission = vi.fn(async () => ({
outcome: { outcome: "selected", optionId: "allow" },
}));
const mockClient = {
sessionUpdate: async () => {},
requestPermission,
} as unknown as AcpClient;
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
const session = injectSession(agent, "session-1");
const pending = agent.canUseTool("session-1")("Bash", { command: "echo hello" }, {
signal: new AbortController().signal,
suggestions: [],
toolUseID: "tool-1",
} as any);
await Promise.resolve();
expect(requestPermission).not.toHaveBeenCalled();
expect(session.waitingForToolCall.has("tool-1")).toBe(true);
const notifications = toAcpNotifications(
[{ type: "tool_use", id: "tool-1", name: "Bash", input: { command: "echo hello" } }],
"assistant",
"session-1",
session.toolUseCache,
session.waitingForToolCall,
mockClient,
console,
);
expect(notifications[0].update.sessionUpdate).toBe("tool_call");
expect(requestPermission).not.toHaveBeenCalled();
await expect(pending).resolves.toMatchObject({ behavior: "allow" });
expect(requestPermission).toHaveBeenCalledTimes(1);
expect(session.waitingForToolCall.has("tool-1")).toBe(false);
});
it("forwards the tool-call signal so a pending permission request is cancelled on abort", async () => {
let receivedSignal: AbortSignal | undefined;
const mockClient = {
@@ -1877,13 +1835,7 @@ describe("permission request cancellation", () => {
},
} as unknown as AcpClient;
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
const session = injectSession(agent, "session-1");
session.toolUseCache["tool-1"] = {
type: "tool_use",
id: "tool-1",
name: "Bash",
input: { command: "ls" },
};
injectSession(agent, "session-1");
const controller = new AbortController();
const pending = agent.canUseTool("session-1")("Bash", { command: "ls" }, {
@@ -1908,13 +1860,7 @@ describe("permission request cancellation", () => {
requestPermission: async () => ({ outcome: { outcome: "cancelled" } }),
} as unknown as AcpClient;
const agent = new ClaudeAcpAgent(mockClient, { log: () => {}, error: () => {} });
const session = injectSession(agent, "session-1");
session.toolUseCache["tool-1"] = {
type: "tool_use",
id: "tool-1",
name: "Bash",
input: { command: "ls" },
};
injectSession(agent, "session-1");
await expect(
agent.canUseTool("session-1")("Bash", { command: "ls" }, {
@@ -2618,7 +2564,6 @@ describe("session/close", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
return agent.sessions[sessionId]!;
@@ -2704,7 +2649,6 @@ describe("session/delete", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
return agent.sessions[sessionId]!;
@@ -2807,7 +2751,6 @@ describe("getOrCreateSession param change detection", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
return agent.sessions[sessionId]!;
@@ -5022,7 +4965,6 @@ describe("post-error recovery", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
return { interrupt };
@@ -5593,7 +5535,6 @@ describe("session/cancel wedge recovery (issue #680)", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
return { interrupt };
@@ -5725,8 +5666,9 @@ describe("streamEventToAcpNotifications", () => {
pingMessage,
"test-session",
{},
new Map(),
{ sessionUpdate: async () => {} } as unknown as AcpClient,
{ sessionUpdate: async () => {} } as unknown as Parameters<
typeof streamEventToAcpNotifications
>[3],
logger,
);
@@ -5748,17 +5690,9 @@ describe("streamEventToAcpNotifications", () => {
},
} as Parameters<typeof streamEventToAcpNotifications>[0];
const result = streamEventToAcpNotifications(
message,
"test",
{},
new Map(),
{} as AcpClient,
console,
{
messageId,
},
);
const result = streamEventToAcpNotifications(message, "test", {}, {} as AcpClient, console, {
messageId,
});
expect(result).toEqual([
{
@@ -5782,7 +5716,6 @@ describe("toAcpNotifications messageId", () => {
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
{ messageId },
@@ -5806,7 +5739,6 @@ describe("toAcpNotifications messageId", () => {
"user",
"test",
{},
new Map(),
{} as AcpClient,
console,
{ messageId },
@@ -5821,7 +5753,6 @@ describe("toAcpNotifications messageId", () => {
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
{ messageId },
@@ -5833,15 +5764,7 @@ describe("toAcpNotifications messageId", () => {
});
it("omits messageId when none is supplied", () => {
const result = toAcpNotifications(
"hello",
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
);
const result = toAcpNotifications("hello", "assistant", "test", {}, {} as AcpClient, console);
expect(result[0].update).not.toHaveProperty("messageId");
});
@@ -5858,7 +5781,6 @@ describe("toAcpNotifications messageId", () => {
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
{ messageId, registerHooks: false },
@@ -5875,7 +5797,6 @@ describe("toAcpNotifications thinking chunks", () => {
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
);
@@ -5897,7 +5818,6 @@ describe("toAcpNotifications thinking chunks", () => {
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
);
@@ -5911,7 +5831,6 @@ describe("toAcpNotifications thinking chunks", () => {
"assistant",
"test",
{},
new Map(),
{} as AcpClient,
console,
);
@@ -6069,7 +5988,6 @@ describe("agent selection config option", () => {
contextWindowSize: 200000,
taskState: new Map(),
toolUseCache: {},
waitingForToolCall: new Map(),
messageIdToUuid: new Map(),
};
return { session: agent.sessions[sessionId]!, applyFlagSettings };

View File

@@ -124,8 +124,6 @@ describe("session config options", () => {
),
configOptions: structuredClone(MOCK_CONFIG_OPTIONS),
contextWindowSize: 200000,
toolUseCache: {},
waitingForToolCall: new Map(),
};
}
@@ -1114,12 +1112,6 @@ describe("session config options", () => {
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
],
};
session.toolUseCache["toolu_1"] = {
type: "tool_use",
id: "toolu_1",
name: "ExitPlanMode",
input: { plan: "do stuff" },
};
const canUseTool = (agent as any).canUseTool(SESSION_ID);
const signal = new AbortController().signal;
@@ -1152,12 +1144,6 @@ describe("session config options", () => {
],
};
permissionResponse = { outcome: { outcome: "selected", optionId: "auto" } };
session.toolUseCache["toolu_2"] = {
type: "tool_use",
id: "toolu_2",
name: "ExitPlanMode",
input: { plan: "do stuff" },
};
const canUseTool = (agent as any).canUseTool(SESSION_ID);
const result = await canUseTool(
@@ -1185,12 +1171,6 @@ describe("session config options", () => {
{ id: "dontAsk", name: "Don't Ask", description: "Deny if not pre-approved" },
],
};
session.toolUseCache["toolu_3"] = {
type: "tool_use",
id: "toolu_3",
name: "ExitPlanMode",
input: { plan: "do stuff" },
};
const canUseTool = (agent as any).canUseTool(SESSION_ID);
const signal = new AbortController().signal;

View File

@@ -10,13 +10,7 @@ import {
BetaBashCodeExecutionResultBlock,
BetaBashCodeExecutionToolResultBlockParam,
} from "@anthropic-ai/sdk/resources/beta.mjs";
import {
AcpClient,
toAcpNotifications,
ToolUseCache,
Logger,
WaitingForToolCall,
} from "../acp-agent.js";
import { AcpClient, toAcpNotifications, ToolUseCache, Logger } from "../acp-agent.js";
import {
toolUpdateFromToolResult,
createPostToolUseHook,
@@ -43,7 +37,6 @@ describe("rawOutput in tool call updates", () => {
input: { command: "echo hello" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
const toolResult: ToolResultBlockParam = {
type: "tool_result",
@@ -57,7 +50,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -80,7 +72,6 @@ describe("rawOutput in tool call updates", () => {
input: { file_path: "/test/file.txt" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
// ToolResultBlockParam content can be string or array of TextBlockParam
const toolResult: ToolResultBlockParam = {
@@ -95,7 +86,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -118,7 +108,6 @@ describe("rawOutput in tool call updates", () => {
input: { query: "test" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
// BetaMCPToolResultBlock content can be string or Array<BetaTextBlock>
const toolResult: BetaMCPToolResultBlock = {
@@ -133,7 +122,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -156,7 +144,6 @@ describe("rawOutput in tool call updates", () => {
input: { term: "test" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
// BetaTextBlock requires citations field
const arrayContent: BetaTextBlock[] = [
@@ -176,7 +163,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -199,7 +185,6 @@ describe("rawOutput in tool call updates", () => {
input: { query: "test search" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
// BetaWebSearchResultBlock from SDK
const searchResults: BetaWebSearchResultBlock[] = [
@@ -223,7 +208,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -246,7 +230,6 @@ describe("rawOutput in tool call updates", () => {
input: { command: "ls -la" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
// BetaBashCodeExecutionResultBlock from SDK
const bashResult: BetaBashCodeExecutionResultBlock = {
@@ -268,7 +251,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -291,7 +273,6 @@ describe("rawOutput in tool call updates", () => {
input: { command: "invalid_command" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
const toolResult: ToolResultBlockParam = {
type: "tool_result",
@@ -305,7 +286,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -328,7 +308,6 @@ describe("rawOutput in tool call updates", () => {
input: { todos: [{ content: "Test task", status: "pending" }] },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
const toolResult: ToolResultBlockParam = {
type: "tool_result",
@@ -342,7 +321,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -373,14 +351,11 @@ describe("rawOutput in tool call updates", () => {
is_error: false,
};
const waitingForToolCall: WaitingForToolCall = new Map();
const notifications = toAcpNotifications(
[toolResult],
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -408,7 +383,6 @@ describe("rawOutput in tool call updates", () => {
input: { file_path: "/test/image.png" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
const imageBlock: ImageBlockParam = {
type: "image",
@@ -427,7 +401,6 @@ describe("rawOutput in tool call updates", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -806,7 +779,6 @@ describe("Bash terminal output", () => {
// Reset before each test: toAcpNotifications prunes the cache entry once it
// maps the tool_result, so a shared object would be empty by the 2nd test.
let toolUseCache: ToolUseCache;
const waitingForToolCall: WaitingForToolCall = new Map();
beforeEach(() => {
toolUseCache = {
toolu_bash: {
@@ -842,7 +814,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ clientCapabilities },
@@ -883,7 +854,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -910,7 +880,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ clientCapabilities },
@@ -926,7 +895,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ clientCapabilities: { _meta: { terminal_output: true } } },
@@ -947,7 +915,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCacheWithoutSupport,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -980,7 +947,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ clientCapabilities },
@@ -1003,7 +969,6 @@ describe("Bash terminal output", () => {
describe("toolUseCache pruning", () => {
it("retains the tool_use entry until its result, then prunes it", () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const toolUse = {
type: "tool_use" as const,
id: "toolu_read",
@@ -1012,15 +977,7 @@ describe("Bash terminal output", () => {
};
// tool_use is cached and kept (the matching result hasn't arrived yet).
toAcpNotifications(
[toolUse],
"assistant",
"s",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
toAcpNotifications([toolUse], "assistant", "s", toolUseCache, mockClient, mockLogger);
expect(toolUseCache.toolu_read).toBeDefined();
// tool_result resolves it, so the entry is pruned to bound memory.
@@ -1029,15 +986,7 @@ describe("Bash terminal output", () => {
tool_use_id: "toolu_read",
content: [{ type: "text", text: "hello" }],
};
toAcpNotifications(
[toolResult],
"assistant",
"s",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
toAcpNotifications([toolResult], "assistant", "s", toolUseCache, mockClient, mockLogger);
expect(toolUseCache.toolu_read).toBeUndefined();
});
});
@@ -1045,7 +994,6 @@ describe("Bash terminal output", () => {
describe("post-tool-use hook sends diff content for Edit tool", () => {
it("should include content and locations from structuredPatch in hook update", async () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
@@ -1071,7 +1019,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
);
@@ -1126,7 +1073,6 @@ describe("Bash terminal output", () => {
it("should include multiple diff blocks for replaceAll with multiple hunks", async () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
@@ -1152,7 +1098,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
);
@@ -1213,7 +1158,6 @@ describe("Bash terminal output", () => {
it("should not include content/locations for non-Edit tools", async () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
@@ -1234,7 +1178,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
);
@@ -1270,7 +1213,6 @@ describe("Bash terminal output", () => {
// tool_use time and was never corrected after the tool ran.
it("should emit a real diff for Write of an existing file (type: update)", async () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
sessionUpdate: async (notification: any) => {
@@ -1293,7 +1235,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
);
@@ -1348,7 +1289,6 @@ describe("Bash terminal output", () => {
it("should still emit a sensible diff for Write of a brand-new file (type: create)", async () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
sessionUpdate: async (notification: any) => {
@@ -1371,7 +1311,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
);
@@ -1429,7 +1368,6 @@ describe("Bash terminal output", () => {
};
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
// Capture session updates sent by the hook callback
const hookUpdates: any[] = [];
@@ -1451,7 +1389,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
{ clientCapabilities },
@@ -1481,7 +1418,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
{ clientCapabilities },
@@ -1533,7 +1469,6 @@ describe("Bash terminal output", () => {
it("should not include terminal _meta in hook update when client lacks terminal_output support", async () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const hookUpdates: any[] = [];
const mockClientWithUpdate = {
@@ -1555,7 +1490,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
// No clientCapabilities — terminal_output not supported
@@ -1580,7 +1514,6 @@ describe("Bash terminal output", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClientWithUpdate,
mockLogger,
);
@@ -1712,7 +1645,6 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
it("should not throw when TodoWrite tool_use has undefined input", () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const notifications = toAcpNotifications(
[
@@ -1726,7 +1658,6 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -1738,7 +1669,6 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
it("should still emit plan update when TodoWrite has valid input", () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const notifications = toAcpNotifications(
[
@@ -1752,7 +1682,6 @@ describe("toAcpNotifications - TodoWrite with undefined input regression", () =>
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
);
@@ -1851,7 +1780,6 @@ describe("toAcpNotifications - Task* tools", () => {
it("suppresses tool_call for TaskCreate/TaskUpdate/TaskList/TaskGet on tool_use", () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const taskState: TaskState = new Map();
const notifications = toAcpNotifications(
@@ -1869,7 +1797,6 @@ describe("toAcpNotifications - Task* tools", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -1881,7 +1808,6 @@ describe("toAcpNotifications - Task* tools", () => {
it("emits a plan snapshot after a TaskCreate tool_result and accumulates state", () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const taskState: TaskState = new Map();
toAcpNotifications(
@@ -1896,7 +1822,6 @@ describe("toAcpNotifications - Task* tools", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -1914,7 +1839,6 @@ describe("toAcpNotifications - Task* tools", () => {
"user",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -1939,7 +1863,6 @@ describe("toAcpNotifications - Task* tools", () => {
"assistant",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -1957,7 +1880,6 @@ describe("toAcpNotifications - Task* tools", () => {
"user",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -1974,7 +1896,6 @@ describe("toAcpNotifications - Task* tools", () => {
it("emits a plan snapshot reflecting status changes after a TaskUpdate tool_result", () => {
const toolUseCache: ToolUseCache = {};
const waitingForToolCall: WaitingForToolCall = new Map();
const taskState: TaskState = new Map([["1", { subject: "First", status: "pending" as const }]]);
toolUseCache["update-1"] = {
type: "tool_use",
@@ -1999,7 +1920,6 @@ describe("toAcpNotifications - Task* tools", () => {
"user",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -2017,7 +1937,6 @@ describe("toAcpNotifications - Task* tools", () => {
"list-1": { type: "tool_use", id: "list-1", name: "TaskList", input: {} },
"get-1": { type: "tool_use", id: "get-1", name: "TaskGet", input: { taskId: "1" } },
};
const waitingForToolCall: WaitingForToolCall = new Map();
const taskState: TaskState = new Map([
["1", { subject: "Existing", status: "in_progress" as const }],
]);
@@ -2030,7 +1949,6 @@ describe("toAcpNotifications - Task* tools", () => {
"user",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },
@@ -2049,7 +1967,6 @@ describe("toAcpNotifications - Task* tools", () => {
input: { subject: "A", description: "" },
},
};
const waitingForToolCall: WaitingForToolCall = new Map();
const taskState: TaskState = new Map();
const notifications = toAcpNotifications(
@@ -2064,7 +1981,6 @@ describe("toAcpNotifications - Task* tools", () => {
"user",
"test-session",
toolUseCache,
waitingForToolCall,
mockClient,
mockLogger,
{ taskState },