{"version":3,"file":"engine.mjs","names":["chatCompletion","describeRequestError","GatewayConfig","OpenAiMessage","OpenAiToolCall","answerUnansweredToolCalls","buildRequestMessages","trimTurns","Turn","Conversation","AiAssistantLimits","AiAssistantTool","AiChatItem","AiProposalConfig","ProposalStatus","DEFAULT_LIMITS","Required","maxRepairRounds","maxToolRounds","maxHistoryTurns","TRIM_NOTICE","PROPOSAL_OUTCOME","Record","pending","applied","dismissed","undone","AiEngineConfig","gateway","buildSystemPrompt","buildContext","requestParams","tools","proposals","limits","AiConversation","argString","args","key","value","undefined","parseArgs","call","error","JSON","parse","function","arguments","notifyContextSwitch","conversation","noticeText","historyMarker","isEmpty","pushItem","role","isNotice","text","appendTurn","content","setProposalStatus","itemId","status","runTurn","getConfig","userText","model","contextKey","Promise","trim","isSending","setSending","setDraft","turn","commitTurn","outcome","length","inserted","note","cfg","toolByName","Map","map","t","definition","name","apiTools","Object","values","p","turns","dropped","trimNoticeShown","messages","append","msgs","push","assistantEcho","message","toolCalls","tool_calls","complete","delta","d","buildToolResults","rejected","errors","all","tc","tool","get","result","id","execute","err","Error","respond","tool_call_id","stringify","terminal","primary","choice","repairsUsed","round","assistantMessage","reason","finish_reason","isError","respondCall","find","respondTool","proposalCall","proposalName","proposalConfig","question","validate","built","build","details","proposal","proposalToolName","proposalContextKey","proposalStatus","explanation","changes","m","recordProposalOutcome","e","join"],"sources":["../../src/aiAssistant/engine.ts"],"sourcesContent":["import { chatCompletion, describeRequestError, GatewayConfig, OpenAiMessage, OpenAiToolCall } from './openai';\nimport { answerUnansweredToolCalls, buildRequestMessages, trimTurns, Turn } from './conversation';\nimport type { Conversation } from './conversationState';\nimport type { AiAssistantLimits, AiAssistantTool, AiChatItem, AiProposalConfig, ProposalStatus } from './types';\n\nconst DEFAULT_LIMITS: Required<AiAssistantLimits> = {\n  maxRepairRounds: 1,\n  maxToolRounds: 6,\n  maxHistoryTurns: 10,\n};\n\nconst TRIM_NOTICE = 'Earlier messages are no longer sent to the model. Use Clear to start a fresh conversation.';\n\n/** What the model is told a proposal card's fate was, once the user decides. */\nexport const PROPOSAL_OUTCOME: Record<ProposalStatus, string> = {\n  pending: 'shown to the user as a proposal card; awaiting their decision',\n  applied: 'applied by the user',\n  dismissed: 'dismissed by the user',\n  undone: 'applied, then undone by the user',\n};\n\nexport interface AiEngineConfig {\n  gateway: GatewayConfig;\n  /**\n   * The stable part of the prompt: role, rules, schema. Called on every request\n   * so it can follow a subject change (a different entity), but it should come\n   * back identical from one turn to the next — together with the kept history\n   * it is the cacheable prefix. Live state belongs in `buildContext`.\n   */\n  buildSystemPrompt: () => string;\n  /**\n   * The live part: selection, view state, today's date. Called on every request\n   * and sent inside the incoming user message, after the history, so a change\n   * here never invalidates the cached prefix.\n   */\n  buildContext?: () => string;\n  /**\n   * Extra OpenAI request-body params merged into every completion call\n   * (temperature, max_completion_tokens, reasoning_effort, ...) — pure pass-through.\n   */\n  requestParams?: Record<string, unknown>;\n  tools?: AiAssistantTool[];\n  /**\n   * Proposal flows keyed by their tool name. Several may be live at once (a\n   * page assistant plus a docked copilot); an incoming propose call is routed\n   * to the config registered under the called tool's name.\n   */\n  proposals?: Record<string, AiProposalConfig>;\n  limits?: AiAssistantLimits;\n}\n\nexport type AiConversation = Conversation<AiChatItem>;\n\n/** A string field of parsed tool arguments, or undefined when absent or not a string. */\nexport const argString = (args: unknown, key: string): string | undefined => {\n  const value = (args as Record<string, unknown> | null)?.[key];\n  return typeof value === 'string' ? value : undefined;\n};\n\nconst parseArgs = (call: OpenAiToolCall): { args?: unknown; error?: string } => {\n  try {\n    return { args: JSON.parse(call.function?.arguments ?? '') as unknown };\n  } catch {\n    return { error: 'Tool arguments were not valid JSON. Call the tool again with valid JSON.' };\n  }\n};\n\n/**\n * Records a mid-conversation context switch (e.g. the user picked a different\n * entity): a divider in the chat for the user, and a note in the history so\n * the model knows earlier turns referred to a different context. The note is\n * a user-role message — Anthropic has no system role inside messages, and\n * consecutive user turns are merged, so this is provider-neutral. No-op on an\n * empty conversation.\n */\nexport const notifyContextSwitch = (conversation: AiConversation, noticeText: string, historyMarker: string) => {\n  if (conversation.isEmpty) return;\n  conversation.pushItem({ role: 'assistant', isNotice: true, text: noticeText });\n  conversation.appendTurn([{ role: 'user', content: historyMarker }]);\n};\n\nexport const setProposalStatus = (conversation: AiConversation, itemId: number, status: ProposalStatus) =>\n  conversation.setProposalStatus(itemId, status, PROPOSAL_OUTCOME[status]);\n\n/**\n * One user turn of the OpenAI tool loop: completion → execute tools / validate\n * a proposal (one repair round) → repeat, until the model answers in text, a\n * terminal tool ends the turn, or a limit is hit. All domain knowledge (prompt,\n * tools, proposal handling) comes from `config`; `getConfig` is re-read for\n * proposal routing so the flow never runs against a stale closure.\n *\n * History is the raw Chat Completions message log kept as whole turns (the\n * user message and everything the model did in reply, tool traffic included)\n * so the model remembers what it looked up and proposed. A turn is only kept\n * once every tool call in it has a tool message answering it. See\n * conversation.ts for the rules and their sources.\n */\nexport const runTurn = async (\n  conversation: AiConversation,\n  getConfig: () => AiEngineConfig,\n  userText: string,\n  model: string,\n  contextKey?: string,\n): Promise<void> => {\n  const content = userText.trim();\n  if (!content || conversation.isSending) return;\n\n  conversation.pushItem({ role: 'user', text: content });\n  conversation.setSending(true);\n  conversation.setDraft('');\n\n  // The turn as history will keep it: the raw user text, then everything the\n  // model did in reply. The request's copy of the user message carries the\n  // live context block; history's does not.\n  const turn: Turn = [{ role: 'user', content }];\n  const commitTurn = (outcome?: (call: OpenAiToolCall) => unknown): OpenAiMessage[] => {\n    if (turn.length < 2) return []; // nothing came back — keep history true to what was answered\n    const inserted = answerUnansweredToolCalls(turn, call =>\n      outcome ? outcome(call as OpenAiToolCall) : { status: 'ignored', note: 'The turn ended before this call was processed.' },\n    );\n    conversation.appendTurn(turn);\n    return inserted as OpenAiMessage[];\n  };\n\n  try {\n    const cfg = getConfig();\n    const limits = { ...DEFAULT_LIMITS, ...cfg.limits };\n    const tools = cfg.tools ?? [];\n    const toolByName = new Map(tools.map(t => [t.definition.function.name, t]));\n    const proposals = cfg.proposals ?? {};\n    const apiTools = [...Object.values(proposals).map(p => p.definition), ...tools.map(t => t.definition)];\n\n    // Leave room for this turn.\n    const { turns, dropped } = trimTurns(conversation.turns, limits.maxHistoryTurns - 1);\n    conversation.turns = turns;\n    if (dropped > 0 && !conversation.trimNoticeShown) {\n      conversation.trimNoticeShown = true;\n      conversation.pushItem({ role: 'assistant', isNotice: true, text: TRIM_NOTICE });\n    }\n\n    const messages = buildRequestMessages(\n      cfg.buildSystemPrompt(),\n      turns,\n      content,\n      cfg.buildContext?.() ?? '',\n    ) as OpenAiMessage[];\n    /** Appends to both the request and the turn history keeps. */\n    const append = (...msgs: OpenAiMessage[]) => {\n      messages.push(...msgs);\n      turn.push(...msgs);\n    };\n    const assistantEcho = (message: OpenAiMessage | undefined, toolCalls: OpenAiToolCall[]): OpenAiMessage => ({\n      role: 'assistant',\n      content: message?.content ?? null,\n      tool_calls: toolCalls,\n    });\n\n    const complete = (msgs: OpenAiMessage[]) => {\n      conversation.setDraft('');\n      return chatCompletion(\n        cfg.gateway,\n        {\n          ...cfg.requestParams,\n          model,\n          messages: msgs,\n          ...(apiTools.length > 0 ? { tools: apiTools } : {}),\n        },\n        delta => conversation.setDraft(d => d + delta),\n      );\n    };\n\n    /**\n     * Answers every tool_call_id of one assistant turn (required by the API):\n     * the rejected propose call gets its validation errors, executable tools\n     * run, everything else gets an explanatory error.\n     */\n    const buildToolResults = async (\n      toolCalls: OpenAiToolCall[],\n      rejected: { call: OpenAiToolCall; errors: string[] } | null,\n    ): Promise<OpenAiMessage[]> =>\n      Promise.all(\n        toolCalls.map(async tc => {\n          const name = tc.function?.name ?? '';\n          const tool = toolByName.get(name);\n          let result: unknown;\n          if (rejected && tc.id === rejected.call.id) {\n            result = { status: 'rejected', errors: rejected.errors };\n          } else if (tool?.execute) {\n            const { args, error } = parseArgs(tc);\n            try {\n              result = error ? { error } : await tool.execute(args);\n            } catch (err) {\n              result = { error: err instanceof Error && err.message ? err.message : 'Tool execution failed.' };\n            }\n          } else if (proposals[name] || tool?.respond) {\n            result = { status: 'ignored', note: 'Only one proposal or response is processed per turn.' };\n          } else {\n            result = { error: `Unknown tool \"${name}\".` };\n          }\n          return { role: 'tool', tool_call_id: tc.id, content: JSON.stringify(result) };\n        }),\n      );\n\n    /** Outcome for the one terminal call; the rest of that turn's calls were not processed. */\n    const terminal = (primary: OpenAiToolCall, result: unknown) => (call: OpenAiToolCall) =>\n      call.id === primary.id\n        ? result\n        : { status: 'ignored', note: 'Only one proposal or response is processed per turn.' };\n\n    let choice = await complete(messages);\n\n    let repairsUsed = 0;\n    for (let round = 0; round < limits.maxToolRounds; round++) {\n      const assistantMessage = choice?.message;\n      const toolCalls: OpenAiToolCall[] = assistantMessage?.tool_calls ?? [];\n\n      // Plain text reply. finish_reason 'length' means the model hit the\n      // completion-token limit mid-answer; 'content_filter' means the provider\n      // withheld it. Say so rather than presenting a fragment.\n      if (toolCalls.length === 0) {\n        const text = assistantMessage?.content?.trim();\n        const reason = choice?.finish_reason;\n        if (reason === 'content_filter' && !text) {\n          conversation.pushItem({ role: 'assistant', isError: true, text: \"The provider's content filter blocked the response.\" });\n          commitTurn();\n          return;\n        }\n        conversation.pushItem({\n          role: 'assistant',\n          text: text\n            ? reason === 'length'\n              ? `${text}\\n\\n(The response was cut off by the length limit. Ask for a shorter answer or continue.)`\n              : text\n            : 'I did not get a usable response from the model. Try rephrasing.',\n          isError: !text,\n        });\n        if (text) append({ role: 'assistant', content: text });\n        commitTurn();\n        return;\n      }\n\n      // Terminal tools first: a respond tool ends the turn with its text.\n      const respondCall = toolCalls.find(tc => toolByName.get(tc.function?.name ?? '')?.respond);\n      const respondTool = respondCall ? toolByName.get(respondCall.function?.name ?? '')?.respond : undefined;\n      const proposalCall = toolCalls.find(tc => !!proposals[tc.function?.name ?? '']);\n      const primary = proposalCall ?? respondCall;\n      const proposalName = proposalCall?.function?.name ?? '';\n      const proposalConfig = proposalCall ? getConfig().proposals?.[proposalName] ?? proposals[proposalName] : undefined;\n\n      if (primary) {\n        const { args, error } = parseArgs(primary);\n        let errors: string[] = error ? [error] : [];\n\n        if (errors.length === 0 && respondTool && primary === respondCall && primary !== proposalCall) {\n          const text = respondTool(args);\n          conversation.pushItem({ role: 'assistant', text });\n          append(assistantEcho(assistantMessage, toolCalls));\n          commitTurn(terminal(primary, { status: 'asked', question: text }));\n          return;\n        }\n\n        if (errors.length === 0) {\n          errors = proposalConfig?.validate?.(args) ?? [];\n        }\n\n        if (errors.length === 0 && proposalConfig) {\n          const built = proposalConfig.build(args);\n\n          if (!built || built.details.length === 0) {\n            // Proposal that changes nothing — treat as conversation\n            const text = built?.text || argString(args, 'explanation') || 'No changes needed.';\n            conversation.pushItem({ role: 'assistant', text });\n            append(assistantEcho(assistantMessage, toolCalls));\n            commitTurn(terminal(primary, { status: 'no_change', note: text }));\n            return;\n          }\n\n          const itemId = conversation.pushItem({\n            role: 'assistant',\n            text: built.text,\n            proposal: built,\n            proposalToolName: proposalName,\n            proposalContextKey: contextKey,\n            proposalStatus: 'pending',\n          });\n          append(assistantEcho(assistantMessage, toolCalls));\n          const inserted = commitTurn(\n            terminal(primary, {\n              status: PROPOSAL_OUTCOME.pending,\n              explanation: built.text,\n              changes: built.details.map(d => d.text),\n            }),\n          );\n          const outcome = inserted.find(m => m.tool_call_id === primary.id);\n          if (outcome) conversation.recordProposalOutcome(itemId, outcome);\n          return;\n        }\n\n        if (repairsUsed >= limits.maxRepairRounds) {\n          conversation.pushItem({\n            role: 'assistant',\n            isError: true,\n            text: `I couldn't produce a valid change for that request:\\n${errors.map(e => `• ${e}`).join('\\n')}`,\n          });\n          append(assistantEcho(assistantMessage, toolCalls));\n          commitTurn(terminal(primary, { status: 'rejected', errors }));\n          return;\n        }\n\n        // Repair round: hand the validation errors back as the tool result\n        repairsUsed++;\n        append(assistantEcho(assistantMessage, toolCalls), ...(await buildToolResults(toolCalls, { call: primary, errors })));\n        choice = await complete(messages);\n        continue;\n      }\n\n      // Lookup round: execute the tool calls and let the model continue\n      append(assistantEcho(assistantMessage, toolCalls), ...(await buildToolResults(toolCalls, null)));\n      choice = await complete(messages);\n    }\n\n    conversation.pushItem({\n      role: 'assistant',\n      isError: true,\n      text: 'The model made too many tool calls without producing an answer. Try rephrasing.',\n    });\n    commitTurn(); // the completed lookup rounds are kept; the unanswered final call is not\n  } catch (err) {\n    conversation.pushItem({ role: 'assistant', isError: true, text: `Request failed: ${describeRequestError(err)}` });\n    commitTurn();\n  } finally {\n    conversation.setDraft('');\n    conversation.setSending(false);\n  }\n};\n"],"mappings":"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"}