@agentdocumentformat/cli 0.8.2 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adf.mjs CHANGED
@@ -9,7 +9,7 @@ import "./chunks/chunk-M2RX3S2T.mjs";
9
9
 
10
10
  // src/main/cli/bin.ts
11
11
  function version() {
12
- return "0.8.2";
12
+ return "0.8.3";
13
13
  }
14
14
  var DAEMON_HELP = `Usage: adf daemon [--port <n>] [--host <h>] [--settings <file>]
15
15
  adf daemon start [--port <n>] [--force]
@@ -191,9 +191,9 @@ async function main(argv) {
191
191
  let { daemonPaths } = await import("./chunks/daemon-control-MKN7G5M3.mjs");
192
192
  process.env.ADF_DAEMON_PIDFILE = daemonPaths(Number(process.env.ADF_DAEMON_PORT ?? 7385)).pidFile;
193
193
  }
194
- return await import("./chunks/daemon-HEQ3FUYH.mjs"), -1;
194
+ return await import("./chunks/daemon-YHD7PUQI.mjs"), -1;
195
195
  }
196
- let [{ runCli, tuiInvocation }, control] = await Promise.all([import("./chunks/cli-PCVFB6B4.mjs"), import("./chunks/daemon-control-MKN7G5M3.mjs")]);
196
+ let [{ runCli, tuiInvocation }, control] = await Promise.all([import("./chunks/cli-UVIJAGRZ.mjs"), import("./chunks/daemon-control-MKN7G5M3.mjs")]);
197
197
  return tuiInvocation(argv) && process.env.NODE_ENV === void 0 && (process.env.NODE_ENV = "production", process.env.ADF_NODE_ENV_DEFAULTED = "1"), await runCli(argv, {
198
198
  fetch: globalThis.fetch.bind(globalThis),
199
199
  stdout: (text) => process.stdout.write(text),
@@ -6,7 +6,7 @@ const __filename = __adfFileURLToPath(import.meta.url);
6
6
  const __dirname = __adfDirname(__filename);
7
7
  import {
8
8
  DaemonClient
9
- } from "./chunk-IMVMSU3A.mjs";
9
+ } from "./chunk-FUQRYTGX.mjs";
10
10
  import {
11
11
  auth_flow_exports
12
12
  } from "./chunk-BHY7QDCM.mjs";
@@ -155,7 +155,7 @@ import {
155
155
  PROMPT_PREFILL_KEY,
156
156
  SERVER_STOPPED_TEXT,
157
157
  StoreProvider,
158
- askQuestion,
158
+ askQuestions,
159
159
  emptyTranscript,
160
160
  findTracked,
161
161
  folderName,
@@ -163,6 +163,7 @@ import {
163
163
  isBusyState,
164
164
  isCredentialsLocked,
165
165
  isTrackedKey,
166
+ parseAskResult,
166
167
  parseServer,
167
168
  servedText,
168
169
  serverBadge,
@@ -178,7 +179,7 @@ import {
178
179
  useClient,
179
180
  useStore,
180
181
  useTuiSelector
181
- } from "./chunk-RJDOVMM4.mjs";
182
+ } from "./chunk-OHTB4UW4.mjs";
182
183
  import "./chunk-75DC5EWN.mjs";
183
184
  import {
184
185
  require_jsx_runtime
@@ -186,7 +187,7 @@ import {
186
187
  import {
187
188
  cjs
188
189
  } from "./chunk-SGUTYISF.mjs";
189
- import "./chunk-7JZXMDDF.mjs";
190
+ import "./chunk-WPT3FAFX.mjs";
190
191
  import {
191
192
  require_react
192
193
  } from "./chunk-6GJ4QHRI.mjs";
@@ -2196,7 +2197,11 @@ function Prompt({ width, focused }) {
2196
2197
  }, []);
2197
2198
  let highlighted = Math.min(menuIndex, Math.max(0, menu.length - 1)), submit = (text) => {
2198
2199
  let trimmed = text.trim();
2199
- return trimmed ? (setHistory((prev) => [...prev.filter((h) => h !== trimmed), trimmed].slice(-HISTORY_LIMIT)), setMenuIndex(0), (async () => {
2200
+ if (!trimmed) {
2201
+ let scope2 = createScope(store, exit);
2202
+ return !config2?.onSubmit || !config2.submitsEmpty?.(scope2) ? !1 : (config2.onSubmit("", { ...scope2, args: [], rest: "" }), !0);
2203
+ }
2204
+ return setHistory((prev) => [...prev.filter((h) => h !== trimmed), trimmed].slice(-HISTORY_LIMIT)), setMenuIndex(0), (async () => {
2200
2205
  if (await runSlash(trimmed, registry, store, exit)) return;
2201
2206
  let ctx = { ...createScope(store, exit), args: [], rest: trimmed };
2202
2207
  if (config2?.onSubmit && await config2.onSubmit(trimmed, ctx)) return;
@@ -2212,7 +2217,7 @@ function Prompt({ width, focused }) {
2212
2217
  let summary = store.getState().agents[ctx.agentId]?.summary;
2213
2218
  store.actions.toast(`Sent to ${summary?.handle || summary?.name || ctx.agentId} ${theme2.glyph.pointer} ${ctx.loop ?? MAIN_LOOP}`, "success", 2500);
2214
2219
  }
2215
- })(), !0) : !1;
2220
+ })(), !0;
2216
2221
  };
2217
2222
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "column", width, children: [
2218
2223
  menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { flexDirection: "column", marginBottom: 0, children: menu.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Text, { wrap: "truncate-end", children: [
@@ -3872,9 +3877,40 @@ function sayText(item) {
3872
3877
  let message = inputRecord(item.input)?.message;
3873
3878
  return typeof message == "string" ? message : prettyJson(item.input);
3874
3879
  }
3875
- function askAnswer(item) {
3876
- if (item.result !== void 0)
3877
- return item.result.startsWith("Human answered: ") ? item.result.slice(16) : item.result;
3880
+ function askAnswers(item) {
3881
+ if (!(item.result === void 0 || item.status === "error"))
3882
+ return parseAskResult(askQuestions(item.input), item.result);
3883
+ }
3884
+ function askFormHeight(questions, w, answers) {
3885
+ return questions.reduce((n, q) => n + wrappedHeight(q.question || " ", w) + (answers ? 1 : 0), 0);
3886
+ }
3887
+ function askFormText(questions, answers) {
3888
+ return questions.map((q, i) => answers ? `${q.question}
3889
+ ${answers[i] ?? "(skipped)"}` : q.question).join(`
3890
+
3891
+ `);
3892
+ }
3893
+ function askStep(ask, answers) {
3894
+ let index = Math.min(answers.length, ask.questions.length - 1);
3895
+ return { index, question: ask.questions[index] };
3896
+ }
3897
+ function askHint(q) {
3898
+ return q.options ? `Type ${q.multiple ? "numbers (1,3)" : "a number"}, or your own answer` : "Type your answer";
3899
+ }
3900
+ function readAskReply(q, text) {
3901
+ let reply = text.trim();
3902
+ if (!reply) return { answer: null };
3903
+ let options = q.options;
3904
+ if (!options) return { answer: reply };
3905
+ if (/^\d+(\s*[,\s]\s*\d+)*$/.test(reply)) {
3906
+ let picks = [...new Set(reply.split(/[\s,]+/).map(Number))];
3907
+ if (picks.every((n) => n >= 1 && n <= options.length)) {
3908
+ if (!q.multiple && picks.length > 1) return { error: "This question takes one pick" };
3909
+ let labels = picks.map((n) => options[n - 1].label);
3910
+ return { answer: q.multiple ? labels : labels[0] };
3911
+ }
3912
+ }
3913
+ return { answer: q.multiple ? [reply] : reply };
3878
3914
  }
3879
3915
  function statusLineText(item, sep4 = "\xB7", arrow = "\u2192") {
3880
3916
  let input = inputRecord(item.input);
@@ -3910,8 +3946,10 @@ function itemHeight(item, width, flags) {
3910
3946
  switch (view2) {
3911
3947
  case "say":
3912
3948
  return markdownHeight(sayText(item) || " ", w) + (item.status === "error" ? 1 : 0) + 1;
3913
- case "ask":
3914
- return 1 + wrappedHeight(askQuestion(item.input) || " ", w) + (item.result !== void 0 ? 1 : 0) + 1;
3949
+ case "ask": {
3950
+ let answers = askAnswers(item);
3951
+ return 1 + askFormHeight(askQuestions(item.input), w, answers) + (item.result !== void 0 && !answers ? 1 : 0) + 1;
3952
+ }
3915
3953
  case "status":
3916
3954
  return 2;
3917
3955
  case "row":
@@ -3922,7 +3960,7 @@ function itemHeight(item, width, flags) {
3922
3960
  case "hil":
3923
3961
  return 2 + (item.reason ? 1 : 0) + (item.feedback ? 1 : 0) + (flags.expanded ? wrappedHeight(prettyJson(item.input), w - 4) : 0) + 1;
3924
3962
  case "ask":
3925
- return 1 + wrappedHeight(item.question || " ", w) + (item.answer ? 1 : 0) + 1;
3963
+ return 1 + askFormHeight(item.questions, w) + 1;
3926
3964
  case "notice":
3927
3965
  return 1;
3928
3966
  case "context":
@@ -3951,12 +3989,8 @@ function itemText(item) {
3951
3989
  switch (toolView(item)) {
3952
3990
  case "say":
3953
3991
  return sayText(item);
3954
- case "ask": {
3955
- let answer = askAnswer(item);
3956
- return answer ? `${askQuestion(item.input)}
3957
-
3958
- ${answer}` : askQuestion(item.input);
3959
- }
3992
+ case "ask":
3993
+ return askFormText(askQuestions(item.input), askAnswers(item));
3960
3994
  default:
3961
3995
  return [`${item.name} ${prettyJson(item.input)}`, item.result ?? ""].filter(Boolean).join(`
3962
3996
 
@@ -3965,9 +3999,7 @@ ${answer}` : askQuestion(item.input);
3965
3999
  case "hil":
3966
4000
  return `${item.tool} ${prettyJson(item.input)}`;
3967
4001
  case "ask":
3968
- return item.answer ? `${item.question}
3969
-
3970
- ${item.answer}` : item.question;
4002
+ return askFormText(item.questions);
3971
4003
  case "context":
3972
4004
  return item.ownerCommand ? `${item.ownerCommand.command} [${item.ownerCommand.status}${item.ownerCommand.authorized ? ", authorized" : ""}]
3973
4005
  ${item.text}` : item.text;
@@ -9908,6 +9940,23 @@ function lineCount(text) {
9908
9940
  return text ? text.split(`
9909
9941
  `).length : 0;
9910
9942
  }
9943
+ function AskForm({ questions, answers }) {
9944
+ let theme2 = useTheme(), g = theme2.glyph;
9945
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_jsx_runtime38.Fragment, { children: questions.map((q, i) => {
9946
+ let answer = answers?.[i];
9947
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", children: [
9948
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { wrap: "wrap", color: theme2.color.text, children: q.question || " " }),
9949
+ answers ? answer != null ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: theme2.color.user, children: [
9950
+ g.pointer,
9951
+ " ",
9952
+ oneLine(answer) || "(empty)"
9953
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: theme2.color.dim, children: [
9954
+ g.pointer,
9955
+ " skipped"
9956
+ ] }) : null
9957
+ ] }, i);
9958
+ }) });
9959
+ }
9911
9960
  function ToolBody({ item, w, expanded }) {
9912
9961
  let theme2 = useTheme(), g = theme2.glyph, view2 = toolView(item), failed = item.status === "error", status = item.status === "running" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Spinner, { color: theme2.color.tool }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { color: failed ? theme2.color.error : theme2.color.success, children: failed ? g.cross : g.check }), asyncBadge = isAsyncTool(item) ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { color: theme2.color.dim, children: " [async]" }) : null;
9913
9962
  if (expanded && isExpandable(item)) {
@@ -9969,7 +10018,7 @@ ${g.ellipsis} ${result2.more} more lines` : ""
9969
10018
  ] })
9970
10019
  ] });
9971
10020
  case "ask": {
9972
- let pending = item.status === "running", answer = askAnswer(item), reason2 = toolReason(item);
10021
+ let pending = item.status === "running", answers = askAnswers(item), reason2 = toolReason(item);
9973
10022
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", width: w, borderStyle: theme2.ascii ? "classic" : "single", borderLeft: !0, borderRight: !1, borderTop: !1, borderBottom: !1, borderColor: pending ? theme2.color.warn : theme2.color.dim, paddingLeft: 1, children: [
9974
10023
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", children: [
9975
10024
  /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { bold: !0, color: pending ? theme2.color.warn : theme2.color.muted, children: [
@@ -9983,11 +10032,11 @@ ${g.ellipsis} ${result2.more} more lines` : ""
9983
10032
  reason2
9984
10033
  ] }) : null
9985
10034
  ] }),
9986
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { wrap: "wrap", color: theme2.color.text, children: askQuestion(item.input) || " " }),
9987
- answer !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: failed ? theme2.color.error : theme2.color.user, children: [
9988
- failed ? g.cross : g.pointer,
10035
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(AskForm, { questions: askQuestions(item.input), answers }),
10036
+ item.result !== void 0 && !answers ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: theme2.color.error, children: [
10037
+ g.cross,
9989
10038
  " ",
9990
- oneLine(answer) || "(empty)"
10039
+ oneLine(item.result) || "(no detail)"
9991
10040
  ] }) : null
9992
10041
  ] });
9993
10042
  }
@@ -10168,12 +10217,7 @@ function ItemBody({ item, width, expanded, showThinking, queued }) {
10168
10217
  "? ",
10169
10218
  item.status === "pending" ? "the agent asks you" : "answered"
10170
10219
  ] }),
10171
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { wrap: "wrap", color: theme2.color.text, children: item.question }),
10172
- item.answer ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: theme2.color.user, children: [
10173
- g.pointer,
10174
- " ",
10175
- item.answer
10176
- ] }) : null
10220
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(AskForm, { questions: item.questions })
10177
10221
  ] });
10178
10222
  case "notice":
10179
10223
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Text, { wrap: "truncate-end", color: item.level === "warn" ? theme2.color.warn : item.event === "marker" ? theme2.color.info : theme2.color.muted, children: [
@@ -10326,7 +10370,11 @@ var import_jsx_runtime40 = __toESM(require_jsx_runtime(), 1);
10326
10370
  function topCard(model2) {
10327
10371
  return model2.tasks.length > 0 ? "hil" : model2.asks.length > 0 ? "ask" : model2.suspended ? "suspend" : null;
10328
10372
  }
10329
- var ASK_LINES = 3;
10373
+ var ASK_LINES = 3, ASK_OPTIONS = 6;
10374
+ function askRows(ask, answers, inner) {
10375
+ let { question } = askStep(ask, answers), options = question.options?.length ?? 0, optionRows = Math.min(options, ASK_OPTIONS) + (options > ASK_OPTIONS ? 1 : 0);
10376
+ return 1 + Math.min(ASK_LINES, wrappedHeight(question.question || " ", inner)) + optionRows + 1 + 1;
10377
+ }
10330
10378
  function taskReason(task) {
10331
10379
  let meta = task.approval_meta;
10332
10380
  if (meta)
@@ -10338,7 +10386,7 @@ function dockHeight(model2, width) {
10338
10386
  case "hil":
10339
10387
  return queued + 2 + 3 + (taskReason(model2.tasks[0]) ? 1 : 0);
10340
10388
  case "ask":
10341
- return queued + 2 + 2 + Math.min(ASK_LINES, wrappedHeight(model2.asks[0].question || " ", Math.max(8, width - 4)));
10389
+ return queued + 2 + askRows(model2.asks[0], model2.askAnswers ?? [], Math.max(8, width - 4));
10342
10390
  case "suspend":
10343
10391
  return queued + 2 + 2;
10344
10392
  default:
@@ -10423,21 +10471,56 @@ function Dock({ model: model2, width, focused, agentLabel: agentLabel5, onAction
10423
10471
  ].sort((a, b) => (a.off ? 1 : 0) - (b.off ? 1 : 0)))
10424
10472
  ] }));
10425
10473
  })() : null,
10426
- card === "ask" ? border(theme2.color.warn, /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
10427
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { wrap: "truncate-end", bold: !0, color: theme2.color.warn, children: [
10428
- "? ",
10429
- agentLabel5,
10430
- " asks you",
10431
- model2.asks.length > 1 ? ` (+${model2.asks.length - 1} more)` : ""
10432
- ] }),
10433
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Box_default, { height: Math.min(ASK_LINES, wrappedHeight(model2.asks[0].question || " ", inner)), overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { wrap: "wrap", color: theme2.color.text, children: model2.asks[0].question }) }),
10434
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { color: theme2.color.dim, wrap: "truncate-end", children: [
10435
- "Type the answer in the prompt ",
10436
- g.sep,
10437
- " Enter sends it to the agent ",
10438
- focused ? `${g.sep} a to focus the prompt` : ""
10439
- ] })
10440
- ] })) : null,
10474
+ card === "ask" ? (() => {
10475
+ let ask = model2.asks[0], { index, question } = askStep(ask, model2.askAnswers ?? []), options = question.options ?? [], total = ask.questions.length, number = (n) => `${n}.`.padStart(String(options.length).length + 1);
10476
+ return border(theme2.color.warn, /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
10477
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { wrap: "truncate-end", children: [
10478
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { bold: !0, color: theme2.color.warn, children: [
10479
+ "? ",
10480
+ agentLabel5,
10481
+ " asks you"
10482
+ ] }),
10483
+ total > 1 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { color: theme2.color.muted, children: [
10484
+ " ",
10485
+ index + 1,
10486
+ "/",
10487
+ total
10488
+ ] }) : null,
10489
+ model2.asks.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { color: theme2.color.muted, children: [
10490
+ " (+",
10491
+ model2.asks.length - 1,
10492
+ " more)"
10493
+ ] }) : null
10494
+ ] }),
10495
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Box_default, { height: Math.min(ASK_LINES, wrappedHeight(question.question || " ", inner)), overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { wrap: "wrap", color: theme2.color.text, children: question.question }) }),
10496
+ options.slice(0, ASK_OPTIONS).map((option, i) => /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { wrap: "truncate-end", children: [
10497
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme2.color.accent, children: number(i + 1) }),
10498
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { color: theme2.color.text, children: [
10499
+ " ",
10500
+ option.label
10501
+ ] }),
10502
+ option.description ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { color: theme2.color.dim, children: [
10503
+ " ",
10504
+ option.description
10505
+ ] }) : null
10506
+ ] }, i)),
10507
+ options.length > ASK_OPTIONS ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { wrap: "truncate-end", color: theme2.color.dim, children: [
10508
+ g.ellipsis,
10509
+ " ",
10510
+ options.length - ASK_OPTIONS,
10511
+ " more, ",
10512
+ ASK_OPTIONS + 1,
10513
+ "\u2013",
10514
+ options.length,
10515
+ " by number"
10516
+ ] }) : null,
10517
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { wrap: "truncate-end", color: theme2.color.dim, children: askHint(question) }),
10518
+ act([
10519
+ { keys: "a", label: "answer", action: "answer" },
10520
+ { keys: "x", label: index > 0 ? "skip the rest" : total > 1 ? "skip all" : "skip", action: "skip" }
10521
+ ])
10522
+ ] }));
10523
+ })() : null,
10441
10524
  card === "suspend" ? border(theme2.color.warn, /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
10442
10525
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { bold: !0, color: theme2.color.warn, children: [
10443
10526
  g.warn,
@@ -10619,6 +10702,41 @@ function chunk(line, width) {
10619
10702
  return out;
10620
10703
  }
10621
10704
 
10705
+ // src/main/tui/views/chat/asks.ts
10706
+ var ASK_PROGRESS_KEY = "chat:askProgress";
10707
+ function progressKey(agentId, loop, requestId) {
10708
+ return `${transcriptKey(agentId, loop)}\0${requestId}`;
10709
+ }
10710
+ function askProgress(state, agentId, loop, ask) {
10711
+ return state.viewState[ASK_PROGRESS_KEY]?.[progressKey(agentId, loop, ask.requestId)] ?? [];
10712
+ }
10713
+ function setProgress(store, key, answers) {
10714
+ let next = { ...store.getState().viewState[ASK_PROGRESS_KEY] };
10715
+ answers ? next[key] = answers : delete next[key], store.actions.setViewState(ASK_PROGRESS_KEY, next);
10716
+ }
10717
+ function pendingAskFor(state, agentId, loop) {
10718
+ if (!agentId) return;
10719
+ let agent2 = state.agents[agentId], asks = pendingAsksFor(agent2, loop, state.transcripts);
10720
+ return topCard({ tasks: pendingTasksFor(agent2, loop), asks, suspended: !1, queued: [] }) === "ask" ? asks[0] : void 0;
10721
+ }
10722
+ async function replyToAsk(store, agentId, loop, ask, text) {
10723
+ let key = progressKey(agentId, loop, ask.requestId), sofar = askProgress(store.getState(), agentId, loop, ask), { index, question } = askStep(ask, sofar), read = readAskReply(question, text);
10724
+ if ("error" in read) {
10725
+ store.actions.toast(read.error, "warn");
10726
+ return;
10727
+ }
10728
+ let answers = [...sofar.slice(0, index), read.answer];
10729
+ if (answers.length < ask.questions.length) {
10730
+ setProgress(store, key, answers);
10731
+ return;
10732
+ }
10733
+ await store.actions.answerAsk(agentId, ask.requestId, answers, ask.loop ?? loop), setProgress(store, key, null);
10734
+ }
10735
+ async function skipAsk(store, agentId, loop, ask) {
10736
+ let sofar = askProgress(store.getState(), agentId, loop, ask);
10737
+ await store.actions.answerAsk(agentId, ask.requestId, ask.questions.map((_, i) => sofar[i] ?? null), ask.loop ?? loop), setProgress(store, progressKey(agentId, loop, ask.requestId), null);
10738
+ }
10739
+
10622
10740
  // src/main/tui/views/chat/commands.ts
10623
10741
  var TRIGGER_TYPES = ["startup", "timer", "inbox", "outbox", "file_change", "chat", "tool_call", "task_create", "task_complete", "log_entry", "llm_call"];
10624
10742
  function readChatState(scope) {
@@ -10830,8 +10948,8 @@ function ChatView({ width: paneWidth, height, focused }) {
10830
10948
  };
10831
10949
  }, [agentId, loopName]);
10832
10950
  let transcripts = useTuiSelector((s) => s.transcripts);
10833
- useTuiSelector((s) => s.web?.server);
10834
- let site = siteOf(store.getState(), agentId), tabs = loopTabs(agent2), running2 = loopRunning2(agent2, loop) || transcript.live, state = loopStateLabel(agent2, loop, loopName), turn = chat2.turns[key], turnStart = running2 ? turn && turn.endedAt === void 0 ? turn.startedAt : mountedAt.current : null, tasks2 = pendingTasksFor(agent2, loopName), asks = pendingAsksFor(agent2, loopName, transcripts), queued = queuedItems(transcript.items, running2, turnStart), queuedIds = new Set(queued.map((q) => q.id)), dockModel = { tasks: tasks2, asks, suspended: loopName === MAIN_LOOP && agent2?.executorState === "suspended", queued }, unseen = new Set(Object.entries(latest).filter(([name, at]) => at > (chat2.seen[agentId ? transcriptKey(agentId, name) : ""] ?? 0)).map(([name]) => name)), pendingLoops = new Set((agent2?.pendingTasks ?? []).filter((t) => t.status === "pending_approval").map((t) => typeof t.origin == "string" && t.origin.startsWith("loop:") ? t.origin.slice(5) : MAIN_LOOP)), markers = chat2.markers[key], older = transcript.oldestOffset > 0 ? [{ id: "chat:older", at: 0, local: !0, kind: "notice", level: "info", text: transcript.loading ? `loading earlier history${g.ellipsis}` : `${transcript.oldestOffset} earlier rows ${g.sep} scroll up or Home to load` }] : [], items = useMergedItems(transcript.items, markers, older), heightCache = (0, import_react35.useRef)(/* @__PURE__ */ new WeakMap()), isExpanded = (item) => !!chat2.expanded[`${key}|${item.id}`], heights = items.map((item) => {
10951
+ useTuiSelector((s) => s.web?.server), useTuiSelector((s) => s.viewState[ASK_PROGRESS_KEY]);
10952
+ let site = siteOf(store.getState(), agentId), tabs = loopTabs(agent2), running2 = loopRunning2(agent2, loop) || transcript.live, state = loopStateLabel(agent2, loop, loopName), turn = chat2.turns[key], turnStart = running2 ? turn && turn.endedAt === void 0 ? turn.startedAt : mountedAt.current : null, tasks2 = pendingTasksFor(agent2, loopName), asks = pendingAsksFor(agent2, loopName, transcripts), queued = queuedItems(transcript.items, running2, turnStart), queuedIds = new Set(queued.map((q) => q.id)), askAnswers2 = agentId && asks[0] ? askProgress(store.getState(), agentId, loopName, asks[0]) : void 0, dockModel = { tasks: tasks2, asks, askAnswers: askAnswers2, suspended: loopName === MAIN_LOOP && agent2?.executorState === "suspended", queued }, unseen = new Set(Object.entries(latest).filter(([name, at]) => at > (chat2.seen[agentId ? transcriptKey(agentId, name) : ""] ?? 0)).map(([name]) => name)), pendingLoops = new Set((agent2?.pendingTasks ?? []).filter((t) => t.status === "pending_approval").map((t) => typeof t.origin == "string" && t.origin.startsWith("loop:") ? t.origin.slice(5) : MAIN_LOOP)), markers = chat2.markers[key], older = transcript.oldestOffset > 0 ? [{ id: "chat:older", at: 0, local: !0, kind: "notice", level: "info", text: transcript.loading ? `loading earlier history${g.ellipsis}` : `${transcript.oldestOffset} earlier rows ${g.sep} scroll up or Home to load` }] : [], items = useMergedItems(transcript.items, markers, older), heightCache = (0, import_react35.useRef)(/* @__PURE__ */ new WeakMap()), isExpanded = (item) => !!chat2.expanded[`${key}|${item.id}`], heights = items.map((item) => {
10835
10953
  let expanded = isExpanded(item), f = `${expanded ? 1 : 0}${chat2.showThinking ? 1 : 0}${queuedIds.has(item.id) ? 1 : 0}`, cached = heightCache.current.get(item);
10836
10954
  if (cached && cached.w === width && cached.f === f) return cached.h;
10837
10955
  let h = Math.max(1, itemHeight(item, width, { expanded, showThinking: chat2.showThinking }));
@@ -10900,6 +11018,15 @@ function ChatView({ width: paneWidth, height, focused }) {
10900
11018
  actions4.respondSuspend(agentId, !0);
10901
11019
  return;
10902
11020
  }
11021
+ if (action === "answer") {
11022
+ actions4.setFocus("input");
11023
+ return;
11024
+ }
11025
+ if (action === "skip") {
11026
+ let ask = asks[0];
11027
+ ask && skipAsk(store, agentId, loopName, ask);
11028
+ return;
11029
+ }
10903
11030
  if (action === "shutdown") {
10904
11031
  actions4.confirm({ title: "Shut down", message: `Shut ${agentLabel4(agent2)} down instead of resuming?`, danger: !0 }).then((ok) => {
10905
11032
  ok && actions4.respondSuspend(agentId, !1);
@@ -10972,7 +11099,7 @@ function ChatView({ width: paneWidth, height, focused }) {
10972
11099
  let explicit = selectedItem?.kind === "hil" && selectedItem.status === "pending" && selectedItem.taskId === target2.taskId, prior = armed.current;
10973
11100
  return explicit || prior && prior.taskId === target2.taskId && prior.key === input && Date.now() - prior.at < APPROVE_ARM_MS ? (armed.current = null, dockAction(input === "y" ? "approve" : "always")) : (armed.current = { taskId: target2.taskId, key: input, at: Date.now() }, actions4.toast(`Press ${input} again to ${input === "y" ? "approve" : "always approve"} ${target2.tool} (${target2.taskId})`, "warn", APPROVE_ARM_MS)), !0;
10974
11101
  }
10975
- return card === "hil" && (input === "n" || input === "f" || input === "v") ? (dockAction(input === "n" ? "reject" : input === "f" ? "feedback" : "details"), !0) : card === "suspend" && (input === "y" || input === "n") ? (dockAction(input === "y" ? "resume" : "shutdown"), !0) : input === "a" && card === "ask" ? (actions4.setFocus("input"), !0) : !1;
11102
+ return card === "hil" && (input === "n" || input === "f" || input === "v") ? (dockAction(input === "n" ? "reject" : input === "f" ? "feedback" : "details"), !0) : card === "suspend" && (input === "y" || input === "n") ? (dockAction(input === "y" ? "resume" : "shutdown"), !0) : card === "ask" && (input === "a" || input === "x") ? (dockAction(input === "a" ? "answer" : "skip"), !0) : !1;
10976
11103
  }, { layer: "main", active: focused }), useKeys((_input, k) => !k.escape || store.getState().focus === "sidebar" ? !1 : running2 ? interrupt() : !1, { layer: "view" });
10977
11104
  let transcriptRef = (0, import_react35.useRef)(null);
10978
11105
  useWheel(transcriptRef, (delta) => scrollBy(-delta * WHEEL_STEP), { active: !!agentId });
@@ -11091,16 +11218,12 @@ function infoStyle(key, theme2) {
11091
11218
  return { color: theme2.color.dim };
11092
11219
  }
11093
11220
  }
11094
- function pendingAskFor(state, agentId, loop) {
11095
- if (agentId)
11096
- return pendingAsksFor(state.agents[agentId], loop, state.transcripts)[0];
11097
- }
11098
11221
  function placeholder(scope) {
11099
11222
  let state = scope.state(), agent2 = scope.agentId ? state.agents[scope.agentId] : void 0, stopped = scope.stoppedKey ? findTracked(state, scope.stoppedKey) : void 0;
11100
11223
  if (stopped) return stopped.agent.status === "needs_review" ? `${stopped.agent.name} is stopped and needs review \xB7 Shift+Tab, then s reviews it` : `${stopped.agent.name} is stopped \xB7 a message starts it, then sends \xB7 or Shift+Tab, then s`;
11101
11224
  if (!agent2 || !scope.agentId) return "Select an agent (Tab \u2192 sidebar) or type /help";
11102
11225
  let label2 = agentLabel4(agent2), target2 = scope.loop === MAIN_LOOP ? label2 : `${label2} \u203A ${scope.loop}`;
11103
- if (pendingAskFor(state, scope.agentId, scope.loop)) return `Answer ${label2}'s question \xB7 Enter sends the answer`;
11226
+ if (pendingAskFor(state, scope.agentId, scope.loop)) return `Answer ${label2}'s question \xB7 Enter on empty skips it`;
11104
11227
  let loopState = agent2.loops?.find((l) => l.info.name === scope.loop);
11105
11228
  return loopState && !loopState.info.enabled ? `${scope.loop} is disabled \xB7 /loop on ${scope.loop} to enable it` : loopRunning2(agent2, loopState) ? `Message ${target2} \xB7 queued until the turn ends \xB7 Esc interrupts` : `Message ${target2} / commands \xB7 ${keyLabel(newlineKey())} newline`;
11106
11229
  }
@@ -11126,8 +11249,10 @@ var chat = {
11126
11249
  placeholder,
11127
11250
  onSubmit: async (text, ctx) => {
11128
11251
  let ask = pendingAskFor(ctx.state(), ctx.agentId, ctx.loop);
11129
- return !ask || !ctx.agentId ? !1 : (await ctx.actions.answerAsk(ctx.agentId, ask.requestId, text, ask.loop ?? ctx.loop), !0);
11252
+ return !ask || !ctx.agentId ? !1 : (await replyToAsk(ctx.store, ctx.agentId, ctx.loop, ask, text), !0);
11130
11253
  },
11254
+ // Enter on an empty prompt skips the question the dock shows.
11255
+ submitsEmpty: (scope) => !!pendingAskFor(scope.state(), scope.agentId, scope.loop),
11131
11256
  historyKey: (scope) => scope.agentId ? transcriptKey(scope.agentId, scope.loop) : "none",
11132
11257
  draftKey: (scope) => scope.agentId ? `chat:${transcriptKey(scope.agentId, scope.loop)}` : "chat",
11133
11258
  complete: completeFiles
@@ -11176,7 +11301,8 @@ var chat = {
11176
11301
  title: "Approvals and questions (the card under the transcript)",
11177
11302
  keys: [
11178
11303
  { keys: "y", label: "Approve: press twice, or once on a selected approval \xB7 resume a suspended agent. Mouse mode: click the card\u2019s buttons" },
11179
- { keys: "a", label: "Always approve this tool for the agent (twice, then confirm; it won\u2019t ask again). Not offered for protection overrides: those are one-time only \xB7 on a question card: answer it" },
11304
+ { keys: "a", label: "Always approve this tool for the agent (twice, then confirm; it won\u2019t ask again). Not offered for protection overrides: those are one-time only \xB7 on a question card: answer it in the prompt" },
11305
+ { keys: "x", label: "On a question card: skip the questions not answered yet and send the answers" },
11180
11306
  { keys: "n", label: "Reject \xB7 shut a suspended agent down (asks)" },
11181
11307
  { keys: "f", label: "Reject with feedback: the agent sees your text" },
11182
11308
  { keys: "v", label: "Full details of the tool call" },
@@ -877,9 +877,9 @@ var DaemonClient = class {
877
877
  asks(agentId) {
878
878
  return this.get(`/agents/${enc(agentId)}/asks`);
879
879
  }
880
- /** `loop` picks the asking loop when request ids repeat across loops. */
881
- answerAsk(agentId, requestId, answer, loop) {
882
- return this.post(`/agents/${enc(agentId)}/asks/${enc(requestId)}/respond`, { answer, ...loop ? { loop } : {} });
880
+ /** One answer per question (`null` = skipped). `loop` picks the asking loop when request ids repeat across loops. */
881
+ answerAsk(agentId, requestId, answers, loop) {
882
+ return this.post(`/agents/${enc(agentId)}/asks/${enc(requestId)}/respond`, { answers, ...loop ? { loop } : {} });
883
883
  }
884
884
  respondSuspend(agentId, resume) {
885
885
  return this.post(`/agents/${enc(agentId)}/suspend/respond`, { resume });
@@ -14,9 +14,10 @@ import {
14
14
  cjs
15
15
  } from "./chunk-SGUTYISF.mjs";
16
16
  import {
17
+ ask_form_exports,
17
18
  loop_parser_exports,
18
19
  owner_command_row_exports
19
- } from "./chunk-7JZXMDDF.mjs";
20
+ } from "./chunk-WPT3FAFX.mjs";
20
21
  import {
21
22
  require_react
22
23
  } from "./chunk-6GJ4QHRI.mjs";
@@ -140,7 +141,7 @@ function reconcileSelection(state, previousSummaries = {}, trackedFresh = !1) {
140
141
  }
141
142
 
142
143
  // src/main/tui/state/transcript.ts
143
- var MAX_TRANSCRIPT_ITEMS = 2e3, { parseLoopToDisplay } = cjs(loop_parser_exports), { attachmentText } = cjs(file_ref_exports);
144
+ var MAX_TRANSCRIPT_ITEMS = 2e3, { parseLoopToDisplay } = cjs(loop_parser_exports), { attachmentText } = cjs(file_ref_exports), { askQuestions, parseAskResult } = cjs(ask_form_exports);
144
145
  function withAttachments(text, attachments) {
145
146
  if (!Array.isArray(attachments) || attachments.length === 0) return text;
146
147
  let lines = attachments.map(attachmentText);
@@ -181,9 +182,9 @@ function unresolvedTasks(items) {
181
182
  }
182
183
  return out;
183
184
  }
184
- function askQuestion(input) {
185
- let q = input && typeof input == "object" ? input.question : void 0;
186
- return typeof q == "string" ? q : "";
185
+ function askKey(questions) {
186
+ return questions.map((q) => q.question.trim()).join(`
187
+ `);
187
188
  }
188
189
  function historyToItems(entries) {
189
190
  let items = [], toolsById = /* @__PURE__ */ new Map();
@@ -256,7 +257,7 @@ function mergeHistory(existing, incoming) {
256
257
  return live ? { ...item, status: live.status, result: live.result, completedAt: live.completedAt } : item;
257
258
  }), historyTools = /* @__PURE__ */ new Set(), historyCommands = new Set(history.flatMap((item) => item.kind === "context" && item.ownerCommand ? [item.ownerCommand.id] : [])), historyTexts = /* @__PURE__ */ new Map(), countText = (key) => historyTexts.set(key, (historyTexts.get(key) ?? 0) + 1);
258
259
  for (let item of history)
259
- item.kind === "tool" && item.toolUseId && historyTools.add(item.toolUseId), (item.kind === "user" || item.kind === "assistant" || item.kind === "thinking") && countText(`${item.kind}:${item.text.trim()}`), item.kind === "tool" && item.name === "ask" && countText(`ask:${askQuestion(item.input).trim()}`);
260
+ item.kind === "tool" && item.toolUseId && historyTools.add(item.toolUseId), (item.kind === "user" || item.kind === "assistant" || item.kind === "thinking") && countText(`${item.kind}:${item.text.trim()}`), item.kind === "tool" && item.name === "ask" && countText(`ask:${askKey(askQuestions(item.input))}`);
260
261
  let consume = (key) => {
261
262
  let n = historyTexts.get(key) ?? 0;
262
263
  return n <= 0 ? !1 : (historyTexts.set(key, n - 1), !0);
@@ -272,7 +273,7 @@ function mergeHistory(existing, incoming) {
272
273
  case "context":
273
274
  return !!item.ownerCommand && !historyCommands.has(item.ownerCommand.id);
274
275
  case "ask":
275
- return !consume(`ask:${item.question.trim()}`);
276
+ return !consume(`ask:${askKey(item.questions)}`);
276
277
  default:
277
278
  return !0;
278
279
  }
@@ -348,10 +349,10 @@ function applyEventToItems(items, event) {
348
349
  if (toolUseId && items.some((item) => item.kind === "tool" && item.toolUseId === toolUseId)) return items;
349
350
  let tool = { id: localId(), at, local: !0, kind: "tool", toolUseId, name: str(p.name) ?? "tool", input: p.input, status: "running" };
350
351
  if (tool.name === "ask") {
351
- let question = askQuestion(p.input).trim(), card = -1;
352
+ let key = askKey(askQuestions(p.input)), card = -1;
352
353
  for (let i = items.length - 1; i >= 0; i--) {
353
354
  let item = items[i];
354
- if (item.kind === "ask" && item.question.trim() === question) {
355
+ if (item.kind === "ask" && askKey(item.questions) === key) {
355
356
  card = i;
356
357
  break;
357
358
  }
@@ -402,13 +403,13 @@ function applyEventToItems(items, event) {
402
403
  }
403
404
  case "ask.requested": {
404
405
  let requestId = str(p.request_id);
405
- return !requestId || items.some((item) => item.kind === "ask" && item.requestId === requestId) ? items : cap([...finalizeStreaming(items), { id: localId(), at, local: !0, kind: "ask", requestId, question: str(p.question) ?? "", status: "pending" }]);
406
+ return !requestId || items.some((item) => item.kind === "ask" && item.requestId === requestId) ? items : cap([...finalizeStreaming(items), { id: localId(), at, local: !0, kind: "ask", requestId, questions: askQuestions({ questions: p.questions }), status: "pending" }]);
406
407
  }
407
408
  case "ask.resolved": {
408
409
  let requestId = str(p.request_id), index = items.findIndex((item) => item.kind === "ask" && item.requestId === requestId);
409
410
  if (index < 0) return items;
410
411
  let next = items.slice();
411
- return next[index] = { ...next[index], status: "answered", answer: str(p.preview) }, next;
412
+ return next[index] = { ...next[index], status: "answered" }, next;
412
413
  }
413
414
  case "agent.error": {
414
415
  let text = describeAgentError(p);
@@ -838,7 +839,7 @@ function applyEvent(state, event, log = !0) {
838
839
  let requestId = typeof p.request_id == "string" ? p.request_id : void 0;
839
840
  requestId && (next = patchAgent(next, agentId, (agent) => agent.pendingAsks.some((a) => sameAsk(a, loop, requestId)) ? agent : {
840
841
  ...agent,
841
- pendingAsks: [...agent.pendingAsks, { requestId, question: typeof p.question == "string" ? p.question : "", loop }]
842
+ pendingAsks: [...agent.pendingAsks, { requestId, question: String(p.question), questions: askQuestions({ questions: p.questions }), loop }]
842
843
  }));
843
844
  break;
844
845
  }
@@ -1399,9 +1400,9 @@ function createTuiStore(options) {
1399
1400
  }
1400
1401
  await actions.refreshHil(agentId);
1401
1402
  },
1402
- async answerAsk(agentId, requestId, answer, loop) {
1403
- let result = await run("Answer", (c) => c.answerAsk(agentId, requestId, answer, loop));
1404
- result && toast(result.answered ? "Answer sent" : "Ask was no longer pending", result.answered ? "success" : "warn"), await actions.refreshHil(agentId);
1403
+ async answerAsk(agentId, requestId, answers, loop) {
1404
+ let result = await run("Answer", (c) => c.answerAsk(agentId, requestId, answers, loop)), sent = answers.every((a) => a === null) ? "Skipped" : answers.length > 1 ? "Answers sent" : "Answer sent";
1405
+ result && toast(result.answered ? sent : "Ask was no longer pending", result.answered ? "success" : "warn"), await actions.refreshHil(agentId);
1405
1406
  },
1406
1407
  async respondSuspend(agentId, resume) {
1407
1408
  await run("Suspend", (c) => c.respondSuspend(agentId, resume)) && toast(resume ? "Resumed" : "Shutting down", "success"), await actions.refreshAgent(agentId);
@@ -1785,9 +1786,10 @@ export {
1785
1786
  stoppedLabel,
1786
1787
  stoppedError,
1787
1788
  findTracked,
1789
+ askQuestions,
1790
+ parseAskResult,
1788
1791
  emptyTranscript,
1789
1792
  isAsyncInput,
1790
- askQuestion,
1791
1793
  transcriptKey,
1792
1794
  isBusyState,
1793
1795
  parseServer,