@agentclientprotocol/claude-agent-acp 0.40.0 → 0.41.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 +15 -0
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +119 -1
- package/package.json +2 -2
package/dist/acp-agent.d.ts
CHANGED
|
@@ -52,6 +52,17 @@ type Session = {
|
|
|
52
52
|
}>;
|
|
53
53
|
nextPendingOrder: number;
|
|
54
54
|
abortController: AbortController;
|
|
55
|
+
/** Per-turn signal the active prompt loop races `query.next()` against.
|
|
56
|
+
* Aborted by cancel() (after a grace period) to force the loop to return
|
|
57
|
+
* "cancelled" when the SDK is wedged and `query.next()` never yields again
|
|
58
|
+
* (issue #680). Distinct from `abortController`: this only wakes the loop;
|
|
59
|
+
* it does NOT touch the SDK query/subprocess. Undefined when no prompt is
|
|
60
|
+
* actively consuming the query. */
|
|
61
|
+
cancelController?: AbortController;
|
|
62
|
+
/** Pending grace-period timer that aborts `cancelController`. Cleared when
|
|
63
|
+
* the loop returns normally so the backstop never fires after a clean
|
|
64
|
+
* cancel. */
|
|
65
|
+
forceCancelTimer?: ReturnType<typeof setTimeout>;
|
|
55
66
|
emitRawSDKMessages: boolean | SDKMessageFilter[];
|
|
56
67
|
/** Context window size of the last top-level assistant model, carried across
|
|
57
68
|
* prompts so mid-stream usage_update notifications report a correct `size`
|
|
@@ -184,6 +195,10 @@ export declare class ClaudeAcpAgent implements Agent {
|
|
|
184
195
|
clientCapabilities?: ClientCapabilities;
|
|
185
196
|
logger: Logger;
|
|
186
197
|
gatewayAuthRequest?: GatewayAuthRequest;
|
|
198
|
+
/** Grace period before a `session/cancel` forces a wedged prompt loop to
|
|
199
|
+
* return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
|
|
200
|
+
* tests can shrink it. */
|
|
201
|
+
forceCancelGraceMs: number;
|
|
187
202
|
constructor(client: AgentSideConnection, logger?: Logger);
|
|
188
203
|
initialize(request: InitializeRequest): Promise<InitializeResponse>;
|
|
189
204
|
newSession(params: NewSessionRequest): Promise<NewSessionResponse>;
|
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,EACL,KAAK,EACL,mBAAmB,EACnB,mBAAmB,EAGnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EAEnB,iBAAiB,EACjB,kBAAkB,EAElB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EAEpB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,6BAA6B,EAC7B,8BAA8B,EAC9B,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EAEtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,UAAU,EAKV,SAAS,EAET,OAAO,EACP,cAAc,EACd,gBAAgB,EAChB,KAAK,
|
|
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
|
@@ -27,6 +27,16 @@ const ZERO_USAGE = Object.freeze({
|
|
|
27
27
|
cache_creation_input_tokens: 0,
|
|
28
28
|
});
|
|
29
29
|
const DEFAULT_CONTEXT_WINDOW = 200000;
|
|
30
|
+
/** Floor after `session/cancel` before the adapter forces the active prompt
|
|
31
|
+
* loop to return "cancelled". `query.interrupt()` normally makes the SDK
|
|
32
|
+
* yield a trailing idle within milliseconds, and the loop returns through its
|
|
33
|
+
* usual path — so this timer is armed and cleared, never fired, on healthy
|
|
34
|
+
* cancels. It only trips when the SDK is genuinely wedged (e.g. a
|
|
35
|
+
* `TaskOutput { block: true }` poll against a hung background task — issue
|
|
36
|
+
* #680) and never yields. The value is deliberately loose: it's an
|
|
37
|
+
* "obviously stuck" ceiling, not a guess at interrupt latency, so it can't
|
|
38
|
+
* pre-empt a slow-but-healthy interrupt. */
|
|
39
|
+
const DEFAULT_FORCE_CANCEL_GRACE_MS = 30_000;
|
|
30
40
|
/** Compute a stable fingerprint of the session-defining params so we can
|
|
31
41
|
* detect when a loadSession/resumeSession call requires tearing down and
|
|
32
42
|
* recreating the underlying Query process. MCP servers are sorted by name
|
|
@@ -234,6 +244,10 @@ export function describeAlwaysAllow(suggestions, toolName) {
|
|
|
234
244
|
export class ClaudeAcpAgent {
|
|
235
245
|
constructor(client, logger) {
|
|
236
246
|
this.backgroundTerminals = {};
|
|
247
|
+
/** Grace period before a `session/cancel` forces a wedged prompt loop to
|
|
248
|
+
* return "cancelled". See {@link DEFAULT_FORCE_CANCEL_GRACE_MS}. Mutable so
|
|
249
|
+
* tests can shrink it. */
|
|
250
|
+
this.forceCancelGraceMs = DEFAULT_FORCE_CANCEL_GRACE_MS;
|
|
237
251
|
this.sessions = {};
|
|
238
252
|
this.client = client;
|
|
239
253
|
this.toolUseCache = {};
|
|
@@ -463,6 +477,12 @@ export class ClaudeAcpAgent {
|
|
|
463
477
|
// forward it to clients as structured `data`, sparing them from
|
|
464
478
|
// pattern-matching on the human-readable message text.
|
|
465
479
|
let lastAssistantError;
|
|
480
|
+
// When a streaming classifier refuses a turn, the assistant message carries
|
|
481
|
+
// stop_reason "refusal" and structured stop_details. We capture the
|
|
482
|
+
// human-readable explanation here so the terminal `result` can surface it
|
|
483
|
+
// to the user (the refused assistant message itself usually has no content)
|
|
484
|
+
// and report ACP's dedicated `refusal` stop reason.
|
|
485
|
+
let lastRefusalExplanation = null;
|
|
466
486
|
// Tracks whether we're inside a compaction. The SDK emits the terminal
|
|
467
487
|
// `status` (compact_result success/failed) twice for a single failed
|
|
468
488
|
// compaction, and the two messages are indistinguishable — so we report the
|
|
@@ -495,9 +515,26 @@ export class ClaudeAcpAgent {
|
|
|
495
515
|
let handedOff = false;
|
|
496
516
|
let errored = false;
|
|
497
517
|
let stopReason = "end_turn";
|
|
518
|
+
// Wake-up channel so cancel() can force this loop to return "cancelled"
|
|
519
|
+
// even when query.next() is wedged and never yields again (issue #680).
|
|
520
|
+
const cancelController = new AbortController();
|
|
521
|
+
session.cancelController = cancelController;
|
|
522
|
+
const cancelled = new Promise((resolve) => {
|
|
523
|
+
cancelController.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
524
|
+
});
|
|
498
525
|
try {
|
|
499
526
|
while (true) {
|
|
500
|
-
const
|
|
527
|
+
const nextMessage = session.query.next();
|
|
528
|
+
const next = await Promise.race([nextMessage, cancelled]);
|
|
529
|
+
if (cancelController.signal.aborted) {
|
|
530
|
+
// The SDK never yielded after interrupt() (e.g. a wedged TaskOutput
|
|
531
|
+
// block). Abandon the in-flight next() — swallowing any later
|
|
532
|
+
// rejection so it can't surface as an unhandled rejection — and
|
|
533
|
+
// honor the cancel per the ACP contract.
|
|
534
|
+
void nextMessage.catch(() => { });
|
|
535
|
+
return { stopReason: "cancelled" };
|
|
536
|
+
}
|
|
537
|
+
const { value: message, done } = next;
|
|
501
538
|
if (done || !message) {
|
|
502
539
|
if (session.cancelled) {
|
|
503
540
|
return { stopReason: "cancelled" };
|
|
@@ -637,6 +674,22 @@ export class ClaudeAcpAgent {
|
|
|
637
674
|
});
|
|
638
675
|
break;
|
|
639
676
|
}
|
|
677
|
+
case "commands_changed": {
|
|
678
|
+
// Push the full slash-command list after a mid-session change
|
|
679
|
+
// (e.g. skills discovered dynamically as the agent works in a
|
|
680
|
+
// subdirectory). The client should REPLACE its cached command
|
|
681
|
+
// list with this payload: supportedCommands() is captured once
|
|
682
|
+
// at initialize and never reflects mid-session changes, so we
|
|
683
|
+
// forward message.commands directly rather than re-querying.
|
|
684
|
+
await this.client.sessionUpdate({
|
|
685
|
+
sessionId: message.session_id,
|
|
686
|
+
update: {
|
|
687
|
+
sessionUpdate: "available_commands_update",
|
|
688
|
+
availableCommands: getAvailableSlashCommands(message.commands),
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
640
693
|
case "hook_started":
|
|
641
694
|
case "hook_progress":
|
|
642
695
|
case "hook_response":
|
|
@@ -704,6 +757,25 @@ export class ClaudeAcpAgent {
|
|
|
704
757
|
}
|
|
705
758
|
break;
|
|
706
759
|
}
|
|
760
|
+
// A refusal can arrive on any result subtype (and may even set
|
|
761
|
+
// is_error), so handle it before the subtype switch — otherwise the
|
|
762
|
+
// is_error throw below would surface it as an internal error. The
|
|
763
|
+
// refused assistant message carries no visible content, so surface
|
|
764
|
+
// the classifier's explanation (when available) and report ACP's
|
|
765
|
+
// dedicated `refusal` stop reason.
|
|
766
|
+
if (message.stop_reason === "refusal" && !isTaskNotification) {
|
|
767
|
+
if (lastRefusalExplanation) {
|
|
768
|
+
await this.client.sessionUpdate({
|
|
769
|
+
sessionId: params.sessionId,
|
|
770
|
+
update: {
|
|
771
|
+
sessionUpdate: "agent_message_chunk",
|
|
772
|
+
content: { type: "text", text: lastRefusalExplanation },
|
|
773
|
+
},
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
stopReason = "refusal";
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
707
779
|
switch (message.subtype) {
|
|
708
780
|
case "success": {
|
|
709
781
|
if (message.result.includes("Please run /login")) {
|
|
@@ -854,6 +926,9 @@ export class ClaudeAcpAgent {
|
|
|
854
926
|
if (message.error) {
|
|
855
927
|
lastAssistantError = message.error;
|
|
856
928
|
}
|
|
929
|
+
if (message.message.stop_reason === "refusal") {
|
|
930
|
+
lastRefusalExplanation = message.message.stop_details?.explanation ?? null;
|
|
931
|
+
}
|
|
857
932
|
}
|
|
858
933
|
// Strip <command-*>/<local-command-stdout> markers and render any
|
|
859
934
|
// remaining prose. Skill bodies and built-in slash commands (e.g.
|
|
@@ -973,6 +1048,16 @@ export class ClaudeAcpAgent {
|
|
|
973
1048
|
throw error;
|
|
974
1049
|
}
|
|
975
1050
|
finally {
|
|
1051
|
+
// The loop is returning — interrupt() succeeded or the prompt finished
|
|
1052
|
+
// — so disarm the force-cancel backstop and release the wake-up channel
|
|
1053
|
+
// (only if we still own it; a handoff installs the next prompt's).
|
|
1054
|
+
if (session.forceCancelTimer) {
|
|
1055
|
+
clearTimeout(session.forceCancelTimer);
|
|
1056
|
+
session.forceCancelTimer = undefined;
|
|
1057
|
+
}
|
|
1058
|
+
if (session.cancelController === cancelController) {
|
|
1059
|
+
session.cancelController = undefined;
|
|
1060
|
+
}
|
|
976
1061
|
if (!handedOff) {
|
|
977
1062
|
session.promptRunning = false;
|
|
978
1063
|
if (errored) {
|
|
@@ -1008,6 +1093,27 @@ export class ClaudeAcpAgent {
|
|
|
1008
1093
|
pending.resolve(true);
|
|
1009
1094
|
}
|
|
1010
1095
|
session.pendingMessages.clear();
|
|
1096
|
+
// Arm a backstop before interrupting: if a prompt is actively consuming
|
|
1097
|
+
// the query and interrupt() doesn't make the SDK yield (e.g. a wedged
|
|
1098
|
+
// TaskOutput block — issue #680), force the loop to return "cancelled"
|
|
1099
|
+
// after the floor elapses so the pending session/prompt still resolves per
|
|
1100
|
+
// the ACP cancellation contract instead of hanging forever. The loop's
|
|
1101
|
+
// `finally` clears this timer when interrupt() works and it returns through
|
|
1102
|
+
// the normal idle path, so on healthy cancels it is armed but never fires.
|
|
1103
|
+
//
|
|
1104
|
+
// Arm at most once per turn: the floor is an absolute ceiling from the
|
|
1105
|
+
// first cancel, so a client that re-sends cancel (each call still retries
|
|
1106
|
+
// interrupt() below) can't keep pushing the deadline out.
|
|
1107
|
+
if (session.promptRunning &&
|
|
1108
|
+
session.cancelController &&
|
|
1109
|
+
!session.cancelController.signal.aborted &&
|
|
1110
|
+
!session.forceCancelTimer) {
|
|
1111
|
+
const cancelController = session.cancelController;
|
|
1112
|
+
session.forceCancelTimer = setTimeout(() => {
|
|
1113
|
+
this.logger.error(`Session ${params.sessionId}: cancel floor elapsed without the SDK yielding; forcing "cancelled". The underlying query may still be wedged — a new session may be required.`);
|
|
1114
|
+
cancelController.abort();
|
|
1115
|
+
}, this.forceCancelGraceMs);
|
|
1116
|
+
}
|
|
1011
1117
|
await session.query.interrupt();
|
|
1012
1118
|
}
|
|
1013
1119
|
/** Cleanly tear down a session: cancel in-flight work, dispose resources,
|
|
@@ -1018,6 +1124,18 @@ export class ClaudeAcpAgent {
|
|
|
1018
1124
|
return;
|
|
1019
1125
|
}
|
|
1020
1126
|
await this.cancel({ sessionId });
|
|
1127
|
+
// cancel() arms the force-cancel floor and interrupts gracefully, but a
|
|
1128
|
+
// wedged prompt loop only wakes when `cancelController` aborts — closing
|
|
1129
|
+
// the query/abortController below doesn't touch it. Since we're tearing the
|
|
1130
|
+
// session down anyway, wake the loop now so the in-flight prompt() resolves
|
|
1131
|
+
// immediately instead of after the floor, and clear the timer so it can't
|
|
1132
|
+
// outlive the deleted session (it isn't unref'd and would otherwise keep
|
|
1133
|
+
// the event loop alive until it fires).
|
|
1134
|
+
if (session.forceCancelTimer) {
|
|
1135
|
+
clearTimeout(session.forceCancelTimer);
|
|
1136
|
+
session.forceCancelTimer = undefined;
|
|
1137
|
+
}
|
|
1138
|
+
session.cancelController?.abort();
|
|
1021
1139
|
session.settingsManager.dispose();
|
|
1022
1140
|
session.abortController.abort();
|
|
1023
1141
|
session.query.close();
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.41.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",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"license": "Apache-2.0",
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@agentclientprotocol/sdk": "0.24.0",
|
|
64
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
64
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.162",
|
|
65
65
|
"zod": "^3.25.0 || ^4.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|