@agentclientprotocol/claude-agent-acp 0.54.0 → 0.55.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 +17 -1
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +309 -39
- package/dist/elicitation.d.ts +45 -0
- package/dist/elicitation.d.ts.map +1 -1
- package/dist/elicitation.js +78 -0
- package/package.json +4 -4
package/dist/acp-agent.d.ts
CHANGED
|
@@ -384,9 +384,25 @@ export declare class ClaudeAcpAgent {
|
|
|
384
384
|
* `canUseTool` since that is where the SDK routes the tool's permission check.
|
|
385
385
|
*/
|
|
386
386
|
private handleAskUserQuestion;
|
|
387
|
+
/**
|
|
388
|
+
* Handle `request_user_dialog` control requests — blocking dialogs the CLI
|
|
389
|
+
* asks the host to render. Only kinds declared in `supportedDialogKinds`
|
|
390
|
+
* are ever emitted; everything unexpected is answered `cancelled` (the
|
|
391
|
+
* required answer for unrecognized kinds), which applies the dialog's
|
|
392
|
+
* default behavior CLI-side. Today the only declared kind is the
|
|
393
|
+
* refusal-fallback consent prompt, rendered as an ACP form elicitation.
|
|
394
|
+
*/
|
|
395
|
+
private handleUserDialog;
|
|
387
396
|
private sendAvailableCommandsUpdate;
|
|
388
397
|
private updateConfigOption;
|
|
389
398
|
private applyConfigOptionValue;
|
|
399
|
+
/** Reconcile adapter model state after the SDK persistently swapped the
|
|
400
|
+
* session's model out from under us (refusal fallback). The SDK already
|
|
401
|
+
* made the switch, so this must NOT call `query.setModel` — it only
|
|
402
|
+
* updates our bookkeeping (currentModelId, context window, mode clamping,
|
|
403
|
+
* effort/Fast-mode options) via the same `applyConfigOptionValue` path a
|
|
404
|
+
* user-driven model change takes, then notifies the client. */
|
|
405
|
+
private syncModelAfterRefusalFallback;
|
|
390
406
|
/** Replace the Fast mode option in `session.configOptions` so it reflects
|
|
391
407
|
* `enabled` (and the client's current boolean-capability). A no-op when the
|
|
392
408
|
* option isn't present, so callers must confirm the current model surfaces
|
|
@@ -489,7 +505,7 @@ export declare function resolveModelPreference(models: ModelInfo[], preference:
|
|
|
489
505
|
* - The Default option is unaffected by `availableModels` — it always remains
|
|
490
506
|
* available, even when the allowlist is `[]`.
|
|
491
507
|
*/
|
|
492
|
-
export declare function applyAvailableModelsAllowlist(sdkModels: ModelInfo[], allowlist: string[]): ModelInfo[];
|
|
508
|
+
export declare function applyAvailableModelsAllowlist(sdkModels: ModelInfo[], allowlist: string[], settingsModelOverrides?: Record<string, string>): ModelInfo[];
|
|
493
509
|
export declare function promptToClaude(prompt: PromptRequest): SDKUserMessage;
|
|
494
510
|
/**
|
|
495
511
|
* Resolves the ACP `messageId` for a Claude SDK message (live) or a persisted
|
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,EACnB,aAAa,EAGb,iBAAiB,EACjB,kBAAkB,EAElB,aAAa,EACb,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EAEpB,wBAAwB,EACxB,yBAAyB,EACzB,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,oBAAoB,EACpB,qBAAqB,EAEtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,SAAS,EACT,UAAU,EAEV,aAAa,EAKb,SAAS,
|
|
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
|
@@ -7,7 +7,7 @@ import * as os from "node:os";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
9
|
import packageJson from "../package.json" with { type: "json" };
|
|
10
|
-
import { applyAskElicitationResponse, askUserQuestionsToCreateRequest, createElicitationResponseToElicitResult, extractAskUserQuestions, mcpElicitationToCreateRequest, } from "./elicitation.js";
|
|
10
|
+
import { applyAskElicitationResponse, askUserQuestionsToCreateRequest, createElicitationResponseToElicitResult, extractAskUserQuestions, extractRefusalFallbackPrompt, mcpElicitationToCreateRequest, REFUSAL_FALLBACK_DIALOG_KIND, refusalFallbackResultFromResponse, refusalFallbackToCreateRequest, } from "./elicitation.js";
|
|
11
11
|
import { SettingsManager } from "./settings.js";
|
|
12
12
|
import { applyTaskCreate, applyTaskUpdate, createPostToolUseHook, createTaskHook, parseTaskCreateOutput, planEntries, registerHookCallback, taskStateToPlanEntries, toolInfoFromToolUse, toolUpdateFromDiffToolResponse, toolUpdateFromToolResult, } from "./tools.js";
|
|
13
13
|
import { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./utils.js";
|
|
@@ -42,6 +42,12 @@ const DEFAULT_CONTEXT_WINDOW = 200000;
|
|
|
42
42
|
* "obviously stuck" ceiling, not a guess at interrupt latency, so it can't
|
|
43
43
|
* pre-empt a slow-but-healthy interrupt. */
|
|
44
44
|
const DEFAULT_FORCE_CANCEL_GRACE_MS = 30_000;
|
|
45
|
+
/** Error surfaced when the SDK declares a turn over (`session_state_changed:
|
|
46
|
+
* idle`, its authoritative turn-over signal) without ever emitting the turn's
|
|
47
|
+
* `result` — a model stream that dropped mid-turn, or an async agent that
|
|
48
|
+
* completed/stalled without the host turn resolving (issue #825). */
|
|
49
|
+
const TURN_NO_RESULT_MESSAGE = "The turn ended without a result: the agent went idle while this prompt was still in flight " +
|
|
50
|
+
"(e.g. the model stream dropped mid-turn). Any partial output may be incomplete; please retry.";
|
|
45
51
|
/** Compute a stable fingerprint of the session-defining params so we can
|
|
46
52
|
* detect when a loadSession/resumeSession call requires tearing down and
|
|
47
53
|
* recreating the underlying Query process. MCP servers are sorted by name
|
|
@@ -660,6 +666,20 @@ export class ClaudeAcpAgent {
|
|
|
660
666
|
// Stop reason accumulated for the active turn (result subtype, refusal,
|
|
661
667
|
// max_tokens, …). Reset per turn; read when the turn settles at idle.
|
|
662
668
|
let stopReason = "end_turn";
|
|
669
|
+
// How many trailing `session_state_changed: idle` messages are already
|
|
670
|
+
// accounted for: every user-turn result that terminates a turn (settle,
|
|
671
|
+
// reject, or orphan skip) is followed by one, as is a cancelled turn
|
|
672
|
+
// settled by the next turn's echo hand-off. The idle handler absorbs owed
|
|
673
|
+
// idles; an idle that arrives when NONE is owed while the active turn is
|
|
674
|
+
// still unsettled means the SDK ended the turn without ever emitting its
|
|
675
|
+
// result, so the turn will never settle on its own (issue #825).
|
|
676
|
+
// Stream-level debt, deliberately NOT reset per turn: a lagged idle can
|
|
677
|
+
// arrive after the next turn has already activated (issue #773), and the
|
|
678
|
+
// debt is what attributes it to the turn that owed it. Over-counting (an
|
|
679
|
+
// idle the SDK never emits, e.g. CLI binaries without session-state
|
|
680
|
+
// events — issue #497) is benign: the counter just absorbs one future
|
|
681
|
+
// idle, and detection degrades to the status quo rather than misfiring.
|
|
682
|
+
let owedTrailingIdles = 0;
|
|
663
683
|
const resetTurnScratch = () => {
|
|
664
684
|
lastAssistantTotalUsage = null;
|
|
665
685
|
lastAssistantUsage = null;
|
|
@@ -780,9 +800,19 @@ export class ClaudeAcpAgent {
|
|
|
780
800
|
// settle "cancelled" even when query.next() is wedged (issue #680). Re-armed
|
|
781
801
|
// after each fire so the consumer keeps serving later turns.
|
|
782
802
|
let cancelController = session.cancelController;
|
|
803
|
+
// The in-flight query.next(), kept across abort wake-ups that don't
|
|
804
|
+
// consume a message, so no yielded message is ever dropped — async
|
|
805
|
+
// generators serialize next() calls, so racing a SECOND next() while one
|
|
806
|
+
// is pending would make the abandoned one swallow a message (e.g. a
|
|
807
|
+
// force-cancelled turn's late result, whose orphan accounting below
|
|
808
|
+
// depends on actually seeing it).
|
|
809
|
+
let pendingNext = null;
|
|
783
810
|
try {
|
|
784
811
|
while (true) {
|
|
785
|
-
|
|
812
|
+
pendingNext ??= session.query
|
|
813
|
+
.next()
|
|
814
|
+
.then((result) => ({ kind: "message", result }));
|
|
815
|
+
const nextMessage = pendingNext;
|
|
786
816
|
// Fresh abort listener per iteration, removed when next() wins, so a
|
|
787
817
|
// long-lived session doesn't accumulate listeners on one signal.
|
|
788
818
|
let onAbort;
|
|
@@ -790,25 +820,36 @@ export class ClaudeAcpAgent {
|
|
|
790
820
|
onAbort = () => resolve("abort");
|
|
791
821
|
cancelController.signal.addEventListener("abort", onAbort, { once: true });
|
|
792
822
|
});
|
|
793
|
-
const raced = await Promise.race([
|
|
794
|
-
nextMessage.then((result) => ({ kind: "message", result })),
|
|
795
|
-
abortRace,
|
|
796
|
-
]);
|
|
823
|
+
const raced = await Promise.race([nextMessage, abortRace]);
|
|
797
824
|
cancelController.signal.removeEventListener("abort", onAbort);
|
|
798
825
|
if (raced === "abort") {
|
|
799
|
-
// cancel()/teardown woke us
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
|
|
826
|
+
// cancel()/teardown woke us: settle the active turn "cancelled" per
|
|
827
|
+
// the ACP contract. The SDK never acknowledged this turn (that's why
|
|
828
|
+
// the force-cancel backstop fired), so if it later recovers from the
|
|
829
|
+
// wedge it will still emit the turn's result — with no live turn to
|
|
830
|
+
// match — followed by its trailing idle. Pre-count it as an orphan
|
|
831
|
+
// so that late result is skipped (not promoted onto the next queued
|
|
832
|
+
// prompt) and its trailer is recorded as owed, not read as the next
|
|
833
|
+
// turn being abandoned. Stale counts self-heal: activation resets
|
|
834
|
+
// them (see activateTurn).
|
|
835
|
+
if (session.activeTurn && !session.activeTurn.settled) {
|
|
836
|
+
session.pendingOrphanResults = (session.pendingOrphanResults ?? 0) + 1;
|
|
837
|
+
}
|
|
804
838
|
settleActive({ stopReason: "cancelled" });
|
|
839
|
+
// If the session is being torn down, abandon the in-flight next()
|
|
840
|
+
// (swallowing any later rejection so it can't surface as unhandled)
|
|
841
|
+
// and stop; otherwise re-arm and keep consuming — `pendingNext`
|
|
842
|
+
// stays in flight so its eventual message is processed, not dropped.
|
|
805
843
|
if (!this.sessions[params.sessionId]) {
|
|
844
|
+
void nextMessage.catch(() => { });
|
|
806
845
|
return;
|
|
807
846
|
}
|
|
808
847
|
cancelController = new AbortController();
|
|
809
848
|
session.cancelController = cancelController;
|
|
810
849
|
continue;
|
|
811
850
|
}
|
|
851
|
+
// A message arrived: this next() is consumed; arm a fresh one next pass.
|
|
852
|
+
pendingNext = null;
|
|
812
853
|
const { value: message, done } = raced.result;
|
|
813
854
|
if (done || !message) {
|
|
814
855
|
// The stream ended. Settle the in-flight turns FIRST, then release the
|
|
@@ -941,20 +982,55 @@ export class ClaudeAcpAgent {
|
|
|
941
982
|
}
|
|
942
983
|
case "session_state_changed": {
|
|
943
984
|
if (message.state === "idle") {
|
|
944
|
-
// A non-cancelled turn
|
|
945
|
-
// `result` (issue #773),
|
|
946
|
-
//
|
|
947
|
-
//
|
|
948
|
-
//
|
|
949
|
-
//
|
|
950
|
-
//
|
|
951
|
-
//
|
|
952
|
-
//
|
|
953
|
-
//
|
|
954
|
-
//
|
|
955
|
-
|
|
985
|
+
// A non-cancelled turn normally settled at its terminal
|
|
986
|
+
// `result` already (issue #773), and that result recorded an
|
|
987
|
+
// owed trailing idle — absorbed here via the decrement. We
|
|
988
|
+
// must NOT settle `activeTurn` on an owed idle: `idle`
|
|
989
|
+
// carries no turn identity, and it can lag (the SDK flushes
|
|
990
|
+
// held-back results / drains background agents first), so by
|
|
991
|
+
// the time it arrives the SDK may have echoed the NEXT turn
|
|
992
|
+
// and activated it — settling now would resolve that new
|
|
993
|
+
// turn prematurely with end_turn and ~zero usage, dropping
|
|
994
|
+
// its real result. A cancelled turn relies on `idle`: its
|
|
995
|
+
// `result` is dropped at the `session.cancelled` guard, so
|
|
996
|
+
// it never settles at a result and must settle here.
|
|
997
|
+
//
|
|
998
|
+
// An idle that is NOT owed while the active turn is still
|
|
999
|
+
// unsettled is the issue #825 signature: `idle` is the SDK's
|
|
1000
|
+
// authoritative turn-over signal (it fires after held-back
|
|
1001
|
+
// results flush and background agents drain), so a turn that
|
|
1002
|
+
// reaches it without a result will never get one — the model
|
|
1003
|
+
// stream dropped mid-turn, or an async agent
|
|
1004
|
+
// completed/stalled without the host turn resolving. Fail
|
|
1005
|
+
// the turn NOW so its session/prompt gets a terminal
|
|
1006
|
+
// response, instead of leaving it hanging until the next
|
|
1007
|
+
// prompt drains the wreckage.
|
|
1008
|
+
if (session.cancelled && session.activeTurn && !session.activeTurn.settled) {
|
|
956
1009
|
settleActive({ stopReason: "cancelled" });
|
|
957
1010
|
}
|
|
1011
|
+
else if (owedTrailingIdles > 0) {
|
|
1012
|
+
// Absorb a settled turn's trailing idle. Also covers a
|
|
1013
|
+
// cancel that landed between a turn's counted result and
|
|
1014
|
+
// this lagged idle (no active turn to settle): the idle
|
|
1015
|
+
// still belongs to that settled turn, and skipping the
|
|
1016
|
+
// decrement would leak the debt permanently.
|
|
1017
|
+
owedTrailingIdles--;
|
|
1018
|
+
}
|
|
1019
|
+
else if (!session.cancelled &&
|
|
1020
|
+
session.activeTurn &&
|
|
1021
|
+
!session.activeTurn.settled) {
|
|
1022
|
+
// Deliberately only the ACTIVE turn: a queued turn that
|
|
1023
|
+
// was never echoed is NOT failed here, because an idle
|
|
1024
|
+
// can legitimately precede the SDK picking up freshly
|
|
1025
|
+
// pushed input (the idle was emitted before the SDK read
|
|
1026
|
+
// it) — failing the queue head on that race would reject
|
|
1027
|
+
// a prompt the SDK is about to run. A turn abandoned
|
|
1028
|
+
// before its echo therefore still hangs until cancel or
|
|
1029
|
+
// the next prompt; only a timer could tell those apart.
|
|
1030
|
+
this.logger.error(`Session ${params.sessionId}: SDK went idle without emitting a result ` +
|
|
1031
|
+
`for the active turn; failing the in-flight prompt (issue #825)`);
|
|
1032
|
+
failActive(RequestError.internalError(errorKindData("no_result"), TURN_NO_RESULT_MESSAGE));
|
|
1033
|
+
}
|
|
958
1034
|
// The SDK generates the session title in a background task and
|
|
959
1035
|
// persists it to the session file; `idle` is the turn-over
|
|
960
1036
|
// signal, so it's the point at which a new title may have
|
|
@@ -1109,10 +1185,65 @@ export class ClaudeAcpAgent {
|
|
|
1109
1185
|
case "notification":
|
|
1110
1186
|
case "api_retry":
|
|
1111
1187
|
case "thinking_tokens":
|
|
1112
|
-
case "model_refusal_fallback":
|
|
1113
|
-
case "model_refusal_no_fallback":
|
|
1114
1188
|
// Todo: process via status api: https://docs.claude.com/en/docs/claude-code/hooks#hook-output
|
|
1115
1189
|
break;
|
|
1190
|
+
case "model_refusal_fallback": {
|
|
1191
|
+
// The SDK retried a refused turn on the fallback model and made
|
|
1192
|
+
// the swap persistent for the session. Without a notice the
|
|
1193
|
+
// user just sees regenerated output; without the state sync the
|
|
1194
|
+
// client's model picker (and the model-dependent options
|
|
1195
|
+
// rebuilt from it) keeps advertising a model the session is no
|
|
1196
|
+
// longer running.
|
|
1197
|
+
//
|
|
1198
|
+
// Current CLIs only emit direction "retry" (persistent swap).
|
|
1199
|
+
// "revert"/"sticky" are retained in the SDK enum for older
|
|
1200
|
+
// CLIs, where "revert" marked a turn-only fallback — for that
|
|
1201
|
+
// direction the session stays on the original model, so skip
|
|
1202
|
+
// the persistent-swap claim and the state sync.
|
|
1203
|
+
const persistent = message.direction !== "revert";
|
|
1204
|
+
const category = message.api_refusal_category
|
|
1205
|
+
? ` (${message.api_refusal_category})`
|
|
1206
|
+
: "";
|
|
1207
|
+
const explanation = message.api_refusal_explanation
|
|
1208
|
+
? `\n\n${message.api_refusal_explanation}`
|
|
1209
|
+
: "";
|
|
1210
|
+
const outcome = persistent
|
|
1211
|
+
? `The session will continue on ${message.fallback_model}.`
|
|
1212
|
+
: `The session stays on ${message.original_model}.`;
|
|
1213
|
+
await this.client.sessionUpdate({
|
|
1214
|
+
sessionId: message.session_id,
|
|
1215
|
+
update: {
|
|
1216
|
+
sessionUpdate: "agent_message_chunk",
|
|
1217
|
+
content: {
|
|
1218
|
+
type: "text",
|
|
1219
|
+
text: `**Model fallback:** ${message.original_model} declined this request${category}; retried with ${message.fallback_model}. ${outcome}${explanation}`,
|
|
1220
|
+
},
|
|
1221
|
+
},
|
|
1222
|
+
});
|
|
1223
|
+
if (persistent) {
|
|
1224
|
+
await this.syncModelAfterRefusalFallback(params.sessionId, session, message.fallback_model);
|
|
1225
|
+
}
|
|
1226
|
+
break;
|
|
1227
|
+
}
|
|
1228
|
+
case "model_refusal_no_fallback":
|
|
1229
|
+
// The refusal ends the turn as an error; the terminal `result`
|
|
1230
|
+
// handler settles it with ACP's `refusal` stop reason and
|
|
1231
|
+
// streams `lastRefusalExplanation`. The assistant frame's
|
|
1232
|
+
// stop_details is the primary source for that explanation —
|
|
1233
|
+
// this structured banner is the backup source when the frame
|
|
1234
|
+
// carried none (older CLIs, gateways that drop stop_details).
|
|
1235
|
+
//
|
|
1236
|
+
// `refused_user_message_uuid` is explicitly null when the
|
|
1237
|
+
// refused turn was not human-authored (a background
|
|
1238
|
+
// task-notification followup or auto-continuation) — don't
|
|
1239
|
+
// let those pollute the user turn's explanation. `undefined`
|
|
1240
|
+
// (older CLIs that omit the field) can't be attributed either
|
|
1241
|
+
// way, so keep seeding — the same exposure the assistant-frame
|
|
1242
|
+
// capture already has.
|
|
1243
|
+
if (!lastRefusalExplanation && message.refused_user_message_uuid !== null) {
|
|
1244
|
+
lastRefusalExplanation = message.api_refusal_explanation ?? message.content;
|
|
1245
|
+
}
|
|
1246
|
+
break;
|
|
1116
1247
|
default:
|
|
1117
1248
|
unreachable(message, this.logger);
|
|
1118
1249
|
break;
|
|
@@ -1139,6 +1270,23 @@ export class ClaudeAcpAgent {
|
|
|
1139
1270
|
if (!isTaskNotification) {
|
|
1140
1271
|
ensureActiveTurn();
|
|
1141
1272
|
}
|
|
1273
|
+
// Every user-turn result terminates a turn (settle, reject, or
|
|
1274
|
+
// orphan skip) and the SDK follows it with a trailing
|
|
1275
|
+
// `session_state_changed: idle` — record the debt so the idle
|
|
1276
|
+
// handler absorbs that idle rather than reading it as a turn the
|
|
1277
|
+
// SDK abandoned (issue #825). One exclusion: the cancelled ACTIVE
|
|
1278
|
+
// turn's own result. It is dropped at the `session.cancelled`
|
|
1279
|
+
// guard, and either the idle itself settles the turn (consuming
|
|
1280
|
+
// the trailer) or the next echo's hand-off does (which records
|
|
1281
|
+
// the debt there instead) — counting here too would double it.
|
|
1282
|
+
// Results skipped while cancelled with NO active turn — orphaned
|
|
1283
|
+
// queued turns the SDK still ran, or a force-cancelled turn's
|
|
1284
|
+
// late result after the backstop settled it — get no such settle,
|
|
1285
|
+
// so their trailers must be counted here or they'd later be read
|
|
1286
|
+
// as the next healthy turn being abandoned and false-fail it.
|
|
1287
|
+
if (!isTaskNotification && (!session.cancelled || !session.activeTurn)) {
|
|
1288
|
+
owedTrailingIdles++;
|
|
1289
|
+
}
|
|
1142
1290
|
// Accumulate usage into the user turn's tally. Skip task-notification
|
|
1143
1291
|
// followups: their cost is real but is reported separately via the
|
|
1144
1292
|
// usage_update below, and `session.accumulatedUsage` is only reset on
|
|
@@ -1415,9 +1563,18 @@ export class ClaudeAcpAgent {
|
|
|
1415
1563
|
// "cancelled" per the ACP contract rather than "end_turn" —
|
|
1416
1564
|
// otherwise a cancel followed quickly by the next prompt
|
|
1417
1565
|
// would report the cancelled turn as a normal completion.
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1566
|
+
if (session.cancelled) {
|
|
1567
|
+
// The cancelled turn settles here, but the trailing idle
|
|
1568
|
+
// its interrupt produces is still in flight — record the
|
|
1569
|
+
// debt so that lagged idle is absorbed rather than read
|
|
1570
|
+
// as the freshly-activated turn ending without a result
|
|
1571
|
+
// (which would false-fail a healthy turn — issue #825).
|
|
1572
|
+
owedTrailingIdles++;
|
|
1573
|
+
settleActive({ stopReason: "cancelled" });
|
|
1574
|
+
}
|
|
1575
|
+
else {
|
|
1576
|
+
settleActive({ stopReason: "end_turn", usage: sessionUsage(session) });
|
|
1577
|
+
}
|
|
1421
1578
|
}
|
|
1422
1579
|
activateTurn(queued);
|
|
1423
1580
|
}
|
|
@@ -1445,7 +1602,13 @@ export class ClaudeAcpAgent {
|
|
|
1445
1602
|
lastAssistantError = message.error;
|
|
1446
1603
|
}
|
|
1447
1604
|
if (message.message.stop_reason === "refusal") {
|
|
1448
|
-
|
|
1605
|
+
// Keep any explanation already seeded by a
|
|
1606
|
+
// `model_refusal_no_fallback` banner — the banner/frame
|
|
1607
|
+
// ordering is CLI-dependent, and a frame whose stop_details
|
|
1608
|
+
// was dropped (the case the banner backup exists for) must
|
|
1609
|
+
// not clobber the seed back to null.
|
|
1610
|
+
lastRefusalExplanation =
|
|
1611
|
+
message.message.stop_details?.explanation ?? lastRefusalExplanation;
|
|
1449
1612
|
}
|
|
1450
1613
|
}
|
|
1451
1614
|
// Strip <command-*>/<local-command-stdout> markers and render any
|
|
@@ -1613,9 +1776,16 @@ export class ClaudeAcpAgent {
|
|
|
1613
1776
|
}
|
|
1614
1777
|
break;
|
|
1615
1778
|
}
|
|
1779
|
+
// `conversation_reset` (from `/clear`, plan-mode exit, fresh-session
|
|
1780
|
+
// flows) is safe to drop: turn lifecycle here is driven by
|
|
1781
|
+
// results/idle, and the client owns its own transcript view.
|
|
1782
|
+
// `control_request_progress` only reports on side_question control
|
|
1783
|
+
// requests, which this adapter never issues.
|
|
1616
1784
|
case "tool_use_summary":
|
|
1617
1785
|
case "auth_status":
|
|
1618
1786
|
case "prompt_suggestion":
|
|
1787
|
+
case "conversation_reset":
|
|
1788
|
+
case "control_request_progress":
|
|
1619
1789
|
break;
|
|
1620
1790
|
default:
|
|
1621
1791
|
unreachable(message);
|
|
@@ -2210,6 +2380,44 @@ export class ClaudeAcpAgent {
|
|
|
2210
2380
|
}
|
|
2211
2381
|
return { behavior: "allow", updatedInput: outcome.updatedInput };
|
|
2212
2382
|
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Handle `request_user_dialog` control requests — blocking dialogs the CLI
|
|
2385
|
+
* asks the host to render. Only kinds declared in `supportedDialogKinds`
|
|
2386
|
+
* are ever emitted; everything unexpected is answered `cancelled` (the
|
|
2387
|
+
* required answer for unrecognized kinds), which applies the dialog's
|
|
2388
|
+
* default behavior CLI-side. Today the only declared kind is the
|
|
2389
|
+
* refusal-fallback consent prompt, rendered as an ACP form elicitation.
|
|
2390
|
+
*/
|
|
2391
|
+
handleUserDialog(sessionId) {
|
|
2392
|
+
return async (request, { signal }) => {
|
|
2393
|
+
if (request.dialogKind !== REFUSAL_FALLBACK_DIALOG_KIND) {
|
|
2394
|
+
return { behavior: "cancelled" };
|
|
2395
|
+
}
|
|
2396
|
+
const prompt = extractRefusalFallbackPrompt(request.payload);
|
|
2397
|
+
if (!prompt) {
|
|
2398
|
+
this.logger.error(`refusal_fallback_prompt payload had an unexpected shape; cancelling the dialog: ${JSON.stringify(request.payload)}`);
|
|
2399
|
+
return { behavior: "cancelled" };
|
|
2400
|
+
}
|
|
2401
|
+
let response;
|
|
2402
|
+
try {
|
|
2403
|
+
response = await this.client.unstable_createElicitation(refusalFallbackToCreateRequest(prompt, sessionId), signal);
|
|
2404
|
+
}
|
|
2405
|
+
catch (error) {
|
|
2406
|
+
// A cancellation we requested (signal aborted) is expected teardown;
|
|
2407
|
+
// anything else is a client failure. Either way the safe answer is
|
|
2408
|
+
// `cancelled` — the CLI applies the dialog's default (keep the
|
|
2409
|
+
// refusal) rather than switching models without consent.
|
|
2410
|
+
if (!signal.aborted) {
|
|
2411
|
+
this.logger.error(`Failed to present refusal fallback elicitation: ${error}`);
|
|
2412
|
+
}
|
|
2413
|
+
return { behavior: "cancelled" };
|
|
2414
|
+
}
|
|
2415
|
+
if (signal.aborted) {
|
|
2416
|
+
return { behavior: "cancelled" };
|
|
2417
|
+
}
|
|
2418
|
+
return { behavior: "completed", result: refusalFallbackResultFromResponse(response) };
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2213
2421
|
async sendAvailableCommandsUpdate(sessionId) {
|
|
2214
2422
|
const session = this.sessions[sessionId];
|
|
2215
2423
|
if (!session)
|
|
@@ -2257,8 +2465,16 @@ export class ClaudeAcpAgent {
|
|
|
2257
2465
|
}
|
|
2258
2466
|
session.models = { ...session.models, currentModelId: value };
|
|
2259
2467
|
// Recompute availableModes for the new model and clamp the current
|
|
2260
|
-
// mode if the SDK no longer offers it (today: "auto" on Haiku).
|
|
2261
|
-
|
|
2468
|
+
// mode if the SDK no longer offers it (today: "auto" on Haiku). An
|
|
2469
|
+
// unknown model (an SDK-initiated refusal fallback to a model outside
|
|
2470
|
+
// the user's `availableModels` allowlist — user-driven switches are
|
|
2471
|
+
// validated against the options first) tells us nothing about its
|
|
2472
|
+
// capabilities, so keep the current modes rather than spuriously
|
|
2473
|
+
// downgrading (e.g. kicking the user out of "auto" for a model that
|
|
2474
|
+
// does support it).
|
|
2475
|
+
const newAvailableModes = newModelInfo
|
|
2476
|
+
? buildAvailableModes(newModelInfo)
|
|
2477
|
+
: session.modes.availableModes;
|
|
2262
2478
|
// Capture BEFORE mutating session.modes so the log message reflects
|
|
2263
2479
|
// the invalidated mode rather than "default".
|
|
2264
2480
|
const previousModeId = session.modes.currentModeId;
|
|
@@ -2339,6 +2555,36 @@ export class ClaudeAcpAgent {
|
|
|
2339
2555
|
}
|
|
2340
2556
|
}
|
|
2341
2557
|
}
|
|
2558
|
+
/** Reconcile adapter model state after the SDK persistently swapped the
|
|
2559
|
+
* session's model out from under us (refusal fallback). The SDK already
|
|
2560
|
+
* made the switch, so this must NOT call `query.setModel` — it only
|
|
2561
|
+
* updates our bookkeeping (currentModelId, context window, mode clamping,
|
|
2562
|
+
* effort/Fast-mode options) via the same `applyConfigOptionValue` path a
|
|
2563
|
+
* user-driven model change takes, then notifies the client. */
|
|
2564
|
+
async syncModelAfterRefusalFallback(sessionId, session, fallbackModel) {
|
|
2565
|
+
// Map the SDK-reported model onto one of the session's model options
|
|
2566
|
+
// (handles display names and `resolvedModel` ids). The fallback model may
|
|
2567
|
+
// not be among the options — e.g. excluded by the user's
|
|
2568
|
+
// `availableModels` allowlist — in which case we track the raw id: the
|
|
2569
|
+
// picker shows no selection, but the model-dependent bookkeeping and any
|
|
2570
|
+
// later `setModel` round-trip stay truthful to what the SDK is running.
|
|
2571
|
+
const resolved = resolveModelPreference(session.modelInfos, fallbackModel);
|
|
2572
|
+
const value = resolved?.value ?? fallbackModel;
|
|
2573
|
+
if (session.models.currentModelId === value)
|
|
2574
|
+
return;
|
|
2575
|
+
try {
|
|
2576
|
+
await this.updateConfigOption(sessionId, MODEL_CONFIG_ID, value);
|
|
2577
|
+
}
|
|
2578
|
+
catch (err) {
|
|
2579
|
+
// This runs on the consumer loop: a throw here tears down the query
|
|
2580
|
+
// stream (failAllTurns + closeQueryStream) and bricks the session —
|
|
2581
|
+
// far worse than stale bookkeeping. The user-driven RPC path lets the
|
|
2582
|
+
// same errors propagate to fail just that request; here we log and
|
|
2583
|
+
// move on, matching the setPermissionMode containment inside
|
|
2584
|
+
// applyConfigOptionValue.
|
|
2585
|
+
this.logger.error(`Failed to reconcile model state after refusal fallback to "${fallbackModel}":`, err);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2342
2588
|
/** Replace the Fast mode option in `session.configOptions` so it reflects
|
|
2343
2589
|
* `enabled` (and the client's current boolean-capability). A no-op when the
|
|
2344
2590
|
* option isn't present, so callers must confirm the current model surfaces
|
|
@@ -2586,6 +2832,18 @@ export class ClaudeAcpAgent {
|
|
|
2586
2832
|
...(elicitationSupport.form || elicitationSupport.url
|
|
2587
2833
|
? { onElicitation: this.handleMcpElicitation(sessionId, elicitationSupport) }
|
|
2588
2834
|
: {}),
|
|
2835
|
+
// Render the CLI's refusal-fallback consent prompt ("<model> declined —
|
|
2836
|
+
// retry with <fallback>?") as an ACP form elicitation. Declaring the
|
|
2837
|
+
// kind is the opt-in: the CLI never emits an undeclared dialog, and the
|
|
2838
|
+
// flow instead degrades to the classic refusal error ending the turn.
|
|
2839
|
+
// Gated on form elicitation since that's the only ACP surface that can
|
|
2840
|
+
// present a choice outside a tool call.
|
|
2841
|
+
...(elicitationSupport.form
|
|
2842
|
+
? {
|
|
2843
|
+
onUserDialog: this.handleUserDialog(sessionId),
|
|
2844
|
+
supportedDialogKinds: [REFUSAL_FALLBACK_DIALOG_KIND],
|
|
2845
|
+
}
|
|
2846
|
+
: {}),
|
|
2589
2847
|
pathToClaudeCodeExecutable: process.env.CLAUDE_CODE_EXECUTABLE ?? (await claudeCliPath()),
|
|
2590
2848
|
extraArgs: {
|
|
2591
2849
|
...userProvidedOptions?.extraArgs,
|
|
@@ -2701,8 +2959,9 @@ export class ClaudeAcpAgent {
|
|
|
2701
2959
|
// configOptions, the current-model resolver, and the stored modelInfos
|
|
2702
2960
|
// consistent with what the user configured.
|
|
2703
2961
|
const settingsAvailableModels = settingsManager.getSettings().availableModels;
|
|
2962
|
+
const settingsModelOverrides = settingsManager.getSettings().modelOverrides;
|
|
2704
2963
|
const allowedModels = Array.isArray(settingsAvailableModels)
|
|
2705
|
-
? applyAvailableModelsAllowlist(initializationResult.models, settingsAvailableModels)
|
|
2964
|
+
? applyAvailableModelsAllowlist(initializationResult.models, settingsAvailableModels, settingsModelOverrides)
|
|
2706
2965
|
: initializationResult.models;
|
|
2707
2966
|
const models = await getAvailableModels(q, allowedModels, initializationResult.models, settingsManager, this.logger);
|
|
2708
2967
|
// Gate `auto` (and future model-specific modes) on the resolved model's
|
|
@@ -2839,9 +3098,9 @@ function totalTokens(usage) {
|
|
|
2839
3098
|
}
|
|
2840
3099
|
/**
|
|
2841
3100
|
* Build the `data` payload attached to a `RequestError.internalError` when we
|
|
2842
|
-
* have a categorical error from the Claude SDK
|
|
2843
|
-
* categorical error is available, matching
|
|
2844
|
-
* `undefined` to `RequestError.internalError`.
|
|
3101
|
+
* have a categorical error — from the Claude SDK, or one of the adapter's own
|
|
3102
|
+
* kinds. Returns `undefined` when no categorical error is available, matching
|
|
3103
|
+
* the previous behavior of passing `undefined` to `RequestError.internalError`.
|
|
2845
3104
|
*
|
|
2846
3105
|
* The `errorKind` field is a convention for ACP clients to dispatch on
|
|
2847
3106
|
* without having to pattern-match the human-readable message text. Clients
|
|
@@ -3256,7 +3515,7 @@ function resolveSettingsModel(models, settingsModel, logger) {
|
|
|
3256
3515
|
* - The Default option is unaffected by `availableModels` — it always remains
|
|
3257
3516
|
* available, even when the allowlist is `[]`.
|
|
3258
3517
|
*/
|
|
3259
|
-
export function applyAvailableModelsAllowlist(sdkModels, allowlist) {
|
|
3518
|
+
export function applyAvailableModelsAllowlist(sdkModels, allowlist, settingsModelOverrides) {
|
|
3260
3519
|
// Default is always preserved per the docs. Synthesize one if the SDK
|
|
3261
3520
|
// didn't surface it so downstream code (e.g. `getAvailableModels` picking
|
|
3262
3521
|
// `models[0]` as a fallback) still has something to work with.
|
|
@@ -3268,18 +3527,29 @@ export function applyAvailableModelsAllowlist(sdkModels, allowlist) {
|
|
|
3268
3527
|
const result = [defaultModel];
|
|
3269
3528
|
const seen = new Set([defaultModel.value]);
|
|
3270
3529
|
const sdkModelsWithoutDefault = sdkModels.filter((m) => m.value !== "default");
|
|
3530
|
+
// Bedrock/Vertex deployments enforce short aliases (e.g. "claude-opus-4-6")
|
|
3531
|
+
// in availableModels but require provider-specific IDs at the API. We still
|
|
3532
|
+
// resolve `sdkMatch` against the alias (`trimmed`) — that's what the
|
|
3533
|
+
// matching heuristics above are built for, and override targets (ARNs,
|
|
3534
|
+
// opaque provider IDs) often won't textually resemble anything in
|
|
3535
|
+
// `sdkModelsWithoutDefault`. Only the entry's surfaced `value` becomes the
|
|
3536
|
+
// override target, so it's what `setModel` ends up passing to the API.
|
|
3271
3537
|
for (const entry of allowlist) {
|
|
3272
3538
|
const trimmed = entry.trim();
|
|
3273
3539
|
if (!trimmed || seen.has(trimmed))
|
|
3274
3540
|
continue;
|
|
3541
|
+
const overridden = settingsModelOverrides?.[trimmed];
|
|
3542
|
+
const effective = overridden ?? trimmed;
|
|
3543
|
+
if (seen.has(effective))
|
|
3544
|
+
continue;
|
|
3275
3545
|
const sdkMatch = resolveModelPreference(sdkModelsWithoutDefault, trimmed);
|
|
3276
3546
|
if (sdkMatch) {
|
|
3277
|
-
result.push({ ...sdkMatch, value:
|
|
3547
|
+
result.push({ ...sdkMatch, value: effective });
|
|
3278
3548
|
}
|
|
3279
3549
|
else {
|
|
3280
|
-
result.push({ value:
|
|
3550
|
+
result.push({ value: effective, displayName: trimmed, description: "" });
|
|
3281
3551
|
}
|
|
3282
|
-
seen.add(
|
|
3552
|
+
seen.add(effective);
|
|
3283
3553
|
}
|
|
3284
3554
|
// The custom model option (ANTHROPIC_CUSTOM_MODEL_OPTION) is exempt from the
|
|
3285
3555
|
// allowlist, the same way Default is. Per the model-config docs it adds an
|
package/dist/elicitation.d.ts
CHANGED
|
@@ -79,4 +79,49 @@ export type AskUserQuestionOutcome = {
|
|
|
79
79
|
* rather than the turn aborting); cancel aborts the tool call.
|
|
80
80
|
*/
|
|
81
81
|
export declare function applyAskElicitationResponse(response: CreateElicitationResponse, toolInput: Record<string, unknown>, questions: AskUserQuestion[]): AskUserQuestionOutcome;
|
|
82
|
+
/**
|
|
83
|
+
* The `request_user_dialog` kind the CLI emits when a model refusal has a
|
|
84
|
+
* fallback available but needs user consent before retrying (e.g. Claude Fable
|
|
85
|
+
* declining a request with Opus available as the fallback). Declaring this
|
|
86
|
+
* kind in `supportedDialogKinds` is the opt-in: the CLI fails closed and never
|
|
87
|
+
* emits an undeclared kind — the flow degrades to the classic refusal error
|
|
88
|
+
* ending the turn.
|
|
89
|
+
*/
|
|
90
|
+
export declare const REFUSAL_FALLBACK_DIALOG_KIND = "refusal_fallback_prompt";
|
|
91
|
+
/**
|
|
92
|
+
* Payload of the `refusal_fallback_prompt` dialog. The dialog protocol
|
|
93
|
+
* transports payloads opaquely, so this shape is recovered from the CLI's own
|
|
94
|
+
* schema (v2.1.177): `originalModel`/`fallbackModel` are required strings;
|
|
95
|
+
* `apiRefusalCategory` (nullable), `guidanceText`, and
|
|
96
|
+
* `retractedMessageUuids` are optional. We ignore `retractedMessageUuids` —
|
|
97
|
+
* ACP has no way to retract already-streamed chunks.
|
|
98
|
+
*/
|
|
99
|
+
export type RefusalFallbackPrompt = {
|
|
100
|
+
originalModel: string;
|
|
101
|
+
fallbackModel: string;
|
|
102
|
+
apiRefusalCategory: string | null;
|
|
103
|
+
guidanceText?: string;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Validate the opaque dialog payload into a {@link RefusalFallbackPrompt}.
|
|
107
|
+
* Returns `null` when the required fields are missing or mistyped (a newer CLI
|
|
108
|
+
* may reshape the payload), so the caller can cancel the dialog and let the
|
|
109
|
+
* CLI apply its default behavior instead of rendering something misleading.
|
|
110
|
+
*/
|
|
111
|
+
export declare function extractRefusalFallbackPrompt(payload: Record<string, unknown>): RefusalFallbackPrompt | null;
|
|
112
|
+
/**
|
|
113
|
+
* Render the refusal-fallback consent prompt as an ACP form elicitation: a
|
|
114
|
+
* single-select between retrying on the fallback model and keeping the
|
|
115
|
+
* refusal. The enum `const`s are the dialog's wire result values, so the
|
|
116
|
+
* response maps back without a translation table.
|
|
117
|
+
*/
|
|
118
|
+
export declare function refusalFallbackToCreateRequest(prompt: RefusalFallbackPrompt, sessionId: string): CreateElicitationRequest;
|
|
119
|
+
/**
|
|
120
|
+
* Map the elicitation response back to the dialog's result enum. Only an
|
|
121
|
+
* explicit accept-with-retry resolves to `retry_fallback`; decline, cancel, a
|
|
122
|
+
* skipped field, or an unrecognized value all keep the refusal — the dialog's
|
|
123
|
+
* own default — so a dismissed or half-filled form can never trigger a model
|
|
124
|
+
* switch the user didn't ask for.
|
|
125
|
+
*/
|
|
126
|
+
export declare function refusalFallbackResultFromResponse(response: CreateElicitationResponse): string;
|
|
82
127
|
//# sourceMappingURL=elicitation.d.ts.map
|
|
@@ -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,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"}
|
|
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;AAgBD;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AAEtE;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,qBAAqB,GAAG,IAAI,CAW9B;AAYD;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,qBAAqB,EAC7B,SAAS,EAAE,MAAM,GAChB,wBAAwB,CAuB1B;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAM7F"}
|
package/dist/elicitation.js
CHANGED
|
@@ -209,3 +209,81 @@ function normalizeElicitationSchema(schema) {
|
|
|
209
209
|
}
|
|
210
210
|
return { ...schema, type: "object" };
|
|
211
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* The `request_user_dialog` kind the CLI emits when a model refusal has a
|
|
214
|
+
* fallback available but needs user consent before retrying (e.g. Claude Fable
|
|
215
|
+
* declining a request with Opus available as the fallback). Declaring this
|
|
216
|
+
* kind in `supportedDialogKinds` is the opt-in: the CLI fails closed and never
|
|
217
|
+
* emits an undeclared kind — the flow degrades to the classic refusal error
|
|
218
|
+
* ending the turn.
|
|
219
|
+
*/
|
|
220
|
+
export const REFUSAL_FALLBACK_DIALOG_KIND = "refusal_fallback_prompt";
|
|
221
|
+
/**
|
|
222
|
+
* Validate the opaque dialog payload into a {@link RefusalFallbackPrompt}.
|
|
223
|
+
* Returns `null` when the required fields are missing or mistyped (a newer CLI
|
|
224
|
+
* may reshape the payload), so the caller can cancel the dialog and let the
|
|
225
|
+
* CLI apply its default behavior instead of rendering something misleading.
|
|
226
|
+
*/
|
|
227
|
+
export function extractRefusalFallbackPrompt(payload) {
|
|
228
|
+
const { originalModel, fallbackModel, apiRefusalCategory, guidanceText } = payload;
|
|
229
|
+
if (typeof originalModel !== "string" || typeof fallbackModel !== "string") {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
originalModel,
|
|
234
|
+
fallbackModel,
|
|
235
|
+
apiRefusalCategory: typeof apiRefusalCategory === "string" ? apiRefusalCategory : null,
|
|
236
|
+
...(typeof guidanceText === "string" && guidanceText ? { guidanceText } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/** Form-field key carrying the user's choice in the refusal-fallback form. */
|
|
240
|
+
const REFUSAL_FALLBACK_CHOICE_KEY = "choice";
|
|
241
|
+
/** Wire values of the dialog's result enum (CLI schema). `edit_prompt` is
|
|
242
|
+
* deliberately not offered: in the CLI it prefills the composer with the
|
|
243
|
+
* refused prompt for edit-and-retry, and ACP has no composer-prefill surface
|
|
244
|
+
* — the user can simply edit and resend on their own. */
|
|
245
|
+
const RETRY_FALLBACK_RESULT = "retry_fallback";
|
|
246
|
+
const KEEP_REFUSAL_RESULT = "cancelled";
|
|
247
|
+
/**
|
|
248
|
+
* Render the refusal-fallback consent prompt as an ACP form elicitation: a
|
|
249
|
+
* single-select between retrying on the fallback model and keeping the
|
|
250
|
+
* refusal. The enum `const`s are the dialog's wire result values, so the
|
|
251
|
+
* response maps back without a translation table.
|
|
252
|
+
*/
|
|
253
|
+
export function refusalFallbackToCreateRequest(prompt, sessionId) {
|
|
254
|
+
const category = prompt.apiRefusalCategory ? ` (${prompt.apiRefusalCategory})` : "";
|
|
255
|
+
const guidance = prompt.guidanceText ? `\n\n${prompt.guidanceText}` : "";
|
|
256
|
+
return {
|
|
257
|
+
mode: "form",
|
|
258
|
+
sessionId,
|
|
259
|
+
message: `${prompt.originalModel} declined this request${category}. ` +
|
|
260
|
+
`Retry with ${prompt.fallbackModel}? The session would continue on ${prompt.fallbackModel}.` +
|
|
261
|
+
guidance,
|
|
262
|
+
requestedSchema: {
|
|
263
|
+
type: "object",
|
|
264
|
+
properties: {
|
|
265
|
+
[REFUSAL_FALLBACK_CHOICE_KEY]: {
|
|
266
|
+
type: "string",
|
|
267
|
+
oneOf: [
|
|
268
|
+
{ const: RETRY_FALLBACK_RESULT, title: `Retry with ${prompt.fallbackModel}` },
|
|
269
|
+
{ const: KEEP_REFUSAL_RESULT, title: "Keep the refusal" },
|
|
270
|
+
],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Map the elicitation response back to the dialog's result enum. Only an
|
|
278
|
+
* explicit accept-with-retry resolves to `retry_fallback`; decline, cancel, a
|
|
279
|
+
* skipped field, or an unrecognized value all keep the refusal — the dialog's
|
|
280
|
+
* own default — so a dismissed or half-filled form can never trigger a model
|
|
281
|
+
* switch the user didn't ask for.
|
|
282
|
+
*/
|
|
283
|
+
export function refusalFallbackResultFromResponse(response) {
|
|
284
|
+
if (response.action !== "accept") {
|
|
285
|
+
return KEEP_REFUSAL_RESULT;
|
|
286
|
+
}
|
|
287
|
+
const choice = response.content?.[REFUSAL_FALLBACK_CHOICE_KEY];
|
|
288
|
+
return choice === RETRY_FALLBACK_RESULT ? RETRY_FALLBACK_RESULT : KEEP_REFUSAL_RESULT;
|
|
289
|
+
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.55.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",
|
|
@@ -64,14 +64,14 @@
|
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@agentclientprotocol/sdk": "1.1.0",
|
|
67
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
67
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.198",
|
|
68
68
|
"zod": "^3.25.0 || ^4.0.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@anthropic-ai/sdk": "0.
|
|
71
|
+
"@anthropic-ai/sdk": "0.109.1",
|
|
72
72
|
"@eslint/js": "10.0.1",
|
|
73
73
|
"@tsconfig/node22": "22.0.5",
|
|
74
|
-
"@types/node": "26.0
|
|
74
|
+
"@types/node": "26.1.0",
|
|
75
75
|
"@typescript-eslint/eslint-plugin": "8.62.1",
|
|
76
76
|
"@typescript-eslint/parser": "8.62.1",
|
|
77
77
|
"eslint": "10.6.0",
|