@cargo-ai/cli 1.0.95 → 1.0.97

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":"approvals.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/approvals.ts"],"names":[],"mappings":"AA2CA,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CA2BT;AAqBD,eAAO,MAAM,kBAAkB,2HAC2F,CAAC;AAE3H,0BAA0B;AAC1B,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD"}
1
+ {"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/approvals.ts"],"names":[],"mappings":"AA0CA,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACjC,OAAO,CA2BT;AAqBD,eAAO,MAAM,kBAAkB,2HAC2F,CAAC;AAE3H,0BAA0B;AAC1B,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAExD"}
@@ -13,7 +13,6 @@ const AUTO_APPROVED_TOOL_NAMES = new Set([
13
13
  "TaskCreate",
14
14
  "TaskUpdate",
15
15
  "TaskStop",
16
- "AskUserQuestion",
17
16
  ]);
18
17
  /**
19
18
  * Cargo's MCP tools run unattended in an interactive session, `execute_action`
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Claude Code's `AskUserQuestion` — a structured multiple-choice prompt.
3
+ *
4
+ * The native picker cannot run here: `allow-reads` classifies it as readonly,
5
+ * so Claude Code starts it without asking us, then waits on a UI this session
6
+ * does not have. The native tool is withheld for that reason, via session
7
+ * `disallowedTools` — not a settings.json deny of the same name, which would
8
+ * also disable this host replacement. This session implements the tool as a
9
+ * host tool: the questions are shown in Ask's own picker, and the answers are
10
+ * the tool result.
11
+ *
12
+ * The input schema has to name `questions`. The Claude Code bridge turns host
13
+ * tool schemas into a Zod object shape; an empty shape strips every argument,
14
+ * and the picker sees `{}`.
15
+ */
16
+ import { jsonSchema } from "ai";
17
+ export declare const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
18
+ export declare const ASK_USER_QUESTION_DENIED_REASON = "Nobody is at the terminal to answer. Ask in your reply text instead.";
19
+ export declare const ASK_USER_QUESTION_MISSING_INPUT_REASON = "AskUserQuestion requires `questions`: an array of 1\u20134 items, each with `question`, `header`, `options` (each `{ label, description }`), and `multiSelect`. Call it again with those fields filled in.";
20
+ type AskUserQuestionHostTool = {
21
+ description: string;
22
+ inputSchema: ReturnType<typeof jsonSchema>;
23
+ execute: (data: unknown) => Promise<unknown>;
24
+ };
25
+ export type AskUserQuestionAsk = (input: unknown) => Promise<Record<string, string> | undefined>;
26
+ type BuiltinToolFiltering = {
27
+ mode: "allow" | "deny";
28
+ toolNames: string[];
29
+ };
30
+ /**
31
+ * Withhold Claude Code's native `AskUserQuestion` without dropping a host tool
32
+ * of the same name.
33
+ *
34
+ * HarnessAgent `inactiveTools` cannot do this: it also filters the user tool
35
+ * out of `toolSpecs`, so the replacement is never registered. A settings.json
36
+ * deny of `AskUserQuestion` matches the host tool too. Merging a deny into the
37
+ * session's builtin filtering becomes `disallowedTools` for the native tool
38
+ * only; host `execute` still runs.
39
+ */
40
+ export declare function denyNativeAskUserQuestion(existing: BuiltinToolFiltering | undefined): BuiltinToolFiltering;
41
+ /**
42
+ * Host-side `AskUserQuestion`. Same name as the builtin so the model keeps
43
+ * calling it; HarnessAgent runs `execute` on the host instead of Claude Code.
44
+ */
45
+ export declare function buildAskUserQuestionHostTool(getAsk: () => AskUserQuestionAsk | undefined): Record<string, AskUserQuestionHostTool>;
46
+ export type AskUserQuestionOption = {
47
+ label: string;
48
+ description: string | undefined;
49
+ };
50
+ export type AskUserQuestionItem = {
51
+ question: string;
52
+ header: string | undefined;
53
+ options: AskUserQuestionOption[];
54
+ multiSelect: boolean;
55
+ };
56
+ /**
57
+ * Pull the questions out of a tool input, whether the bridge sent a JSON
58
+ * string or the parsed object.
59
+ */
60
+ export declare function readAskUserQuestions(toolInput: unknown): AskUserQuestionItem[] | undefined;
61
+ /** First question, for the spinner — not the whole JSON payload. */
62
+ export declare function summarizeAskUserQuestion(toolInput: unknown): string | undefined;
63
+ /**
64
+ * Told to the model as the denial reason so it can continue with the answers
65
+ * rather than asking again.
66
+ */
67
+ export declare function formatAskUserQuestionAnswers(answers: Record<string, string>): string;
68
+ export {};
69
+ //# sourceMappingURL=askUserQuestion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askUserQuestion.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/askUserQuestion.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAoB,MAAM,IAAI,CAAC;AAElD,eAAO,MAAM,2BAA2B,oBAAoB,CAAC;AAE7D,eAAO,MAAM,+BAA+B,yEAC4B,CAAC;AAEzE,eAAO,MAAM,sCAAsC,+MACsJ,CAAC;AA4C1M,KAAK,uBAAuB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;IAC3C,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,CAC/B,KAAK,EAAE,OAAO,KACX,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;AAEjD,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,GACzC,oBAAoB,CAsBtB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,kBAAkB,GAAG,SAAS,GAC3C,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CA2BzC;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,OAAO,GACjB,mBAAmB,EAAE,GAAG,SAAS,CA0BnC;AAED,oEAAoE;AACpE,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,OAAO,GACjB,MAAM,GAAG,SAAS,CAkBpB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAER"}
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Claude Code's `AskUserQuestion` — a structured multiple-choice prompt.
3
+ *
4
+ * The native picker cannot run here: `allow-reads` classifies it as readonly,
5
+ * so Claude Code starts it without asking us, then waits on a UI this session
6
+ * does not have. The native tool is withheld for that reason, via session
7
+ * `disallowedTools` — not a settings.json deny of the same name, which would
8
+ * also disable this host replacement. This session implements the tool as a
9
+ * host tool: the questions are shown in Ask's own picker, and the answers are
10
+ * the tool result.
11
+ *
12
+ * The input schema has to name `questions`. The Claude Code bridge turns host
13
+ * tool schemas into a Zod object shape; an empty shape strips every argument,
14
+ * and the picker sees `{}`.
15
+ */
16
+ import { jsonSchema } from "ai";
17
+ export const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
18
+ export const ASK_USER_QUESTION_DENIED_REASON = "Nobody is at the terminal to answer. Ask in your reply text instead.";
19
+ export const ASK_USER_QUESTION_MISSING_INPUT_REASON = "AskUserQuestion requires `questions`: an array of 1–4 items, each with `question`, `header`, `options` (each `{ label, description }`), and `multiSelect`. Call it again with those fields filled in.";
20
+ const ASK_USER_QUESTION_JSON_SCHEMA = {
21
+ type: "object",
22
+ properties: {
23
+ questions: {
24
+ type: "array",
25
+ description: "1–4 multiple-choice questions to present to the user in this terminal.",
26
+ items: {
27
+ type: "object",
28
+ properties: {
29
+ question: {
30
+ type: "string",
31
+ description: "The question to ask.",
32
+ },
33
+ header: {
34
+ type: "string",
35
+ description: "Short label shown above the question.",
36
+ },
37
+ options: {
38
+ type: "array",
39
+ items: {
40
+ type: "object",
41
+ properties: {
42
+ label: { type: "string" },
43
+ description: { type: "string" },
44
+ preview: { type: "string" },
45
+ },
46
+ required: ["label", "description"],
47
+ },
48
+ },
49
+ multiSelect: {
50
+ type: "boolean",
51
+ description: "Whether the user may pick more than one option.",
52
+ },
53
+ },
54
+ required: ["question", "header", "options", "multiSelect"],
55
+ },
56
+ },
57
+ },
58
+ required: ["questions"],
59
+ };
60
+ /**
61
+ * Withhold Claude Code's native `AskUserQuestion` without dropping a host tool
62
+ * of the same name.
63
+ *
64
+ * HarnessAgent `inactiveTools` cannot do this: it also filters the user tool
65
+ * out of `toolSpecs`, so the replacement is never registered. A settings.json
66
+ * deny of `AskUserQuestion` matches the host tool too. Merging a deny into the
67
+ * session's builtin filtering becomes `disallowedTools` for the native tool
68
+ * only; host `execute` still runs.
69
+ */
70
+ export function denyNativeAskUserQuestion(existing) {
71
+ if (existing === undefined) {
72
+ return { mode: "deny", toolNames: [ASK_USER_QUESTION_TOOL_NAME] };
73
+ }
74
+ if (existing.mode === "deny") {
75
+ if (existing.toolNames.includes(ASK_USER_QUESTION_TOOL_NAME) === true) {
76
+ return existing;
77
+ }
78
+ return {
79
+ mode: "deny",
80
+ toolNames: [...existing.toolNames, ASK_USER_QUESTION_TOOL_NAME],
81
+ };
82
+ }
83
+ return {
84
+ mode: "allow",
85
+ toolNames: existing.toolNames.filter((name) => name !== ASK_USER_QUESTION_TOOL_NAME),
86
+ };
87
+ }
88
+ /**
89
+ * Host-side `AskUserQuestion`. Same name as the builtin so the model keeps
90
+ * calling it; HarnessAgent runs `execute` on the host instead of Claude Code.
91
+ */
92
+ export function buildAskUserQuestionHostTool(getAsk) {
93
+ return {
94
+ [ASK_USER_QUESTION_TOOL_NAME]: {
95
+ description: "Ask the user multiple-choice questions via a structured UI. This session presents the options in the terminal and returns their answers.",
96
+ inputSchema: jsonSchema(ASK_USER_QUESTION_JSON_SCHEMA),
97
+ execute: async (data) => {
98
+ const ask = getAsk();
99
+ if (ask === undefined) {
100
+ return { error: ASK_USER_QUESTION_DENIED_REASON };
101
+ }
102
+ if (readAskUserQuestions(data) === undefined) {
103
+ return { error: ASK_USER_QUESTION_MISSING_INPUT_REASON };
104
+ }
105
+ const answers = await ask(data);
106
+ if (answers === undefined || Object.keys(answers).length === 0) {
107
+ return { error: ASK_USER_QUESTION_DENIED_REASON };
108
+ }
109
+ return { answers };
110
+ },
111
+ },
112
+ };
113
+ }
114
+ /**
115
+ * Pull the questions out of a tool input, whether the bridge sent a JSON
116
+ * string or the parsed object.
117
+ */
118
+ export function readAskUserQuestions(toolInput) {
119
+ const record = asRecord(toolInput);
120
+ if (record === undefined) {
121
+ return undefined;
122
+ }
123
+ const rawQuestions = record["questions"];
124
+ if (Array.isArray(rawQuestions) === false || rawQuestions.length === 0) {
125
+ return undefined;
126
+ }
127
+ const questions = [];
128
+ for (const rawQuestion of rawQuestions) {
129
+ const parsed = readQuestion(rawQuestion);
130
+ if (parsed === undefined) {
131
+ return undefined;
132
+ }
133
+ questions.push(parsed);
134
+ }
135
+ return questions;
136
+ }
137
+ /** First question, for the spinner — not the whole JSON payload. */
138
+ export function summarizeAskUserQuestion(toolInput) {
139
+ const questions = readAskUserQuestions(toolInput);
140
+ if (questions === undefined) {
141
+ return undefined;
142
+ }
143
+ const first = questions[0];
144
+ if (first === undefined) {
145
+ return undefined;
146
+ }
147
+ if (questions.length === 1) {
148
+ return first.question;
149
+ }
150
+ return `${first.question} (+${String(questions.length - 1)} more)`;
151
+ }
152
+ /**
153
+ * Told to the model as the denial reason so it can continue with the answers
154
+ * rather than asking again.
155
+ */
156
+ export function formatAskUserQuestionAnswers(answers) {
157
+ return `The user answered: ${JSON.stringify(answers)}. Continue with these answers. Do not call AskUserQuestion again for these questions.`;
158
+ }
159
+ function readQuestion(raw) {
160
+ const record = asRecord(raw);
161
+ if (record === undefined) {
162
+ return undefined;
163
+ }
164
+ const question = record["question"];
165
+ const rawOptions = record["options"];
166
+ if (typeof question !== "string" || question.length === 0) {
167
+ return undefined;
168
+ }
169
+ if (Array.isArray(rawOptions) === false || rawOptions.length === 0) {
170
+ return undefined;
171
+ }
172
+ const options = [];
173
+ for (const rawOption of rawOptions) {
174
+ const parsed = readOption(rawOption);
175
+ if (parsed === undefined) {
176
+ return undefined;
177
+ }
178
+ options.push(parsed);
179
+ }
180
+ const header = record["header"];
181
+ return {
182
+ question,
183
+ header: typeof header === "string" ? header : undefined,
184
+ options,
185
+ multiSelect: record["multiSelect"] === true,
186
+ };
187
+ }
188
+ function readOption(raw) {
189
+ const record = asRecord(raw);
190
+ if (record === undefined) {
191
+ return undefined;
192
+ }
193
+ const label = record["label"];
194
+ const description = record["description"];
195
+ if (typeof label !== "string" || label.length === 0) {
196
+ return undefined;
197
+ }
198
+ return {
199
+ label,
200
+ description: typeof description === "string" ? description : undefined,
201
+ };
202
+ }
203
+ function asRecord(value) {
204
+ if (typeof value === "string") {
205
+ try {
206
+ const parsed = JSON.parse(value);
207
+ return typeof parsed === "object" && parsed !== null
208
+ ? parsed
209
+ : undefined;
210
+ }
211
+ catch {
212
+ return undefined;
213
+ }
214
+ }
215
+ return typeof value === "object" && value !== null
216
+ ? value
217
+ : undefined;
218
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"claudeConfig.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/claudeConfig.ts"],"names":[],"mappings":"AA2BA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C"}
1
+ {"version":3,"file":"claudeConfig.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/claudeConfig.ts"],"names":[],"mappings":"AAyCA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C"}
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { getConfigDir } from "@cargo-ai/cdk/cli";
4
+ import { ASK_USER_QUESTION_TOOL_NAME } from "./askUserQuestion.js";
4
5
  /**
5
6
  * Tools that spawn a subagent, refused at source.
6
7
  *
@@ -22,6 +23,16 @@ import { getConfigDir } from "@cargo-ai/cdk/cli";
22
23
  * Code versions and a rule for a tool that does not exist costs nothing.
23
24
  */
24
25
  const SUBAGENT_TOOL_NAMES = ["Agent", "Task"];
26
+ /**
27
+ * Native `AskUserQuestion` used to live on this list. A deny-by-name rule
28
+ * matches the host replacement of the same name, so model calls came back
29
+ * disabled instead of reaching host `execute`. Native withholding is now
30
+ * session `disallowedTools` — see `denyNativeAskUserQuestion`. Names here
31
+ * are stripped from an existing deny array so a previous Ask run cannot
32
+ * keep the collision around.
33
+ */
34
+ const SETTINGS_DENIED_TOOL_NAMES = SUBAGENT_TOOL_NAMES;
35
+ const SETTINGS_UNDENIED_TOOL_NAMES = [ASK_USER_QUESTION_TOOL_NAME];
25
36
  /**
26
37
  * The Claude Agent SDK loads every filesystem settings source it can find when
27
38
  * `settingSources` is omitted, and the harness bridge omits it. A local turn
@@ -52,24 +63,23 @@ export function askClaudeConfigDir() {
52
63
  }
53
64
  const dir = join(getConfigDir(), "ask-claude-config");
54
65
  mkdirSync(dir, { recursive: true });
55
- denySubagentTools(dir);
66
+ denySettingsTools(dir);
56
67
  return dir;
57
68
  }
58
69
  /**
59
- * Add the subagent deny rules to the settings file in Ask's own config
60
- * directory, keeping whatever else is in there.
70
+ * Add the deny rules to the settings file in Ask's own config directory,
71
+ * keeping whatever else is in there.
61
72
  *
62
73
  * Merged rather than written whole, because Claude Code keeps its own state
63
74
  * under this directory.
64
75
  *
65
76
  * A failure here is reported rather than swallowed. There is no second line of
66
- * defence to fall back on: the approval-time refusal in `isAlwaysDenied` is
67
- * never reached, which is the whole reason this exists — so a settings file
68
- * that could not be written means subagents run unattended, and the user is
69
- * the only one who can tell whether that matters. Reported, not thrown: it
70
- * must not fail the question they just asked.
77
+ * defence to fall back on for tools `allow-reads` never asks about — so a
78
+ * settings file that could not be written means those tools run unattended,
79
+ * and the user is the only one who can tell whether that matters. Reported,
80
+ * not thrown: it must not fail the question they just asked.
71
81
  */
72
- function denySubagentTools(dir) {
82
+ function denySettingsTools(dir) {
73
83
  const path = join(dir, "settings.json");
74
84
  try {
75
85
  const existing = existsSync(path)
@@ -84,16 +94,21 @@ function denySubagentTools(dir) {
84
94
  : {};
85
95
  const denyValue = permissions["deny"];
86
96
  const deny = Array.isArray(denyValue) ? denyValue : [];
87
- const missing = SUBAGENT_TOOL_NAMES.filter((name) => deny.includes(name) === false);
88
- if (missing.length === 0) {
97
+ const kept = deny.filter((name) => {
98
+ return typeof name === "string"
99
+ ? SETTINGS_UNDENIED_TOOL_NAMES.includes(name) === false
100
+ : true;
101
+ });
102
+ const missing = SETTINGS_DENIED_TOOL_NAMES.filter((name) => kept.includes(name) === false);
103
+ if (missing.length === 0 && kept.length === deny.length) {
89
104
  return;
90
105
  }
91
106
  writeFileSync(path, `${JSON.stringify({
92
107
  ...settings,
93
- permissions: { ...permissions, deny: [...deny, ...missing] },
108
+ permissions: { ...permissions, deny: [...kept, ...missing] },
94
109
  }, null, 2)}\n`);
95
110
  }
96
111
  catch (error) {
97
- process.stderr.write(`Could not write ${path}, so subagent tools are not withheld from this session: ${error instanceof Error ? error.message : "unknown error"}\n`);
112
+ process.stderr.write(`Could not write ${path}, so Agent and Task are not withheld from this session: ${error instanceof Error ? error.message : "unknown error"}\n`);
98
113
  }
99
114
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/create.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAmB3C,OAAO,EAGL,KAAK,kBAAkB,EAKxB,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,YAAY,GAAG;IACzB,YAAY,EAAE,CACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,KACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC5C,sEAAsE;IACtE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAcF,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,GAAG,EAAE,GAAG,CAAC;IACT;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;CAClC,GAAG,OAAO,CAAC,YAAY,CAAC,CA4SxB"}
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/create.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAuB3C,OAAO,EAGL,KAAK,kBAAkB,EAKxB,MAAM,WAAW,CAAC;AAGnB,MAAM,MAAM,YAAY,GAAG;IACzB,YAAY,EAAE,CACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,KACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC5C,sEAAsE;IACtE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAcF,wBAAsB,kBAAkB,CAAC,KAAK,EAAE;IAC9C,GAAG,EAAE,GAAG,CAAC;IACT;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;CAClC,GAAG,OAAO,CAAC,YAAY,CAAC,CAsUxB"}
@@ -2,6 +2,7 @@ import { HarnessAgent } from "@ai-sdk/harness/agent";
2
2
  import { createClaudeCode } from "@ai-sdk/harness-claude-code";
3
3
  import { createLocalSandbox } from "ai-sdk-sandbox-local";
4
4
  import { pruneHarnessArtifacts } from "./artifacts.js";
5
+ import { buildAskUserQuestionHostTool, denyNativeAskUserQuestion, } from "./askUserQuestion.js";
5
6
  import { askClaudeConfigDir } from "./claudeConfig.js";
6
7
  import { ingestTurn } from "./ingest.js";
7
8
  import { localHarnessInstructions, localHarnessSkills, } from "./instructions.js";
@@ -39,9 +40,15 @@ export async function createLocalHarness(input) {
39
40
  setupPhase("stale harness artifacts pruned");
40
41
  const anthropicBaseUrl = `${input.apiBaseUrl.replace(/\/$/, "")}/sandbox/anthropic`;
41
42
  const release = input.release;
42
- const hostTools = input.allowWorkspaceMutations === false || release === undefined
43
+ const releaseTools = input.allowWorkspaceMutations === false || release === undefined
43
44
  ? {}
44
45
  : buildReleaseHostTools(input.api, release.actions);
46
+ // Bound per prompt: the picker lives on the turn, the tool on the session.
47
+ const askUserQuestionAsk = { current: undefined };
48
+ const hostTools = {
49
+ ...releaseTools,
50
+ ...buildAskUserQuestionHostTool(() => askUserQuestionAsk.current),
51
+ };
45
52
  const mcpServers = {
46
53
  [CARGO_MCP_SERVER_NAME]: cargoMcpServer(input.apiBaseUrl),
47
54
  };
@@ -51,19 +58,29 @@ export async function createLocalHarness(input) {
51
58
  mcpServers[name] = server;
52
59
  }
53
60
  }
61
+ // Pin auth the same way the backend harness stream does. Credentials go
62
+ // through `auth`, not `env`: passing them via `env` alone lets
63
+ // createClaudeCode auto-detect AI Gateway from the host process and ignore
64
+ // the Cargo proxy.
65
+ const claudeCode = createClaudeCode({
66
+ auth: {
67
+ ANTHROPIC_BASE_URL: anthropicBaseUrl,
68
+ ANTHROPIC_API_KEY: input.proxyToken,
69
+ },
70
+ env: { CLAUDE_CONFIG_DIR: askClaudeConfigDir() },
71
+ mcpServers,
72
+ });
54
73
  const harnessAgent = new HarnessAgent({
55
- // Pin auth the same way the backend harness stream does. Credentials go
56
- // through `auth`, not `env`: passing them via `env` alone lets
57
- // createClaudeCode auto-detect AI Gateway from the host process and ignore
58
- // the Cargo proxy.
59
- harness: createClaudeCode({
60
- auth: {
61
- ANTHROPIC_BASE_URL: anthropicBaseUrl,
62
- ANTHROPIC_API_KEY: input.proxyToken,
63
- },
64
- env: { CLAUDE_CONFIG_DIR: askClaudeConfigDir() },
65
- mcpServers,
66
- }),
74
+ // Native AskUserQuestion is withheld on the session, not via
75
+ // `inactiveTools` (that would drop the host tool of the same name) and
76
+ // not via settings.json deny (that name matches the replacement too).
77
+ harness: {
78
+ ...claudeCode,
79
+ doStart: async (startOpts) => claudeCode.doStart({
80
+ ...startOpts,
81
+ builtinToolFiltering: denyNativeAskUserQuestion(startOpts.builtinToolFiltering),
82
+ }),
83
+ },
67
84
  id: "cargo-ai-ask",
68
85
  sandbox: withGetPortEndpoint(createLocalSandbox({ cwd: workspace.sandboxCwd })),
69
86
  sandboxConfig: workspace.workDir === undefined ? {} : { workDir: workspace.workDir },
@@ -166,6 +183,8 @@ export async function createLocalHarness(input) {
166
183
  }
167
184
  const accumulator = createTurnAccumulator();
168
185
  let answer;
186
+ askUserQuestionAsk.current =
187
+ options === undefined ? undefined : options.onUserQuestion;
169
188
  try {
170
189
  answer = await runPromptTurn({
171
190
  driver: {
@@ -230,6 +249,9 @@ export async function createLocalHarness(input) {
230
249
  });
231
250
  throw error;
232
251
  }
252
+ finally {
253
+ askUserQuestionAsk.current = undefined;
254
+ }
233
255
  if (didReachAnthropic(accumulator.usage) === true) {
234
256
  proxyLastUsedAtMs = Date.now();
235
257
  }
@@ -1,4 +1,5 @@
1
1
  export { isAlwaysDenied, isAutoApproved } from "./approvals.js";
2
+ export { ASK_USER_QUESTION_DENIED_REASON, ASK_USER_QUESTION_MISSING_INPUT_REASON, ASK_USER_QUESTION_TOOL_NAME, buildAskUserQuestionHostTool, denyNativeAskUserQuestion, formatAskUserQuestionAnswers, readAskUserQuestions, summarizeAskUserQuestion, } from "./askUserQuestion.js";
2
3
  export { createLocalHarness, type LocalHarness } from "./create.js";
3
4
  export { localHarnessInstructions, localHarnessSkills, } from "./instructions.js";
4
5
  export { resolveWorkspaceLayout } from "./layout.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EACL,+BAA+B,EAC/B,sCAAsC,EACtC,2BAA2B,EAC3B,4BAA4B,EAC5B,yBAAyB,EACzB,4BAA4B,EAC5B,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EACL,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,aAAa,EACb,YAAY,EACZ,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export { isAlwaysDenied, isAutoApproved } from "./approvals.js";
2
+ export { ASK_USER_QUESTION_DENIED_REASON, ASK_USER_QUESTION_MISSING_INPUT_REASON, ASK_USER_QUESTION_TOOL_NAME, buildAskUserQuestionHostTool, denyNativeAskUserQuestion, formatAskUserQuestionAnswers, readAskUserQuestions, summarizeAskUserQuestion, } from "./askUserQuestion.js";
2
3
  export { createLocalHarness } from "./create.js";
3
4
  export { localHarnessInstructions, localHarnessSkills, } from "./instructions.js";
4
5
  export { resolveWorkspaceLayout } from "./layout.js";
@@ -1 +1 @@
1
- {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAsBxC;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,GACnB,MAAM,CAkBR;AAED,0BAA0B;AAC1B,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAM/D"}
1
+ {"version":3,"file":"instructions.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAwBxC;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,GACnB,MAAM,CAkBR;AAED,0BAA0B;AAC1B,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAM/D"}
@@ -4,6 +4,8 @@ const LOCAL_WORKING_COPY_INSTRUCTIONS = `You are working in the user's local wor
4
4
 
5
5
  The user confirms each Bash, Write, Edit and NotebookEdit call, and can only answer one at a time. Ask for a single one of those per step and wait for its result before asking for the next; two in one step leaves the second waiting on a confirmation it will never be offered. Reads (Read, Glob, Grep) and the Cargo \`mcp__cargo__*\` tools need no confirmation — batch those freely.
6
6
 
7
+ When you need a multiple-choice answer, call AskUserQuestion. This session presents the options in the terminal and returns the user's answers as the tool result. Do not wait for a Claude Code question picker.
8
+
7
9
  Prefer minimal, focused diffs; match the existing code style; run the project's own checks when they exist. End each task with a short summary of what changed and how it was validated.`;
8
10
  const CDK_RAILS_INSTRUCTIONS = `This directory is a Cargo CDK project.
9
11
  - Never edit cargo.state.json, cargo.state.lock, or .cargo-ai/ by hand — they are generated. Repoint this project's deploy state with \`cargo-cdk state bind\`.
@@ -43,6 +43,13 @@ export type LocalStreamHandlers = {
43
43
  * do rather than silently running commands on the user's machine.
44
44
  */
45
45
  onApproval?: (approval: LocalToolApproval) => Promise<boolean>;
46
+ /**
47
+ * Present `AskUserQuestion` in this session's own picker. Used by the host
48
+ * tool that replaces Claude Code's native picker, and by the approval path
49
+ * if a call is still offered. Omitted (`--print`) means the model is told
50
+ * to ask in its reply instead.
51
+ */
52
+ onUserQuestion?: (input: unknown) => Promise<Record<string, string> | undefined>;
46
53
  onActivity?: (activity: LocalToolActivity) => void;
47
54
  /** Path of a file the turn wrote or edited, as the tool named it. */
48
55
  onFileTouched?: (path: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"turn.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/turn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"turn.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/harness/turn.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { DENIED_TOOL_REASON, isAlwaysDenied, isAutoApproved, } from "./approvals.js";
2
+ import { ASK_USER_QUESTION_DENIED_REASON, ASK_USER_QUESTION_TOOL_NAME, formatAskUserQuestionAnswers, summarizeAskUserQuestion, } from "./askUserQuestion.js";
2
3
  import { addTurnUsage, createTurnAccumulator, } from "./usage.js";
3
4
  /**
4
5
  * How many times one turn may suspend for approvals. Each round trip is a
@@ -101,15 +102,20 @@ export async function runPromptTurn(input) {
101
102
  // Refused before the user is asked: not their call to make, and a
102
103
  // prompt for it could not say what it was granting.
103
104
  const denied = isAlwaysDenied(approval.toolName);
104
- const approved = denied === true
105
+ const question = approval.toolName === ASK_USER_QUESTION_TOOL_NAME
106
+ ? await settleAskUserQuestion(approval.input, handlers.onUserQuestion)
107
+ : undefined;
108
+ const approved = question !== undefined
105
109
  ? false
106
- : isAutoApproved(approval.toolName, {
107
- unattended: onApproval === undefined,
108
- }) === true
109
- ? true
110
- : onApproval === undefined
111
- ? false
112
- : await onApproval(approval);
110
+ : denied === true
111
+ ? false
112
+ : isAutoApproved(approval.toolName, {
113
+ unattended: onApproval === undefined,
114
+ }) === true
115
+ ? true
116
+ : onApproval === undefined
117
+ ? false
118
+ : await onApproval(approval);
113
119
  // Recorded against the call so the retraction pass below knows it is
114
120
  // settled: an approved one is running and must be left alone, a refused
115
121
  // one is certainly not and needs no reprieve to find out. Absent when
@@ -132,7 +138,11 @@ export async function runPromptTurn(input) {
132
138
  approved,
133
139
  // Told why, so it does something else rather than asking again
134
140
  // until the approval-round cap ends the turn.
135
- reason: denied === true ? DENIED_TOOL_REASON : undefined,
141
+ reason: question !== undefined
142
+ ? question.reason
143
+ : denied === true
144
+ ? DENIED_TOOL_REASON
145
+ : undefined,
136
146
  },
137
147
  toolCall: {
138
148
  type: "tool-call",
@@ -400,7 +410,7 @@ async function consumeTurn(input) {
400
410
  }
401
411
  continue;
402
412
  }
403
- if (part["type"] === "tool-call") {
413
+ if (isToolCallPart(part) === true) {
404
414
  const toolCallId = part["toolCallId"];
405
415
  const toolName = part["toolName"];
406
416
  if (typeof toolCallId === "string" && typeof toolName === "string") {
@@ -444,12 +454,12 @@ async function consumeTurn(input) {
444
454
  }
445
455
  continue;
446
456
  }
447
- if (part["type"] === "tool-result" || part["type"] === "tool-error") {
457
+ if (isToolEndPart(part) === true) {
448
458
  const toolCallId = part["toolCallId"];
449
459
  const recorded = typeof toolCallId === "string"
450
460
  ? input.toolCalls.get(toolCallId)
451
461
  : undefined;
452
- const isError = part["type"] === "tool-error";
462
+ const isError = isToolErrorPart(part);
453
463
  // A preliminary result is progress from a tool that is still running,
454
464
  // so it must not clear the spinner or tell the stall watchdog the tool
455
465
  // is done.
@@ -616,11 +626,51 @@ function summarizeToolInput(toolInput) {
616
626
  return truncate(value);
617
627
  }
618
628
  }
629
+ const question = summarizeAskUserQuestion(toolInput);
630
+ if (question !== undefined) {
631
+ return truncate(question);
632
+ }
619
633
  return truncate(JSON.stringify(record));
620
634
  }
635
+ async function settleAskUserQuestion(toolInput, onUserQuestion) {
636
+ if (onUserQuestion === undefined) {
637
+ return { reason: ASK_USER_QUESTION_DENIED_REASON };
638
+ }
639
+ const answers = await onUserQuestion(toolInput);
640
+ if (answers === undefined || Object.keys(answers).length === 0) {
641
+ return { reason: ASK_USER_QUESTION_DENIED_REASON };
642
+ }
643
+ return { reason: formatAskUserQuestionAnswers(answers) };
644
+ }
621
645
  function truncate(value) {
622
646
  const flat = value.replace(/\s+/g, " ").trim();
623
647
  return flat.length > TOOL_SUMMARY_MAX_LENGTH
624
648
  ? `${flat.slice(0, TOOL_SUMMARY_MAX_LENGTH - 1)}…`
625
649
  : flat;
626
650
  }
651
+ /**
652
+ * `fullStream` still uses `tool-call`. The UI message stream — and some
653
+ * harness adapters after AI SDK 7 — spell the assembled call
654
+ * `tool-input-available` instead. Treat them as the same moment: the tool
655
+ * is now executing.
656
+ */
657
+ function isToolCallPart(part) {
658
+ return (part["type"] === "tool-call" || part["type"] === "tool-input-available");
659
+ }
660
+ /**
661
+ * Same split for the result. Missing the UI names left the tool "running"
662
+ * after it had already finished, which is the spinner-stuck-on-every-tool
663
+ * hang: the stall watchdog switched to its long deadline and waited there.
664
+ */
665
+ function isToolEndPart(part) {
666
+ return (part["type"] === "tool-result" ||
667
+ part["type"] === "tool-error" ||
668
+ part["type"] === "tool-output-available" ||
669
+ part["type"] === "tool-output-error" ||
670
+ part["type"] === "tool-output-denied");
671
+ }
672
+ function isToolErrorPart(part) {
673
+ return (part["type"] === "tool-error" ||
674
+ part["type"] === "tool-output-error" ||
675
+ part["type"] === "tool-output-denied");
676
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAmD3C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAkHF;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAyND;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CAulB7B;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAqGhB"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAoD3C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAkHF;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAyND;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CAsmB7B;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAqGhB"}
@@ -13,7 +13,7 @@ import { saveAskState } from "../persist/persist.js";
13
13
  import { ASK_STATE_VERSION } from "../persist/state.js";
14
14
  import { claimChatTurn, proxyTokenDeadlineMs, releaseChatTurn, reportApiFailure, } from "./claim.js";
15
15
  import { pickThinkingWord } from "./thinking.js";
16
- import { appendAssistantDelta, askToolApproval, beginAssistantTurn, describeToolActivity, endAssistantTurn, setAssistantFooter, showAssistantTurn, showCard, showNotices, showToolRun, showTouchedFiles, showUserTurn, } from "./tree.js";
16
+ import { appendAssistantDelta, askToolApproval, askUserQuestions, beginAssistantTurn, describeToolActivity, endAssistantTurn, setAssistantFooter, showAssistantTurn, showCard, showNotices, showToolRun, showTouchedFiles, showUserTurn, } from "./tree.js";
17
17
  function requireInteractive() {
18
18
  if (isInteractive() === false || process.stdout.isTTY !== true) {
19
19
  failWith("Ask needs a terminal. Re-run with --print for one JSON turn.", {
@@ -663,6 +663,20 @@ export async function runAskSession(options) {
663
663
  });
664
664
  return true;
665
665
  },
666
+ onUserQuestion: async (input) => {
667
+ if (live !== undefined) {
668
+ endAssistantTurn(live);
669
+ live = undefined;
670
+ }
671
+ stopSpinner();
672
+ await typing.pause();
673
+ try {
674
+ return await askUserQuestions(input);
675
+ }
676
+ finally {
677
+ typing.resume();
678
+ }
679
+ },
666
680
  });
667
681
  stopSpinner();
668
682
  if (live !== undefined) {
@@ -1,5 +1,5 @@
1
1
  import { type Spinner } from "../../runHandler.js";
2
- import type { LocalToolApproval } from "../harness/index.js";
2
+ import type { LocalToolApproval } from "../harness/turn.js";
3
3
  /**
4
4
  * Greedy wrap on spaces. A word longer than the width keeps its own line
5
5
  * rather than being cut mid-token.
@@ -87,4 +87,13 @@ export declare function showNotices(notices: string[]): void;
87
87
  * │ ● Yes …
88
88
  */
89
89
  export declare function askToolApproval(approval: LocalToolApproval): Promise<"yes" | "always" | "no">;
90
+ /**
91
+ * Claude Code's question picker, in this session's tree. Each question is one
92
+ * `selectOne` — the same prompt as a tool approval, so the rail stays intact.
93
+ *
94
+ * Multi-select is flattened to one pick: this terminal can only answer one
95
+ * row at a time, and a comma-joined bag of labels is worse than the one the
96
+ * user actually chose.
97
+ */
98
+ export declare function askUserQuestions(toolInput: unknown): Promise<Record<string, string> | undefined>;
90
99
  //# sourceMappingURL=tree.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/tree.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AA6B7D;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAe9D;AA0DD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM1D;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAK3C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA+DF,4DAA4D;AAC5D,wBAAgB,kBAAkB,IAAI,iBAAiB,CAItD;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,MAAM,GACZ,IAAI,CAeN;AAkBD,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAON;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAK9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIpD;AAaD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,MAAM,CAYR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,IAAI,CAYP;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,EAC1B,GAAG,EAAE,MAAM,GACV,IAAI,CAWN;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAQnD;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC,CAkBlC"}
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../../../src/commands/ask/session/tree.ts"],"names":[],"mappings":"AAIA,OAAO,EAAuB,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAKxE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AA6B5D;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAe9D;AA0DD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM1D;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAK3C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA+DF,4DAA4D;AAC5D,wBAAgB,kBAAkB,IAAI,iBAAiB,CAItD;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,MAAM,GACZ,IAAI,CAeN;AAkBD,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,MAAM,GACb,IAAI,CAON;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,IAAI,CAK9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAIpD;AAiBD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,MAAM,CAYR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,IAAI,CAYP;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,EAC1B,GAAG,EAAE,MAAM,GACV,IAAI,CAWN;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAQnD;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,CAAC,KAAK,GAAG,QAAQ,GAAG,IAAI,CAAC,CAkBlC;AAID;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CA6B7C"}
@@ -1,6 +1,7 @@
1
1
  import { isAbsolute, relative } from "node:path";
2
2
  import { selectOne } from "@cargo-ai/cdk/cli";
3
3
  import { clipToWidth, colors } from "../../runHandler.js";
4
+ import { ASK_USER_QUESTION_TOOL_NAME, readAskUserQuestions, } from "../harness/askUserQuestion.js";
4
5
  /**
5
6
  * What an assistant turn is marked with, alongside `◇` for a step and `│` for
6
7
  * the rail.
@@ -217,6 +218,9 @@ export function showAssistantTurn(body) {
217
218
  }
218
219
  /** Common tool names arrive lowercase (`bash`); natives keep their case. */
219
220
  function toolLabel(toolName) {
221
+ if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
222
+ return "Ask user";
223
+ }
220
224
  if (toolName.length === 0) {
221
225
  return "tool";
222
226
  }
@@ -316,3 +320,37 @@ export async function askToolApproval(approval) {
316
320
  });
317
321
  return decision === "always" ? "always" : decision === "no" ? "no" : "yes";
318
322
  }
323
+ const SKIP_QUESTION = "__skip__";
324
+ /**
325
+ * Claude Code's question picker, in this session's tree. Each question is one
326
+ * `selectOne` — the same prompt as a tool approval, so the rail stays intact.
327
+ *
328
+ * Multi-select is flattened to one pick: this terminal can only answer one
329
+ * row at a time, and a comma-joined bag of labels is worse than the one the
330
+ * user actually chose.
331
+ */
332
+ export async function askUserQuestions(toolInput) {
333
+ const questions = readAskUserQuestions(toolInput);
334
+ if (questions === undefined) {
335
+ return undefined;
336
+ }
337
+ const answers = {};
338
+ for (const question of questions) {
339
+ const header = question.header === undefined ? "" : `${question.header}: `;
340
+ const first = question.options[0];
341
+ const chosen = await selectOne({
342
+ message: `${header}${question.question}`,
343
+ recommended: first === undefined ? undefined : first.label,
344
+ cancelValue: SKIP_QUESTION,
345
+ choices: question.options.map((option) => ({
346
+ value: option.label,
347
+ label: option.label,
348
+ hint: option.description,
349
+ })),
350
+ });
351
+ if (chosen !== SKIP_QUESTION) {
352
+ answers[question.question] = chosen;
353
+ }
354
+ }
355
+ return Object.keys(answers).length === 0 ? undefined : answers;
356
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"mailbox.d.ts","sourceRoot":"","sources":["../../../src/commands/mailboxManagement/mailbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAUxC,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CAySN"}
1
+ {"version":3,"file":"mailbox.d.ts","sourceRoot":"","sources":["../../../src/commands/mailboxManagement/mailbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAUxC,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,MAAM,GAAG,GAChB,IAAI,CA4SN"}
@@ -121,6 +121,9 @@ Example:
121
121
  .description("Start provider warm-up (dummy mail) and the Cargo send ramp")
122
122
  .option("--daily-target <n>", "Warm-up messages per day at full ramp (defaults to 40 if omitted)")
123
123
  .addHelpText("after", `
124
+ Warm-up starts on its own the first time the mailbox becomes active.
125
+ This command is the retry after a failed enrolment, or after stop-warmup.
126
+
124
127
  Example:
125
128
  $ cargo-ai mailboxManagement mailbox start-warmup 550e8400-... --daily-target 40`)
126
129
  .action(async (uuid, opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cargo-ai/cli",
3
- "version": "1.0.95",
3
+ "version": "1.0.97",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://getcargo.ai",
@@ -67,11 +67,11 @@
67
67
  "dependencies": {
68
68
  "@ai-sdk/harness": "^1.0.95",
69
69
  "@ai-sdk/harness-claude-code": "^1.0.99",
70
- "@cargo-ai/api": "^1.0.83",
70
+ "@cargo-ai/api": "^1.0.84",
71
71
  "@cargo-ai/app-sdk": "^1.0.10",
72
- "@cargo-ai/cdk": "^1.0.81",
72
+ "@cargo-ai/cdk": "^1.0.82",
73
73
  "@cargo-ai/types": "^1.0.79",
74
- "@cargo-ai/utils": "^1.0.30",
74
+ "@cargo-ai/utils": "^1.0.31",
75
75
  "@cargo-ai/worker-sdk": "^1.0.17",
76
76
  "@modelcontextprotocol/sdk": "1.29.0",
77
77
  "ai": "^7.0.84",