@lucascouts/claude-agent-acp-plus 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"elicitation.d.ts","sourceRoot":"","sources":["../src/elicitation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EACV,wBAAwB,EAKzB,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;AAgBD;;;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;AAwBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,eAAe,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CAqD1B;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;;;;;;;;;;;GAWG;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,CA+B1B;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAG7F"}
1
+ {"version":3,"file":"elicitation.d.ts","sourceRoot":"","sources":["../src/elicitation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EACV,wBAAwB,EAKzB,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;AAgBD;;;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;AAgCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,eAAe,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CA2D1B;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;;;;;;;;;;;GAWG;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,CA+B1B;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,CAG7F"}
@@ -89,6 +89,13 @@ function questionCustomFieldKey(index) {
89
89
  * extensions (`_claude/...`).
90
90
  */
91
91
  const OPTION_META_KEY = "_claude/askUserQuestionOption";
92
+ /**
93
+ * Shared `_meta` key for marking a per-question free-text field as the custom
94
+ * answer companion for a select question. This intentionally has no
95
+ * agent-specific namespace so ACP clients can recognize the same marker across
96
+ * Codex, Claude, and other AskUserQuestion bridges.
97
+ */
98
+ const CUSTOM_ANSWER_META_KEY = "_askUserQuestionCustomAnswer";
92
99
  /**
93
100
  * Render the AskUserQuestion tool's questions as an ACP form elicitation.
94
101
  *
@@ -137,6 +144,12 @@ export function askUserQuestionsToCreateRequest(questions, sessionId, toolCallId
137
144
  type: "string",
138
145
  title: "Other",
139
146
  description: "Type your own answer instead of choosing an option above (optional).",
147
+ _meta: {
148
+ [CUSTOM_ANSWER_META_KEY]: {
149
+ questionId: questionFieldKey(index),
150
+ isCustomAnswer: true,
151
+ },
152
+ },
140
153
  };
141
154
  });
142
155
  const requestedSchema = {
@@ -8,7 +8,7 @@ import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk";
8
8
  * `undefined`, or empty `displayName` or `description` is treated as an empty
9
9
  * string, so this never throws.
10
10
  *
11
- * See the module doc for WHY this is a heuristic (SDK 0.3.204 `ModelInfo` has no
11
+ * See the module doc for WHY this is a heuristic (SDK 0.3.220 `ModelInfo` has no
12
12
  * deprecation field).
13
13
  *
14
14
  * @param info - A single SDK model row.
@@ -1 +1 @@
1
- {"version":3,"file":"model-deprecation.d.ts","sourceRoot":"","sources":["../src/model-deprecation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AA4BhE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAI1D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAEtE"}
1
+ {"version":3,"file":"model-deprecation.d.ts","sourceRoot":"","sources":["../src/model-deprecation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAiChE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAI1D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAEtE"}
@@ -1,12 +1,17 @@
1
1
  /**
2
2
  * Model deprecation heuristic.
3
3
  *
4
- * The Claude Agent SDK's `ModelInfo` (0.3.204) exposes NO deprecation flag —
5
- * its only capability fields are `supportsEffort` / `supportedEffortLevels`,
6
- * `supportsAdaptiveThinking`, `supportsFastMode`, and `supportsAutoMode`. There
4
+ * The Claude Agent SDK's `ModelInfo` exposes NO deprecation flag — its only
5
+ * capability fields are `supportsEffort` / `supportedEffortLevels`,
6
+ * `supportsAdaptiveThinking`, `supportsFastMode`, and `supportsAutoMode`
7
+ * (`resolvedModel` is an alias-resolution hint, not a lifecycle signal). There
7
8
  * is therefore no authoritative, machine-readable signal telling a host that a
8
9
  * model row has been retired.
9
10
  *
11
+ * LAST VERIFIED AGAINST SDK 0.3.220 (story 008, R5.5) — the type is byte-for-byte
12
+ * unchanged from 0.3.204/0.3.212, so the heuristic below is still necessary.
13
+ * Re-check this claim on every SDK bump and update the version named here.
14
+ *
10
15
  * As a stand-in this module scans the human-facing copy — `displayName` and
11
16
  * `description` — for the words "deprecated" or "legacy" (case-insensitive).
12
17
  * This is deliberately a HEURISTIC: it can only catch models the SDK happens to
@@ -31,7 +36,7 @@ const DEPRECATION_MARKER = /deprecated|legacy/i;
31
36
  * `undefined`, or empty `displayName` or `description` is treated as an empty
32
37
  * string, so this never throws.
33
38
  *
34
- * See the module doc for WHY this is a heuristic (SDK 0.3.204 `ModelInfo` has no
39
+ * See the module doc for WHY this is a heuristic (SDK 0.3.220 `ModelInfo` has no
35
40
  * deprecation field).
36
41
  *
37
42
  * @param info - A single SDK model row.
@@ -75,11 +75,21 @@ export declare function listCheckpoints(messages: SDKMessage[]): Checkpoint[];
75
75
  export declare function formatCheckpointList(cps: Checkpoint[]): string;
76
76
  /**
77
77
  * Confirm a completed rewind, naming the checkpoint that was restored by its
78
- * index and excerpt (R3.4).
78
+ * index and excerpt (R3.4). When the SDK reports files it refused to touch for
79
+ * link safety, the confirmation is qualified with their count (R1.1) in the
80
+ * grammatical number that count calls for (R1.4), so the sentence cannot claim
81
+ * a clean success the rewind did not have.
82
+ *
83
+ * The unqualified sentence is unchanged character for character: an absent,
84
+ * zero, negative or non-numeric count returns exactly what this function
85
+ * returned before the qualifier existed (R1.2). The SDK exposes a COUNT only,
86
+ * never the refused paths, so the message deliberately names no files.
79
87
  *
80
88
  * @param cp The checkpoint whose files were restored.
89
+ * @param skippedLinks How many tracked files the SDK left unchanged for link
90
+ * safety (`RewindFilesResult.skippedLinks`); omit or pass 0 when none were.
81
91
  */
82
- export declare function formatRewindResult(cp: Checkpoint): string;
92
+ export declare function formatRewindResult(cp: Checkpoint, skippedLinks?: number): string;
83
93
  /**
84
94
  * Build a `/rewind` usage/error message. Always names the `/rewind` command and,
85
95
  * when at least one checkpoint exists, the valid index range (R3.5). An optional
@@ -122,7 +132,9 @@ export interface RewindDeps {
122
132
  * nothing-to-rewind notice). `restore` fetches + rebuilds the list, validates
123
133
  * the requested index, and on a hit calls `query.rewindFiles` with the tracked
124
134
  * uuid inside a try/catch — a rejection (or a `canRewind: false` result) is
125
- * reported via {@link formatRewindError} and never claims success. An `invalid`
135
+ * reported via {@link formatRewindError} and never claims success. A success
136
+ * that refused files for link safety carries that count into the confirmation
137
+ * (R1.1), so a partial rewind is not reported as a whole one. An `invalid`
126
138
  * invocation, an out-of-range index, or a failed transcript read each emit an
127
139
  * error and leave files untouched (`rewindFiles` is never called); the
128
140
  * `invalid` path still reads the transcript so its usage message can name the
@@ -1 +1 @@
1
- {"version":3,"file":"rewind-command.d.ts","sourceRoot":"","sources":["../src/rewind-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAMpF,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,uFAAuF;AACvF,MAAM,MAAM,gBAAgB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAkBjF;AAkED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CA4BpE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,MAAM,CAY9D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAEzD;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,MAAM,EAAE;QACN,aAAa,CAAC,YAAY,EAAE;YAC1B,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE;gBAAE,aAAa,EAAE,qBAAqB,CAAC;gBAAC,OAAO,EAAE;oBAAE,IAAI,EAAE,MAAM,CAAC;oBAAC,IAAI,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,CAAC;SAC3F,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACnB,CAAC;IACF,oDAAoD;IACpD,KAAK,EAAE;QAAE,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;KAAE,CAAC;IAC1E,sEAAsE;IACtE,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC9D;AAqBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,IAAI,CAAC,CA8Df"}
1
+ {"version":3,"file":"rewind-command.d.ts","sourceRoot":"","sources":["../src/rewind-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAMpF,qFAAqF;AACrF,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,uFAAuF;AACvF,MAAM,MAAM,gBAAgB,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAkBjF;AAkED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE,CA4BpE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,MAAM,CAY9D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED,8DAA8D;AAC9D,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,MAAM,EAAE;QACN,aAAa,CAAC,YAAY,EAAE;YAC1B,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE;gBAAE,aAAa,EAAE,qBAAqB,CAAC;gBAAC,OAAO,EAAE;oBAAE,IAAI,EAAE,MAAM,CAAC;oBAAC,IAAI,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,CAAC;SAC3F,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KACnB,CAAC;IACF,oDAAoD;IACpD,KAAK,EAAE;QAAE,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;KAAE,CAAC;IAC1E,sEAAsE;IACtE,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC9D;AAqBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAkEf"}
@@ -170,12 +170,37 @@ export function formatCheckpointList(cps) {
170
170
  }
171
171
  /**
172
172
  * Confirm a completed rewind, naming the checkpoint that was restored by its
173
- * index and excerpt (R3.4).
173
+ * index and excerpt (R3.4). When the SDK reports files it refused to touch for
174
+ * link safety, the confirmation is qualified with their count (R1.1) in the
175
+ * grammatical number that count calls for (R1.4), so the sentence cannot claim
176
+ * a clean success the rewind did not have.
177
+ *
178
+ * The unqualified sentence is unchanged character for character: an absent,
179
+ * zero, negative or non-numeric count returns exactly what this function
180
+ * returned before the qualifier existed (R1.2). The SDK exposes a COUNT only,
181
+ * never the refused paths, so the message deliberately names no files.
174
182
  *
175
183
  * @param cp The checkpoint whose files were restored.
184
+ * @param skippedLinks How many tracked files the SDK left unchanged for link
185
+ * safety (`RewindFilesResult.skippedLinks`); omit or pass 0 when none were.
176
186
  */
177
- export function formatRewindResult(cp) {
178
- return `Rewound files to checkpoint ${cp.index}: "${cp.excerpt}".`;
187
+ export function formatRewindResult(cp, skippedLinks) {
188
+ const confirmation = `Rewound files to checkpoint ${cp.index}: "${cp.excerpt}".`;
189
+ // Type AND value are checked: the SDK field is optional, so `undefined` is
190
+ // the normal path, and testing `> 0` on a number also rejects NaN, which a
191
+ // `typeof` test alone would admit (`typeof NaN === "number"`).
192
+ //
193
+ // It does NOT reject every malformed number: `Infinity` and `0.5` pass this
194
+ // guard and would render as "Infinity files were" / "0.5 files were". The
195
+ // SDK returns an integer count, so those are UNREACHABLE rather than
196
+ // handled — stated plainly because a comment claiming a guarantee it does
197
+ // not deliver is the exact failure this function was changed to fix.
198
+ if (typeof skippedLinks === "number" && skippedLinks > 0) {
199
+ // Subject + verb agree with the count (R1.4): "1 file was", "2 files were".
200
+ const refusal = skippedLinks === 1 ? "1 file was" : `${skippedLinks} files were`;
201
+ return `${confirmation} ${refusal} left unchanged for link safety.`;
202
+ }
203
+ return confirmation;
179
204
  }
180
205
  /**
181
206
  * Build a `/rewind` usage/error message. Always names the `/rewind` command and,
@@ -216,7 +241,9 @@ async function emit(deps, text) {
216
241
  * nothing-to-rewind notice). `restore` fetches + rebuilds the list, validates
217
242
  * the requested index, and on a hit calls `query.rewindFiles` with the tracked
218
243
  * uuid inside a try/catch — a rejection (or a `canRewind: false` result) is
219
- * reported via {@link formatRewindError} and never claims success. An `invalid`
244
+ * reported via {@link formatRewindError} and never claims success. A success
245
+ * that refused files for link safety carries that count into the confirmation
246
+ * (R1.1), so a partial rewind is not reported as a whole one. An `invalid`
220
247
  * invocation, an out-of-range index, or a failed transcript read each emit an
221
248
  * error and leave files untouched (`rewindFiles` is never called); the
222
249
  * `invalid` path still reads the transcript so its usage message can name the
@@ -263,8 +290,12 @@ export async function handleRewindCommand(deps, invocation) {
263
290
  await emit(deps, formatRewindError(checkpoints.length, `There is no checkpoint ${invocation.index}.`));
264
291
  return;
265
292
  }
293
+ // Declared out here (same shape as `checkpoints` above) so the confirmation
294
+ // below can read `skippedLinks` off it; the assignment and every early
295
+ // `return` stay inside the try/catch exactly where they were.
296
+ let result;
266
297
  try {
267
- const result = await deps.query.rewindFiles(checkpoint.uuid);
298
+ result = await deps.query.rewindFiles(checkpoint.uuid);
268
299
  if (result.canRewind === false) {
269
300
  await emit(deps, formatRewindError(checkpoints.length, `Rewind failed: ${result.error ?? "the SDK could not rewind these files"}.`));
270
301
  return;
@@ -274,5 +305,5 @@ export async function handleRewindCommand(deps, invocation) {
274
305
  await emit(deps, formatRewindError(checkpoints.length, `Rewind failed: ${reasonText(error)}.`));
275
306
  return;
276
307
  }
277
- await emit(deps, formatRewindResult(checkpoint));
308
+ await emit(deps, formatRewindResult(checkpoint, result.skippedLinks));
278
309
  }
@@ -5,11 +5,15 @@
5
5
  * session's thinking intent with the legacy `MAX_THINKING_TOKENS` env
6
6
  * resolution into the SDK's `thinking` option.
7
7
  *
8
- * Kept self-contained (no import from the daily-churning `acp-agent.ts`,
9
- * mirroring the `ask-user-question-fallback.ts` precedent) so the logic is
10
- * unit-testable in isolation; `acp-agent.ts` wires it into
11
- * `buildConfigOptions`/`setSessionConfigOption`/query creation in sub-tasks
12
- * 1.2/1.3.
8
+ * Kept self-contained — no import from `acp-agent.ts`; what this module needs
9
+ * from the adapter (a logger, the raw env value) is injected instead, which
10
+ * also leaves the logic unit-testable in isolation. This mirrors the
11
+ * `rewind-command.ts` precedent, likewise isolated from the adapter so it can
12
+ * be unit tested without a live SDK session. The reason is merge cost:
13
+ * `acp-agent.ts` is a ~7,900-line file that changes upstream almost daily, so
14
+ * every coupling to it is paid again at each sync. `acp-agent.ts` wires these
15
+ * helpers into `buildConfigOptions`, `setSessionConfigOption` and query
16
+ * creation.
13
17
  */
14
18
  import type { SessionConfigOption } from "@agentclientprotocol/sdk";
15
19
  import type { ThinkingConfig } from "@anthropic-ai/claude-agent-sdk";
@@ -53,9 +57,9 @@ export declare function resolveThinkingSelection(value: unknown): boolean | null
53
57
  * it); `0` → disabled; a positive integer → a fixed token budget. Anything
54
58
  * else is ignored with a logged error, i.e. treated as unset.
55
59
  *
56
- * NOTE: duplicated from the unexported `resolveThinkingConfig` in
57
- * `acp-agent.ts` (behavior and log message identical); sub-task 1.3 makes
58
- * `acp-agent.ts` consume this export and removes the duplication.
60
+ * This is the single source of truth for that translation: `acp-agent.ts`
61
+ * carries no copy of it and reaches it only through `effectiveThinkingConfig`
62
+ * below.
59
63
  *
60
64
  * @param raw The raw `MAX_THINKING_TOKENS` value (pass
61
65
  * `process.env.MAX_THINKING_TOKENS`).
@@ -1 +1 @@
1
- {"version":3,"file":"thinking-option.d.ts","sourceRoot":"","sources":["../src/thinking-option.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,oDAAoD;AACpD,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,qDAAqD;AACrD,eAAO,MAAM,YAAY,QAAQ,CAAC;AAGlC;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAahF;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,cAAc,GACrB,cAAc,GAAG,SAAS,CAQ5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,GAAG,SAAS,EAC3B,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,cAAc,GACrB,cAAc,GAAG,SAAS,CAI5B"}
1
+ {"version":3,"file":"thinking-option.d.ts","sourceRoot":"","sources":["../src/thinking-option.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErE,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,oDAAoD;AACpD,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,qDAAqD;AACrD,eAAO,MAAM,YAAY,QAAQ,CAAC;AAGlC;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAahF;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAIvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,cAAc,GACrB,cAAc,GAAG,SAAS,CAQ5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,GAAG,SAAS,EAC3B,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,EAAE,cAAc,GACrB,cAAc,GAAG,SAAS,CAI5B"}
@@ -5,11 +5,15 @@
5
5
  * session's thinking intent with the legacy `MAX_THINKING_TOKENS` env
6
6
  * resolution into the SDK's `thinking` option.
7
7
  *
8
- * Kept self-contained (no import from the daily-churning `acp-agent.ts`,
9
- * mirroring the `ask-user-question-fallback.ts` precedent) so the logic is
10
- * unit-testable in isolation; `acp-agent.ts` wires it into
11
- * `buildConfigOptions`/`setSessionConfigOption`/query creation in sub-tasks
12
- * 1.2/1.3.
8
+ * Kept self-contained — no import from `acp-agent.ts`; what this module needs
9
+ * from the adapter (a logger, the raw env value) is injected instead, which
10
+ * also leaves the logic unit-testable in isolation. This mirrors the
11
+ * `rewind-command.ts` precedent, likewise isolated from the adapter so it can
12
+ * be unit tested without a live SDK session. The reason is merge cost:
13
+ * `acp-agent.ts` is a ~7,900-line file that changes upstream almost daily, so
14
+ * every coupling to it is paid again at each sync. `acp-agent.ts` wires these
15
+ * helpers into `buildConfigOptions`, `setSessionConfigOption` and query
16
+ * creation.
13
17
  */
14
18
  /** Stable id for the Thinking session config option. */
15
19
  export const THINKING_CONFIG_ID = "thinking";
@@ -62,9 +66,9 @@ export function resolveThinkingSelection(value) {
62
66
  * it); `0` → disabled; a positive integer → a fixed token budget. Anything
63
67
  * else is ignored with a logged error, i.e. treated as unset.
64
68
  *
65
- * NOTE: duplicated from the unexported `resolveThinkingConfig` in
66
- * `acp-agent.ts` (behavior and log message identical); sub-task 1.3 makes
67
- * `acp-agent.ts` consume this export and removes the duplication.
69
+ * This is the single source of truth for that translation: `acp-agent.ts`
70
+ * carries no copy of it and reaches it only through `effectiveThinkingConfig`
71
+ * below.
68
72
  *
69
73
  * @param raw The raw `MAX_THINKING_TOKENS` value (pass
70
74
  * `process.env.MAX_THINKING_TOKENS`).
package/dist/tools.d.ts CHANGED
@@ -3,7 +3,6 @@ import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
3
3
  import { TaskCreateInput, TaskCreateOutput, TaskUpdateInput } from "@anthropic-ai/claude-agent-sdk/sdk-tools.js";
4
4
  import { ToolResultBlockParam, WebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources";
5
5
  import { BetaBashCodeExecutionToolResultBlockParam, BetaCodeExecutionToolResultBlockParam, BetaRequestMCPToolResultBlockParam, BetaTextEditorCodeExecutionToolResultBlockParam, BetaToolResultBlockParam, BetaToolSearchToolResultBlockParam, BetaWebFetchToolResultBlockParam, BetaWebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources/beta.mjs";
6
- import { Logger } from "./acp-agent.js";
7
6
  interface ToolInfo {
8
7
  title: string;
9
8
  kind: ToolKind;
@@ -82,7 +81,7 @@ export declare function toolUpdateFromDiffToolResponse(toolResponse: unknown): {
82
81
  export declare const registerHookCallback: (toolUseID: string, { onPostToolUseHook, }: {
83
82
  onPostToolUseHook?: (toolUseID: string, toolInput: unknown, toolResponse: unknown) => Promise<void>;
84
83
  }) => void;
85
- export declare const createPostToolUseHook: (logger?: Logger, options?: {
84
+ export declare const createPostToolUseHook: (options?: {
86
85
  onEnterPlanMode?: () => Promise<void>;
87
86
  }) => HookCallback;
88
87
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACT,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAaL,eAAe,EACf,gBAAgB,EAChB,eAAe,EAKhB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAGL,oBAAoB,EAEpB,6BAA6B,EAE9B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAEL,yCAAyC,EAGzC,qCAAqC,EAGrC,kCAAkC,EAGlC,+CAA+C,EAI/C,wBAAwB,EACxB,kCAAkC,EAIlC,gCAAgC,EAEhC,iCAAiC,EAClC,MAAM,sCAAsC,CAAC;AA2B9C,UAAU,QAAQ;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/B,KAAK,CAAC,EAAE;QACN,aAAa,CAAC,EAAE;YACd,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC;QACF,eAAe,CAAC,EAAE;YAChB,WAAW,EAAE,MAAM,CAAC;YACpB,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;QACF,aAAa,CAAC,EAAE;YACd,WAAW,EAAE,MAAM,CAAC;YACpB,SAAS,EAAE,MAAM,CAAC;YAClB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;SACvB,CAAC;KACH,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAQpE;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,GAAG,EACZ,sBAAsB,GAAE,OAAe,EACvC,GAAG,CAAC,EAAE,MAAM,GACX,QAAQ,CAqWV;AAoFD,wBAAgB,wBAAwB,CACtC,UAAU,EACN,oBAAoB,GACpB,wBAAwB,GACxB,iCAAiC,GACjC,gCAAgC,GAChC,6BAA6B,GAC7B,qCAAqC,GACrC,yCAAyC,GACzC,+CAA+C,GAC/C,kCAAkC,GAClC,kCAAkC,EACtC,OAAO,EAAE,GAAG,GAAG,SAAS,EACxB,sBAAsB,GAAE,OAAe,EACvC,aAAa,CAAC,EAAE,OAAO,GACtB,UAAU,CAmUZ;AAiHD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;IAChD,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,eAAe,EAAE,CAAA;CAAE,GAAG,SAAS,GAAG,SAAS,EAAE,CAMxF;AAED;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AACF,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,SAAS,CAiCpF;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,eAAe,GAAG,SAAS,EAClC,MAAM,EAAE,gBAAgB,GAAG,SAAS,GACnC,IAAI,CASN;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAiB1F;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE,CAMpE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQnD;AAeD;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,YAAY,EAAE,OAAO,GAAG;IACrE,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAoCA;AAcD,eAAO,MAAM,oBAAoB,GAC/B,WAAW,MAAM,EACjB,wBAEG;IACD,iBAAiB,CAAC,EAAE,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,YAAY,EAAE,OAAO,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,SAKF,CAAC;AAGF,eAAO,MAAM,qBAAqB,GAC/B,UAAU;IAAE,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,KAAG,YAiBtD,CAAC;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GACxB,SAAS;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,KAAG,YAsBpE,CAAC"}
package/dist/tools.js CHANGED
@@ -362,56 +362,39 @@ function structuredResult(toolUseResult) {
362
362
  * matching rather than mangle the report.
363
363
  */
364
364
  function stripAgentTrailer(text) {
365
- // Matched by index rather than by tail-anchored regexes: an unanchored
366
- // `…\s*$` pattern retries from every start position, so it costs O(n²) on
367
- // text that repeats the trailer's opening token — and a subagent can echo
368
- // exactly such text verbatim into its report. The index form is linear and
369
- // keeps the same tail-anchored, independent semantics.
370
- let out = text;
371
- const beforeUsage = out.trimEnd();
372
- if (beforeUsage.endsWith("</usage>")) {
373
- // FIRST `<usage>`, not the last: the original pattern was unanchored, so
374
- // it matched from the leftmost opener and its lazy body then stretched to
375
- // the final `</usage>` to satisfy `$` — i.e. everything from the first
376
- // opener onward is part of the stripped trailer.
377
- const start = beforeUsage.indexOf("<usage>");
378
- if (start !== -1) {
379
- out = beforeUsage.slice(0, start);
380
- if (out.endsWith("\n"))
381
- out = out.slice(0, -1);
382
- }
365
+ return stripAgentIdLine(stripUsageBlock(text));
366
+ }
367
+ const USAGE_OPEN = "<usage>";
368
+ const USAGE_CLOSE = "</usage>";
369
+ /** Remove a trailing `<usage>…</usage>` block, plus trailing whitespace and
370
+ * one preceding newline. Matches from the *last* `<usage>` so a report that
371
+ * merely mentions the marker earlier isn't truncated at the mention. */
372
+ function stripUsageBlock(text) {
373
+ const body = text.trimEnd();
374
+ if (!body.endsWith(USAGE_CLOSE)) {
375
+ return text;
383
376
  }
384
- // `agentId: <id> (…)`. The original `\n?` was optional, so the trailer did
385
- // NOT have to start a line. Its match is nonetheless unique: `[^)]*` forbids
386
- // `)` in the body, so the closing paren can only be the final character and
387
- // the opener can only be the LAST `(` — which pins the whole match without
388
- // scanning from every position.
389
- const beforeAgentId = out.trimEnd();
390
- if (beforeAgentId.endsWith(")")) {
391
- const open = beforeAgentId.lastIndexOf("(");
392
- const body = open === -1 ? null : beforeAgentId.slice(open + 1, beforeAgentId.length - 1);
393
- // A `)` inside the body rules the trailer out (no earlier `(` can help:
394
- // that stray `)` would still fall inside its body).
395
- if (body !== null && !body.includes(")")) {
396
- const head = beforeAgentId.slice(0, open);
397
- if (head.endsWith(" ")) {
398
- const beforeSpace = head.slice(0, -1);
399
- const idStart = beforeSpace.lastIndexOf(" ") + 1;
400
- const id = beforeSpace.slice(idStart);
401
- if (AGENT_ID.test(id) && beforeSpace.slice(0, idStart).endsWith("agentId: ")) {
402
- let start = idStart - "agentId: ".length;
403
- if (start > 0 && beforeAgentId[start - 1] === "\n")
404
- start -= 1;
405
- out = beforeAgentId.slice(0, start);
406
- }
407
- }
408
- }
377
+ const open = body.lastIndexOf(USAGE_OPEN, body.length - USAGE_CLOSE.length - USAGE_OPEN.length);
378
+ if (open === -1) {
379
+ return text;
409
380
  }
410
- return out;
381
+ return body.slice(0, open > 0 && body[open - 1] === "\n" ? open - 1 : open);
382
+ }
383
+ /** The continuation line, anchored to a whole line so the regex has a single
384
+ * start position and no ambiguous repetition (`[\w-]+` can't consume the
385
+ * following space, `[^)]*` can't consume the closing paren) — it runs in
386
+ * linear time on any input. */
387
+ const AGENT_ID_LINE = /^agentId: [\w-]+ \([^)]*\)$/;
388
+ /** Remove a final `agentId: <id> (…)` line, plus trailing whitespace and the
389
+ * newline that preceded the line. */
390
+ function stripAgentIdLine(text) {
391
+ const body = text.trimEnd();
392
+ const lineStart = body.lastIndexOf("\n") + 1;
393
+ if (!AGENT_ID_LINE.test(body.slice(lineStart))) {
394
+ return text;
395
+ }
396
+ return body.slice(0, Math.max(lineStart - 1, 0));
411
397
  }
412
- /** The `<id>` of an `agentId:` trailer — tested only against an already
413
- * isolated, space-delimited token, never scanned across the whole report. */
414
- const AGENT_ID = /^[\w-]+$/;
415
398
  /** Apply {@link stripAgentTrailer} across a raw tool_result `content` (plain
416
399
  * string or block array), leaving non-text blocks untouched. */
417
400
  function stripAgentTrailerFromContent(content) {
@@ -518,7 +501,17 @@ export function toolUpdateFromToolResult(toolResult, toolUse, supportsTerminalOu
518
501
  }
519
502
  case "Bash": {
520
503
  const result = toolResult.content;
521
- const terminalId = "tool_use_id" in toolResult ? String(toolResult.tool_use_id) : "";
504
+ // The terminal was announced under the tool_use's own id (see
505
+ // `toolInfoFromToolUse`), so key the output/exit metas off that: it is the
506
+ // id the client actually created a terminal for. `toolResult.tool_use_id`
507
+ // is the same value whenever present — the caller looks the tool_use up by
508
+ // it — so preferring `toolUse.id` only adds a source for the case where the
509
+ // result block carries no id at all. Anything that isn't a non-empty
510
+ // string is no id at all: `""` matches no terminal, and stringifying a
511
+ // present-but-undefined field would invent the literal `"undefined"`.
512
+ const terminalIdOf = (id) => typeof id === "string" && id.length > 0 ? id : undefined;
513
+ const terminalId = terminalIdOf(toolUse?.id) ??
514
+ terminalIdOf("tool_use_id" in toolResult ? toolResult.tool_use_id : undefined);
522
515
  const isError = "is_error" in toolResult && toolResult.is_error;
523
516
  // Extract output and exit code from either format:
524
517
  // 1. The structured BashOutput (message-level tool_use_result): its
@@ -593,7 +586,14 @@ export function toolUpdateFromToolResult(toolResult, toolUse, supportsTerminalOu
593
586
  return toAcpContentUpdate(result, isError);
594
587
  }
595
588
  }
596
- if (supportsTerminalOutput) {
589
+ // Without a terminal id there is nothing the client can reconcile these
590
+ // metas against, and emitting them anyway strands the output: a client that
591
+ // buffers output/exit for terminals it has not been told about (Zed keeps
592
+ // them in `pending_terminal_output`/`pending_terminal_exit`, drained only on
593
+ // a matching create) would hold them forever behind an id that never
594
+ // arrives, showing an empty terminal. Fall through to the code-block
595
+ // rendering below instead.
596
+ if (supportsTerminalOutput && terminalId !== undefined) {
597
597
  return {
598
598
  content: [{ type: "terminal", terminalId }],
599
599
  _meta: {
@@ -944,7 +944,7 @@ export const registerHookCallback = (toolUseID, { onPostToolUseHook, }) => {
944
944
  };
945
945
  };
946
946
  /* A callback for Claude Code that is called when receiving a PostToolUse hook */
947
- export const createPostToolUseHook = (logger = console, options) => async (input, toolUseID) => {
947
+ export const createPostToolUseHook = (options) => async (input, toolUseID) => {
948
948
  if (input.hook_event_name === "PostToolUse") {
949
949
  // Handle EnterPlanMode tool - notify client of mode change after successful execution
950
950
  if (input.tool_name === "EnterPlanMode" && options?.onEnterPlanMode) {
@@ -954,12 +954,8 @@ export const createPostToolUseHook = (logger = console, options) => async (input
954
954
  const onPostToolUseHook = toolUseCallbacks[toolUseID]?.onPostToolUseHook;
955
955
  if (onPostToolUseHook) {
956
956
  await onPostToolUseHook(toolUseID, input.tool_input, input.tool_response);
957
- delete toolUseCallbacks[toolUseID]; // Cleanup after execution
958
- }
959
- else {
960
- logger.error(`No onPostToolUseHook found for tool use ID: ${toolUseID}`);
961
- delete toolUseCallbacks[toolUseID];
962
957
  }
958
+ delete toolUseCallbacks[toolUseID]; // Cleanup after execution
963
959
  }
964
960
  }
965
961
  return { continue: true };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.0",
6
+ "version": "0.5.1",
7
7
  "description": "ACP adapter for the Claude Agent SDK with VS Code-parity UX for Zed and other ACP clients — checkbox multi-select questions, refusal consent dialog, dynamic agent name. Fork of claude-agent-acp.",
8
8
  "main": "dist/lib.js",
9
9
  "types": "dist/lib.d.ts",
@@ -64,21 +64,28 @@
64
64
  "node": ">=24"
65
65
  },
66
66
  "dependencies": {
67
- "@agentclientprotocol/sdk": "1.2.1",
68
- "@anthropic-ai/claude-agent-sdk": "0.3.207",
67
+ "@agentclientprotocol/sdk": "1.3.0",
68
+ "@anthropic-ai/claude-agent-sdk": "0.3.220",
69
69
  "zod": "^3.25.0 || ^4.0.0"
70
70
  },
71
+ "overrides": {
72
+ "@hono/node-server": "~2.0.6",
73
+ "brace-expansion": "^5.0.9",
74
+ "fast-uri": "^3.1.5",
75
+ "hono": "^4.12.34",
76
+ "ip-address": "^10.4.0"
77
+ },
71
78
  "devDependencies": {
72
- "@anthropic-ai/sdk": "0.111.0",
79
+ "@anthropic-ai/sdk": "0.115.0",
73
80
  "@eslint/js": "10.0.1",
74
81
  "@tsconfig/node24": "24.0.4",
75
- "@types/node": "26.1.1",
76
- "@typescript-eslint/eslint-plugin": "8.63.0",
77
- "@typescript-eslint/parser": "8.63.0",
78
- "eslint": "10.7.0",
82
+ "@types/node": "26.1.2",
83
+ "@typescript-eslint/eslint-plugin": "8.65.0",
84
+ "@typescript-eslint/parser": "8.65.0",
85
+ "eslint": "10.8.0",
79
86
  "eslint-config-prettier": "10.1.8",
80
- "globals": "17.7.0",
81
- "prettier": "3.9.5",
87
+ "globals": "17.8.0",
88
+ "prettier": "3.9.6",
82
89
  "ts-node": "10.9.2",
83
90
  "typescript": "6.0.3",
84
91
  "vitest": "4.1.10"
@@ -1,78 +0,0 @@
1
- /**
2
- * Fallback for the built-in AskUserQuestion tool when the connected client does
3
- * NOT advertise `elicitation.form`. Upstream disables the tool for such clients;
4
- * this module lets the agent instead route each question through ACP's
5
- * `session/request_permission` dialog. The path is gated by the
6
- * `ACP_ASKUSERQUESTION_FALLBACK` env var so it can be turned off to restore
7
- * byte-for-byte upstream behavior.
8
- *
9
- * Alongside the env gate this module provides the pure mapping/orchestration
10
- * helpers (`questionPermissionOptions`, `handleAskUserQuestionViaPermission`);
11
- * the ACP client is injected by the adapter that wires them in.
12
- */
13
- import type { PermissionOption } from "@agentclientprotocol/sdk";
14
- import { type AskUserQuestion } from "./elicitation.js";
15
- /**
16
- * Whether the AskUserQuestion permission fallback is enabled for clients lacking
17
- * `elicitation.form`. Defaults ON: only an explicit `0` or `false` (trimmed,
18
- * case-insensitive) turns it off; any other value — unset, `1`, `true`, … —
19
- * leaves it on.
20
- *
21
- * @param env Environment map to read `ACP_ASKUSERQUESTION_FALLBACK` from (pass
22
- * `process.env`).
23
- */
24
- export declare function askUserQuestionFallbackEnabled(env: Record<string, string | undefined>): boolean;
25
- /**
26
- * Map one AskUserQuestion question to the options shown in ACP's
27
- * `session/request_permission` dialog: one `allow_once` option per choice (in
28
- * order) followed by a trailing `reject_once` "Skip this question" option.
29
- *
30
- * Each choice's `optionId` is its bare `label` — the value the tool records as
31
- * the answer — so the description is never folded into the id; a non-empty
32
- * `description` is instead appended to the human-readable `name` as
33
- * `"<label> — <description>"`. The skip option is given a random `optionId`
34
- * that cannot collide with any label, so the orchestrator can recognize a skip
35
- * by id alone.
36
- *
37
- * @param question The single question whose choices become permission options.
38
- */
39
- export declare function questionPermissionOptions(question: AskUserQuestion): PermissionOption[];
40
- /**
41
- * Route an AskUserQuestion tool call through ACP permission requests rather than
42
- * a form elicitation, for clients that lack `elicitation.form`. Each question is
43
- * asked sequentially via the injected `requestPermission`; the selected label is
44
- * accumulated into `answers` (keyed by the question text), a skipped question is
45
- * omitted, and a multi-select question naturally degrades to a single label
46
- * since each option is single-select here.
47
- *
48
- * Mirrors the upstream form handler's contract: input with no parseable
49
- * questions denies with the identical message, and an aborted signal or a
50
- * cancelled request throws `"Tool use aborted"`, discarding every accumulated
51
- * answer. On success it returns the original `toolInput` augmented with
52
- * `answers` as `updatedInput`.
53
- *
54
- * Pure and injectable — the real ACP client is wired in by the adapter — so the
55
- * orchestration can be exercised without a transport.
56
- *
57
- * @param toolInput Raw AskUserQuestion tool input (expected to carry `questions`).
58
- * @param requestPermission Injected permission-request function, invoked once per
59
- * question, resolving to the user's selection or a cancellation.
60
- * @param signal Abort signal for the tool call; checked on entry and around each
61
- * request.
62
- */
63
- export declare function handleAskUserQuestionViaPermission(toolInput: Record<string, unknown>, requestPermission: (req: {
64
- question: AskUserQuestion;
65
- options: PermissionOption[];
66
- }) => Promise<{
67
- outcome: "selected";
68
- optionId: string;
69
- } | {
70
- outcome: "cancelled";
71
- }>, signal: AbortSignal): Promise<{
72
- behavior: "allow";
73
- updatedInput: Record<string, unknown>;
74
- } | {
75
- behavior: "deny";
76
- message: string;
77
- }>;
78
- //# sourceMappingURL=ask-user-question-fallback.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ask-user-question-fallback.d.ts","sourceRoot":"","sources":["../src/ask-user-question-fallback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEjF;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAO/F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,eAAe,GAAG,gBAAgB,EAAE,CAYvF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,kCAAkC,CACtD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,iBAAiB,EAAE,CAAC,GAAG,EAAE;IACvB,QAAQ,EAAE,eAAe,CAAC;IAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B,KAAK,OAAO,CAAC;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,CAAC,EACnF,MAAM,EAAE,WAAW,GAClB,OAAO,CACN;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC5D;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CACxC,CAyBA"}