@agentclientprotocol/claude-agent-acp 0.48.0 → 0.50.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 +28 -2
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +217 -78
- package/dist/settings.js +9 -6
- package/dist/utils.js +3 -5
- package/package.json +10 -6
package/dist/acp-agent.d.ts
CHANGED
|
@@ -253,10 +253,15 @@ export declare function describeAlwaysAllow(suggestions: PermissionUpdate[] | un
|
|
|
253
253
|
*/
|
|
254
254
|
export interface AcpClient {
|
|
255
255
|
sessionUpdate(params: SessionNotification): Promise<void>;
|
|
256
|
-
|
|
256
|
+
/** `signal`, when aborted, sends `$/cancel_request` for the in-flight
|
|
257
|
+
* permission request so the client can dismiss its prompt (and settle our
|
|
258
|
+
* await) instead of leaving the dialog open after the turn was cancelled. */
|
|
259
|
+
requestPermission(params: RequestPermissionRequest, signal?: AbortSignal): Promise<RequestPermissionResponse>;
|
|
257
260
|
readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
|
|
258
261
|
writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
|
|
259
|
-
|
|
262
|
+
/** `signal`, when aborted, sends `$/cancel_request` for the in-flight
|
|
263
|
+
* elicitation so the client can dismiss its prompt and settle our await. */
|
|
264
|
+
unstable_createElicitation(params: CreateElicitationRequest, signal?: AbortSignal): Promise<CreateElicitationResponse>;
|
|
260
265
|
unstable_completeElicitation(params: CompleteElicitationNotification): Promise<void>;
|
|
261
266
|
/** Send a custom (extension) notification, e.g. `_claude/sdkMessage`. */
|
|
262
267
|
extNotification(method: string, params: Record<string, unknown>): Promise<void>;
|
|
@@ -325,6 +330,14 @@ export declare class ClaudeAcpAgent {
|
|
|
325
330
|
private replaySessionHistory;
|
|
326
331
|
readTextFile(params: ReadTextFileRequest): Promise<ReadTextFileResponse>;
|
|
327
332
|
writeTextFile(params: WriteTextFileRequest): Promise<WriteTextFileResponse>;
|
|
333
|
+
/** Forward a permission request to the client, wiring the tool call's
|
|
334
|
+
* `signal` through as a `cancellationSignal`. When the turn is cancelled
|
|
335
|
+
* while the client's prompt is still open the signal aborts, the SDK sends
|
|
336
|
+
* `$/cancel_request`, and the client settles the request (a `cancelled`
|
|
337
|
+
* outcome or a `requestCancelled` rejection). Either way we surface the same
|
|
338
|
+
* "Tool use aborted" the callers already expect, so a cancelled dialog no
|
|
339
|
+
* longer leaves the `await` hanging. */
|
|
340
|
+
private requestPermissionFromClient;
|
|
328
341
|
canUseTool(sessionId: string): CanUseTool;
|
|
329
342
|
/**
|
|
330
343
|
* Handle elicitation requests that originate from MCP servers by forwarding
|
|
@@ -394,6 +407,19 @@ export declare function streamEventToAcpNotifications(message: SDKPartialAssista
|
|
|
394
407
|
taskState?: TaskState;
|
|
395
408
|
messageId?: string;
|
|
396
409
|
}): SessionNotification[];
|
|
410
|
+
/** Run a `session/prompt` while honoring `$/cancel_request` for it. ACP clients
|
|
411
|
+
* normally stop a turn with the `session/cancel` notification, but `signal`
|
|
412
|
+
* (the prompt request's abort signal) also fires when the client sends the
|
|
413
|
+
* generic `$/cancel_request` for this prompt — the protocol's complementary
|
|
414
|
+
* cancellation fallback. Route that to the same `agent.cancel` path so a client
|
|
415
|
+
* using only the generic mechanism still stops the turn (and the prompt
|
|
416
|
+
* resolves "cancelled" instead of running to completion).
|
|
417
|
+
*
|
|
418
|
+
* The listener is scoped to this call: once the prompt settles it is removed,
|
|
419
|
+
* so a later teardown-time abort of the (per-request) signal can't cancel a
|
|
420
|
+
* subsequent turn. `signal` also aborts on connection close, in which case
|
|
421
|
+
* cancelling the in-flight turn is the desired behavior anyway. */
|
|
422
|
+
export declare function runPromptWithCancellation(agent: Pick<ClaudeAcpAgent, "prompt" | "cancel" | "logger">, params: PromptRequest, signal: AbortSignal): Promise<PromptResponse>;
|
|
397
423
|
export declare function runAcp(): {
|
|
398
424
|
connection: import("@agentclientprotocol/sdk").AgentConnection;
|
|
399
425
|
agent: ClaudeAcpAgent;
|
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":"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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
|
@@ -253,14 +253,17 @@ export function describeAlwaysAllow(suggestions, toolName) {
|
|
|
253
253
|
* construction.
|
|
254
254
|
*/
|
|
255
255
|
class ClientConnection {
|
|
256
|
+
ctx;
|
|
256
257
|
constructor(ctx) {
|
|
257
258
|
this.ctx = ctx;
|
|
258
259
|
}
|
|
259
260
|
sessionUpdate(params) {
|
|
260
261
|
return this.ctx.notify(methods.client.session.update, params);
|
|
261
262
|
}
|
|
262
|
-
requestPermission(params) {
|
|
263
|
-
return this.ctx.request(methods.client.session.requestPermission, params
|
|
263
|
+
requestPermission(params, signal) {
|
|
264
|
+
return this.ctx.request(methods.client.session.requestPermission, params, {
|
|
265
|
+
cancellationSignal: signal,
|
|
266
|
+
});
|
|
264
267
|
}
|
|
265
268
|
readTextFile(params) {
|
|
266
269
|
return this.ctx.request(methods.client.fs.readTextFile, params);
|
|
@@ -268,8 +271,10 @@ class ClientConnection {
|
|
|
268
271
|
writeTextFile(params) {
|
|
269
272
|
return this.ctx.request(methods.client.fs.writeTextFile, params);
|
|
270
273
|
}
|
|
271
|
-
unstable_createElicitation(params) {
|
|
272
|
-
return this.ctx.request(methods.client.elicitation.create, params
|
|
274
|
+
unstable_createElicitation(params, signal) {
|
|
275
|
+
return this.ctx.request(methods.client.elicitation.create, params, {
|
|
276
|
+
cancellationSignal: signal,
|
|
277
|
+
});
|
|
273
278
|
}
|
|
274
279
|
unstable_completeElicitation(params) {
|
|
275
280
|
return this.ctx.notify(methods.client.elicitation.complete, params);
|
|
@@ -279,11 +284,16 @@ class ClientConnection {
|
|
|
279
284
|
}
|
|
280
285
|
}
|
|
281
286
|
export class ClaudeAcpAgent {
|
|
287
|
+
sessions;
|
|
288
|
+
client;
|
|
289
|
+
clientCapabilities;
|
|
290
|
+
logger;
|
|
291
|
+
gatewayAuthRequest;
|
|
292
|
+
/** Grace period before a `session/cancel` forces a wedged prompt loop to
|
|
293
|
+
* return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
|
|
294
|
+
* tests can shrink it. */
|
|
295
|
+
forceCancelGraceMs = DEFAULT_FORCE_CANCEL_GRACE_MS;
|
|
282
296
|
constructor(client, logger) {
|
|
283
|
-
/** Grace period before a `session/cancel` forces a wedged prompt loop to
|
|
284
|
-
* return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
|
|
285
|
-
* tests can shrink it. */
|
|
286
|
-
this.forceCancelGraceMs = DEFAULT_FORCE_CANCEL_GRACE_MS;
|
|
287
297
|
this.sessions = {};
|
|
288
298
|
this.client = client;
|
|
289
299
|
this.logger = logger ?? console;
|
|
@@ -577,12 +587,17 @@ export class ClaudeAcpAgent {
|
|
|
577
587
|
// (whose delta events don't carry it) can all be tagged with the same,
|
|
578
588
|
// replay-stable id.
|
|
579
589
|
let currentStreamMessageId;
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
//
|
|
584
|
-
|
|
585
|
-
|
|
590
|
+
// The text/thinking blocks that have actually streamed live as
|
|
591
|
+
// `stream_event` deltas for the message the next consolidated `assistant`
|
|
592
|
+
// will repeat, in stream order, each accumulated to its full streamed text.
|
|
593
|
+
// The consolidated handler diffs each assembled block against these and
|
|
594
|
+
// forwards only the un-streamed remainder — nothing if it streamed in full
|
|
595
|
+
// (the common case), the whole block if it never streamed (a non-streaming
|
|
596
|
+
// gateway), or just the tail if the stream was cut short mid-block. Matching
|
|
597
|
+
// on content rather than the Anthropic message id makes dedupe robust to
|
|
598
|
+
// gateways that don't carry a stable/matching id across the stream and the
|
|
599
|
+
// consolidated message. Reset after each consolidated message consumes it.
|
|
600
|
+
const streamedBlocks = [];
|
|
586
601
|
// Stop reason accumulated for the active turn (result subtype, refusal,
|
|
587
602
|
// max_tokens, …). Reset per turn; read when the turn settles at idle.
|
|
588
603
|
let stopReason = "end_turn";
|
|
@@ -593,12 +608,14 @@ export class ClaudeAcpAgent {
|
|
|
593
608
|
lastAssistantError = undefined;
|
|
594
609
|
lastRefusalExplanation = null;
|
|
595
610
|
compactionInProgress = false;
|
|
596
|
-
// Do NOT reset currentStreamMessageId here.
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
//
|
|
601
|
-
//
|
|
611
|
+
// Do NOT reset currentStreamMessageId or streamedBlocks here. Turn
|
|
612
|
+
// activation can fire mid-message (the replayed user echo with
|
|
613
|
+
// --replay-user-messages lands between a message's blocks); clearing the
|
|
614
|
+
// streamed-content record on activation would drop the blocks that
|
|
615
|
+
// streamed before the echo, so the consolidated assistant message would
|
|
616
|
+
// re-emit them as duplicates. streamedBlocks is bounded instead by being
|
|
617
|
+
// cleared when each consolidated message consumes it. #785 stopped
|
|
618
|
+
// resetting the streamed-content tracking here but left this line.
|
|
602
619
|
stopReason = "end_turn";
|
|
603
620
|
session.accumulatedUsage = {
|
|
604
621
|
inputTokens: 0,
|
|
@@ -1189,20 +1206,50 @@ export class ClaudeAcpAgent {
|
|
|
1189
1206
|
// it) can all be tagged with the same, replay-stable id.
|
|
1190
1207
|
if (message.event.type === "message_start") {
|
|
1191
1208
|
currentStreamMessageId = message.event.message.id || undefined;
|
|
1209
|
+
// A new top-level message starts: clear any streamed-content
|
|
1210
|
+
// residue from a prior message that never reached its
|
|
1211
|
+
// consolidated reset — a cancelled turn breaks out before the
|
|
1212
|
+
// reset, and the synthetic-auth/system/local-command paths
|
|
1213
|
+
// `break` early too. Block indices restart at 0 each message, so
|
|
1214
|
+
// leftover entries would otherwise collide with this message's
|
|
1215
|
+
// blocks and re-emit (or truncate) already-streamed text. Gated on
|
|
1216
|
+
// `parent_tool_use_id === null` so a subagent stream can't clear
|
|
1217
|
+
// the top-level record. Fires once, before any of this message's
|
|
1218
|
+
// blocks, so it doesn't disturb the mid-message turn-activation
|
|
1219
|
+
// path the way resetting on turn activation would.
|
|
1220
|
+
if (message.parent_tool_use_id === null) {
|
|
1221
|
+
streamedBlocks.length = 0;
|
|
1222
|
+
}
|
|
1192
1223
|
}
|
|
1193
|
-
//
|
|
1194
|
-
//
|
|
1195
|
-
//
|
|
1196
|
-
//
|
|
1197
|
-
// can't attribute its content to the top-level message
|
|
1198
|
-
|
|
1199
|
-
|
|
1224
|
+
// Accumulate the text/thinking actually streamed live, so the
|
|
1225
|
+
// `assistant` case below can diff its assembled blocks against what
|
|
1226
|
+
// already reached the client as chunks and forward only the
|
|
1227
|
+
// remainder. Gated on `parent_tool_use_id === null` so a subagent
|
|
1228
|
+
// stream can't attribute its content to the top-level message.
|
|
1229
|
+
// Contiguous deltas of the same block (same index and type) extend
|
|
1230
|
+
// the current entry; anything else opens a new one.
|
|
1231
|
+
if (message.parent_tool_use_id === null &&
|
|
1200
1232
|
message.event.type === "content_block_delta") {
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1233
|
+
const delta = message.event.delta;
|
|
1234
|
+
const chunk = delta.type === "text_delta"
|
|
1235
|
+
? { type: "text", text: delta.text }
|
|
1236
|
+
: delta.type === "thinking_delta"
|
|
1237
|
+
? { type: "thinking", text: delta.thinking }
|
|
1238
|
+
: undefined;
|
|
1239
|
+
// Skip empty deltas (some gateways emit empty thinking chunks —
|
|
1240
|
+
// #793): appending "" is a no-op, but pushing a "" entry would
|
|
1241
|
+
// create a block the consolidated handler's `text.length > 0`
|
|
1242
|
+
// guard can never consume, stalling the diff cursor and
|
|
1243
|
+
// re-emitting the next block as a duplicate.
|
|
1244
|
+
if (chunk && chunk.text.length > 0) {
|
|
1245
|
+
const index = message.event.index;
|
|
1246
|
+
const last = streamedBlocks[streamedBlocks.length - 1];
|
|
1247
|
+
if (last && last.index === index && last.type === chunk.type) {
|
|
1248
|
+
last.text += chunk.text;
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1251
|
+
streamedBlocks.push({ index, type: chunk.type, text: chunk.text });
|
|
1252
|
+
}
|
|
1206
1253
|
}
|
|
1207
1254
|
}
|
|
1208
1255
|
if (message.parent_tool_use_id === null &&
|
|
@@ -1376,40 +1423,65 @@ export class ClaudeAcpAgent {
|
|
|
1376
1423
|
}
|
|
1377
1424
|
let content;
|
|
1378
1425
|
if (message.type === "assistant" && message.parent_tool_use_id === null) {
|
|
1379
|
-
// Top-level assistant message:
|
|
1380
|
-
// streamed live as
|
|
1381
|
-
//
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1426
|
+
// Top-level assistant message: each text/thinking block may have
|
|
1427
|
+
// already been streamed live as deltas. Diff each against what
|
|
1428
|
+
// streamed (`streamedBlocks`, in document order) and forward only
|
|
1429
|
+
// the un-streamed remainder — nothing if it streamed in full (the
|
|
1430
|
+
// common case), the whole block if it never streamed (a
|
|
1431
|
+
// non-streaming gateway), or just the tail if the stream was cut
|
|
1432
|
+
// short mid-block. `streamPos` walks the streamed blocks in step
|
|
1433
|
+
// with the assembled text/thinking blocks; tool_use and other
|
|
1434
|
+
// blocks pass through untouched (their own `toolUseCache` collapses
|
|
1435
|
+
// the streamed/assembled pair) without advancing it.
|
|
1436
|
+
const blocks = message.message.content;
|
|
1437
|
+
const kept = [];
|
|
1438
|
+
let streamPos = 0;
|
|
1439
|
+
for (const item of blocks) {
|
|
1386
1440
|
if (item.type !== "text" && item.type !== "thinking") {
|
|
1387
|
-
|
|
1441
|
+
kept.push(item);
|
|
1442
|
+
continue;
|
|
1388
1443
|
}
|
|
1389
|
-
|
|
1390
|
-
//
|
|
1391
|
-
//
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
if (streamedLive) {
|
|
1395
|
-
return false;
|
|
1444
|
+
const full = item.type === "text" ? item.text : item.thinking;
|
|
1445
|
+
// Empty assembled blocks carry nothing (some gateways emit an
|
|
1446
|
+
// empty `thinking` block before the real text) — drop them.
|
|
1447
|
+
if (full.length === 0) {
|
|
1448
|
+
continue;
|
|
1396
1449
|
}
|
|
1397
|
-
//
|
|
1398
|
-
//
|
|
1399
|
-
//
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1450
|
+
// A streamed block of the same type whose accumulated text is a
|
|
1451
|
+
// prefix of this one was already (at least partly) delivered as
|
|
1452
|
+
// chunks; consume it and forward only what's left. A non-empty
|
|
1453
|
+
// streamed text is required so an empty/aborted streamed block
|
|
1454
|
+
// doesn't swallow the assembled copy.
|
|
1455
|
+
const streamed = streamedBlocks[streamPos];
|
|
1456
|
+
if (streamed &&
|
|
1457
|
+
streamed.type === item.type &&
|
|
1458
|
+
streamed.text.length > 0 &&
|
|
1459
|
+
full.startsWith(streamed.text)) {
|
|
1460
|
+
streamPos++;
|
|
1461
|
+
const remainder = full.slice(streamed.text.length);
|
|
1462
|
+
if (remainder.length === 0) {
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
// Overwrite in place with just the un-streamed tail (the
|
|
1466
|
+
// assembled message isn't read again after this) so the block
|
|
1467
|
+
// keeps its exact SDK type.
|
|
1468
|
+
if (item.type === "text") {
|
|
1469
|
+
item.text = remainder;
|
|
1470
|
+
}
|
|
1471
|
+
else {
|
|
1472
|
+
item.thinking = remainder;
|
|
1473
|
+
}
|
|
1474
|
+
kept.push(item);
|
|
1475
|
+
continue;
|
|
1403
1476
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
// dedupe — drop it so the streamed-id sets stay bounded to in-flight
|
|
1408
|
-
// streams rather than growing for the session's whole life.
|
|
1409
|
-
if (id !== undefined) {
|
|
1410
|
-
streamedTextIds.delete(id);
|
|
1411
|
-
streamedThinkingIds.delete(id);
|
|
1477
|
+
// Not matched: never streamed (or the stream diverged from the
|
|
1478
|
+
// assembled text) — forward the block in full.
|
|
1479
|
+
kept.push(item);
|
|
1412
1480
|
}
|
|
1481
|
+
content = kept;
|
|
1482
|
+
// Consumed: reset so the next message's blocks accumulate fresh and
|
|
1483
|
+
// the record stays bounded to the in-flight message.
|
|
1484
|
+
streamedBlocks.length = 0;
|
|
1413
1485
|
}
|
|
1414
1486
|
else if (message.type === "assistant") {
|
|
1415
1487
|
// Subagent assistant message (`parent_tool_use_id !== null`). It is
|
|
@@ -1794,6 +1866,24 @@ export class ClaudeAcpAgent {
|
|
|
1794
1866
|
const response = await this.client.writeTextFile(params);
|
|
1795
1867
|
return response;
|
|
1796
1868
|
}
|
|
1869
|
+
/** Forward a permission request to the client, wiring the tool call's
|
|
1870
|
+
* `signal` through as a `cancellationSignal`. When the turn is cancelled
|
|
1871
|
+
* while the client's prompt is still open the signal aborts, the SDK sends
|
|
1872
|
+
* `$/cancel_request`, and the client settles the request (a `cancelled`
|
|
1873
|
+
* outcome or a `requestCancelled` rejection). Either way we surface the same
|
|
1874
|
+
* "Tool use aborted" the callers already expect, so a cancelled dialog no
|
|
1875
|
+
* longer leaves the `await` hanging. */
|
|
1876
|
+
async requestPermissionFromClient(params, signal) {
|
|
1877
|
+
try {
|
|
1878
|
+
return await this.client.requestPermission(params, signal);
|
|
1879
|
+
}
|
|
1880
|
+
catch (error) {
|
|
1881
|
+
if (signal.aborted) {
|
|
1882
|
+
throw new Error("Tool use aborted", { cause: error });
|
|
1883
|
+
}
|
|
1884
|
+
throw error;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1797
1887
|
canUseTool(sessionId) {
|
|
1798
1888
|
return async (toolName, toolInput, { signal, suggestions, toolUseID }) => {
|
|
1799
1889
|
const alwaysAllowLabel = describeAlwaysAllow(suggestions, toolName);
|
|
@@ -1836,7 +1926,7 @@ export class ClaudeAcpAgent {
|
|
|
1836
1926
|
// `buildAvailableModes`/`ALLOW_BYPASS`. The `plan` option is a
|
|
1837
1927
|
// "keep planning" reject path; it's always present in `availableModes`.
|
|
1838
1928
|
const options = optionsAll.filter((o) => session.modes.availableModes.some((m) => m.id === o.optionId));
|
|
1839
|
-
const response = await this.
|
|
1929
|
+
const response = await this.requestPermissionFromClient({
|
|
1840
1930
|
options,
|
|
1841
1931
|
sessionId,
|
|
1842
1932
|
toolCall: {
|
|
@@ -1844,7 +1934,7 @@ export class ClaudeAcpAgent {
|
|
|
1844
1934
|
rawInput: toolInput,
|
|
1845
1935
|
...toolInfoFromToolUse({ name: toolName, input: toolInput, id: toolUseID }, supportsTerminalOutput, session?.cwd),
|
|
1846
1936
|
},
|
|
1847
|
-
});
|
|
1937
|
+
}, signal);
|
|
1848
1938
|
if (signal.aborted || response.outcome?.outcome === "cancelled") {
|
|
1849
1939
|
throw new Error("Tool use aborted");
|
|
1850
1940
|
}
|
|
@@ -1887,7 +1977,7 @@ export class ClaudeAcpAgent {
|
|
|
1887
1977
|
],
|
|
1888
1978
|
};
|
|
1889
1979
|
}
|
|
1890
|
-
const response = await this.
|
|
1980
|
+
const response = await this.requestPermissionFromClient({
|
|
1891
1981
|
options: [
|
|
1892
1982
|
{
|
|
1893
1983
|
kind: "allow_always",
|
|
@@ -1903,7 +1993,7 @@ export class ClaudeAcpAgent {
|
|
|
1903
1993
|
rawInput: toolInput,
|
|
1904
1994
|
...toolInfoFromToolUse({ name: toolName, input: toolInput, id: toolUseID }, supportsTerminalOutput, session?.cwd),
|
|
1905
1995
|
},
|
|
1906
|
-
});
|
|
1996
|
+
}, signal);
|
|
1907
1997
|
if (signal.aborted || response.outcome?.outcome === "cancelled") {
|
|
1908
1998
|
throw new Error("Tool use aborted");
|
|
1909
1999
|
}
|
|
@@ -1953,13 +2043,18 @@ export class ClaudeAcpAgent {
|
|
|
1953
2043
|
return { action: "decline" };
|
|
1954
2044
|
}
|
|
1955
2045
|
try {
|
|
1956
|
-
const response = await this.client.unstable_createElicitation(createRequest);
|
|
2046
|
+
const response = await this.client.unstable_createElicitation(createRequest, signal);
|
|
1957
2047
|
if (signal.aborted) {
|
|
1958
2048
|
return { action: "cancel" };
|
|
1959
2049
|
}
|
|
1960
2050
|
return createElicitationResponseToElicitResult(response);
|
|
1961
2051
|
}
|
|
1962
2052
|
catch (error) {
|
|
2053
|
+
// A cancellation we requested (signal aborted) settles as a cancel, not
|
|
2054
|
+
// a hard decline — the elicitation was abandoned, not refused.
|
|
2055
|
+
if (signal.aborted) {
|
|
2056
|
+
return { action: "cancel" };
|
|
2057
|
+
}
|
|
1963
2058
|
this.logger.error(`Failed to forward MCP elicitation: ${error}`);
|
|
1964
2059
|
return { action: "decline" };
|
|
1965
2060
|
}
|
|
@@ -1978,9 +2073,14 @@ export class ClaudeAcpAgent {
|
|
|
1978
2073
|
const createRequest = askUserQuestionsToCreateRequest(questions, sessionId, toolUseID);
|
|
1979
2074
|
let response;
|
|
1980
2075
|
try {
|
|
1981
|
-
response = await this.client.unstable_createElicitation(createRequest);
|
|
2076
|
+
response = await this.client.unstable_createElicitation(createRequest, signal);
|
|
1982
2077
|
}
|
|
1983
2078
|
catch (error) {
|
|
2079
|
+
// A cancellation we requested (signal aborted) settles as an aborted tool
|
|
2080
|
+
// use, matching the post-response check below.
|
|
2081
|
+
if (signal.aborted) {
|
|
2082
|
+
throw new Error("Tool use aborted", { cause: error });
|
|
2083
|
+
}
|
|
1984
2084
|
this.logger.error(`Failed to present AskUserQuestion elicitation: ${error}`);
|
|
1985
2085
|
return { behavior: "deny", message: "Could not present the question to the user." };
|
|
1986
2086
|
}
|
|
@@ -2025,18 +2125,22 @@ export class ClaudeAcpAgent {
|
|
|
2025
2125
|
session.configOptions = session.configOptions.map((o) => o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o);
|
|
2026
2126
|
}
|
|
2027
2127
|
else if (configId === "model") {
|
|
2128
|
+
// `ModelInfo.supportsAutoMode` is the canonical SDK signal for clamping
|
|
2129
|
+
// modes below; its `displayName`/`description` also let us infer the
|
|
2130
|
+
// context window for semantic aliases (e.g. `default`) whose ID alone
|
|
2131
|
+
// carries no "1m" token.
|
|
2132
|
+
const newModelInfo = session.modelInfos.find((m) => m.value === value);
|
|
2028
2133
|
if (session.models.currentModelId !== value) {
|
|
2029
2134
|
// The cached context window was learned for the previous model; reset
|
|
2030
2135
|
// to the new model's heuristic so mid-stream updates between now and
|
|
2031
2136
|
// the next `result` reflect the user's selection instead of the old
|
|
2032
2137
|
// model's window.
|
|
2033
|
-
session.contextWindowSize =
|
|
2138
|
+
session.contextWindowSize =
|
|
2139
|
+
inferContextWindowFromModel(value, newModelInfo?.displayName, newModelInfo?.description) ?? DEFAULT_CONTEXT_WINDOW;
|
|
2034
2140
|
}
|
|
2035
2141
|
session.models = { ...session.models, currentModelId: value };
|
|
2036
2142
|
// Recompute availableModes for the new model and clamp the current
|
|
2037
2143
|
// mode if the SDK no longer offers it (today: "auto" on Haiku).
|
|
2038
|
-
// `ModelInfo.supportsAutoMode` is the canonical SDK signal.
|
|
2039
|
-
const newModelInfo = session.modelInfos.find((m) => m.value === value);
|
|
2040
2144
|
const newAvailableModes = buildAvailableModes(newModelInfo);
|
|
2041
2145
|
// Capture BEFORE mutating session.modes so the log message reflects
|
|
2042
2146
|
// the invalidated mode rather than "default".
|
|
@@ -2494,7 +2598,7 @@ export class ClaudeAcpAgent {
|
|
|
2494
2598
|
currentAgent,
|
|
2495
2599
|
abortController,
|
|
2496
2600
|
emitRawSDKMessages: sessionMeta?.claudeCode?.emitRawSDKMessages ?? false,
|
|
2497
|
-
contextWindowSize: inferContextWindowFromModel(models.currentModelId) ?? DEFAULT_CONTEXT_WINDOW,
|
|
2601
|
+
contextWindowSize: inferContextWindowFromModel(models.currentModelId, currentModelInfo?.displayName, currentModelInfo?.description) ?? DEFAULT_CONTEXT_WINDOW,
|
|
2498
2602
|
taskState,
|
|
2499
2603
|
toolUseCache: {},
|
|
2500
2604
|
messageIdToUuid: new Map(),
|
|
@@ -3442,6 +3546,34 @@ export function streamEventToAcpNotifications(message, sessionId, toolUseCache,
|
|
|
3442
3546
|
return [];
|
|
3443
3547
|
}
|
|
3444
3548
|
}
|
|
3549
|
+
/** Run a `session/prompt` while honoring `$/cancel_request` for it. ACP clients
|
|
3550
|
+
* normally stop a turn with the `session/cancel` notification, but `signal`
|
|
3551
|
+
* (the prompt request's abort signal) also fires when the client sends the
|
|
3552
|
+
* generic `$/cancel_request` for this prompt — the protocol's complementary
|
|
3553
|
+
* cancellation fallback. Route that to the same `agent.cancel` path so a client
|
|
3554
|
+
* using only the generic mechanism still stops the turn (and the prompt
|
|
3555
|
+
* resolves "cancelled" instead of running to completion).
|
|
3556
|
+
*
|
|
3557
|
+
* The listener is scoped to this call: once the prompt settles it is removed,
|
|
3558
|
+
* so a later teardown-time abort of the (per-request) signal can't cancel a
|
|
3559
|
+
* subsequent turn. `signal` also aborts on connection close, in which case
|
|
3560
|
+
* cancelling the in-flight turn is the desired behavior anyway. */
|
|
3561
|
+
export async function runPromptWithCancellation(agent, params, signal) {
|
|
3562
|
+
const onAbort = () => {
|
|
3563
|
+
// Fire-and-forget: nothing awaits this listener, so swallow (and log) any
|
|
3564
|
+
// rejection rather than surfacing it as an unhandled rejection.
|
|
3565
|
+
agent.cancel({ sessionId: params.sessionId }).catch((error) => {
|
|
3566
|
+
agent.logger.error(`Failed to cancel prompt via $/cancel_request: ${error}`);
|
|
3567
|
+
});
|
|
3568
|
+
};
|
|
3569
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
3570
|
+
try {
|
|
3571
|
+
return await agent.prompt(params);
|
|
3572
|
+
}
|
|
3573
|
+
finally {
|
|
3574
|
+
signal.removeEventListener("abort", onAbort);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3445
3577
|
export function runAcp() {
|
|
3446
3578
|
const input = nodeToWebWritable(process.stdout);
|
|
3447
3579
|
const output = nodeToWebReadable(process.stdin);
|
|
@@ -3466,7 +3598,7 @@ export function runAcp() {
|
|
|
3466
3598
|
.onRequest(methods.agent.session.setMode, (ctx) => agent.setSessionMode(ctx.params))
|
|
3467
3599
|
.onRequest(methods.agent.session.setConfigOption, (ctx) => agent.setSessionConfigOption(ctx.params))
|
|
3468
3600
|
.onRequest(methods.agent.authenticate, (ctx) => agent.authenticate(ctx.params))
|
|
3469
|
-
.onRequest(methods.agent.session.prompt, (ctx) => agent.
|
|
3601
|
+
.onRequest(methods.agent.session.prompt, (ctx) => runPromptWithCancellation(agent, ctx.params, ctx.signal))
|
|
3470
3602
|
.onNotification(methods.agent.session.cancel, (ctx) => agent.cancel(ctx.params))
|
|
3471
3603
|
.connect(stream);
|
|
3472
3604
|
agent = new ClaudeAcpAgent(new ClientConnection(connection.client));
|
|
@@ -3479,14 +3611,21 @@ function commonPrefixLength(a, b) {
|
|
|
3479
3611
|
}
|
|
3480
3612
|
return i;
|
|
3481
3613
|
}
|
|
3482
|
-
/** Best-effort first guess of a model's context window
|
|
3483
|
-
*
|
|
3484
|
-
*
|
|
3614
|
+
/** Best-effort first guess of a model's context window, used only as a
|
|
3615
|
+
* fallback when the SDK's authoritative `getContextUsage` is unavailable (and
|
|
3616
|
+
* until a `result` message arrives with the `modelUsage` value).
|
|
3617
|
+
*
|
|
3485
3618
|
* Anthropic 1M-context variants encode "1m" as a distinct token in the SDK
|
|
3486
3619
|
* model ID (e.g., "claude-opus-4-6-1m"), which `\b1m\b` catches without also
|
|
3487
|
-
* matching things like "10m" or embedded substrings.
|
|
3488
|
-
|
|
3489
|
-
|
|
3620
|
+
* matching things like "10m" or embedded substrings. Semantic aliases like
|
|
3621
|
+
* `default` carry no such token in the ID, but the SDK's human-facing
|
|
3622
|
+
* `displayName`/`description` do (e.g. "Opus 4.7 (1M context)"), so callers
|
|
3623
|
+
* pass those too — the same `\b1m\b` token appears in "1M context". The SDK's
|
|
3624
|
+
* `ModelInfo` exposes no structured context-window field, so this text scan is
|
|
3625
|
+
* the only pre-`result` signal available. A miss falls back to the default
|
|
3626
|
+
* window and is corrected by `result.modelUsage` within one turn. */
|
|
3627
|
+
function inferContextWindowFromModel(...texts) {
|
|
3628
|
+
if (texts.some((text) => text != null && /\b1m\b/i.test(text)))
|
|
3490
3629
|
return 1_000_000;
|
|
3491
3630
|
return null;
|
|
3492
3631
|
}
|
package/dist/settings.js
CHANGED
|
@@ -35,13 +35,16 @@ function getManagedSettingsPath() {
|
|
|
35
35
|
* `filterEscalatingDefaultMode`, matching the CLI's trust policy.
|
|
36
36
|
*/
|
|
37
37
|
export class SettingsManager {
|
|
38
|
+
cwd;
|
|
39
|
+
effective = {};
|
|
40
|
+
watchers = [];
|
|
41
|
+
onChange;
|
|
42
|
+
logger;
|
|
43
|
+
initialized = false;
|
|
44
|
+
disposed = false;
|
|
45
|
+
debounceTimer = null;
|
|
46
|
+
initPromise = null;
|
|
38
47
|
constructor(cwd, options) {
|
|
39
|
-
this.effective = {};
|
|
40
|
-
this.watchers = [];
|
|
41
|
-
this.initialized = false;
|
|
42
|
-
this.disposed = false;
|
|
43
|
-
this.debounceTimer = null;
|
|
44
|
-
this.initPromise = null;
|
|
45
48
|
this.cwd = cwd;
|
|
46
49
|
this.onChange = options?.onChange;
|
|
47
50
|
this.logger = options?.logger ?? console;
|
package/dist/utils.js
CHANGED
|
@@ -2,11 +2,9 @@
|
|
|
2
2
|
import { WritableStream, ReadableStream } from "node:stream/web";
|
|
3
3
|
// Useful for bridging push-based and async-iterator-based code.
|
|
4
4
|
export class Pushable {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this.done = false;
|
|
9
|
-
}
|
|
5
|
+
queue = [];
|
|
6
|
+
resolvers = [];
|
|
7
|
+
done = false;
|
|
10
8
|
push(item) {
|
|
11
9
|
if (this.resolvers.length > 0) {
|
|
12
10
|
const resolve = this.resolvers.shift();
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.50.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",
|
|
@@ -59,20 +59,24 @@
|
|
|
59
59
|
},
|
|
60
60
|
"author": "Zed Industries",
|
|
61
61
|
"license": "Apache-2.0",
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22"
|
|
64
|
+
},
|
|
62
65
|
"dependencies": {
|
|
63
|
-
"@agentclientprotocol/sdk": "0.
|
|
64
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
66
|
+
"@agentclientprotocol/sdk": "0.29.0",
|
|
67
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.186",
|
|
65
68
|
"zod": "^3.25.0 || ^4.0.0"
|
|
66
69
|
},
|
|
67
70
|
"devDependencies": {
|
|
68
71
|
"@anthropic-ai/sdk": "0.105.0",
|
|
69
72
|
"@eslint/js": "10.0.1",
|
|
73
|
+
"@tsconfig/node22": "22.0.5",
|
|
70
74
|
"@types/node": "26.0.0",
|
|
71
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
72
|
-
"@typescript-eslint/parser": "8.
|
|
75
|
+
"@typescript-eslint/eslint-plugin": "8.62.0",
|
|
76
|
+
"@typescript-eslint/parser": "8.62.0",
|
|
73
77
|
"eslint": "10.5.0",
|
|
74
78
|
"eslint-config-prettier": "10.1.8",
|
|
75
|
-
"globals": "17.
|
|
79
|
+
"globals": "17.7.0",
|
|
76
80
|
"prettier": "3.8.4",
|
|
77
81
|
"ts-node": "10.9.2",
|
|
78
82
|
"typescript": "6.0.3",
|