{"version":3,"file":"web.mjs","names":["isRecord","asString","toolAskUserQuestion"],"sources":["../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/askUserText.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/AskUserQuestionToolMessage.tsx","../../src/types/askUserWire.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_lib/questionnaireDraft.ts","../../src/extensions/workspaces/sessions/(frontend)/tool/_components/QuestionnairePrompt.tsx","../../src/extensions/workspaces/sessions/(frontend)/tool/askUserQuestion.web.tsx","../../generated/web.ts"],"sourcesContent":["/**\n * Pure text shaping for the ask_user_question card, apart from React so the\n * unit suite covers it without a DOM. Args and details are wire JSON; the\n * shapes narrowed here mirror src/types/questionnaire.ts, which this browser\n * module may not import at runtime.\n */\n\ntype Args = Readonly<Record<string, unknown>>;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string {\n  return typeof value === 'string' ? value : '';\n}\n\nexport interface AskCallSummary {\n  /** \"N question(s)\", the TUI heading's purpose. */\n  count: string;\n  /** The question headers, comma separated; empty when none carry one. */\n  headers: string;\n}\n\n/** The call header: how many questions, and their headers in parentheses. */\nexport function askCallSummary(args: Args): AskCallSummary {\n  const questions = Array.isArray(args.questions) ? args.questions.filter(isRecord) : [];\n  const headers = questions.map((question) => asString(question.header)).filter(Boolean);\n  return {\n    count: `${questions.length} question${questions.length === 1 ? '' : 's'}`,\n    headers: headers.join(', '),\n  };\n}\n\nexport interface AnsweredQuestion {\n  question: string;\n  answer: string;\n}\n\nexport type AskResultView =\n  | { kind: 'cancelled' }\n  | { kind: 'voice'; prompt: string }\n  | { kind: 'answers'; answers: AnsweredQuestion[] }\n  | { kind: 'text'; text: string };\n\n/**\n * The result body the TUI's renderResult chooses: a cancelled glyph, the\n * voice prompt when the questionnaire was handed to voice, the answered\n * list, or the raw text when the tool attached no details (an error path).\n */\nexport function askResultView(details: unknown, output: string): AskResultView {\n  if (!isRecord(details) || !Array.isArray(details.answers)) return { kind: 'text', text: output };\n  if (details.cancelled === true) return { kind: 'cancelled' };\n  if (details.delivery === 'voice' && typeof details.voicePrompt === 'string') {\n    return { kind: 'voice', prompt: details.voicePrompt };\n  }\n  const answers = details.answers.filter(isRecord).map((answer) => ({\n    question: asString(answer.question),\n    answer:\n      answer.kind === 'multi'\n        ? (Array.isArray(answer.selected) ? answer.selected : []).filter((v) => typeof v === 'string').join(', ')\n        : asString(answer.answer),\n  }));\n  return { kind: 'answers', answers };\n}\n","import type { ToolMessageRenderProps } from '@agimon-ai/doompi-core/web';\nimport {\n  MessageItem,\n  MessageItemBody,\n  MessageItemHeader,\n  MessageItemStatus,\n  toolTone,\n} from '@agimon-ai/doompi-web-components';\n\nimport { askCallSummary, askResultView } from '../_lib/askUserText';\n\n/** The answered list, or the outcome that stood in for it. */\nfunction AskUserQuestionBody({ result, output, running, isError }: ToolMessageRenderProps) {\n  if (running) {\n    return (\n      <MessageItemBody data-testid=\"tool-result-ask_user_question\">\n        {/* The questionnaire is below, where the input usually is; saying only\n            \"waiting\" would leave the reader looking for it. */}\n        <MessageItemStatus tone=\"running\">answer below to continue</MessageItemStatus>\n      </MessageItemBody>\n    );\n  }\n  const view = askResultView(result?.details, output);\n  if (view.kind === 'cancelled') {\n    return (\n      <MessageItemBody data-testid=\"tool-result-ask_user_question\">\n        <MessageItemStatus tone=\"running\">cancelled</MessageItemStatus>\n      </MessageItemBody>\n    );\n  }\n  if (view.kind === 'voice') {\n    return (\n      <MessageItemBody data-testid=\"tool-result-ask_user_question\">\n        <pre className=\"whitespace-pre-wrap break-words text-doom-dim\">{view.prompt}</pre>\n      </MessageItemBody>\n    );\n  }\n  if (view.kind === 'text') {\n    return (\n      <MessageItemBody data-testid=\"tool-result-ask_user_question\">\n        {view.text ? (\n          <pre className={`whitespace-pre-wrap break-words ${isError ? 'text-doom-red' : 'text-doom-dim'}`}>\n            {view.text}\n          </pre>\n        ) : (\n          <MessageItemStatus tone={isError ? 'error' : 'ok'}>{isError ? 'failed' : 'submitted'}</MessageItemStatus>\n        )}\n      </MessageItemBody>\n    );\n  }\n  return (\n    <MessageItemBody data-testid=\"tool-result-ask_user_question\">\n      {view.answers.length === 0 ? (\n        <MessageItemStatus tone=\"ok\">submitted</MessageItemStatus>\n      ) : (\n        <ul className=\"flex flex-col gap-1\">\n          {view.answers.map((answer, index) => (\n            <li key={`${String(index)}-${answer.question}`} className=\"break-words\">\n              <span className=\"text-doom-green\">✓</span> <span className=\"text-doom-cyan\">{answer.question}</span>:{' '}\n              <span className=\"text-doom-dim\">{answer.answer}</span>\n            </li>\n          ))}\n        </ul>\n      )}\n    </MessageItemBody>\n  );\n}\n\n/**\n * The ask_user_question tool's timeline item: \"N question(s) (headers)\" in\n * the header, the web half of the TUI renderCall, and the answered list or\n * the outcome that stood in for it in the body. The live questionnaire\n * arrives as an extension-UI dialog; this is the transcript record.\n */\nexport function AskUserQuestionToolMessage(props: ToolMessageRenderProps) {\n  const summary = askCallSummary(props.args);\n  return (\n    <MessageItem tone={toolTone({ running: props.running, isError: props.isError })}>\n      <MessageItemHeader title=\"ask_user_question\">\n        <span data-testid=\"tool-call-ask_user_question\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n          <span className=\"text-doom-hi\">{summary.count}</span>\n          {summary.headers ? <span className=\"min-w-0 truncate text-doom-faint\">({summary.headers})</span> : null}\n        </span>\n      </MessageItemHeader>\n      <AskUserQuestionBody {...props} />\n    </MessageItem>\n  );\n}\n","import type { QuestionAnswer } from './questionnaire';\n\n/**\n * How a rich client answers the whole questionnaire in one reply.\n *\n * Pi's select request carries a title and a list of labels and nothing else,\n * so over RPC the questionnaire degrades into one request per question, asked\n * forwards only. A client that can already see the whole call (the cockpit\n * reads it from the tool's arguments) has no use for that: it renders every\n * question at once, lets the reader move between them, and replies once.\n *\n * The reply rides on the select response, because that is the only channel a\n * blocked extension has. An ordinary label is still an ordinary label, so a\n * client that knows nothing about this keeps the per-question flow exactly as\n * it was; only a response carrying the prefix is read as the whole set.\n *\n * It lives under src/types because that is the one server root a browser\n * bundle may read, so neither half can drift from the other.\n */\nexport const ANSWER_ENVELOPE_PREFIX = 'doom-ask-user/v1:';\n\n/** A response that claims the prefix but cannot be read is refused, never guessed at. */\nexport type AnswerEnvelopeRead =\n  | { readonly kind: 'absent' }\n  | { readonly kind: 'malformed' }\n  | { readonly kind: 'envelope'; readonly payload: unknown };\n\n/** Beyond this a response is not a questionnaire answer, whatever it claims. */\nexport const MAX_ENVELOPE_BYTES = 64 * 1024;\n\nexport function encodeAnswerEnvelope(answers: readonly QuestionAnswer[]): string {\n  return `${ANSWER_ENVELOPE_PREFIX}${JSON.stringify({ answers })}`;\n}\n\nexport function decodeAnswerEnvelope(value: string | undefined): AnswerEnvelopeRead {\n  if (value === undefined || !value.startsWith(ANSWER_ENVELOPE_PREFIX)) return { kind: 'absent' };\n  if (value.length > MAX_ENVELOPE_BYTES) return { kind: 'malformed' };\n  try {\n    return { kind: 'envelope', payload: JSON.parse(value.slice(ANSWER_ENVELOPE_PREFIX.length)) };\n  } catch {\n    // Unreadable JSON behind the prefix: the sender meant an envelope and got\n    // it wrong, which is a refusal rather than a label the model should see.\n    return { kind: 'malformed' };\n  }\n}\n","/**\n * The questionnaire the cockpit is filling in, as a value.\n *\n * Apart from React so the unit suite covers it without a DOM, the same way\n * askUserText.ts is. The questions come from the running tool call's\n * arguments, which are wire JSON: the shapes narrowed here mirror\n * src/schemas/questionnaire.ts, which a browser module may not import.\n *\n * Everything is held here until the reader submits, which is the whole point.\n * A step the reader goes back to still has what they chose, and the agent is\n * asked once rather than once per question.\n */\nimport type { QuestionAnswer } from '../../../../../../types/questionnaire';\n\n/** The row every question gets, so a reader always has a way past a bad set of options. */\nexport const CUSTOM_LABEL = 'Type something.';\n\nexport interface PromptOption {\n  label: string;\n  description: string;\n  preview?: string;\n}\n\nexport interface PromptQuestion {\n  question: string;\n  header: string;\n  multiSelect: boolean;\n  options: PromptOption[];\n}\n\nexport interface QuestionDraft {\n  /** Chosen option labels: at most one unless the question is multiSelect. */\n  selected: string[];\n  /** Text typed in place of an option; null while the reader has not typed any. */\n  custom: string | null;\n  notes: string;\n}\n\nexport type QuestionnaireDraft = readonly QuestionDraft[];\n\nconst EMPTY_ENTRY: QuestionDraft = { selected: [], custom: null, notes: '' };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asString(value: unknown): string {\n  return typeof value === 'string' ? value : '';\n}\n\nfunction readOption(value: unknown): PromptOption | null {\n  if (!isRecord(value)) return null;\n  const label = asString(value.label);\n  if (label === '') return null;\n  const preview = asString(value.preview);\n  return { label, description: asString(value.description), ...(preview === '' ? {} : { preview }) };\n}\n\n/** The questions as the tool was called with them; an unreadable call yields none. */\nexport function readPromptQuestions(args: Readonly<Record<string, unknown>>): PromptQuestion[] {\n  if (!Array.isArray(args.questions)) return [];\n  return args.questions.flatMap((entry) => {\n    if (!isRecord(entry)) return [];\n    const question = asString(entry.question);\n    if (question === '') return [];\n    const options = (Array.isArray(entry.options) ? entry.options : []).flatMap((option) => {\n      const read = readOption(option);\n      return read === null ? [] : [read];\n    });\n    return [{ question, header: asString(entry.header), multiSelect: entry.multiSelect === true, options }];\n  });\n}\n\nexport function emptyDraft(count: number): QuestionnaireDraft {\n  return Array.from({ length: count }, () => EMPTY_ENTRY);\n}\n\nexport function draftEntry(draft: QuestionnaireDraft, index: number): QuestionDraft {\n  return draft[index] ?? EMPTY_ENTRY;\n}\n\nfunction withEntry(draft: QuestionnaireDraft, index: number, patch: Partial<QuestionDraft>): QuestionnaireDraft {\n  if (index < 0 || index >= draft.length) return draft;\n  return draft.map((entry, position) => (position === index ? { ...entry, ...patch } : entry));\n}\n\n/**\n * Picks an option. A single-select question replaces its choice; a multiSelect\n * one toggles. Either way the typed answer is dropped, because an option and\n * free text are two answers to the same question and only one can be sent.\n */\nexport function chooseOption(\n  draft: QuestionnaireDraft,\n  index: number,\n  label: string,\n  multiSelect: boolean,\n): QuestionnaireDraft {\n  const current = draftEntry(draft, index).selected;\n  const selected = multiSelect\n    ? current.includes(label)\n      ? current.filter((entry) => entry !== label)\n      : [...current, label]\n    : [label];\n  return withEntry(draft, index, { selected, custom: null });\n}\n\n/** Types an answer instead of choosing one; null returns to the options. */\nexport function setCustom(draft: QuestionnaireDraft, index: number, custom: string | null): QuestionnaireDraft {\n  return withEntry(draft, index, { custom, selected: [] });\n}\n\nexport function setNotes(draft: QuestionnaireDraft, index: number, notes: string): QuestionnaireDraft {\n  return withEntry(draft, index, { notes });\n}\n\nexport function isAnswered(entry: QuestionDraft): boolean {\n  return entry.selected.length > 0 || (entry.custom !== null && entry.custom.trim() !== '');\n}\n\n/** The next question still needing an answer, searching after `from` first; -1 when none is left. */\nexport function nextUnanswered(draft: QuestionnaireDraft, from: number): number {\n  const after = draft.findIndex((entry, index) => index > from && !isAnswered(entry));\n  return after >= 0 ? after : draft.findIndex((entry) => !isAnswered(entry));\n}\n\nexport function isComplete(draft: QuestionnaireDraft): boolean {\n  return draft.length > 0 && draft.every(isAnswered);\n}\n\n/**\n * The answers as the tool reads them back. `preview` is left off: the session\n * half looks it up from the option it declared, so sending one here would only\n * be a claim it has to ignore.\n */\nexport function draftAnswers(questions: readonly PromptQuestion[], draft: QuestionnaireDraft): QuestionAnswer[] {\n  return questions.flatMap((question, index): QuestionAnswer[] => {\n    const entry = draftEntry(draft, index);\n    if (!isAnswered(entry)) return [];\n    const notes = entry.notes.trim();\n    const base = { questionIndex: index, question: question.question, ...(notes === '' ? {} : { notes }) };\n    if (entry.custom !== null && entry.custom.trim() !== '') {\n      return [{ ...base, kind: 'custom' as const, answer: entry.custom }];\n    }\n    if (question.multiSelect) {\n      return [{ ...base, kind: 'multi' as const, answer: null, selected: [...entry.selected] }];\n    }\n    return [{ ...base, kind: 'option' as const, answer: entry.selected[0] ?? null }];\n  });\n}\n","import type { ToolPromptRenderProps } from '@agimon-ai/doompi-core/web';\nimport {\n  Badge,\n  Button,\n  Kbd,\n  Markdown,\n  OptionLabel,\n  OptionRow,\n  SectionLabel,\n  Separator,\n  Textarea,\n} from '@agimon-ai/doompi-web-components';\nimport { type KeyboardEvent, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { encodeAnswerEnvelope } from '../../../../../../types/askUserWire';\nimport {\n  chooseOption,\n  CUSTOM_LABEL,\n  draftAnswers,\n  draftEntry,\n  emptyDraft,\n  isAnswered,\n  isComplete,\n  nextUnanswered,\n  type PromptQuestion,\n  type QuestionnaireDraft,\n  readPromptQuestions,\n  setCustom,\n  setNotes,\n} from '../_lib/questionnaireDraft';\n\n/** The step bar: which questions are answered, which one is open, and a click back to any of them. */\nfunction StepBar({\n  questions,\n  draft,\n  current,\n  onPick,\n}: {\n  questions: readonly PromptQuestion[];\n  draft: QuestionnaireDraft;\n  current: number;\n  onPick: (index: number) => void;\n}) {\n  return (\n    <div data-testid=\"questionnaire-steps\" className=\"flex min-w-0 flex-wrap items-center gap-1.5\">\n      {questions.map((question, index) => {\n        const answered = isAnswered(draftEntry(draft, index));\n        const active = index === current;\n        return (\n          <Button\n            key={question.question}\n            variant=\"ghost\"\n            size=\"xs\"\n            data-testid={`questionnaire-step-${String(index)}`}\n            data-step-state={active ? 'current' : answered ? 'answered' : 'pending'}\n            onClick={() => onPick(index)}\n            className={`gap-1.5 rounded-full px-2.5 py-0.5 text-xs ${\n              active\n                ? 'bg-doom-selected text-doom-on-selected'\n                : answered\n                  ? 'text-doom-green hover:bg-doom-panel'\n                  : 'text-doom-faint hover:bg-doom-panel'\n            }`}\n          >\n            <span aria-hidden>{answered ? '✓' : '○'}</span>\n            {question.header || `question ${String(index + 1)}`}\n          </Button>\n        );\n      })}\n    </div>\n  );\n}\n\n/**\n * The frame's shortcuts stop at a text field: while the caret is in one the\n * arrows and Enter belong to the field, so they are kept from bubbling. Enter\n * commits the field the way the terminal editor does, Shift+Enter writes a\n * newline, and Escape and Cmd/Ctrl+Enter still reach the frame.\n */\nfunction fieldKeys(commit: () => void) {\n  return (event: KeyboardEvent<HTMLTextAreaElement>): void => {\n    if (event.key === 'Escape') return;\n    if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) return;\n    event.stopPropagation();\n    if (event.key !== 'Enter' || event.shiftKey) return;\n    event.preventDefault();\n    commit();\n  };\n}\n\n/** One question's options, its typed-answer row, and the preview of whatever is focused. */\nfunction QuestionBody({\n  question,\n  index,\n  draft,\n  cursor,\n  onCursor,\n  onDraft,\n  onCommit,\n}: {\n  question: PromptQuestion;\n  index: number;\n  draft: QuestionnaireDraft;\n  cursor: number;\n  onCursor: (cursor: number) => void;\n  /** `settles` is true only for an edit that finishes the question, which is what may move on. */\n  onDraft: (draft: QuestionnaireDraft, settles: boolean) => void;\n  /** Enter in a text field: hands the keyboard back to the frame, settling the question or not. */\n  onCommit: (settles: boolean) => void;\n}) {\n  const entry = draftEntry(draft, index);\n  const typing = entry.custom !== null;\n  const preview = question.options[cursor]?.preview;\n  return (\n    <div className=\"flex min-w-0 flex-col gap-2\">\n      <p data-testid=\"questionnaire-question\" className=\"break-words text-base leading-relaxed text-doom-hi\">\n        {question.question}\n      </p>\n\n      <div role=\"listbox\" aria-label=\"options\" className=\"flex min-w-0 flex-col\">\n        {question.options.map((option, optionIndex) => {\n          const chosen = entry.selected.includes(option.label);\n          return (\n            <OptionRow\n              key={option.label}\n              active={optionIndex === cursor && !typing}\n              data-testid={`questionnaire-option-${String(optionIndex)}`}\n              data-chosen={chosen}\n              onMouseEnter={() => onCursor(optionIndex)}\n              // A multiSelect question is not finished by one click: moving on\n              // there would take the list away before a second option could be\n              // picked, which is the whole point of the question.\n              onClick={() =>\n                onDraft(chooseOption(draft, index, option.label, question.multiSelect), !question.multiSelect)\n              }\n              className=\"min-w-0 items-start gap-2.5 rounded-md px-2.5 py-1.5 whitespace-normal\"\n            >\n              <span className={`mt-[2px] shrink-0 text-sm ${chosen ? 'text-doom-green' : 'text-doom-faint'}`}>\n                {question.multiSelect ? (chosen ? '[x]' : '[ ]') : chosen ? '●' : '○'}\n              </span>\n              <OptionLabel className=\"min-w-0 flex flex-col gap-0.5 text-left\">\n                <span className={`break-words text-sm ${chosen ? 'text-doom-hi' : 'text-doom-text'}`}>\n                  {option.label}\n                </span>\n                {option.description ? (\n                  <span className=\"break-words text-sm leading-relaxed whitespace-normal text-doom-dim\">\n                    {option.description}\n                  </span>\n                ) : null}\n              </OptionLabel>\n            </OptionRow>\n          );\n        })}\n\n        <OptionRow\n          active={typing}\n          data-testid=\"questionnaire-option-custom\"\n          onClick={() => onDraft(setCustom(draft, index, entry.custom ?? ''), false)}\n          className=\"min-w-0 items-center gap-2.5 rounded-md px-2.5 py-1.5 whitespace-normal\"\n        >\n          <span className=\"shrink-0 text-sm text-doom-faint\">✎</span>\n          <OptionLabel className=\"min-w-0 text-sm whitespace-normal text-doom-text\">{CUSTOM_LABEL}</OptionLabel>\n        </OptionRow>\n      </div>\n\n      {typing ? (\n        <div className=\"flex flex-col gap-1\">\n          <SectionLabel>your answer</SectionLabel>\n          <Textarea\n            data-testid=\"questionnaire-custom\"\n            autoFocus\n            rows={2}\n            value={entry.custom ?? ''}\n            placeholder=\"type your answer…\"\n            onChange={(event) => onDraft(setCustom(draft, index, event.target.value), false)}\n            onKeyDown={fieldKeys(() => onCommit(true))}\n            className=\"text-sm\"\n          />\n        </div>\n      ) : null}\n\n      {preview ? (\n        <div data-testid=\"questionnaire-preview\" className=\"flex flex-col gap-1\">\n          <SectionLabel>preview</SectionLabel>\n          <div className=\"max-h-40 overflow-y-auto border-l-2 border-doom-edge-magenta pl-3 text-sm text-doom-dim\">\n            <Markdown text={preview} />\n          </div>\n        </div>\n      ) : null}\n\n      <div className=\"flex flex-col gap-1\">\n        <SectionLabel>note (optional)</SectionLabel>\n        <Textarea\n          data-testid=\"questionnaire-notes\"\n          rows={1}\n          value={entry.notes}\n          placeholder=\"anything the options do not cover\"\n          onChange={(event) => onDraft(setNotes(draft, index, event.target.value), false)}\n          onKeyDown={fieldKeys(() => onCommit(false))}\n          className=\"text-sm\"\n        />\n      </div>\n    </div>\n  );\n}\n\n/**\n * The questionnaire, standing in for the composer input while the agent waits.\n *\n * Every question is here at once, because the cockpit can see the whole call\n * even though the request it answers can only carry one question's labels.\n * That is what makes the step bar honest: nothing is sent until the reader\n * submits, so going back to a step is just moving a cursor, and the agent is\n * asked exactly once.\n */\nexport function QuestionnairePrompt({ args, dialog, answer, cancel }: ToolPromptRenderProps) {\n  const questions = useMemo(() => readPromptQuestions(args), [args]);\n  const [draft, setDraft] = useState<QuestionnaireDraft>(() => emptyDraft(questions.length));\n  const [current, setCurrent] = useState(0);\n  const [cursor, setCursor] = useState(0);\n  const frame = useRef<HTMLDivElement>(null);\n\n  // A new request is a new questionnaire; nothing from the last one carries over.\n  useEffect(() => {\n    // Remounting on dialog.id would be the alternative, but the prompt host owns the\n    // element, so the reset lives here.\n    // oxlint-disable-next-line react/set-state-in-effect\n    setDraft(emptyDraft(questions.length));\n    setCurrent(0);\n    setCursor(0);\n  }, [dialog.id, questions.length]);\n\n  // The agent is blocked on this, so it takes the keyboard the input had.\n  useEffect(() => frame.current?.focus(), [dialog.id]);\n\n  const question = questions[current];\n  const complete = isComplete(draft);\n\n  const move = (next: QuestionnaireDraft, answeredIndex: number, settles: boolean): void => {\n    setDraft(next);\n    // Follow the reader forwards the way the terminal does, but only on an\n    // edit that finished the question, and only while something is still\n    // unanswered: jumping after the last answer would take the step they just\n    // finished off the screen before they could look at it.\n    if (!settles) return;\n    const pending = nextUnanswered(next, answeredIndex);\n    if (pending >= 0 && pending !== answeredIndex) {\n      setCurrent(pending);\n      setCursor(0);\n    }\n  };\n\n  const show = (index: number): void => {\n    setCurrent(index);\n    setCursor(0);\n  };\n\n  const submit = (): void => {\n    if (!complete) return;\n    answer(encodeAnswerEnvelope(draftAnswers(questions, draft)));\n  };\n\n  if (question === undefined) {\n    return (\n      <div className=\"flex items-center justify-between gap-3 px-3.5 py-3\">\n        <span data-testid=\"questionnaire-empty\" className=\"text-sm text-doom-dim\">\n          the agent asked something this cockpit could not read\n        </span>\n        <Button variant=\"outline\" size=\"sm\" data-testid=\"questionnaire-cancel\" onClick={cancel}>\n          cancel\n        </Button>\n      </div>\n    );\n  }\n\n  const typing = draftEntry(draft, current).custom !== null;\n  return (\n    // The keys belong to the frame rather than to any row, so the reader can\n    // move between questions and options without tabbing through both.\n    <div\n      ref={frame}\n      tabIndex={-1}\n      data-testid=\"questionnaire\"\n      className=\"flex min-w-0 flex-col overflow-hidden outline-none\"\n      onKeyDown={(event) => {\n        if (event.key === 'Escape') {\n          event.preventDefault();\n          cancel();\n          return;\n        }\n        if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {\n          event.preventDefault();\n          submit();\n          return;\n        }\n        // While typing an answer the arrows belong to the textarea.\n        if (typing) return;\n        if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n          event.preventDefault();\n          const count = question.options.length;\n          setCursor((event.key === 'ArrowDown' ? cursor + 1 : cursor - 1 + count) % count);\n          return;\n        }\n        if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {\n          event.preventDefault();\n          const count = questions.length;\n          show((event.key === 'ArrowRight' ? current + 1 : current - 1 + count) % count);\n          return;\n        }\n        if (event.key === 'Enter') {\n          event.preventDefault();\n          const option = question.options[cursor];\n          if (option === undefined) {\n            submit();\n            return;\n          }\n          move(chooseOption(draft, current, option.label, question.multiSelect), current, !question.multiSelect);\n        }\n      }}\n    >\n      <div className=\"flex min-w-0 flex-wrap items-center gap-2 px-3 pt-2.5 pb-2 sm:gap-3 sm:px-3.5\">\n        <StepBar questions={questions} draft={draft} current={current} onPick={show} />\n        <span className=\"min-w-0 flex-1\" />\n        <Badge size=\"xs\" className=\"shrink-0 border-transparent bg-doom-panel py-0.5 text-2xs text-doom-dim\">\n          question {current + 1} / {questions.length}\n        </Badge>\n      </div>\n\n      <Separator />\n\n      <div className=\"max-h-[46vh] min-w-0 overflow-x-hidden overflow-y-auto px-3 py-3 sm:px-3.5\">\n        <QuestionBody\n          question={question}\n          index={current}\n          draft={draft}\n          cursor={cursor}\n          onCursor={setCursor}\n          onDraft={(next, settles) => move(next, current, settles)}\n          onCommit={(settles) => {\n            frame.current?.focus();\n            move(draft, current, settles);\n          }}\n        />\n      </div>\n\n      <Separator />\n\n      <div className=\"flex min-w-0 items-center gap-2 px-3 py-2 sm:px-3.5\">\n        <span data-testid=\"questionnaire-hint\" className=\"min-w-0 truncate text-xs text-doom-faint max-sm:hidden\">\n          <Kbd>←→</Kbd> questions · <Kbd>↑↓</Kbd> options · <Kbd>enter</Kbd>{' '}\n          {question.multiSelect ? 'toggles' : 'selects'} · <Kbd>esc</Kbd> cancels\n        </span>\n        <span className=\"flex-1\" />\n        <Button variant=\"outline\" size=\"md\" data-testid=\"questionnaire-cancel\" onClick={cancel}>\n          cancel\n        </Button>\n        <Button\n          variant=\"primary\"\n          size=\"md\"\n          data-testid=\"questionnaire-submit\"\n          onClick={submit}\n          disabled={!complete}\n          title={complete ? 'send every answer to the agent' : 'answer every question first'}\n          className=\"shrink-0 px-3 max-sm:max-w-32 max-sm:truncate sm:px-3.5\"\n        >\n          submit answers\n        </Button>\n      </div>\n    </div>\n  );\n}\n","import { defineToolRenderer } from '@agimon-ai/doompi-core/web';\n\nimport { AskUserQuestionToolMessage } from './_components/AskUserQuestionToolMessage';\nimport { QuestionnairePrompt } from './_components/QuestionnairePrompt';\nimport { readPromptQuestions } from './_lib/questionnaireDraft';\n\nexport default defineToolRenderer({\n  message: AskUserQuestionToolMessage,\n  prompt: {\n    claims: (dialog, args) =>\n      dialog.method === 'select' &&\n      readPromptQuestions(args).some((question) => dialog.title.startsWith(question.question)),\n    component: QuestionnairePrompt,\n  },\n});\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { defineWebPlugin, type WebPluginContributions } from '@agimon-ai/doompi-core/web';\n\nimport toolAskUserQuestion from '../src/extensions/workspaces/sessions/(frontend)/tool/askUserQuestion.web';\n\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n  (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const webPlugin = defineWebPlugin({\n  id: 'ask-user',\n  session: {\n    get toolRenderers(): WebPluginContributions['toolRenderers'] { return [via({ tools: ['ask_user_question'] }, toolAskUserQuestion)]; },\n  },\n});\n"],"mappings":"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"}