{"version":3,"file":"AskUser.mjs","sources":["../../../src/tools/AskUser.ts"],"sourcesContent":["import { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport { Constants } from '@/common';\n\nexport const AskUserToolName = Constants.ASK_USER;\n\nexport const AskUserDescription =\n  'Ask the user clarification questions with structured options before taking action. ' +\n  'For a single key question, use question+options. For multiple related questions, use steps[] (max 3 steps). ' +\n  'Each step can be single-select or multi-select. After receiving answers, proceed immediately. ' +\n  'BEFORE calling this tool, ALWAYS emit one short sentence of plain-text output (one line, <= 20 words) ' +\n  'explaining why you need more information — e.g. \"I need a few details to help with that.\". ' +\n  'NEVER include an option like \"I\\'ll give it myself\", \"Other\", \"Something else\", \"Custom\", \"None of the above\", ' +\n  'or any equivalent free-text escape hatch — the UI already provides a dedicated \"Something else\" button ' +\n  'for free-text responses. Only list concrete, mutually-exclusive choices.';\n\nconst AskUserOptionSchema = z.object({\n  label: z.string().describe('Short display label'),\n  value: z.string().describe('Machine-readable value returned when selected'),\n  description: z.string().optional().describe('Optional longer description'),\n});\n\nexport const AskUserStepSchema = z.object({\n  question: z.string().describe('The question to ask the user for this step'),\n  options: z\n    .array(AskUserOptionSchema)\n    .min(2)\n    .max(6)\n    .describe('Structured options for this step (2-6 choices)'),\n  type: z\n    .enum(['single', 'multi'])\n    .default('single')\n    .describe('Selection type: single-select or multi-select'),\n});\n\nexport type AskUserStep = z.infer<typeof AskUserStepSchema>;\n\nconst AskUserSchema = z\n  .object({\n    question: z\n      .string()\n      .optional()\n      .describe('The question to ask the user. Be specific and clear.'),\n    options: z\n      .array(AskUserOptionSchema)\n      .min(2)\n      .max(6)\n      .optional()\n      .describe('Structured options for the user (2-6 choices)'),\n    context: z.string().optional().describe('Why you are asking this question'),\n    steps: z\n      .array(AskUserStepSchema)\n      .min(1)\n      .max(5)\n      .optional()\n      .describe('Multi-step wizard: array of question steps (1-5 steps)'),\n  })\n  // passthrough() preserves extra fields like _selectedOption injected by the\n  // approval flow's modifiedArgs — without this, Zod strips unknown keys.\n  .passthrough()\n  .refine(\n    (data) => {\n      // Must have either steps[] OR question+options (legacy format)\n      const hasSteps = Array.isArray(data.steps) && data.steps.length > 0;\n      const hasLegacy =\n        typeof data.question === 'string' && Array.isArray(data.options);\n      return hasSteps || hasLegacy;\n    },\n    {\n      message: 'Either steps[] or question+options must be provided',\n    }\n  );\n\n/**\n * Represents a single step selection from the multi-step wizard UI.\n * Exported for use by both the host web client and browser extension.\n */\nexport type StepSelection = {\n  values: string[];\n  customInput?: string;\n};\n\n/** Field names injected into modifiedArgs by the approval flow */\nexport const HITL_FIELDS = {\n  SELECTIONS: '_selections',\n  SELECTED_OPTION: '_selectedOption',\n  CUSTOM_INPUT: '_customInput',\n} as const;\n\n/**\n * Creates an ask_user tool that lets the LLM ask clarification questions mid-conversation.\n *\n * How it works:\n * 1. LLM calls ask_user(question, options) OR ask_user(steps)\n * 2. ToolNode.requiresApproval() → always true (per-tool rule in askUserRules.js)\n * 3. ToolNode.requestApproval() promotes options/question/steps to top-level fields on the event\n * 4. Frontend HumanInputPopover renders option cards + freeform text input\n * 5. User selects option(s) OR types custom input →\n *    backend resolves with modifiedArgs containing _selections, _selectedOption, or _customInput\n * 6. Tool executes, reads the user's choice, returns result to LLM\n */\n// eslint-disable-next-line @typescript-eslint/explicit-function-return-type\nexport function createAskUserTool() {\n  return tool(\n    async (args) => {\n      const extArgs = args as Record<string, unknown>;\n      const selections = extArgs[HITL_FIELDS.SELECTIONS] as\n        | StepSelection[]\n        | undefined;\n      const customInput = extArgs[HITL_FIELDS.CUSTOM_INPUT] as\n        | string\n        | undefined;\n      const selectedOption = extArgs[HITL_FIELDS.SELECTED_OPTION] as\n        | string\n        | undefined;\n\n      // Multi-step wizard flow: _selections is an array of { values, customInput? } per step\n      if (\n        selections &&\n        Array.isArray(selections) &&\n        Array.isArray(args.steps)\n      ) {\n        const lines: string[] = [];\n        for (let i = 0; i < args.steps.length; i++) {\n          const step = args.steps[i];\n          const sel = selections[i];\n          if (!sel || (sel.values.length === 0 && !sel.customInput)) {\n            lines.push(`${i + 1}. ${step.question} → (skipped)`);\n            continue;\n          }\n          if (sel.customInput) {\n            lines.push(`${i + 1}. ${step.question} → \"${sel.customInput}\"`);\n            continue;\n          }\n          // Resolve value(s) to labels\n          const labels = sel.values.map((v) => {\n            const opt = step.options.find((o) => o.value === v);\n            return opt?.label || v;\n          });\n          lines.push(`${i + 1}. ${step.question} → ${labels.join(', ')}`);\n        }\n        return `User responses:\\n${lines.join('\\n')}`;\n      }\n\n      // Legacy: custom freeform input takes priority — user explicitly chose \"none of these\"\n      if (customInput) {\n        return `User provided custom response: \"${customInput}\"`;\n      }\n\n      if (selectedOption) {\n        const option = args.options?.find((o) => o.value === selectedOption);\n        return `User selected: \"${option?.label || selectedOption}\" (value: ${selectedOption})`;\n      }\n\n      // Fallback — should not happen in interactive mode since approval always fires first\n      return 'No user response received.';\n    },\n    {\n      name: AskUserToolName,\n      description: AskUserDescription,\n      schema: AskUserSchema,\n    }\n  );\n}\n"],"names":[],"mappings":"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"}