@agentclientprotocol/claude-agent-acp 0.47.0 → 0.49.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.
@@ -1,5 +1,5 @@
1
- import { Agent, AgentSideConnection, AuthenticateRequest, CancelNotification, ClientCapabilities, ForkSessionRequest, ForkSessionResponse, InitializeRequest, InitializeResponse, ListSessionsRequest, ListSessionsResponse, LoadSessionRequest, LoadSessionResponse, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, ReadTextFileRequest, ReadTextFileResponse, ResumeSessionRequest, ResumeSessionResponse, SessionConfigOption, SessionModeState, SessionNotification, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, SetSessionModeRequest, SetSessionModeResponse, CloseSessionRequest, CloseSessionResponse, DeleteSessionRequest, DeleteSessionResponse, WriteTextFileRequest, WriteTextFileResponse } from "@agentclientprotocol/sdk";
2
- import { CanUseTool, ModelInfo, Options, PermissionMode, PermissionUpdate, Query, SDKMessageOrigin, SDKPartialAssistantMessage, SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
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";
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";
5
5
  import { SettingsManager } from "./settings.js";
@@ -89,6 +89,14 @@ type Session = {
89
89
  models: SessionModelState;
90
90
  modelInfos: ModelInfo[];
91
91
  configOptions: SessionConfigOption[];
92
+ /** Custom main-thread agent personas the user (or a plugin/project) has
93
+ * configured, discovered via `supportedAgents()` with Claude Code's built-in
94
+ * subagents filtered out. Empty when none are configured, in which case the
95
+ * "agent" config option is omitted entirely. */
96
+ agents: AgentInfo[];
97
+ /** The currently selected main-thread agent name, or "default" for the
98
+ * standard Claude Code agent (no `agent` flag applied). */
99
+ currentAgent: string;
92
100
  abortController: AbortController;
93
101
  /** Signal the consumer races `query.next()` against. Aborted by cancel()
94
102
  * (after a grace period) to force the active turn to settle "cancelled" when
@@ -237,11 +245,27 @@ export declare function resolvePermissionMode(defaultMode?: unknown, logger?: Lo
237
245
  * tool so "Always Allow" is never a blank check without disclosure.
238
246
  */
239
247
  export declare function describeAlwaysAllow(suggestions: PermissionUpdate[] | undefined, toolName: string): string;
240
- export declare class ClaudeAcpAgent implements Agent {
248
+ /**
249
+ * Client-facing surface the agent calls back into. This is the subset of ACP
250
+ * client methods the agent actually uses, expressed as a narrow interface so
251
+ * tests can supply lightweight mocks. In production it is backed by
252
+ * {@link ClientConnection} over the SDK's typed `AgentContext`.
253
+ */
254
+ export interface AcpClient {
255
+ sessionUpdate(params: SessionNotification): Promise<void>;
256
+ requestPermission(params: RequestPermissionRequest): Promise<RequestPermissionResponse>;
257
+ readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
258
+ writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
259
+ unstable_createElicitation(params: CreateElicitationRequest): Promise<CreateElicitationResponse>;
260
+ unstable_completeElicitation(params: CompleteElicitationNotification): Promise<void>;
261
+ /** Send a custom (extension) notification, e.g. `_claude/sdkMessage`. */
262
+ extNotification(method: string, params: Record<string, unknown>): Promise<void>;
263
+ }
264
+ export declare class ClaudeAcpAgent {
241
265
  sessions: {
242
266
  [key: string]: Session;
243
267
  };
244
- client: AgentSideConnection;
268
+ client: AcpClient;
245
269
  clientCapabilities?: ClientCapabilities;
246
270
  logger: Logger;
247
271
  gatewayAuthRequest?: GatewayAuthRequest;
@@ -249,7 +273,7 @@ export declare class ClaudeAcpAgent implements Agent {
249
273
  * return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
250
274
  * tests can shrink it. */
251
275
  forceCancelGraceMs: number;
252
- constructor(client: AgentSideConnection, logger?: Logger);
276
+ constructor(client: AcpClient, logger?: Logger);
253
277
  initialize(request: InitializeRequest): Promise<InitializeResponse>;
254
278
  newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
255
279
  unstable_forkSession(params: ForkSessionRequest): Promise<ForkSessionResponse>;
@@ -327,6 +351,14 @@ export declare class ClaudeAcpAgent implements Agent {
327
351
  private validateCwd;
328
352
  private createSession;
329
353
  }
354
+ export declare const BUILTIN_AGENT_NAMES: Set<string>;
355
+ export declare const DEFAULT_AGENT_ID = "default";
356
+ /** Discover user/plugin/project-configured main-thread agents, excluding the
357
+ * built-in subagents and the reserved "default" sentinel. Returns an empty
358
+ * list if discovery fails so a flaky control request never blocks session
359
+ * creation. */
360
+ export declare function discoverCustomAgents(q: Query): Promise<AgentInfo[]>;
361
+ export declare function buildConfigOptions(modes: SessionModeState, models: SessionModelState, modelInfos: ModelInfo[], currentEffortLevel?: string, agents?: AgentInfo[], currentAgent?: string): SessionConfigOption[];
330
362
  export declare function promptToClaude(prompt: PromptRequest): SDKUserMessage;
331
363
  /**
332
364
  * Resolves the ACP `messageId` for a Claude SDK message (live) or a persisted
@@ -348,7 +380,7 @@ export declare function messageIdForGrouping(message: {
348
380
  * Convert an SDKAssistantMessage (Claude) to a SessionNotification (ACP).
349
381
  * Only handles text, image, and thinking chunks for now.
350
382
  */
351
- export declare function toAcpNotifications(content: string | ContentBlockParam[] | BetaContentBlock[] | BetaRawContentBlockDelta[], role: "assistant" | "user", sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger, options?: {
383
+ export declare function toAcpNotifications(content: string | ContentBlockParam[] | BetaContentBlock[] | BetaRawContentBlockDelta[], role: "assistant" | "user", sessionId: string, toolUseCache: ToolUseCache, client: AcpClient, logger: Logger, options?: {
352
384
  registerHooks?: boolean;
353
385
  clientCapabilities?: ClientCapabilities;
354
386
  parentToolUseId?: string | null;
@@ -356,14 +388,14 @@ export declare function toAcpNotifications(content: string | ContentBlockParam[]
356
388
  taskState?: TaskState;
357
389
  messageId?: string;
358
390
  }): SessionNotification[];
359
- export declare function streamEventToAcpNotifications(message: SDKPartialAssistantMessage, sessionId: string, toolUseCache: ToolUseCache, client: AgentSideConnection, logger: Logger, options?: {
391
+ export declare function streamEventToAcpNotifications(message: SDKPartialAssistantMessage, sessionId: string, toolUseCache: ToolUseCache, client: AcpClient, logger: Logger, options?: {
360
392
  clientCapabilities?: ClientCapabilities;
361
393
  cwd?: string;
362
394
  taskState?: TaskState;
363
395
  messageId?: string;
364
396
  }): SessionNotification[];
365
397
  export declare function runAcp(): {
366
- connection: AgentSideConnection;
398
+ connection: import("@agentclientprotocol/sdk").AgentConnection;
367
399
  agent: ClaudeAcpAgent;
368
400
  };
369
401
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"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"}
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,4 +1,4 @@
1
- import { AgentSideConnection, ndJsonStream, RequestError, } from "@agentclientprotocol/sdk";
1
+ import { agent as acpAgent, methods, ndJsonStream, RequestError, } from "@agentclientprotocol/sdk";
2
2
  import { deleteSession, getSessionMessages, listSessions, query, } from "@anthropic-ai/claude-agent-sdk";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import * as fs from "node:fs/promises";
@@ -246,7 +246,38 @@ export function describeAlwaysAllow(suggestions, toolName) {
246
246
  }
247
247
  return `Always Allow ${parts.join(" and ")}`;
248
248
  }
249
- // Implement the ACP Agent interface
249
+ /**
250
+ * Bridges {@link AcpClient} to the connection-scoped {@link AgentContext}
251
+ * exposed by `AgentApp.connect(...)` as `connection.client`. The peer handle is
252
+ * valid for the entire connection lifetime, so it is captured once at
253
+ * construction.
254
+ */
255
+ class ClientConnection {
256
+ constructor(ctx) {
257
+ this.ctx = ctx;
258
+ }
259
+ sessionUpdate(params) {
260
+ return this.ctx.notify(methods.client.session.update, params);
261
+ }
262
+ requestPermission(params) {
263
+ return this.ctx.request(methods.client.session.requestPermission, params);
264
+ }
265
+ readTextFile(params) {
266
+ return this.ctx.request(methods.client.fs.readTextFile, params);
267
+ }
268
+ writeTextFile(params) {
269
+ return this.ctx.request(methods.client.fs.writeTextFile, params);
270
+ }
271
+ unstable_createElicitation(params) {
272
+ return this.ctx.request(methods.client.elicitation.create, params);
273
+ }
274
+ unstable_completeElicitation(params) {
275
+ return this.ctx.notify(methods.client.elicitation.complete, params);
276
+ }
277
+ extNotification(method, params) {
278
+ return this.ctx.notify(method, params);
279
+ }
280
+ }
250
281
  export class ClaudeAcpAgent {
251
282
  constructor(client, logger) {
252
283
  /** Grace period before a `session/cancel` forces a wedged prompt loop to
@@ -546,12 +577,17 @@ export class ClaudeAcpAgent {
546
577
  // (whose delta events don't carry it) can all be tagged with the same,
547
578
  // replay-stable id.
548
579
  let currentStreamMessageId;
549
- // Per-message-id record of which assistant content actually streamed live
550
- // via `stream_event` deltas, split by block type, so the consolidated
551
- // `assistant` message can drop the duplicate blocks that already reached the
552
- // client as chunks while still forwarding any that a non-streaming gateway
553
- const streamedTextIds = new Set();
554
- const streamedThinkingIds = new Set();
580
+ // The text/thinking blocks that have actually streamed live as
581
+ // `stream_event` deltas for the message the next consolidated `assistant`
582
+ // will repeat, in stream order, each accumulated to its full streamed text.
583
+ // The consolidated handler diffs each assembled block against these and
584
+ // forwards only the un-streamed remainder — nothing if it streamed in full
585
+ // (the common case), the whole block if it never streamed (a non-streaming
586
+ // gateway), or just the tail if the stream was cut short mid-block. Matching
587
+ // on content rather than the Anthropic message id makes dedupe robust to
588
+ // gateways that don't carry a stable/matching id across the stream and the
589
+ // consolidated message. Reset after each consolidated message consumes it.
590
+ const streamedBlocks = [];
555
591
  // Stop reason accumulated for the active turn (result subtype, refusal,
556
592
  // max_tokens, …). Reset per turn; read when the turn settles at idle.
557
593
  let stopReason = "end_turn";
@@ -562,7 +598,14 @@ export class ClaudeAcpAgent {
562
598
  lastAssistantError = undefined;
563
599
  lastRefusalExplanation = null;
564
600
  compactionInProgress = false;
565
- currentStreamMessageId = undefined;
601
+ // Do NOT reset currentStreamMessageId or streamedBlocks here. Turn
602
+ // activation can fire mid-message (the replayed user echo with
603
+ // --replay-user-messages lands between a message's blocks); clearing the
604
+ // streamed-content record on activation would drop the blocks that
605
+ // streamed before the echo, so the consolidated assistant message would
606
+ // re-emit them as duplicates. streamedBlocks is bounded instead by being
607
+ // cleared when each consolidated message consumes it. #785 stopped
608
+ // resetting the streamed-content tracking here but left this line.
566
609
  stopReason = "end_turn";
567
610
  session.accumulatedUsage = {
568
611
  inputTokens: 0,
@@ -1153,20 +1196,50 @@ export class ClaudeAcpAgent {
1153
1196
  // it) can all be tagged with the same, replay-stable id.
1154
1197
  if (message.event.type === "message_start") {
1155
1198
  currentStreamMessageId = message.event.message.id || undefined;
1199
+ // A new top-level message starts: clear any streamed-content
1200
+ // residue from a prior message that never reached its
1201
+ // consolidated reset — a cancelled turn breaks out before the
1202
+ // reset, and the synthetic-auth/system/local-command paths
1203
+ // `break` early too. Block indices restart at 0 each message, so
1204
+ // leftover entries would otherwise collide with this message's
1205
+ // blocks and re-emit (or truncate) already-streamed text. Gated on
1206
+ // `parent_tool_use_id === null` so a subagent stream can't clear
1207
+ // the top-level record. Fires once, before any of this message's
1208
+ // blocks, so it doesn't disturb the mid-message turn-activation
1209
+ // path the way resetting on turn activation would.
1210
+ if (message.parent_tool_use_id === null) {
1211
+ streamedBlocks.length = 0;
1212
+ }
1156
1213
  }
1157
- // Record that this top-level message id actually streamed text/
1158
- // thinking, so the `assistant` case below knows its assembled blocks
1159
- // are duplicates (filter them) rather than the only copy (forward
1160
- // them). Gated on `parent_tool_use_id === null` so a subagent stream
1161
- // can't attribute its content to the top-level message id.
1162
- if (currentStreamMessageId &&
1163
- message.parent_tool_use_id === null &&
1214
+ // Accumulate the text/thinking actually streamed live, so the
1215
+ // `assistant` case below can diff its assembled blocks against what
1216
+ // already reached the client as chunks and forward only the
1217
+ // remainder. Gated on `parent_tool_use_id === null` so a subagent
1218
+ // stream can't attribute its content to the top-level message.
1219
+ // Contiguous deltas of the same block (same index and type) extend
1220
+ // the current entry; anything else opens a new one.
1221
+ if (message.parent_tool_use_id === null &&
1164
1222
  message.event.type === "content_block_delta") {
1165
- if (message.event.delta.type === "text_delta") {
1166
- streamedTextIds.add(currentStreamMessageId);
1167
- }
1168
- else if (message.event.delta.type === "thinking_delta") {
1169
- streamedThinkingIds.add(currentStreamMessageId);
1223
+ const delta = message.event.delta;
1224
+ const chunk = delta.type === "text_delta"
1225
+ ? { type: "text", text: delta.text }
1226
+ : delta.type === "thinking_delta"
1227
+ ? { type: "thinking", text: delta.thinking }
1228
+ : undefined;
1229
+ // Skip empty deltas (some gateways emit empty thinking chunks —
1230
+ // #793): appending "" is a no-op, but pushing a "" entry would
1231
+ // create a block the consolidated handler's `text.length > 0`
1232
+ // guard can never consume, stalling the diff cursor and
1233
+ // re-emitting the next block as a duplicate.
1234
+ if (chunk && chunk.text.length > 0) {
1235
+ const index = message.event.index;
1236
+ const last = streamedBlocks[streamedBlocks.length - 1];
1237
+ if (last && last.index === index && last.type === chunk.type) {
1238
+ last.text += chunk.text;
1239
+ }
1240
+ else {
1241
+ streamedBlocks.push({ index, type: chunk.type, text: chunk.text });
1242
+ }
1170
1243
  }
1171
1244
  }
1172
1245
  if (message.parent_tool_use_id === null &&
@@ -1340,40 +1413,65 @@ export class ClaudeAcpAgent {
1340
1413
  }
1341
1414
  let content;
1342
1415
  if (message.type === "assistant" && message.parent_tool_use_id === null) {
1343
- // Top-level assistant message: drop text/thinking blocks already
1344
- // streamed live as chunks, and forward (as a fallback) any that were
1345
- // not, so non-streaming gateways still deliver the final answer.
1346
- const id = messageIdForGrouping(message);
1347
- content = message.message.content.filter((item) => {
1348
- // Non-text blocks (tool_use, etc.) always pass through; their own
1349
- // dedupe (`toolUseCache`) collapses the streamed/assembled pair.
1416
+ // Top-level assistant message: each text/thinking block may have
1417
+ // already been streamed live as deltas. Diff each against what
1418
+ // streamed (`streamedBlocks`, in document order) and forward only
1419
+ // the un-streamed remainder — nothing if it streamed in full (the
1420
+ // common case), the whole block if it never streamed (a
1421
+ // non-streaming gateway), or just the tail if the stream was cut
1422
+ // short mid-block. `streamPos` walks the streamed blocks in step
1423
+ // with the assembled text/thinking blocks; tool_use and other
1424
+ // blocks pass through untouched (their own `toolUseCache` collapses
1425
+ // the streamed/assembled pair) without advancing it.
1426
+ const blocks = message.message.content;
1427
+ const kept = [];
1428
+ let streamPos = 0;
1429
+ for (const item of blocks) {
1350
1430
  if (item.type !== "text" && item.type !== "thinking") {
1351
- return true;
1431
+ kept.push(item);
1432
+ continue;
1352
1433
  }
1353
- // Already delivered live as a chunk of this exact type — drop the
1354
- // duplicate. Checked per type so streaming one (e.g. text) doesn't
1355
- // suppress an un-streamed block of the other (e.g. thinking).
1356
- const streamedLive = id !== undefined &&
1357
- (item.type === "text" ? streamedTextIds : streamedThinkingIds).has(id);
1358
- if (streamedLive) {
1359
- return false;
1434
+ const full = item.type === "text" ? item.text : item.thinking;
1435
+ // Empty assembled blocks carry nothing (some gateways emit an
1436
+ // empty `thinking` block before the real text) — drop them.
1437
+ if (full.length === 0) {
1438
+ continue;
1360
1439
  }
1361
- // Empty assembled blocks carry nothing (some gateways emit an empty
1362
- // `thinking` block before the real text) — don't forward stray
1363
- // empty chunks.
1364
- const text = item.type === "text" ? item.text : item.thinking;
1365
- if (text.length === 0) {
1366
- return false;
1440
+ // A streamed block of the same type whose accumulated text is a
1441
+ // prefix of this one was already (at least partly) delivered as
1442
+ // chunks; consume it and forward only what's left. A non-empty
1443
+ // streamed text is required so an empty/aborted streamed block
1444
+ // doesn't swallow the assembled copy.
1445
+ const streamed = streamedBlocks[streamPos];
1446
+ if (streamed &&
1447
+ streamed.type === item.type &&
1448
+ streamed.text.length > 0 &&
1449
+ full.startsWith(streamed.text)) {
1450
+ streamPos++;
1451
+ const remainder = full.slice(streamed.text.length);
1452
+ if (remainder.length === 0) {
1453
+ continue;
1454
+ }
1455
+ // Overwrite in place with just the un-streamed tail (the
1456
+ // assembled message isn't read again after this) so the block
1457
+ // keeps its exact SDK type.
1458
+ if (item.type === "text") {
1459
+ item.text = remainder;
1460
+ }
1461
+ else {
1462
+ item.thinking = remainder;
1463
+ }
1464
+ kept.push(item);
1465
+ continue;
1367
1466
  }
1368
- return true;
1369
- });
1370
- // The consolidated message is the last place this id is needed for
1371
- // dedupe — drop it so the streamed-id sets stay bounded to in-flight
1372
- // streams rather than growing for the session's whole life.
1373
- if (id !== undefined) {
1374
- streamedTextIds.delete(id);
1375
- streamedThinkingIds.delete(id);
1467
+ // Not matched: never streamed (or the stream diverged from the
1468
+ // assembled text) — forward the block in full.
1469
+ kept.push(item);
1376
1470
  }
1471
+ content = kept;
1472
+ // Consumed: reset so the next message's blocks accumulate fresh and
1473
+ // the record stays bounded to the in-flight message.
1474
+ streamedBlocks.length = 0;
1377
1475
  }
1378
1476
  else if (message.type === "assistant") {
1379
1477
  // Subagent assistant message (`parent_tool_use_id !== null`). It is
@@ -1989,18 +2087,22 @@ export class ClaudeAcpAgent {
1989
2087
  session.configOptions = session.configOptions.map((o) => o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o);
1990
2088
  }
1991
2089
  else if (configId === "model") {
2090
+ // `ModelInfo.supportsAutoMode` is the canonical SDK signal for clamping
2091
+ // modes below; its `displayName`/`description` also let us infer the
2092
+ // context window for semantic aliases (e.g. `default`) whose ID alone
2093
+ // carries no "1m" token.
2094
+ const newModelInfo = session.modelInfos.find((m) => m.value === value);
1992
2095
  if (session.models.currentModelId !== value) {
1993
2096
  // The cached context window was learned for the previous model; reset
1994
2097
  // to the new model's heuristic so mid-stream updates between now and
1995
2098
  // the next `result` reflect the user's selection instead of the old
1996
2099
  // model's window.
1997
- session.contextWindowSize = inferContextWindowFromModel(value) ?? DEFAULT_CONTEXT_WINDOW;
2100
+ session.contextWindowSize =
2101
+ inferContextWindowFromModel(value, newModelInfo?.displayName, newModelInfo?.description) ?? DEFAULT_CONTEXT_WINDOW;
1998
2102
  }
1999
2103
  session.models = { ...session.models, currentModelId: value };
2000
2104
  // Recompute availableModes for the new model and clamp the current
2001
2105
  // mode if the SDK no longer offers it (today: "auto" on Haiku).
2002
- // `ModelInfo.supportsAutoMode` is the canonical SDK signal.
2003
- const newModelInfo = session.modelInfos.find((m) => m.value === value);
2004
2106
  const newAvailableModes = buildAvailableModes(newModelInfo);
2005
2107
  // Capture BEFORE mutating session.modes so the log message reflects
2006
2108
  // the invalidated mode rather than "default".
@@ -2029,7 +2131,7 @@ export class ClaudeAcpAgent {
2029
2131
  // Rebuild config options since effort levels depend on the selected model
2030
2132
  const effortOpt = session.configOptions.find((o) => o.id === "effort");
2031
2133
  const currentEffort = typeof effortOpt?.currentValue === "string" ? effortOpt.currentValue : undefined;
2032
- session.configOptions = buildConfigOptions(session.modes, session.models, session.modelInfos, currentEffort);
2134
+ session.configOptions = buildConfigOptions(session.modes, session.models, session.modelInfos, currentEffort, session.agents, session.currentAgent);
2033
2135
  // Sync effort with the SDK if it changed after the model switch
2034
2136
  const newEffortOpt = session.configOptions.find((o) => o.id === "effort");
2035
2137
  const newEffort = typeof newEffortOpt?.currentValue === "string" ? newEffortOpt.currentValue : undefined;
@@ -2054,6 +2156,18 @@ export class ClaudeAcpAgent {
2054
2156
  });
2055
2157
  }
2056
2158
  }
2159
+ else if (configId === "agent") {
2160
+ // Live agent switch — no subprocess restart needed. Apply the SDK flag
2161
+ // first so a rejected control request leaves both `currentAgent` and the
2162
+ // config option untouched (no UI/SDK desync). Passing `null` clears the
2163
+ // flag layer back to the standard Claude Code agent; the change takes
2164
+ // effect on the next turn (SDK >= 0.3.161).
2165
+ await session.query.applyFlagSettings({
2166
+ agent: value === DEFAULT_AGENT_ID ? null : value,
2167
+ });
2168
+ session.currentAgent = value;
2169
+ session.configOptions = session.configOptions.map((o) => o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o);
2170
+ }
2057
2171
  else {
2058
2172
  session.configOptions = session.configOptions.map((o) => o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o);
2059
2173
  if (configId === "effort") {
@@ -2405,7 +2519,17 @@ export class ClaudeAcpAgent {
2405
2519
  currentModeId: effectiveMode,
2406
2520
  availableModes,
2407
2521
  };
2408
- const configOptions = buildConfigOptions(modes, models, allowedModels, settingsManager.getSettings().effortLevel);
2522
+ const agents = await discoverCustomAgents(q);
2523
+ // Only adopt the requested agent as the selected value if it's one we
2524
+ // actually surface in the picker. A built-in (filtered out above) or
2525
+ // otherwise-unknown name would leave the config option's `currentValue`
2526
+ // pointing at an entry not in its own `options` list, which clients render
2527
+ // as a blank/invalid selection.
2528
+ const requestedAgent = userProvidedOptions?.agent;
2529
+ const currentAgent = requestedAgent && agents.some((a) => a.name === requestedAgent)
2530
+ ? requestedAgent
2531
+ : DEFAULT_AGENT_ID;
2532
+ const configOptions = buildConfigOptions(modes, models, allowedModels, settingsManager.getSettings().effortLevel, agents, currentAgent);
2409
2533
  // Apply the initial effort level to the SDK so it matches the UI default
2410
2534
  const initialEffort = configOptions.find((o) => o.id === "effort");
2411
2535
  if (initialEffort &&
@@ -2432,9 +2556,11 @@ export class ClaudeAcpAgent {
2432
2556
  models,
2433
2557
  modelInfos: allowedModels,
2434
2558
  configOptions,
2559
+ agents,
2560
+ currentAgent,
2435
2561
  abortController,
2436
2562
  emitRawSDKMessages: sessionMeta?.claudeCode?.emitRawSDKMessages ?? false,
2437
- contextWindowSize: inferContextWindowFromModel(models.currentModelId) ?? DEFAULT_CONTEXT_WINDOW,
2563
+ contextWindowSize: inferContextWindowFromModel(models.currentModelId, currentModelInfo?.displayName, currentModelInfo?.description) ?? DEFAULT_CONTEXT_WINDOW,
2438
2564
  taskState,
2439
2565
  toolUseCache: {},
2440
2566
  messageIdToUuid: new Map(),
@@ -2578,7 +2704,39 @@ function buildAvailableModes(modelInfo) {
2578
2704
  function toSdkEffortLevel(value) {
2579
2705
  return value === undefined || value === "default" ? null : value;
2580
2706
  }
2581
- function buildConfigOptions(modes, models, modelInfos, currentEffortLevel) {
2707
+ // `supportedAgents()` always returns Claude Code's built-in subagents — the
2708
+ // ones used for Task-tool delegation (Explore, Plan, etc.) — even when the user
2709
+ // has configured none of their own. Those aren't meaningful *main-thread*
2710
+ // personas, so we filter them out and only surface the Agent picker when the
2711
+ // user (or a plugin/project) has configured custom agents. Update this set if
2712
+ // the SDK's built-in roster changes.
2713
+ export const BUILTIN_AGENT_NAMES = new Set([
2714
+ "claude",
2715
+ "general-purpose",
2716
+ "Explore",
2717
+ "Plan",
2718
+ "statusline-setup",
2719
+ ]);
2720
+ // Value of the synthetic "Default" entry in the agent picker, which maps to the
2721
+ // standard Claude Code agent (`applyFlagSettings({ agent: null })`). It is a
2722
+ // reserved sentinel: a custom agent named exactly this would collide with it
2723
+ // (two options sharing the value, selection silently routing to `null`), so we
2724
+ // exclude that name from discovery.
2725
+ export const DEFAULT_AGENT_ID = "default";
2726
+ /** Discover user/plugin/project-configured main-thread agents, excluding the
2727
+ * built-in subagents and the reserved "default" sentinel. Returns an empty
2728
+ * list if discovery fails so a flaky control request never blocks session
2729
+ * creation. */
2730
+ export async function discoverCustomAgents(q) {
2731
+ try {
2732
+ const agents = await q.supportedAgents();
2733
+ return agents.filter((a) => !BUILTIN_AGENT_NAMES.has(a.name) && a.name !== DEFAULT_AGENT_ID);
2734
+ }
2735
+ catch {
2736
+ return [];
2737
+ }
2738
+ }
2739
+ export function buildConfigOptions(modes, models, modelInfos, currentEffortLevel, agents = [], currentAgent = DEFAULT_AGENT_ID) {
2582
2740
  const options = [
2583
2741
  {
2584
2742
  id: "mode",
@@ -2635,6 +2793,27 @@ function buildConfigOptions(modes, models, modelInfos, currentEffortLevel) {
2635
2793
  options: effortOptions,
2636
2794
  });
2637
2795
  }
2796
+ // Only surface the Agent picker when there's a real choice — i.e. the user
2797
+ // has configured at least one custom agent (built-ins are filtered out in
2798
+ // discoverCustomAgents). With none configured, "Default" would be the only
2799
+ // entry, so we omit the option entirely.
2800
+ if (agents.length > 0) {
2801
+ options.push({
2802
+ id: "agent",
2803
+ name: "Agent",
2804
+ description: "Main-thread agent persona",
2805
+ type: "select",
2806
+ currentValue: currentAgent,
2807
+ options: [
2808
+ { value: DEFAULT_AGENT_ID, name: "Default", description: "Standard Claude Code agent" },
2809
+ ...agents.map((a) => ({
2810
+ value: a.name,
2811
+ name: a.name,
2812
+ description: a.description || undefined,
2813
+ })),
2814
+ ],
2815
+ });
2816
+ }
2638
2817
  return options;
2639
2818
  }
2640
2819
  // Claude Code CLI persists display strings like "opus[1m]" in settings,
@@ -3062,13 +3241,17 @@ export function toAcpNotifications(content, role, sessionId, toolUseCache, clien
3062
3241
  break;
3063
3242
  case "thinking":
3064
3243
  case "thinking_delta":
3065
- update = {
3066
- sessionUpdate: "agent_thought_chunk",
3067
- content: {
3068
- type: "text",
3069
- text: chunk.thinking,
3070
- },
3071
- };
3244
+ // Recent models default `thinking.display` to "omitted", which streams
3245
+ // signature-only thinking blocks whose text is empty.
3246
+ if (chunk.thinking.length > 0) {
3247
+ update = {
3248
+ sessionUpdate: "agent_thought_chunk",
3249
+ content: {
3250
+ type: "text",
3251
+ text: chunk.thinking,
3252
+ },
3253
+ };
3254
+ }
3072
3255
  break;
3073
3256
  case "tool_use":
3074
3257
  case "server_tool_use":
@@ -3329,11 +3512,30 @@ export function runAcp() {
3329
3512
  const input = nodeToWebWritable(process.stdout);
3330
3513
  const output = nodeToWebReadable(process.stdin);
3331
3514
  const stream = ndJsonStream(input, output);
3515
+ // `connect(...)` returns a connection-scoped peer handle (`connection.client`)
3516
+ // that stays valid for the whole connection, so the agent captures it once.
3517
+ // Handlers close over `agent`, which is assigned synchronously right after
3518
+ // `connect()` returns — before the connection processes any inbound message.
3519
+ // It cannot be `const`: its value depends on `connection.client`, which does
3520
+ // not exist until `connect()` has been called.
3521
+ // eslint-disable-next-line prefer-const
3332
3522
  let agent;
3333
- const connection = new AgentSideConnection((client) => {
3334
- agent = new ClaudeAcpAgent(client);
3335
- return agent;
3336
- }, stream);
3523
+ const connection = acpAgent({ name: "claude-code-acp" })
3524
+ .onRequest(methods.agent.initialize, (ctx) => agent.initialize(ctx.params))
3525
+ .onRequest(methods.agent.session.new, (ctx) => agent.newSession(ctx.params))
3526
+ .onRequest(methods.agent.session.load, (ctx) => agent.loadSession(ctx.params))
3527
+ .onRequest(methods.agent.session.fork, (ctx) => agent.unstable_forkSession(ctx.params))
3528
+ .onRequest(methods.agent.session.list, (ctx) => agent.listSessions(ctx.params))
3529
+ .onRequest(methods.agent.session.delete, (ctx) => agent.deleteSession(ctx.params))
3530
+ .onRequest(methods.agent.session.resume, (ctx) => agent.resumeSession(ctx.params))
3531
+ .onRequest(methods.agent.session.close, (ctx) => agent.closeSession(ctx.params))
3532
+ .onRequest(methods.agent.session.setMode, (ctx) => agent.setSessionMode(ctx.params))
3533
+ .onRequest(methods.agent.session.setConfigOption, (ctx) => agent.setSessionConfigOption(ctx.params))
3534
+ .onRequest(methods.agent.authenticate, (ctx) => agent.authenticate(ctx.params))
3535
+ .onRequest(methods.agent.session.prompt, (ctx) => agent.prompt(ctx.params))
3536
+ .onNotification(methods.agent.session.cancel, (ctx) => agent.cancel(ctx.params))
3537
+ .connect(stream);
3538
+ agent = new ClaudeAcpAgent(new ClientConnection(connection.client));
3337
3539
  return { connection, agent };
3338
3540
  }
3339
3541
  function commonPrefixLength(a, b) {
@@ -3343,14 +3545,21 @@ function commonPrefixLength(a, b) {
3343
3545
  }
3344
3546
  return i;
3345
3547
  }
3346
- /** Best-effort first guess of a model's context window from its ID, used only
3347
- * as a fallback when the SDK's authoritative `getContextUsage` is unavailable
3348
- * (and until a `result` message arrives with the `modelUsage` value).
3548
+ /** Best-effort first guess of a model's context window, used only as a
3549
+ * fallback when the SDK's authoritative `getContextUsage` is unavailable (and
3550
+ * until a `result` message arrives with the `modelUsage` value).
3551
+ *
3349
3552
  * Anthropic 1M-context variants encode "1m" as a distinct token in the SDK
3350
3553
  * model ID (e.g., "claude-opus-4-6-1m"), which `\b1m\b` catches without also
3351
- * matching things like "10m" or embedded substrings. */
3352
- function inferContextWindowFromModel(model) {
3353
- if (/\b1m\b/i.test(model))
3554
+ * matching things like "10m" or embedded substrings. Semantic aliases like
3555
+ * `default` carry no such token in the ID, but the SDK's human-facing
3556
+ * `displayName`/`description` do (e.g. "Opus 4.7 (1M context)"), so callers
3557
+ * pass those too — the same `\b1m\b` token appears in "1M context". The SDK's
3558
+ * `ModelInfo` exposes no structured context-window field, so this text scan is
3559
+ * the only pre-`result` signal available. A miss falls back to the default
3560
+ * window and is corrected by `result.modelUsage` within one turn. */
3561
+ function inferContextWindowFromModel(...texts) {
3562
+ if (texts.some((text) => text != null && /\b1m\b/i.test(text)))
3354
3563
  return 1_000_000;
3355
3564
  return null;
3356
3565
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/dist/tools.js CHANGED
@@ -366,7 +366,9 @@ export function toolUpdateFromToolResult(toolResult, toolUse, supportsTerminalOu
366
366
  // Extract output and exit code from either format:
367
367
  // 1. BetaBashCodeExecutionResultBlock: { type: "bash_code_execution_result", stdout, stderr, return_code }
368
368
  // 2. Plain string content from a regular tool_result
369
- // 3. Array content (e.g. [{ type: "text", text: "..." }])
369
+ // 3. Array content (e.g. [{ type: "text", text: "..." }] for stdout,
370
+ // or [{ type: "image", source: {...} }] when the local Bash tool
371
+ // produces an image, e.g. piping a base64 data URI)
370
372
  let output = "";
371
373
  let exitCode = isError ? 1 : 0;
372
374
  if (result &&
@@ -380,11 +382,19 @@ export function toolUpdateFromToolResult(toolResult, toolUse, supportsTerminalOu
380
382
  else if (typeof result === "string") {
381
383
  output = result;
382
384
  }
383
- else if (Array.isArray(result) &&
384
- result.length > 0 &&
385
- "text" in result[0] &&
386
- typeof result[0].text === "string") {
387
- output = result.map((c) => c.text).join("\n");
385
+ else if (Array.isArray(result) && result.length > 0) {
386
+ const textOnly = result.every((c) => c && typeof c === "object" && typeof c.text === "string");
387
+ if (textOnly) {
388
+ output = result.map((c) => c.text).join("\n");
389
+ }
390
+ else {
391
+ // Image (or mixed non-text) content. Binary payloads can't be
392
+ // streamed through the terminal-output _meta channel, so bypass
393
+ // it and surface the blocks as ACP content. This handles the
394
+ // local Bash tool's image output, which previously failed the
395
+ // text-only guard and was silently dropped.
396
+ return toAcpContentUpdate(result, isError);
397
+ }
388
398
  }
389
399
  if (supportsTerminalOutput) {
390
400
  return {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.47.0",
6
+ "version": "0.49.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",
@@ -60,14 +60,14 @@
60
60
  "author": "Zed Industries",
61
61
  "license": "Apache-2.0",
62
62
  "dependencies": {
63
- "@agentclientprotocol/sdk": "0.26.0",
64
- "@anthropic-ai/claude-agent-sdk": "0.3.179",
63
+ "@agentclientprotocol/sdk": "0.28.1",
64
+ "@anthropic-ai/claude-agent-sdk": "0.3.185",
65
65
  "zod": "^3.25.0 || ^4.0.0"
66
66
  },
67
67
  "devDependencies": {
68
- "@anthropic-ai/sdk": "0.104.2",
68
+ "@anthropic-ai/sdk": "0.105.0",
69
69
  "@eslint/js": "10.0.1",
70
- "@types/node": "25.9.3",
70
+ "@types/node": "26.0.0",
71
71
  "@typescript-eslint/eslint-plugin": "8.61.1",
72
72
  "@typescript-eslint/parser": "8.61.1",
73
73
  "eslint": "10.5.0",