{"version":3,"file":"router-prompt.mjs","names":[],"sources":["../../../../../../../ai/src/supervisor/router-prompt.ts"],"sourcesContent":["import { END } from \"../contracts/end.type\";\nimport type { IterationSnapshot } from \"../contracts/supervisor/iteration-snapshot.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\nimport type { ResolvedIntentEntry } from \"./entries\";\n\n/**\n * Build the per-turn user message the supervisor feeds to the router\n * agent. Carries everything the LLM needs to make a routing decision:\n *\n * - Available intents with descriptions (so the router knows what\n *   to pick from).\n * - The reserved `END` sentinel value it can emit to terminate.\n * - Iteration counter + ceiling so the router can pace itself.\n * - Compact history of prior iterations (intent + short output clip).\n * - The supervisor's original input so the router stays anchored.\n *\n * Note the router's own `systemPrompt` is kept persistent across\n * turns — this function produces only the per-turn USER message.\n */\nexport function buildRouterContextMessage(params: {\n  entries: Map<string, ResolvedIntentEntry>;\n  iteration: number;\n  maxIterations: number;\n  iterations: IterationSnapshot[];\n  input: SupervisorInput;\n  /**\n   * Per-execute state at the START of this iteration (post-merge of\n   * the previous iteration). Rendered as a JSON snippet so the\n   * router can pick the next intent based on what's already filled\n   * in (Q14).\n   */\n  state?: Record<string, unknown>;\n  /**\n   * Reviewer feedback string from the previous iteration's evaluate\n   * verdict. Rendered as its own section so the router weighs it\n   * alongside the intent list (Q18).\n   */\n  feedback?: string;\n  /**\n   * Supervisor-level system prompt text, when configured. Surfaced at\n   * the TOP of the router's per-turn user message so the router reads\n   * team/domain context before the routing mechanics block. Skipped\n   * when the supervisor didn't configure `systemPrompt`.\n   */\n  supervisorPrompt?: string;\n  /**\n   * Resolved natural-language objective from `SupervisorConfig.goal`.\n   * Surfaced as its own labeled section near the top of the router's\n   * user message so routing decisions are objective-aware. Skipped\n   * when no goal was configured.\n   */\n  goal?: string;\n}): string {\n  const {\n    entries,\n    iteration,\n    maxIterations,\n    iterations,\n    input,\n    state,\n    feedback,\n    supervisorPrompt,\n    goal,\n  } = params;\n\n  const intentLines = [...entries.values()].map(\n    entry => `- ${entry.intent}: ${entry.description}`,\n  );\n\n  const historyLines =\n    iterations.length === 0\n      ? [\"(none yet)\"]\n      : iterations.map(snapshot => formatHistoryLine(snapshot));\n\n  const sections: string[] = [];\n\n  if (supervisorPrompt) {\n    sections.push(supervisorPrompt.trim(), \"\");\n  }\n\n  if (goal) {\n    sections.push(\"Goal:\", goal.trim(), \"\");\n  }\n\n  sections.push(\n    \"Available intents:\",\n    ...intentLines,\n    \"\",\n    \"Reserved values:\",\n    `- ${END} = terminate the run`,\n    \"\",\n    `Iteration: ${iteration + 1} / ${maxIterations}`,\n    \"\",\n    \"History:\",\n    ...historyLines,\n  );\n\n  if (state && Object.keys(state).length > 0) {\n    sections.push(\"\", \"Current state:\", safeStringify(state));\n  }\n\n  if (feedback) {\n    sections.push(\"\", `Reviewer feedback from last iteration: ${feedback}`);\n  }\n\n  const renderedInput =\n    typeof input === \"string\" ? input : safeStringify(input);\n\n  sections.push(\"\", `Original input: ${renderedInput}`);\n\n  return sections.join(\"\\n\");\n}\n\nfunction formatHistoryLine(snapshot: IterationSnapshot): string {\n  const branches = Object.entries(snapshot.result).map(\n    ([intent, branch]) => `${intent} → ${clip(branch.output)}`,\n  );\n\n  return `[${snapshot.iteration}] ${branches.join(\" | \")}`;\n}\n\nfunction clip(value: unknown, maxLength = 160): string {\n  if (value === undefined || value === null) {\n    return String(value);\n  }\n\n  const raw = typeof value === \"string\" ? value : safeStringify(value);\n\n  if (raw.length <= maxLength) {\n    return raw;\n  }\n\n  return `${raw.slice(0, maxLength - 1)}…`;\n}\n\nfunction safeStringify(value: unknown): string {\n  try {\n    return JSON.stringify(value);\n  } catch {\n    return `[unserializable: ${typeof value}]`;\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,SAAgB,0BAA0B,QAiC/B;CACT,MAAM,EACJ,SACA,WACA,eACA,YACA,OACA,OACA,UACA,kBACA,SACE;CAEJ,MAAM,cAAc,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KACxC,UAAS,KAAK,MAAM,OAAO,IAAI,MAAM,aACvC;CAEA,MAAM,eACJ,WAAW,WAAW,IAClB,CAAC,YAAY,IACb,WAAW,KAAI,aAAY,kBAAkB,QAAQ,CAAC;CAE5D,MAAM,WAAqB,CAAC;CAE5B,IAAI,kBACF,SAAS,KAAK,iBAAiB,KAAK,GAAG,EAAE;CAG3C,IAAI,MACF,SAAS,KAAK,SAAS,KAAK,KAAK,GAAG,EAAE;CAGxC,SAAS,KACP,sBACA,GAAG,aACH,IACA,oBACA,KAAK,IAAI,uBACT,IACA,cAAc,YAAY,EAAE,KAAK,iBACjC,IACA,YACA,GAAG,YACL;CAEA,IAAI,SAAS,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,GACvC,SAAS,KAAK,IAAI,kBAAkB,cAAc,KAAK,CAAC;CAG1D,IAAI,UACF,SAAS,KAAK,IAAI,0CAA0C,UAAU;CAGxE,MAAM,gBACJ,OAAO,UAAU,WAAW,QAAQ,cAAc,KAAK;CAEzD,SAAS,KAAK,IAAI,mBAAmB,eAAe;CAEpD,OAAO,SAAS,KAAK,IAAI;AAC3B;AAEA,SAAS,kBAAkB,UAAqC;CAC9D,MAAM,WAAW,OAAO,QAAQ,SAAS,MAAM,CAAC,CAAC,KAC9C,CAAC,QAAQ,YAAY,GAAG,OAAO,KAAK,KAAK,OAAO,MAAM,GACzD;CAEA,OAAO,IAAI,SAAS,UAAU,IAAI,SAAS,KAAK,KAAK;AACvD;AAEA,SAAS,KAAK,OAAgB,YAAY,KAAa;CACrD,IAAI,UAAU,UAAa,UAAU,MACnC,OAAO,OAAO,KAAK;CAGrB,MAAM,MAAM,OAAO,UAAU,WAAW,QAAQ,cAAc,KAAK;CAEnE,IAAI,IAAI,UAAU,WAChB,OAAO;CAGT,OAAO,GAAG,IAAI,MAAM,GAAG,YAAY,CAAC,EAAE;AACxC;AAEA,SAAS,cAAc,OAAwB;CAC7C,IAAI;EACF,OAAO,KAAK,UAAU,KAAK;CAC7B,QAAQ;EACN,OAAO,oBAAoB,OAAO,MAAM;CAC1C;AACF"}