{"version":3,"file":"validationService-CS-VJaMo.mjs","names":[],"sources":["../src/constants/questionnaire.ts","../src/constants/tool.ts","../src/services/questionnaireCoordinator/index.ts","../src/schemas/questionnaire.ts","../src/services/responseService/index.ts","../src/types/askUserWire.ts","../src/services/answerEnvelope/index.ts","../src/services/questionnaireService/index.ts","../src/services/validationService/index.ts"],"sourcesContent":["export const MAX_QUESTIONS = 4;\nexport const MIN_OPTIONS = 2;\nexport const MAX_OPTIONS = 4;\nexport const MAX_HEADER_LENGTH = 16;\nexport const MAX_LABEL_LENGTH = 60;\n\nexport const RESERVED_LABELS = ['Other', 'Type something.', 'Next'] as const;\n","import { MAX_OPTIONS, MAX_QUESTIONS, MIN_OPTIONS } from './questionnaire';\nexport const ASK_USER_QUESTION_TOOL_NAME = 'ask_user_question';\n\nexport const DEFAULT_PROMPT_SNIPPET = `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`;\nexport const DEFAULT_PROMPT_GUIDELINES = [\n  `Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions, you can ask up to ${MAX_QUESTIONS} questions per invocation.`,\n  `Each question MUST have ${MIN_OPTIONS}-${MAX_OPTIONS} options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer via the automatically appended \"Type something.\" row on every question, or press Esc to abandon the questionnaire. Do NOT author \"Other\" or \"Type something.\" labels yourself, reserved labels are rejected at runtime.`,\n  'Set multiSelect: true when multiple answers are valid. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs), single-select only. If you recommend a specific option, make that the first option and append \"(Recommended)\" to its label.',\n  'Do not stack multiple ask_user_question calls back-to-back, group all clarifying questions into one invocation.',\n];\n\nexport const ERROR_NO_UI = 'Error: UI not available (running in non-interactive mode)';\nexport const ERROR_NO_CUSTOM_UI =\n  'Error: this client cannot render the questionnaire (custom UI is unavailable). The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead, without using this tool.';\nexport const ERROR_SESSION_LOAD_FAILED =\n  'Error: the questionnaire UI failed to load. The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead.';\nexport const ERROR_SESSION_INACTIVE = 'Error: the questionnaire session is no longer active.';\n","import type { QuestionnaireResult } from '../../types/questionnaire';\n\nexport interface QuestionnaireRunContext {\n  signal: AbortSignal;\n  reportProgress: (result: QuestionnaireResult) => void;\n}\n\nexport type QuestionnaireRunner = (context: QuestionnaireRunContext) => Promise<QuestionnaireResult>;\n\ninterface QueueEntry {\n  id: number;\n  runner: QuestionnaireRunner;\n  controller: AbortController;\n  externalSignal?: AbortSignal;\n  removeAbortListener?: () => void;\n  latestAnswers: QuestionnaireResult['answers'];\n  state: 'queued' | 'active' | 'settled';\n  resolve(result: QuestionnaireResult): void;\n  reject(error: unknown): void;\n}\n\nfunction cancelledResult(entry: QueueEntry): QuestionnaireResult {\n  return {\n    answers: entry.latestAnswers.map((answer) => ({ ...answer })),\n    cancelled: true,\n  };\n}\n\nexport class QuestionnaireCoordinator {\n  private readonly pending: QueueEntry[] = [];\n  private readonly running = new Set<Promise<QuestionnaireResult>>();\n  private active: QueueEntry | undefined;\n  private nextRequestId = 1;\n  private accepting = true;\n\n  enqueue(runner: QuestionnaireRunner, externalSignal?: AbortSignal): Promise<QuestionnaireResult> {\n    if (!this.accepting || externalSignal?.aborted) {\n      return Promise.resolve({ answers: [], cancelled: true });\n    }\n\n    return new Promise<QuestionnaireResult>((resolve, reject) => {\n      const entry: QueueEntry = {\n        id: this.nextRequestId,\n        runner,\n        controller: new AbortController(),\n        ...(externalSignal ? { externalSignal } : {}),\n        latestAnswers: [],\n        state: 'queued',\n        resolve,\n        reject,\n      };\n      this.nextRequestId += 1;\n      const abort = (): void => this.cancel(entry);\n      externalSignal?.addEventListener('abort', abort, { once: true });\n      entry.removeAbortListener = () => externalSignal?.removeEventListener('abort', abort);\n      this.pending.push(entry);\n      this.startNext();\n    });\n  }\n\n  shutdown(): void {\n    if (!this.accepting && !this.active && this.pending.length === 0) return;\n    this.accepting = false;\n    if (this.active) this.cancel(this.active);\n    while (this.pending.length > 0) {\n      const entry = this.pending[0];\n      if (!entry) break;\n      this.cancel(entry);\n    }\n  }\n\n  /** Waits for every started presenter to observe cancellation and finish. */\n  async waitForIdle(): Promise<void> {\n    while (this.running.size > 0) await Promise.allSettled(this.running);\n  }\n\n  private startNext(): void {\n    if (!this.accepting || this.active) return;\n    const entry = this.pending.shift();\n    if (!entry) return;\n    entry.state = 'active';\n    this.active = entry;\n    const reportProgress = (result: QuestionnaireResult): void => {\n      if (this.active?.id !== entry.id || entry.state !== 'active') return;\n      entry.latestAnswers = result.answers.map((answer) => ({ ...answer }));\n    };\n    let operation: Promise<QuestionnaireResult>;\n    try {\n      operation = entry.runner({ signal: entry.controller.signal, reportProgress });\n    } catch (error) {\n      operation = Promise.reject(error);\n    }\n    this.running.add(operation);\n    void operation\n      .then(\n        (result) => this.complete(entry, result),\n        (error: unknown) => this.fail(entry, error),\n      )\n      .finally(() => this.running.delete(operation));\n  }\n\n  private complete(entry: QueueEntry, result: QuestionnaireResult): void {\n    if (this.active?.id !== entry.id || entry.state !== 'active') return;\n    this.releaseActive(entry);\n    entry.resolve(result);\n    this.startNext();\n  }\n\n  private fail(entry: QueueEntry, error: unknown): void {\n    if (this.active?.id !== entry.id || entry.state !== 'active') return;\n    this.releaseActive(entry);\n    entry.reject(error);\n    this.startNext();\n  }\n\n  private cancel(entry: QueueEntry): void {\n    if (entry.state === 'settled') return;\n    if (entry.state === 'queued') {\n      const index = this.pending.indexOf(entry);\n      this.pending.splice(index, 1);\n      entry.state = 'settled';\n      entry.removeAbortListener?.();\n      entry.controller.abort();\n      entry.resolve(cancelledResult(entry));\n      return;\n    }\n\n    const result = cancelledResult(entry);\n    this.releaseActive(entry);\n    entry.controller.abort();\n    entry.resolve(result);\n    this.startNext();\n  }\n\n  private releaseActive(entry: QueueEntry): void {\n    entry.state = 'settled';\n    entry.removeAbortListener?.();\n    this.active = undefined;\n  }\n}\n","import { type Static, Type } from 'typebox';\n\nimport {\n  MAX_QUESTIONS,\n  MIN_OPTIONS,\n  MAX_OPTIONS,\n  MAX_HEADER_LENGTH,\n  MAX_LABEL_LENGTH,\n} from '../constants/questionnaire';\n\nexport const OptionSchema = Type.Object(\n  {\n    label: Type.String({\n      maxLength: MAX_LABEL_LENGTH,\n      description: 'MAX 60 CHARACTERS, hard limit. Concise option label shown to the user, ideally 1-5 words.',\n    }),\n    description: Type.String({\n      description: 'Explanation of what the option means and its important trade-offs.',\n    }),\n    preview: Type.Optional(\n      Type.String({ description: 'Optional markdown preview rendered while this option is focused.' }),\n    ),\n  },\n  { additionalProperties: false },\n);\n\nexport const QuestionSchema = Type.Object(\n  {\n    question: Type.String({ description: 'The complete, clear question to ask the user.' }),\n    header: Type.String({\n      maxLength: MAX_HEADER_LENGTH,\n      description: 'MAX 16 CHARACTERS, short label used when navigating several questions.',\n    }),\n    options: Type.Array(OptionSchema, {\n      minItems: MIN_OPTIONS,\n      maxItems: MAX_OPTIONS,\n      description: 'The 2-4 choices offered for this question.',\n    }),\n    multiSelect: Type.Optional(\n      Type.Boolean({ description: 'Allow more than one option to be selected.', default: false }),\n    ),\n  },\n  { additionalProperties: false },\n);\n\nexport const QuestionParamsSchema = Type.Object(\n  {\n    questions: Type.Array(QuestionSchema, {\n      minItems: 1,\n      maxItems: MAX_QUESTIONS,\n      description: 'Questions to ask the user (1-4 questions).',\n    }),\n  },\n  { additionalProperties: false },\n);\n\nexport type OptionData = Static<typeof OptionSchema>;\nexport type QuestionData = Static<typeof QuestionSchema>;\nexport type QuestionParams = Static<typeof QuestionParamsSchema>;\n","import type { QuestionParams } from '../../schemas/questionnaire';\nimport type { QuestionAnswer, QuestionnaireResult, ToolTextResult } from '../../types/questionnaire';\n\nexport const DECLINE_MESSAGE = 'User declined to answer questions';\nexport const ENVELOPE_PREFIX = 'User has answered your questions:';\nexport const ENVELOPE_SUFFIX = \"You can now continue with the user's answers in mind.\";\nexport const NO_INPUT_PLACEHOLDER = '(no input)';\n\nexport function buildToolResult(text: string, details: QuestionnaireResult): ToolTextResult {\n  return { content: [{ type: 'text', text }], details };\n}\n\nexport function formatAnswer(answer: QuestionAnswer): string {\n  if (answer.kind === 'multi') {\n    return answer.selected && answer.selected.length > 0 ? answer.selected.join(', ') : NO_INPUT_PLACEHOLDER;\n  }\n  return answer.answer && answer.answer.length > 0 ? answer.answer : NO_INPUT_PLACEHOLDER;\n}\n\nexport function buildAnswerSegment(answer: QuestionAnswer): string {\n  const sections = [`\"${answer.question}\"=\"${formatAnswer(answer)}\"`];\n  if (answer.preview) sections.push(`selected preview: ${answer.preview}`);\n  if (answer.notes) sections.push(`user notes: ${answer.notes}`);\n  return `${sections.join('. ')}.`;\n}\n\nexport function buildQuestionnaireResponse(\n  result: QuestionnaireResult | null | undefined,\n  params: QuestionParams,\n): ToolTextResult {\n  if (!result || result.cancelled) {\n    return buildToolResult(DECLINE_MESSAGE, { answers: result?.answers ?? [], cancelled: true });\n  }\n\n  const segments = params.questions.flatMap((_question, index) => {\n    const answer = result.answers.find((candidate) => candidate.questionIndex === index);\n    return answer ? [buildAnswerSegment(answer)] : [];\n  });\n  if (segments.length === 0) {\n    return buildToolResult(DECLINE_MESSAGE, { answers: result.answers, cancelled: true });\n  }\n\n  return buildToolResult(`${ENVELOPE_PREFIX} ${segments.join(' ')} ${ENVELOPE_SUFFIX}`, result);\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","import type { QuestionData, QuestionParams } from '../../schemas/questionnaire';\nimport type { AnswerKind, QuestionAnswer } from '../../types/questionnaire';\n\n/**\n * Read a whole-questionnaire answer envelope back against the questions it claims to answer.\n *\n * The envelope arrives on a select response, which is reader input rather than\n * anything the tool authored, so nothing in it is taken on trust: an index has\n * to name a question that was asked, a chosen label has to be one that\n * question offered, and free text is capped. An envelope that fails is refused\n * rather than repaired, because a half-read answer set reaches the model as\n * the reader's decision and there is no way to tell from there that part of it\n * was invented.\n *\n * `preview` is the clearest case: it is read from the option the tool\n * declared, never from the wire, so the model cannot be shown a preview that\n * was never offered.\n *\n * Host-neutral by construction: the questions and the payload both arrive as\n * arguments, so the same arguments always produce the same result.\n */\nexport const MAX_ANSWER_TEXT_LENGTH = 4000;\n\nexport type AnswerEnvelopeResult =\n  | { readonly ok: true; readonly answers: QuestionAnswer[] }\n  | { readonly ok: false; readonly reason: string };\n\nconst ANSWER_KINDS: readonly string[] = ['option', 'custom', 'multi'] satisfies readonly AnswerKind[];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction optionalNote(value: unknown): string | undefined {\n  if (typeof value !== 'string') return undefined;\n  const trimmed = value.trim();\n  return trimmed === '' ? undefined : trimmed.slice(0, MAX_ANSWER_TEXT_LENGTH);\n}\n\n/** One answer, rebuilt from the question it names rather than copied off the wire. */\nfunction readAnswer(entry: unknown, params: QuestionParams, seen: Set<number>): QuestionAnswer | string {\n  if (!isRecord(entry)) return 'an answer was not an object';\n  const questionIndex = entry.questionIndex;\n  if (typeof questionIndex !== 'number' || !Number.isInteger(questionIndex)) {\n    return 'an answer carried no question index';\n  }\n  const question: QuestionData | undefined = params.questions[questionIndex];\n  if (question === undefined) return `no question ${String(questionIndex)} was asked`;\n  if (seen.has(questionIndex)) return `question ${String(questionIndex)} was answered twice`;\n  seen.add(questionIndex);\n\n  const kind = entry.kind;\n  if (typeof kind !== 'string' || !ANSWER_KINDS.includes(kind)) {\n    return `question ${String(questionIndex)} carried an unknown answer kind`;\n  }\n  const notes = optionalNote(entry.notes);\n  const base = { questionIndex, question: question.question, ...(notes === undefined ? {} : { notes }) };\n\n  if (kind === 'multi') {\n    const selected = entry.selected;\n    if (!Array.isArray(selected)) return `question ${String(questionIndex)} selected nothing readable`;\n    const labels = selected.filter((label): label is string => typeof label === 'string');\n    if (labels.length !== selected.length) return `question ${String(questionIndex)} selected a non-label`;\n    if (labels.some((label) => !question.options.some((option) => option.label === label))) {\n      return `question ${String(questionIndex)} selected an option it was not offered`;\n    }\n    return { ...base, kind: 'multi', answer: null, selected: [...new Set(labels)] };\n  }\n\n  if (kind === 'option') {\n    const label = entry.answer;\n    const chosen = typeof label === 'string' ? question.options.find((option) => option.label === label) : undefined;\n    if (chosen === undefined) return `question ${String(questionIndex)} chose an option it was not offered`;\n    // From the option the tool declared, so a preview the reader never saw\n    // cannot be put in the model's mouth.\n    return { ...base, kind: 'option', answer: chosen.label, ...(chosen.preview ? { preview: chosen.preview } : {}) };\n  }\n\n  const typed = entry.answer;\n  if (typeof typed !== 'string') return `question ${String(questionIndex)} typed nothing readable`;\n  return { ...base, kind: 'custom', answer: typed.slice(0, MAX_ANSWER_TEXT_LENGTH) };\n}\n\nexport function readAnswerEnvelope(payload: unknown, params: QuestionParams): AnswerEnvelopeResult {\n  if (!isRecord(payload) || !Array.isArray(payload.answers)) return { ok: false, reason: 'no answers were carried' };\n  if (payload.answers.length === 0) return { ok: false, reason: 'no answers were carried' };\n  if (payload.answers.length > params.questions.length) return { ok: false, reason: 'more answers than questions' };\n\n  const seen = new Set<number>();\n  const answers: QuestionAnswer[] = [];\n  for (const entry of payload.answers) {\n    const answer = readAnswer(entry, params, seen);\n    if (typeof answer === 'string') return { ok: false, reason: answer };\n    answers.push(answer);\n  }\n  // The model reads them in the order it asked, whatever order they arrived in.\n  answers.sort((left, right) => left.questionIndex - right.questionIndex);\n  return { ok: true, answers };\n}\n","import type { QuestionData, QuestionParams } from '../../schemas/questionnaire';\nimport { decodeAnswerEnvelope } from '../../types/askUserWire';\nimport type { QuestionAnswer, QuestionnaireResult } from '../../types/questionnaire';\nimport { readAnswerEnvelope } from '../answerEnvelope';\n\nconst CUSTOM_LABEL = 'Type something.';\nconst DONE_LABEL = 'Next';\n\nexport interface QuestionnaireInteraction {\n  select(title: string, options: readonly string[], signal?: AbortSignal): Promise<string | undefined>;\n  input(title: string, placeholder: string, signal?: AbortSignal): Promise<string | undefined>;\n}\n\nfunction cancelled(answers: QuestionAnswer[]): QuestionnaireResult {\n  return { answers, cancelled: true };\n}\n\n/**\n * What a rich client replied, when it replied with the whole questionnaire.\n *\n * A client that can see the whole call answers every question at once rather\n * than walking this loop, so any select response may be an envelope instead of\n * a label. A response that is not one leaves the loop exactly as it was, which\n * is what keeps a plain RPC client working unchanged.\n */\ninterface EnvelopeSink {\n  params: QuestionParams;\n  /** Set once a select response carried the whole questionnaire; the loop stops and returns it. */\n  result?: QuestionnaireResult;\n}\n\n/** True when this response was the whole set, in which case the sink now holds the answer. */\nfunction takeEnvelope(sink: EnvelopeSink, selected: string | undefined): boolean {\n  const read = decodeAnswerEnvelope(selected);\n  if (read.kind === 'absent') return false;\n  if (read.kind === 'malformed') {\n    sink.result = { answers: [], cancelled: true, error: 'malformed_answers' };\n    return true;\n  }\n  const envelope = readAnswerEnvelope(read.payload, sink.params);\n  sink.result = envelope.ok\n    ? { answers: envelope.answers, cancelled: false }\n    : { answers: [], cancelled: true, error: 'malformed_answers' };\n  return true;\n}\n\nasync function askForCustomAnswer(\n  interaction: QuestionnaireInteraction,\n  question: QuestionData,\n  questionIndex: number,\n  signal?: AbortSignal,\n): Promise<QuestionAnswer | undefined> {\n  const answer = await interaction.input(question.question, 'Type your answer', signal);\n  if (answer === undefined) return undefined;\n  return {\n    questionIndex,\n    question: question.question,\n    kind: 'custom',\n    answer,\n  };\n}\n\nasync function askSingleChoice(\n  interaction: QuestionnaireInteraction,\n  question: QuestionData,\n  questionIndex: number,\n  sink: EnvelopeSink,\n  signal?: AbortSignal,\n): Promise<QuestionAnswer | undefined> {\n  const labels = [...question.options.map((option) => option.label), CUSTOM_LABEL];\n  const selected = await interaction.select(question.question, labels, signal);\n  if (selected === undefined) return undefined;\n  if (takeEnvelope(sink, selected)) return undefined;\n  if (selected === CUSTOM_LABEL) {\n    return askForCustomAnswer(interaction, question, questionIndex, signal);\n  }\n  const option = question.options.find((candidate) => candidate.label === selected);\n  if (!option) return undefined;\n  return {\n    questionIndex,\n    question: question.question,\n    kind: 'option',\n    answer: option.label,\n    ...(option.preview ? { preview: option.preview } : {}),\n  };\n}\n\nfunction multiChoiceLabels(question: QuestionData, selected: ReadonlySet<string>): string[] {\n  const options = question.options.map((option) => {\n    const marker = selected.has(option.label) ? '[x]' : '[ ]';\n    return `${marker} ${option.label}`;\n  });\n  return [...options, CUSTOM_LABEL, DONE_LABEL];\n}\n\nfunction stripSelectionMarker(value: string): string {\n  return value.replace(/^\\[[ x]\\]\\s/u, '');\n}\n\nasync function askMultipleChoice(\n  interaction: QuestionnaireInteraction,\n  question: QuestionData,\n  questionIndex: number,\n  sink: EnvelopeSink,\n  signal?: AbortSignal,\n): Promise<QuestionAnswer | undefined> {\n  const selected = new Set<string>();\n  while (!signal?.aborted) {\n    const choice = await interaction.select(\n      `${question.question} (choose options, then Next)`,\n      multiChoiceLabels(question, selected),\n      signal,\n    );\n    if (choice === undefined) return undefined;\n    if (takeEnvelope(sink, choice)) return undefined;\n    if (choice === CUSTOM_LABEL) {\n      return askForCustomAnswer(interaction, question, questionIndex, signal);\n    }\n    if (choice === DONE_LABEL) {\n      return {\n        questionIndex,\n        question: question.question,\n        kind: 'multi',\n        answer: null,\n        selected: [...selected],\n      };\n    }\n    const label = stripSelectionMarker(choice);\n    if (!question.options.some((option) => option.label === label)) continue;\n    if (selected.has(label)) selected.delete(label);\n    else selected.add(label);\n  }\n  return undefined;\n}\n\nexport async function runQuestionnaire(\n  interaction: QuestionnaireInteraction,\n  params: QuestionParams,\n  signal?: AbortSignal,\n  reportProgress?: (result: QuestionnaireResult) => void,\n): Promise<QuestionnaireResult> {\n  const answers: QuestionAnswer[] = [];\n  const sink: EnvelopeSink = { params };\n  for (let questionIndex = 0; questionIndex < params.questions.length; questionIndex += 1) {\n    if (signal?.aborted) return cancelled(answers);\n    const question = params.questions[questionIndex];\n    if (!question) continue;\n    const answer = question.multiSelect\n      ? await askMultipleChoice(interaction, question, questionIndex, sink, signal)\n      : await askSingleChoice(interaction, question, questionIndex, sink, signal);\n    // A client that answered everything at once ends the walk here; the loop\n    // is only how a client that can see one question at a time gets through.\n    if (sink.result !== undefined) {\n      const whole = sink.result;\n      if (!whole.cancelled) {\n        reportProgress?.({ ...whole, answers: whole.answers.map((item) => ({ ...item })) });\n      }\n      return whole;\n    }\n    if (!answer) return cancelled(answers);\n    answers.push(answer);\n    reportProgress?.({ answers: answers.map((item) => ({ ...item })), cancelled: false });\n  }\n  return { answers, cancelled: false };\n}\n","import { MAX_QUESTIONS, MIN_OPTIONS, RESERVED_LABELS } from '../../constants/questionnaire';\nimport { type QuestionParams } from '../../schemas/questionnaire';\nimport type { QuestionnaireError } from '../../types/questionnaire';\n\nexport const ERROR_NO_QUESTIONS = 'Error: At least one question is required';\nexport const ERROR_TOO_MANY_QUESTIONS = `Error: At most ${MAX_QUESTIONS} questions are allowed per invocation`;\nexport const ERROR_DUPLICATE_QUESTION = 'Error: Question text must be unique within an invocation';\nexport const ERROR_TOO_FEW_OPTIONS = `Error: Each question requires at least ${MIN_OPTIONS} options`;\nexport const ERROR_RESERVED_LABEL = `Error: Option label is reserved (${RESERVED_LABELS.join(', ')})`;\nexport const ERROR_DUPLICATE_OPTION_LABEL = 'Error: Option labels must be unique within a question';\n\nexport type ValidationResult = { ok: true } | { ok: false; error: QuestionnaireError; message: string };\n\nconst reservedLabels = new Set<string>(RESERVED_LABELS);\n\nexport function validateQuestionnaire(params: QuestionParams): ValidationResult {\n  if (params.questions.length === 0) {\n    return { ok: false, error: 'no_questions', message: ERROR_NO_QUESTIONS };\n  }\n  if (params.questions.length > MAX_QUESTIONS) {\n    return { ok: false, error: 'too_many_questions', message: ERROR_TOO_MANY_QUESTIONS };\n  }\n\n  const seenQuestions = new Set<string>();\n  for (const question of params.questions) {\n    if (seenQuestions.has(question.question)) {\n      return { ok: false, error: 'duplicate_question', message: ERROR_DUPLICATE_QUESTION };\n    }\n    seenQuestions.add(question.question);\n  }\n\n  for (const question of params.questions) {\n    if (question.options.length < MIN_OPTIONS) {\n      return { ok: false, error: 'empty_options', message: ERROR_TOO_FEW_OPTIONS };\n    }\n    const seenLabels = new Set<string>();\n    for (const option of question.options) {\n      if (reservedLabels.has(option.label)) {\n        return { ok: false, error: 'reserved_label', message: ERROR_RESERVED_LABEL };\n      }\n      if (seenLabels.has(option.label)) {\n        return {\n          ok: false,\n          error: 'duplicate_option_label',\n          message: ERROR_DUPLICATE_OPTION_LABEL,\n        };\n      }\n      seenLabels.add(option.label);\n    }\n  }\n\n  return { ok: true };\n}\n"],"mappings":"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"}