@agentclientprotocol/claude-agent-acp 0.51.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp-agent.d.ts +32 -1
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +174 -44
- package/dist/elicitation.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/package.json +7 -7
package/dist/acp-agent.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthenticateRequest, CancelNotification, ClientCapabilities, CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, ListSessionsRequest, ListSessionsResponse, LoadSessionRequest, LoadSessionResponse, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, RequestPermissionRequest, RequestPermissionResponse, ResumeSessionRequest, ResumeSessionResponse, SessionConfigOption, SessionModeState, SessionNotification, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, SetSessionModeRequest, SetSessionModeResponse, CloseSessionRequest, CloseSessionResponse, DeleteSessionRequest, DeleteSessionResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
|
|
1
|
+
import { AuthenticateRequest, CancelNotification, ClientCapabilities, CompleteElicitationNotification, CreateElicitationRequest, CreateElicitationResponse, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, ListSessionsRequest, ListSessionsResponse, LoadSessionRequest, LoadSessionResponse, LogoutRequest, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, RequestPermissionRequest, RequestPermissionResponse, ResumeSessionRequest, ResumeSessionResponse, SessionConfigOption, SessionModeState, SessionNotification, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, SetSessionModeRequest, SetSessionModeResponse, CloseSessionRequest, CloseSessionResponse, DeleteSessionRequest, DeleteSessionResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
|
|
2
2
|
import { AgentInfo, CanUseTool, ModelInfo, Options, PermissionMode, PermissionUpdate, Query, SDKMessageOrigin, SDKPartialAssistantMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
3
3
|
import { ContentBlockParam } from "@anthropic-ai/sdk/resources";
|
|
4
4
|
import { BetaContentBlock, BetaRawContentBlockDelta } from "@anthropic-ai/sdk/resources/beta.mjs";
|
|
@@ -119,12 +119,26 @@ type Session = {
|
|
|
119
119
|
/** Accumulated task list for the session, keyed by task ID. Task IDs are
|
|
120
120
|
* per-session, so this state must not be shared across sessions. */
|
|
121
121
|
taskState: TaskState;
|
|
122
|
+
/** Last session title we pushed to the client via `session_info_update`.
|
|
123
|
+
* The SDK auto-generates a title in a background task and persists it to the
|
|
124
|
+
* session file; we poll it on each turn-end (`session_state_changed: idle`)
|
|
125
|
+
* and only notify the client when it actually changes. Undefined until the
|
|
126
|
+
* first title is observed. */
|
|
127
|
+
lastTitle?: string;
|
|
122
128
|
/** Caches `tool_use` blocks by id so the matching `tool_result` can recover
|
|
123
129
|
* the tool name/input when mapping it to a `tool_call_update`. Per-session
|
|
124
130
|
* (tool_use ids are only unique within a session) and pruned at
|
|
125
131
|
* `tool_result` time so a long-running session doesn't accumulate every
|
|
126
132
|
* tool call for its whole lifetime. */
|
|
127
133
|
toolUseCache: ToolUseCache;
|
|
134
|
+
/** Tracks which tool_use ids we've already emitted a `tool_call` for, so the
|
|
135
|
+
* second source to encounter a tool call sends a `tool_call_update` instead
|
|
136
|
+
* of a duplicate `tool_call`. The SDK can invoke `canUseTool` (→ a permission
|
|
137
|
+
* request, which emits the tool_call eagerly so the client has it before
|
|
138
|
+
* being asked to approve it) either before or after the assistant message's
|
|
139
|
+
* tool_use block streams; this set makes the two paths converge regardless of
|
|
140
|
+
* order. Pruned at `tool_result` time alongside `toolUseCache`. */
|
|
141
|
+
emittedToolCalls: Set<string>;
|
|
128
142
|
/** Maps the ACP `messageId` we expose to clients (see `messageIdForGrouping`)
|
|
129
143
|
* to the SDK message uuid that the Agent SDK's rewind/resume APIs key on
|
|
130
144
|
* (`Query.rewindFiles` takes a user-message uuid; `resumeSessionAt` takes an
|
|
@@ -285,7 +299,14 @@ export declare class ClaudeAcpAgent {
|
|
|
285
299
|
resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse>;
|
|
286
300
|
loadSession(params: LoadSessionRequest): Promise<LoadSessionResponse>;
|
|
287
301
|
listSessions(params: ListSessionsRequest): Promise<ListSessionsResponse>;
|
|
302
|
+
/** Read the SDK-maintained title for a session and, if it changed since the
|
|
303
|
+
* last time we looked, notify the client with a `session_info_update`. The
|
|
304
|
+
* SDK has no push event for the title it auto-generates in the background, so
|
|
305
|
+
* we pull it at turn-end. A missing session file or read error is non-fatal:
|
|
306
|
+
* the title is best-effort and another turn will retry. */
|
|
307
|
+
private maybeUpdateSessionTitle;
|
|
288
308
|
authenticate(_params: AuthenticateRequest): Promise<void>;
|
|
309
|
+
logout(_params: LogoutRequest): Promise<void>;
|
|
289
310
|
prompt(params: PromptRequest): Promise<PromptResponse>;
|
|
290
311
|
/** Lazily start the per-session consumer that drains the SDK query stream for
|
|
291
312
|
* the session's whole life. Idempotent: only the first `prompt()` starts it. */
|
|
@@ -338,6 +359,14 @@ export declare class ClaudeAcpAgent {
|
|
|
338
359
|
* "Tool use aborted" the callers already expect, so a cancelled dialog no
|
|
339
360
|
* longer leaves the `await` hanging. */
|
|
340
361
|
private requestPermissionFromClient;
|
|
362
|
+
/** Emit the `tool_call` a permission request references if it hasn't been sent
|
|
363
|
+
* yet, so the client has the tool call before being asked to approve it. The
|
|
364
|
+
* matching streamed tool_use chunk later refines it with a `tool_call_update`
|
|
365
|
+
* instead of emitting a duplicate (see `emittedToolCalls`). Built via the same
|
|
366
|
+
* `toolCallNotification` helper as the streamed path so the two are identical.
|
|
367
|
+
* Tools the stream renders as a plan (TodoWrite) or suppresses (Task*) are
|
|
368
|
+
* skipped so a permission prompt for them never surfaces a stray tool_call. */
|
|
369
|
+
private ensureToolCallEmitted;
|
|
341
370
|
canUseTool(sessionId: string): CanUseTool;
|
|
342
371
|
/**
|
|
343
372
|
* Handle elicitation requests that originate from MCP servers by forwarding
|
|
@@ -399,12 +428,14 @@ export declare function toAcpNotifications(content: string | ContentBlockParam[]
|
|
|
399
428
|
parentToolUseId?: string | null;
|
|
400
429
|
cwd?: string;
|
|
401
430
|
taskState?: TaskState;
|
|
431
|
+
emittedToolCalls?: Set<string>;
|
|
402
432
|
messageId?: string;
|
|
403
433
|
}): SessionNotification[];
|
|
404
434
|
export declare function streamEventToAcpNotifications(message: SDKPartialAssistantMessage, sessionId: string, toolUseCache: ToolUseCache, client: AcpClient, logger: Logger, options?: {
|
|
405
435
|
clientCapabilities?: ClientCapabilities;
|
|
406
436
|
cwd?: string;
|
|
407
437
|
taskState?: TaskState;
|
|
438
|
+
emittedToolCalls?: Set<string>;
|
|
408
439
|
messageId?: string;
|
|
409
440
|
}): SessionNotification[];
|
|
410
441
|
/** Run a `session/prompt` while honoring `$/cancel_request` for it. ACP clients
|
package/dist/acp-agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,mBAAmB,EAGnB,kBAAkB,EAClB,kBAAkB,EAClB,+BAA+B,EAC/B,wBAAwB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"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"}
|
package/dist/acp-agent.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { agent as acpAgent, methods, ndJsonStream, RequestError, } from "@agentclientprotocol/sdk";
|
|
2
|
-
import { deleteSession, getSessionMessages, listSessions, query, } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
import { deleteSession, getSessionInfo, getSessionMessages, listSessions, query, } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
+
import { execFile } from "node:child_process";
|
|
3
4
|
import { randomUUID } from "node:crypto";
|
|
4
5
|
import * as fs from "node:fs/promises";
|
|
5
6
|
import * as os from "node:os";
|
|
6
7
|
import * as path from "node:path";
|
|
8
|
+
import { promisify } from "node:util";
|
|
7
9
|
import packageJson from "../package.json" with { type: "json" };
|
|
8
10
|
import { applyAskElicitationResponse, askUserQuestionsToCreateRequest, createElicitationResponseToElicitResult, extractAskUserQuestions, mcpElicitationToCreateRequest, } from "./elicitation.js";
|
|
9
11
|
import { SettingsManager } from "./settings.js";
|
|
10
12
|
import { applyTaskCreate, applyTaskUpdate, createPostToolUseHook, createTaskHook, parseTaskCreateOutput, planEntries, registerHookCallback, taskStateToPlanEntries, toolInfoFromToolUse, toolUpdateFromDiffToolResponse, toolUpdateFromToolResult, } from "./tools.js";
|
|
11
13
|
import { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./utils.js";
|
|
12
14
|
export const CLAUDE_CONFIG_DIR = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
|
|
15
|
+
const execFileAsync = promisify(execFile);
|
|
13
16
|
const MAX_TITLE_LENGTH = 256;
|
|
14
17
|
function sanitizeTitle(text) {
|
|
15
18
|
// Replace newlines and collapse whitespace
|
|
@@ -411,6 +414,9 @@ export class ClaudeAcpAgent {
|
|
|
411
414
|
http: true,
|
|
412
415
|
sse: true,
|
|
413
416
|
},
|
|
417
|
+
auth: {
|
|
418
|
+
logout: {},
|
|
419
|
+
},
|
|
414
420
|
loadSession: true,
|
|
415
421
|
sessionCapabilities: {
|
|
416
422
|
additionalDirectories: {},
|
|
@@ -493,6 +499,40 @@ export class ClaudeAcpAgent {
|
|
|
493
499
|
sessions,
|
|
494
500
|
};
|
|
495
501
|
}
|
|
502
|
+
/** Read the SDK-maintained title for a session and, if it changed since the
|
|
503
|
+
* last time we looked, notify the client with a `session_info_update`. The
|
|
504
|
+
* SDK has no push event for the title it auto-generates in the background, so
|
|
505
|
+
* we pull it at turn-end. A missing session file or read error is non-fatal:
|
|
506
|
+
* the title is best-effort and another turn will retry. */
|
|
507
|
+
async maybeUpdateSessionTitle(sessionId, session) {
|
|
508
|
+
let info;
|
|
509
|
+
try {
|
|
510
|
+
info = await getSessionInfo(sessionId, { dir: session.cwd });
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
this.logger.error(`Session ${sessionId}: failed to read session info: ${error}`);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
// `customTitle` is a user-set `/rename`; `summary` is the auto-generated
|
|
517
|
+
// title (or first prompt). Prefer the explicit title when present.
|
|
518
|
+
const rawTitle = info?.customTitle ?? info?.summary;
|
|
519
|
+
if (!rawTitle) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const title = sanitizeTitle(rawTitle);
|
|
523
|
+
if (title === session.lastTitle) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
session.lastTitle = title;
|
|
527
|
+
await this.client.sessionUpdate({
|
|
528
|
+
sessionId,
|
|
529
|
+
update: {
|
|
530
|
+
sessionUpdate: "session_info_update",
|
|
531
|
+
title,
|
|
532
|
+
updatedAt: new Date(info.lastModified).toISOString(),
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
}
|
|
496
536
|
async authenticate(_params) {
|
|
497
537
|
if (_params.methodId === "gateway" || _params.methodId === "gateway-bedrock") {
|
|
498
538
|
this.gatewayAuthRequest = _params;
|
|
@@ -500,6 +540,25 @@ export class ClaudeAcpAgent {
|
|
|
500
540
|
}
|
|
501
541
|
throw new Error("Method not implemented.");
|
|
502
542
|
}
|
|
543
|
+
async logout(_params) {
|
|
544
|
+
// Clear in-memory gateway credentials supplied via `authenticate`. The
|
|
545
|
+
// gateway method never touches the on-disk credential store, so dropping
|
|
546
|
+
// this reference is the whole logout for that path.
|
|
547
|
+
this.gatewayAuthRequest = undefined;
|
|
548
|
+
// For the Claude/Console login methods the credentials live in the native
|
|
549
|
+
// CLI's store (keychain or config dir), which only the binary can clear.
|
|
550
|
+
// `claude auth logout` is non-interactive and idempotent.
|
|
551
|
+
const cliPath = await claudeCliPath();
|
|
552
|
+
try {
|
|
553
|
+
await execFileAsync(cliPath, ["auth", "logout"]);
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
const stderr = typeof error === "object" && error && "stderr" in error
|
|
557
|
+
? String(error.stderr).trim()
|
|
558
|
+
: undefined;
|
|
559
|
+
throw RequestError.internalError({ stderr: stderr || undefined }, `claude auth logout failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
503
562
|
async prompt(params) {
|
|
504
563
|
const session = this.sessions[params.sessionId];
|
|
505
564
|
if (!session) {
|
|
@@ -892,6 +951,11 @@ export class ClaudeAcpAgent {
|
|
|
892
951
|
if (session.cancelled) {
|
|
893
952
|
settleActive({ stopReason: "cancelled" });
|
|
894
953
|
}
|
|
954
|
+
// The SDK generates the session title in a background task and
|
|
955
|
+
// persists it to the session file; `idle` is the turn-over
|
|
956
|
+
// signal, so it's the point at which a new title may have
|
|
957
|
+
// landed. Push it to the client if it changed.
|
|
958
|
+
await this.maybeUpdateSessionTitle(params.sessionId, session);
|
|
895
959
|
}
|
|
896
960
|
break;
|
|
897
961
|
}
|
|
@@ -1042,6 +1106,7 @@ export class ClaudeAcpAgent {
|
|
|
1042
1106
|
case "api_retry":
|
|
1043
1107
|
case "thinking_tokens":
|
|
1044
1108
|
case "model_refusal_fallback":
|
|
1109
|
+
case "model_refusal_no_fallback":
|
|
1045
1110
|
// Todo: process via status api: https://docs.claude.com/en/docs/claude-code/hooks#hook-output
|
|
1046
1111
|
break;
|
|
1047
1112
|
default:
|
|
@@ -1303,6 +1368,7 @@ export class ClaudeAcpAgent {
|
|
|
1303
1368
|
clientCapabilities: this.clientCapabilities,
|
|
1304
1369
|
cwd: session.cwd,
|
|
1305
1370
|
taskState: session.taskState,
|
|
1371
|
+
emittedToolCalls: session.emittedToolCalls,
|
|
1306
1372
|
messageId: currentStreamMessageId,
|
|
1307
1373
|
})) {
|
|
1308
1374
|
await this.client.sessionUpdate(notification);
|
|
@@ -1498,6 +1564,7 @@ export class ClaudeAcpAgent {
|
|
|
1498
1564
|
parentToolUseId: message.parent_tool_use_id,
|
|
1499
1565
|
cwd: session.cwd,
|
|
1500
1566
|
taskState: session.taskState,
|
|
1567
|
+
emittedToolCalls: session.emittedToolCalls,
|
|
1501
1568
|
messageId: messageIdForGrouping(message),
|
|
1502
1569
|
})) {
|
|
1503
1570
|
await this.client.sessionUpdate(notification);
|
|
@@ -1873,7 +1940,14 @@ export class ClaudeAcpAgent {
|
|
|
1873
1940
|
* outcome or a `requestCancelled` rejection). Either way we surface the same
|
|
1874
1941
|
* "Tool use aborted" the callers already expect, so a cancelled dialog no
|
|
1875
1942
|
* longer leaves the `await` hanging. */
|
|
1876
|
-
async requestPermissionFromClient(params, signal) {
|
|
1943
|
+
async requestPermissionFromClient(params, toolName, signal) {
|
|
1944
|
+
// The SDK may invoke `canUseTool` (and therefore this permission request)
|
|
1945
|
+
// before the assistant message's tool_use block streams to us. Some ACP clients
|
|
1946
|
+
// expect the `tool_call` a permission request references to already exist,
|
|
1947
|
+
// so emit it now if it hasn't been sent yet. The streamed tool_use chunk
|
|
1948
|
+
// later refines it with a `tool_call_update` rather than emitting a
|
|
1949
|
+
// duplicate (see `emittedToolCalls` in `toAcpNotifications`).
|
|
1950
|
+
await this.ensureToolCallEmitted(params.sessionId, toolName, params.toolCall.toolCallId, params.toolCall.rawInput);
|
|
1877
1951
|
try {
|
|
1878
1952
|
return await this.client.requestPermission(params, signal);
|
|
1879
1953
|
}
|
|
@@ -1884,6 +1958,28 @@ export class ClaudeAcpAgent {
|
|
|
1884
1958
|
throw error;
|
|
1885
1959
|
}
|
|
1886
1960
|
}
|
|
1961
|
+
/** Emit the `tool_call` a permission request references if it hasn't been sent
|
|
1962
|
+
* yet, so the client has the tool call before being asked to approve it. The
|
|
1963
|
+
* matching streamed tool_use chunk later refines it with a `tool_call_update`
|
|
1964
|
+
* instead of emitting a duplicate (see `emittedToolCalls`). Built via the same
|
|
1965
|
+
* `toolCallNotification` helper as the streamed path so the two are identical.
|
|
1966
|
+
* Tools the stream renders as a plan (TodoWrite) or suppresses (Task*) are
|
|
1967
|
+
* skipped so a permission prompt for them never surfaces a stray tool_call. */
|
|
1968
|
+
async ensureToolCallEmitted(sessionId, toolName, toolCallId, toolInput) {
|
|
1969
|
+
const session = this.sessions[sessionId];
|
|
1970
|
+
if (!session || !shouldEmitToolCall(toolName)) {
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
if (session.emittedToolCalls.has(toolCallId)) {
|
|
1974
|
+
return;
|
|
1975
|
+
}
|
|
1976
|
+
session.emittedToolCalls.add(toolCallId);
|
|
1977
|
+
const supportsTerminalOutput = this.clientCapabilities?._meta?.["terminal_output"] === true;
|
|
1978
|
+
await this.client.sessionUpdate({
|
|
1979
|
+
sessionId,
|
|
1980
|
+
update: toolCallNotification({ id: toolCallId, name: toolName, input: toolInput }, toolInput, supportsTerminalOutput, session.cwd),
|
|
1981
|
+
});
|
|
1982
|
+
}
|
|
1887
1983
|
canUseTool(sessionId) {
|
|
1888
1984
|
return async (toolName, toolInput, { signal, suggestions, toolUseID }) => {
|
|
1889
1985
|
const alwaysAllowLabel = describeAlwaysAllow(suggestions, toolName);
|
|
@@ -1900,6 +1996,9 @@ export class ClaudeAcpAgent {
|
|
|
1900
1996
|
// than the interactive dialog). Present it as an ACP form elicitation and
|
|
1901
1997
|
// feed the answers back as updatedInput for the tool's own call() to read.
|
|
1902
1998
|
if (toolName === "AskUserQuestion" && this.clientCapabilities?.elicitation?.form) {
|
|
1999
|
+
// Like permission requests, the elicitation references this toolUseID, so
|
|
2000
|
+
// make sure the tool_call has surfaced to the client before we send it.
|
|
2001
|
+
await this.ensureToolCallEmitted(sessionId, toolName, toolUseID, toolInput);
|
|
1903
2002
|
return this.handleAskUserQuestion(sessionId, toolInput, toolUseID, signal);
|
|
1904
2003
|
}
|
|
1905
2004
|
if (toolName === "ExitPlanMode") {
|
|
@@ -1934,7 +2033,7 @@ export class ClaudeAcpAgent {
|
|
|
1934
2033
|
rawInput: toolInput,
|
|
1935
2034
|
...toolInfoFromToolUse({ name: toolName, input: toolInput, id: toolUseID }, supportsTerminalOutput, session?.cwd),
|
|
1936
2035
|
},
|
|
1937
|
-
}, signal);
|
|
2036
|
+
}, toolName, signal);
|
|
1938
2037
|
if (signal.aborted || response.outcome?.outcome === "cancelled") {
|
|
1939
2038
|
throw new Error("Tool use aborted");
|
|
1940
2039
|
}
|
|
@@ -1993,7 +2092,7 @@ export class ClaudeAcpAgent {
|
|
|
1993
2092
|
rawInput: toolInput,
|
|
1994
2093
|
...toolInfoFromToolUse({ name: toolName, input: toolInput, id: toolUseID }, supportsTerminalOutput, session?.cwd),
|
|
1995
2094
|
},
|
|
1996
|
-
}, signal);
|
|
2095
|
+
}, toolName, signal);
|
|
1997
2096
|
if (signal.aborted || response.outcome?.outcome === "cancelled") {
|
|
1998
2097
|
throw new Error("Tool use aborted");
|
|
1999
2098
|
}
|
|
@@ -2601,6 +2700,7 @@ export class ClaudeAcpAgent {
|
|
|
2601
2700
|
contextWindowSize: inferContextWindowFromModel(models.currentModelId, currentModelInfo?.displayName, currentModelInfo?.description) ?? DEFAULT_CONTEXT_WINDOW,
|
|
2602
2701
|
taskState,
|
|
2603
2702
|
toolUseCache: {},
|
|
2703
|
+
emittedToolCalls: new Set(),
|
|
2604
2704
|
messageIdToUuid: new Map(),
|
|
2605
2705
|
};
|
|
2606
2706
|
return {
|
|
@@ -3223,6 +3323,53 @@ function applyMessageId(update, messageId) {
|
|
|
3223
3323
|
update.messageId = messageId;
|
|
3224
3324
|
}
|
|
3225
3325
|
}
|
|
3326
|
+
/** Built-in tools that drive the task list (headless/SDK sessions use these
|
|
3327
|
+
* instead of TodoWrite). Their tool_use/tool_result are surfaced as `plan`
|
|
3328
|
+
* snapshots rather than as tool_calls. */
|
|
3329
|
+
function isTaskTool(toolName) {
|
|
3330
|
+
return (toolName === "TaskCreate" ||
|
|
3331
|
+
toolName === "TaskUpdate" ||
|
|
3332
|
+
toolName === "TaskList" ||
|
|
3333
|
+
toolName === "TaskGet");
|
|
3334
|
+
}
|
|
3335
|
+
/** Whether a tool's tool_use surfaces to the client as a standalone
|
|
3336
|
+
* `tool_call`. TodoWrite is rendered as a `plan` and Task* tools are
|
|
3337
|
+
* suppressed (their plan snapshot is emitted at tool_result time), so neither
|
|
3338
|
+
* produces a tool_call. */
|
|
3339
|
+
function shouldEmitToolCall(toolName) {
|
|
3340
|
+
return toolName !== "TodoWrite" && !isTaskTool(toolName);
|
|
3341
|
+
}
|
|
3342
|
+
/** Build the `tool_call` (or, with `refine`, the `tool_call_update`)
|
|
3343
|
+
* notification for a tool_use. Shared by every site that surfaces a tool call:
|
|
3344
|
+
* the streamed tool_use path (first encounter → tool_call, later encounter →
|
|
3345
|
+
* refine) and the permission flow (`ensureToolCallEmitted`), so they can't
|
|
3346
|
+
* drift. The initial `tool_call` carries `status: "pending"` and, for Bash, the
|
|
3347
|
+
* `terminal_info` _meta that the later `terminal_output`/`terminal_exit`
|
|
3348
|
+
* updates key off of; a refining `tool_call_update` carries neither. */
|
|
3349
|
+
function toolCallNotification(toolUse, rawInput, supportsTerminalOutput, cwd, refine = false) {
|
|
3350
|
+
if (refine) {
|
|
3351
|
+
return {
|
|
3352
|
+
_meta: { claudeCode: { toolName: toolUse.name } },
|
|
3353
|
+
toolCallId: toolUse.id,
|
|
3354
|
+
sessionUpdate: "tool_call_update",
|
|
3355
|
+
rawInput,
|
|
3356
|
+
...toolInfoFromToolUse(toolUse, supportsTerminalOutput, cwd),
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
return {
|
|
3360
|
+
_meta: {
|
|
3361
|
+
claudeCode: { toolName: toolUse.name },
|
|
3362
|
+
...(toolUse.name === "Bash" && supportsTerminalOutput
|
|
3363
|
+
? { terminal_info: { terminal_id: toolUse.id } }
|
|
3364
|
+
: {}),
|
|
3365
|
+
},
|
|
3366
|
+
toolCallId: toolUse.id,
|
|
3367
|
+
sessionUpdate: "tool_call",
|
|
3368
|
+
rawInput,
|
|
3369
|
+
status: "pending",
|
|
3370
|
+
...toolInfoFromToolUse(toolUse, supportsTerminalOutput, cwd),
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3226
3373
|
/**
|
|
3227
3374
|
* Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
|
|
3228
3375
|
* Only handles text, image, and thinking chunks for now.
|
|
@@ -3305,10 +3452,7 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
|
|
|
3305
3452
|
};
|
|
3306
3453
|
}
|
|
3307
3454
|
}
|
|
3308
|
-
else if (chunk.name
|
|
3309
|
-
chunk.name === "TaskUpdate" ||
|
|
3310
|
-
chunk.name === "TaskList" ||
|
|
3311
|
-
chunk.name === "TaskGet") {
|
|
3455
|
+
else if (isTaskTool(chunk.name)) {
|
|
3312
3456
|
// Task* tool_use is suppressed; the plan update is emitted at
|
|
3313
3457
|
// tool_result time once we have the task ID (for TaskCreate) and
|
|
3314
3458
|
// confirmation that the change took effect.
|
|
@@ -3360,40 +3504,25 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
|
|
|
3360
3504
|
catch {
|
|
3361
3505
|
// ignore if we can't turn it to JSON
|
|
3362
3506
|
}
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
};
|
|
3507
|
+
// Emit a `tool_call` only the first time this id surfaces to the
|
|
3508
|
+
// client; afterwards refine it with a `tool_call_update`. The first
|
|
3509
|
+
// surface may be this stream chunk OR an earlier permission request
|
|
3510
|
+
// (see `ensureToolCallEmitted`), so emission is tracked separately
|
|
3511
|
+
// from `toolUseCache`. Without an `emittedToolCalls` set we fall back
|
|
3512
|
+
// to cache presence — the historical streaming-only behavior.
|
|
3513
|
+
const emittedToolCalls = options?.emittedToolCalls;
|
|
3514
|
+
const alreadyEmitted = emittedToolCalls ? emittedToolCalls.has(chunk.id) : alreadyCached;
|
|
3515
|
+
emittedToolCalls?.add(chunk.id);
|
|
3516
|
+
if (alreadyEmitted) {
|
|
3517
|
+
// Already surfaced (full assistant message after streaming, or a
|
|
3518
|
+
// permission request emitted it first) — refine with a
|
|
3519
|
+
// tool_call_update rather than emitting a duplicate tool_call.
|
|
3520
|
+
update = toolCallNotification(chunk, rawInput, supportsTerminalOutput, options?.cwd, true);
|
|
3378
3521
|
}
|
|
3379
3522
|
else {
|
|
3380
|
-
// First
|
|
3381
|
-
//
|
|
3382
|
-
update =
|
|
3383
|
-
_meta: {
|
|
3384
|
-
claudeCode: {
|
|
3385
|
-
toolName: chunk.name,
|
|
3386
|
-
},
|
|
3387
|
-
...(chunk.name === "Bash" && supportsTerminalOutput
|
|
3388
|
-
? { terminal_info: { terminal_id: chunk.id } }
|
|
3389
|
-
: {}),
|
|
3390
|
-
},
|
|
3391
|
-
toolCallId: chunk.id,
|
|
3392
|
-
sessionUpdate: "tool_call",
|
|
3393
|
-
rawInput,
|
|
3394
|
-
status: "pending",
|
|
3395
|
-
...toolInfoFromToolUse(chunk, supportsTerminalOutput, options?.cwd),
|
|
3396
|
-
};
|
|
3523
|
+
// First surface (streaming content_block_start or replay) — send as
|
|
3524
|
+
// tool_call (with terminal_info for Bash).
|
|
3525
|
+
update = toolCallNotification(chunk, rawInput, supportsTerminalOutput, options?.cwd);
|
|
3397
3526
|
}
|
|
3398
3527
|
}
|
|
3399
3528
|
break;
|
|
@@ -3406,15 +3535,13 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
|
|
|
3406
3535
|
case "bash_code_execution_tool_result":
|
|
3407
3536
|
case "text_editor_code_execution_tool_result":
|
|
3408
3537
|
case "mcp_tool_result": {
|
|
3538
|
+
options?.emittedToolCalls?.delete(chunk.tool_use_id);
|
|
3409
3539
|
const toolUse = toolUseCache[chunk.tool_use_id];
|
|
3410
3540
|
if (!toolUse) {
|
|
3411
3541
|
logger.error(`[claude-agent-acp] Got a tool result for tool use that wasn't tracked: ${chunk.tool_use_id}`);
|
|
3412
3542
|
break;
|
|
3413
3543
|
}
|
|
3414
|
-
if (toolUse.name
|
|
3415
|
-
toolUse.name === "TaskUpdate" ||
|
|
3416
|
-
toolUse.name === "TaskList" ||
|
|
3417
|
-
toolUse.name === "TaskGet") {
|
|
3544
|
+
if (isTaskTool(toolUse.name)) {
|
|
3418
3545
|
// Headless/SDK sessions emit Task* tools instead of TodoWrite.
|
|
3419
3546
|
// TaskCreate / TaskUpdate mutate the accumulated task list; TaskList
|
|
3420
3547
|
// and TaskGet are read-only so we just suppress their tool_call /
|
|
@@ -3521,6 +3648,7 @@ export function streamEventToAcpNotifications(message, sessionId, toolUseCache,
|
|
|
3521
3648
|
parentToolUseId: message.parent_tool_use_id,
|
|
3522
3649
|
cwd: options?.cwd,
|
|
3523
3650
|
taskState: options?.taskState,
|
|
3651
|
+
emittedToolCalls: options?.emittedToolCalls,
|
|
3524
3652
|
messageId: options?.messageId,
|
|
3525
3653
|
});
|
|
3526
3654
|
case "content_block_delta":
|
|
@@ -3529,6 +3657,7 @@ export function streamEventToAcpNotifications(message, sessionId, toolUseCache,
|
|
|
3529
3657
|
parentToolUseId: message.parent_tool_use_id,
|
|
3530
3658
|
cwd: options?.cwd,
|
|
3531
3659
|
taskState: options?.taskState,
|
|
3660
|
+
emittedToolCalls: options?.emittedToolCalls,
|
|
3532
3661
|
messageId: options?.messageId,
|
|
3533
3662
|
});
|
|
3534
3663
|
// No content. `ping` is a Messages-API keep-alive event that the SDK's
|
|
@@ -3598,6 +3727,7 @@ export function runAcp() {
|
|
|
3598
3727
|
.onRequest(methods.agent.session.setMode, (ctx) => agent.setSessionMode(ctx.params))
|
|
3599
3728
|
.onRequest(methods.agent.session.setConfigOption, (ctx) => agent.setSessionConfigOption(ctx.params))
|
|
3600
3729
|
.onRequest(methods.agent.authenticate, (ctx) => agent.authenticate(ctx.params))
|
|
3730
|
+
.onRequest(methods.agent.logout, (ctx) => agent.logout(ctx.params))
|
|
3601
3731
|
.onRequest(methods.agent.session.prompt, (ctx) => runPromptWithCancellation(agent, ctx.params, ctx.signal))
|
|
3602
3732
|
.onNotification(methods.agent.session.cancel, (ctx) => agent.cancel(ctx.params))
|
|
3603
3733
|
.connect(stream);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elicitation.d.ts","sourceRoot":"","sources":["../src/elicitation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EAI1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAC5F,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,6CAA6C,CAAC;AAErD;;;;;;;;;;;;;GAaG;AAEH,yDAAyD;AACzD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAChB,wBAAwB,GAAG,IAAI,CAyBjC;AAED;;;GAGG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,yBAAyB,GAClC,iBAAiB,CAUnB;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,EAAE,GAAG,IAAI,CAUhG;AA4CD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,eAAe,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CAwD1B;AAED,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,
|
|
1
|
+
{"version":3,"file":"elicitation.d.ts","sourceRoot":"","sources":["../src/elicitation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EAI1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAC5F,OAAO,KAAK,EACV,oBAAoB,EAErB,MAAM,6CAA6C,CAAC;AAErD;;;;;;;;;;;;;GAaG;AAEH,yDAAyD;AACzD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,GAChB,wBAAwB,GAAG,IAAI,CAyBjC;AAED;;;GAGG;AACH,wBAAgB,uCAAuC,CACrD,QAAQ,EAAE,yBAAyB,GAClC,iBAAiB,CAUnB;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,EAAE,GAAG,IAAI,CAUhG;AA4CD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,eAAe,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CAwD1B;AAED,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAChC;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,yBAAyB,EACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,SAAS,EAAE,eAAe,EAAE,GAC3B,sBAAsB,CAkCxB"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolveSettings } from "@anthropic-ai/claude-agent-sdk";
|
|
3
3
|
import { claudeCliPath, runAcp } from "./acp-agent.js";
|
|
4
|
+
import packageJson from "../package.json" with { type: "json" };
|
|
5
|
+
// `--cli` is checked first so that `--version`/`-v` (and any other flags) are
|
|
6
|
+
// forwarded to the wrapped native CLI rather than swallowed by our own version
|
|
7
|
+
// handler below. Our version flag only applies when not delegating.
|
|
4
8
|
if (process.argv.includes("--cli")) {
|
|
5
9
|
const { spawn } = await import("node:child_process");
|
|
6
10
|
const args = process.argv.slice(2).filter((arg) => arg !== "--cli");
|
|
@@ -31,6 +35,10 @@ if (process.argv.includes("--cli")) {
|
|
|
31
35
|
process.exit(1);
|
|
32
36
|
});
|
|
33
37
|
}
|
|
38
|
+
else if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
39
|
+
console.log(packageJson.version);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
34
42
|
else {
|
|
35
43
|
// Apply env vars from the managed-policy tier before any SDK call so the
|
|
36
44
|
// SDK subprocess inherits them. Going through resolveSettings (vs. a raw
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.53.0",
|
|
7
7
|
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
|
|
8
8
|
"main": "dist/lib.js",
|
|
9
9
|
"types": "dist/lib.d.ts",
|
|
@@ -63,21 +63,21 @@
|
|
|
63
63
|
"node": ">=22"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@agentclientprotocol/sdk": "0.
|
|
67
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
66
|
+
"@agentclientprotocol/sdk": "1.0.0",
|
|
67
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.195",
|
|
68
68
|
"zod": "^3.25.0 || ^4.0.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@anthropic-ai/sdk": "0.
|
|
71
|
+
"@anthropic-ai/sdk": "0.106.0",
|
|
72
72
|
"@eslint/js": "10.0.1",
|
|
73
73
|
"@tsconfig/node22": "22.0.5",
|
|
74
|
-
"@types/node": "26.0.
|
|
74
|
+
"@types/node": "26.0.1",
|
|
75
75
|
"@typescript-eslint/eslint-plugin": "8.62.0",
|
|
76
76
|
"@typescript-eslint/parser": "8.62.0",
|
|
77
|
-
"eslint": "10.
|
|
77
|
+
"eslint": "10.6.0",
|
|
78
78
|
"eslint-config-prettier": "10.1.8",
|
|
79
79
|
"globals": "17.7.0",
|
|
80
|
-
"prettier": "3.
|
|
80
|
+
"prettier": "3.9.1",
|
|
81
81
|
"ts-node": "10.9.2",
|
|
82
82
|
"typescript": "6.0.3",
|
|
83
83
|
"vitest": "4.1.9"
|