@agentclientprotocol/claude-agent-acp 0.76.1-preview.8 → 0.77.1-preview.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.
@@ -57,8 +57,9 @@ export declare function extractAskUserQuestions(input: Record<string, unknown>):
57
57
  *
58
58
  * Each question is followed by its own optional free-text "custom answer" field
59
59
  * (`question_<n>_custom`), mirroring the CLI's per-question "Other" box: the
60
- * user can type their own answer instead of (or, for a multi-select question,
61
- * as well as) picking an option, scoped to that specific question. Nothing is
60
+ * user can type their own answer instead of picking an option, add it to a
61
+ * multi-select's picks, or attach it as a note to a single-select's pick (see
62
+ * `applyAskElicitationResponse`), scoped to that specific question. Nothing is
62
63
  * marked required, so the user can also just skip — matching the built-in tool,
63
64
  * which always offers Skip + a free-text box.
64
65
  */
@@ -77,11 +78,15 @@ export type AskUserQuestionOutcome = {
77
78
  * `answers` as a `{ [questionText]: label }` map — the key shape the tool's own
78
79
  * `call()` reads — with multi-selects comma-joined in the CLI's own quoted form
79
80
  * (see `joinMultiSelectAnswer`). A non-empty per-question custom-answer field
80
- * (`question_<n>_custom`) replaces the selection of a single-select question,
81
- * since the user typed their own answer instead of picking one, and joins the
82
- * selection of a multi-select question, where the two fields are independent
83
- * and filling both means both. Decline yields empty answers (the model is told
84
- * the user skipped rather than the turn aborting); cancel — and any
81
+ * (`question_<n>_custom`) joins the selection of a multi-select question, where
82
+ * the two fields are independent and filling both means both. For a
83
+ * single-select question it is the answer when nothing was picked (the user
84
+ * typed their own instead), and otherwise travels beside the pick as the
85
+ * tool's own per-question `annotations[question].notes` — the slot the CLI
86
+ * uses for free text attached to a selection and renders to the model as
87
+ * `"Q"="A" notes: ...` — so a client that presents the box as a notes field
88
+ * cannot make the selection disappear. Decline yields empty answers (the model
89
+ * is told the user skipped rather than the turn aborting); cancel — and any
85
90
  * custom/future action we don't understand — aborts the tool call.
86
91
  */
87
92
  export declare function applyAskElicitationResponse(response: CreateElicitationResponse, toolInput: Record<string, unknown>, questions: AskUserQuestion[]): AskUserQuestionOutcome;
@@ -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;AAgCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,eAAe,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CA6D1B;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;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,yBAAyB,EACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,SAAS,EAAE,eAAe,EAAE,GAC3B,sBAAsB,CA2CxB;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;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,+BAA+B,CAC7C,SAAS,EAAE,eAAe,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CA6D1B;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;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,yBAAyB,EACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,SAAS,EAAE,eAAe,EAAE,GAC3B,sBAAsB,CAgExB;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"}
@@ -108,8 +108,9 @@ const CUSTOM_ANSWER_META_KEY = "_askUserQuestionCustomAnswer";
108
108
  *
109
109
  * Each question is followed by its own optional free-text "custom answer" field
110
110
  * (`question_<n>_custom`), mirroring the CLI's per-question "Other" box: the
111
- * user can type their own answer instead of (or, for a multi-select question,
112
- * as well as) picking an option, scoped to that specific question. Nothing is
111
+ * user can type their own answer instead of picking an option, add it to a
112
+ * multi-select's picks, or attach it as a note to a single-select's pick (see
113
+ * `applyAskElicitationResponse`), scoped to that specific question. Nothing is
113
114
  * marked required, so the user can also just skip — matching the built-in tool,
114
115
  * which always offers Skip + a free-text box.
115
116
  */
@@ -146,7 +147,7 @@ export function askUserQuestionsToCreateRequest(questions, sessionId, toolCallId
146
147
  title: "Other",
147
148
  description: question.multiSelect
148
149
  ? "Type your own answer to add to your selection above (optional)."
149
- : "Type your own answer instead of choosing an option above (optional).",
150
+ : "Type your own answer, or add a note to the option you chose above (optional).",
150
151
  _meta: {
151
152
  [CUSTOM_ANSWER_META_KEY]: {
152
153
  questionId: questionFieldKey(index),
@@ -187,11 +188,15 @@ function joinMultiSelectAnswer(items) {
187
188
  * `answers` as a `{ [questionText]: label }` map — the key shape the tool's own
188
189
  * `call()` reads — with multi-selects comma-joined in the CLI's own quoted form
189
190
  * (see `joinMultiSelectAnswer`). A non-empty per-question custom-answer field
190
- * (`question_<n>_custom`) replaces the selection of a single-select question,
191
- * since the user typed their own answer instead of picking one, and joins the
192
- * selection of a multi-select question, where the two fields are independent
193
- * and filling both means both. Decline yields empty answers (the model is told
194
- * the user skipped rather than the turn aborting); cancel — and any
191
+ * (`question_<n>_custom`) joins the selection of a multi-select question, where
192
+ * the two fields are independent and filling both means both. For a
193
+ * single-select question it is the answer when nothing was picked (the user
194
+ * typed their own instead), and otherwise travels beside the pick as the
195
+ * tool's own per-question `annotations[question].notes` — the slot the CLI
196
+ * uses for free text attached to a selection and renders to the model as
197
+ * `"Q"="A" notes: ...` — so a client that presents the box as a notes field
198
+ * cannot make the selection disappear. Decline yields empty answers (the model
199
+ * is told the user skipped rather than the turn aborting); cancel — and any
195
200
  * custom/future action we don't understand — aborts the tool call.
196
201
  */
197
202
  export function applyAskElicitationResponse(response, toolInput, questions) {
@@ -205,6 +210,7 @@ export function applyAskElicitationResponse(response, toolInput, questions) {
205
210
  // Typed against the tool's own output schema so the answer/response shapes
206
211
  // stay in sync with what the built-in tool's call() expects to read back.
207
212
  const answers = {};
213
+ const annotations = {};
208
214
  questions.forEach((question, index) => {
209
215
  const custom = content[questionCustomFieldKey(index)];
210
216
  const customText = typeof custom === "string" ? custom.trim() : "";
@@ -214,21 +220,42 @@ export function applyAskElicitationResponse(response, toolInput, questions) {
214
220
  : Array.isArray(value)
215
221
  ? value.filter((item) => item !== undefined && item !== null && item !== "").map(String)
216
222
  : [String(value)];
217
- // A single-select question is answered by exactly one thing, so a typed
218
- // custom answer replaces the pick: the user wrote their own answer instead
219
- // of choosing an option. A multi-select is additive, and the form offers
220
- // the selection and the custom box as independent fields — a user who
221
- // fills both means both, so the typed answer joins the checked options.
222
- const items = customText === "" ? picks : question.multiSelect ? [...picks, customText] : [customText];
223
- // A single-select normally holds one item; the plain join only matters if a
224
- // client hands back an array for it, and then mirrors the old behavior.
225
- const text = question.multiSelect ? joinMultiSelectAnswer(items) : items.join(", ");
226
- if (text === "") {
223
+ // A multi-select is additive, and the form offers the selection and the
224
+ // custom box as independent fields — a user who fills both means both, so
225
+ // the typed answer joins the checked options.
226
+ if (question.multiSelect) {
227
+ const text = joinMultiSelectAnswer(customText === "" ? picks : [...picks, customText]);
228
+ if (text !== "") {
229
+ answers[question.question] = text;
230
+ }
231
+ return;
232
+ }
233
+ // A single-select question is answered by exactly one thing. With no option
234
+ // picked, the typed text is that answer (the CLI's "Other"). With an option
235
+ // picked as well, the pick stays the answer and the text rides along as the
236
+ // tool's per-question `notes` annotation, so neither is lost. A single-select
237
+ // normally holds one item; the plain join only matters if a client hands
238
+ // back an array for it, and then mirrors the old behavior.
239
+ const picked = picks.join(", ");
240
+ if (picked === "") {
241
+ if (customText !== "") {
242
+ answers[question.question] = customText;
243
+ }
227
244
  return;
228
245
  }
229
- answers[question.question] = text;
246
+ answers[question.question] = picked;
247
+ if (customText !== "") {
248
+ annotations[question.question] = { notes: customText };
249
+ }
230
250
  });
231
- return { action: "answered", updatedInput: { ...toolInput, answers } };
251
+ return {
252
+ action: "answered",
253
+ updatedInput: {
254
+ ...toolInput,
255
+ answers,
256
+ ...(Object.keys(annotations).length > 0 ? { annotations } : {}),
257
+ },
258
+ };
232
259
  }
233
260
  /**
234
261
  * Coerce an arbitrary MCP `requestedSchema` into an ACP `ElicitationSchema`.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.76.1-preview.8",
6
+ "version": "0.77.1-preview.1",
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",