{"version":3,"sources":["../src/agentLoop.ts"],"sourcesContent":["// Provider-agnostic agentic tool-use loop — PORTED from agent-demo `src/lib/agentLoop.ts`\n// (GROVE_AGENT_SPEC §7: embedded agents REUSE this seam rather than reimplementing it;\n// the SDK is its shared home so every app's agent loop is the same exercised machinery).\n// Renames for the SDK's flat export surface: `ChatMessage`→`AgentMessage`, `Role`→`AgentRole`.\n// Provider-agnostic agentic tool-use loop (LLM_AND_AGENTS_SPEC §3.3). The loop is\n// the heart of the in-browser coding agent: send the conversation + tool list to a\n// ModelClient, execute any tool calls the model emits, append the results, and\n// repeat until the model stops, a spend budget is hit, or a large safety-stop is\n// reached. The ModelClient seam keeps the loop independent of any one provider\n// (host `chat()` impl: chatModelClient.ts).\n//\n// Confinement (G12/T24) is NOT enforced here — it falls out of the capability\n// model: the `tools` handed to the model ARE the app's grant-filtered §5.5\n// catalog (agentTools.ts), and `execute` routes through the host's gated\n// `invoke()`, so an off-catalog/hallucinated tool returns `forbidden` at the host.\n//\n// R3-220 (AHG-1) adds the machinery that lets the loop run LONG enough to build a\n// real app: token accounting (from the provider `usage` delta), automatic context\n// COMPACTION when the window fills, a truncated-tool-call guard, and a spend budget\n// replacing the old fixed 12-turn cap. All of it is inert unless a `contextWindow`\n// is supplied, so a caller that passes none behaves exactly as before.\n//\n// PREFIX STABILITY IS LOAD-BEARING (R3-336). The loop's contribution to prompt caching\n// is structural, not a parameter: `system` and `tools` are fixed for a run and are sent\n// BYTE-IDENTICALLY on every turn, while everything that changes is appended to\n// `messages`. That is what the host's cache breakpoints key on. Rebuilding the system\n// prompt per turn — re-stamping a date, re-ordering the tool list — would cost nothing\n// visible and silently turn every cache read into a cache write, so it is asserted in\n// the tests rather than left as a convention.\n\nimport { anySignal, steerWireText, INTERRUPTED_TURN_TEXT, type SteerMessage, type SteerSource } from './agentSteering';\nimport type { PauseSource } from './agentPause';\n\nexport type TextBlock = { type: 'text'; text: string };\n/**\n * An image the model can look at (R3-339). `data` is base64 with no `data:` prefix,\n * matching the SDK `ContentPart` the transport already accepts.\n *\n * Carried as its OWN block rather than stuffed inside a `tool_result`, because a tool\n * result's content is a string on the wire — the loop appends the image to the same\n * user message that carries the results, which is the shape both host adapters map.\n */\nexport type ImageBlock = { type: 'image'; mimeType: string; data: string };\n/**\n * A block of the model's own reasoning (R3-335).\n *\n * Kept in the message sequence rather than rendered and thrown away, for two reasons:\n * the user needs to see what the model is doing during the long stretches compaction\n * now makes possible, and some providers REQUIRE the block echoed back — with its\n * `signature` — for the following turn of a tool-use chain to stay valid. A loop that\n * drops them is quietly lossy in a way that shows up as degraded output, not an error.\n *\n * `redactedData` carries provider-redacted reasoning: opaque bytes with no readable\n * text, which still have to be replayed in place. Never render it.\n */\nexport type ReasoningBlock = {\n  type: 'reasoning';\n  text: string;\n  signature?: string;\n  redactedData?: string;\n};\nexport type ToolUseBlock = { type: 'tool_use'; id: string; name: string; input: Record<string, unknown> };\nexport type ToolResultBlock = { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean };\nexport type ContentBlock = TextBlock | ToolUseBlock | ToolResultBlock | ImageBlock | ReasoningBlock;\n\n/** A tool the model may call: name, description, and a JSON Schema for its input\n * (`input_schema`, the Anthropic wire name — {@link createChatModelClient} maps it to the\n * chat slot's `ToolDef`). */\nexport interface AgentTool {\n  name: string;\n  description: string;\n  input_schema: Record<string, unknown>;\n}\n\nexport type AgentRole = 'user' | 'assistant';\nexport interface AgentMessage {\n  role: AgentRole;\n  content: ContentBlock[];\n}\n\n/** Provider-reported token counts for one turn (R3-220). `inputTokens` is the size\n *  of everything the provider processed this turn; `outputTokens` is what it\n *  generated. Absent when the provider emits no `usage` delta. */\nexport interface TokenUsage {\n  inputTokens: number;\n  outputTokens: number;\n  /** R3-336 — prompt-cache counters, present only where the provider reports them.\n   *  ABSENT is not zero: it means this provider says nothing about caching, which is a\n   *  different fact from \"nothing was cached\", and conflating them would turn a\n   *  measurement into a guess. */\n  cacheReadTokens?: number;\n  cacheWriteTokens?: number;\n}\n\n/** One model turn: the assistant's emitted blocks + why it stopped (+ usage). */\nexport interface ModelResponse {\n  content: (TextBlock | ToolUseBlock | ReasoningBlock)[];\n  /** Anthropic stop_reason: 'end_turn' | 'tool_use' | 'max_tokens' | 'refusal' | … */\n  stopReason: string;\n  /** Provider token counts for this turn, when reported (R3-220 accounting). */\n  usage?: TokenUsage;\n}\n\n/** The provider seam — one model turn. Implemented by `chatModelClient.ts` over\n *  the host `chat()` slot; faked in tests. When the client streams, it calls\n *  `onTextDelta` with each token slice as it arrives (the assembled turn is still\n *  returned whole); a non-streaming client simply never calls it. */\nexport interface ModelClient {\n  createMessage(req: {\n    system?: string;\n    messages: AgentMessage[];\n    tools: AgentTool[];\n    /** Called with incremental assistant-text slices during a streamed turn. */\n    onTextDelta?: (text: string) => void;\n    /** R3-335: incremental REASONING slices, for a live thinking surface. Never called\n     *  by a provider that does not emit reasoning. */\n    onReasoningDelta?: (text: string) => void;\n    /** R3-224: aborts the in-flight turn — the host stops the upstream provider\n     *  request and stops billing, not just the app-side stream (§3.3). */\n    signal?: AbortSignal;\n  }): Promise<ModelResponse>;\n}\n\n/** Executes one tool call, returning a string result (and whether it errored —\n *  a `forbidden`/failed call comes back as `is_error` so the model can adapt). */\nexport type ToolExecutor = (name: string, input: Record<string, unknown>) => Promise<ToolOutcome>;\n\n/** What one tool call produced. `images` (R3-339) is how a tool hands the model\n *  something to LOOK at; `content` still carries the text the model reads. */\nexport interface ToolOutcome {\n  content: string;\n  isError?: boolean;\n  images?: ImageBlock[];\n}\n\n/** Why a no-tool-call turn looked like a stall rather than a genuine finish. */\nexport type StallReason = 'empty' | 'announced-no-call';\n\n/** Optional UI hooks so a panel can render the loop as it runs. */\nexport interface AgentEvents {\n  /** A streamed token slice of the in-flight assistant turn (live preview). */\n  onAssistantDelta?(text: string): void;\n  /** The complete assistant text for a turn, once the turn is in. */\n  onAssistantText?(text: string): void;\n  onToolUse?(name: string, input: Record<string, unknown>): void;\n  onToolResult?(name: string, result: ToolOutcome): void;\n  /** Fired when the loop nudges a STALLED turn (the model ended without a tool\n   *  call despite empty or \"I'll do X\" intent text) back into action, so a panel\n   *  can show \"nudging the model to continue\" rather than a silent stall. */\n  onNudge?(reason: StallReason): void;\n  /** Fired after every turn with the running context size + window (R3-220\n   *  loop-observability). `contextTokens` is provider-reported when available, else\n   *  a char/4 estimate. */\n  onUsage?(usage: {\n    contextTokens: number;\n    window?: number;\n    spentTokens: number;\n    /** R3-336 — cumulative cache reads/writes across the run, on providers that report\n     *  them. Surfacing this is what makes the caching claim verifiable rather than\n     *  believed; `undefined` means the provider reported nothing. */\n    cacheReadTokens?: number;\n    cacheWriteTokens?: number;\n  }): void;\n  /** Fired when the loop compacts the transcript to stay under the context window;\n   *  `summarizedCount` is how many older messages were folded into the summary.\n   *\n   *  R3-336: a compaction invalidates the conversation-prefix cache it rewrote — the\n   *  durable system+tools prefix survives it — so the next turn pays one prefix\n   *  re-write. `cacheReadTokens`/`cacheWriteTokens` are the run totals AT the\n   *  compaction, which is what lets the cost curve across it be read off rather than\n   *  assumed (exit 2). */\n  onCompact?(info: { summarizedCount: number; cacheReadTokens?: number; cacheWriteTokens?: number }): void;\n  /** Fired when the loop stops because the token/spend budget was exhausted. */\n  onBudgetStop?(info: { spentTokens: number; tokenBudget: number }): void;\n  /** Fired when a turn was truncated (`max_tokens`) while emitting tool calls, so\n   *  the partial calls were failed-and-re-prompted rather than executed (R3-220 F3). */\n  onTruncatedToolCall?(): void;\n  /** R3-335: a streamed slice of the model's reasoning, for a live thinking surface. */\n  onReasoningDelta?(text: string): void;\n  /** R3-335: the complete reasoning block for a turn, once the turn is in. */\n  onReasoning?(block: ReasoningBlock): void;\n  /** R3-333: the loop applied the user's mid-run correction(s). `interrupted` is\n   *  true when an `interrupt`-mode steer cut an in-flight model turn short (as\n   *  opposed to being applied at an ordinary turn boundary). */\n  onSteer?(info: { messages: SteerMessage[]; interrupted: boolean }): void;\n  /** R3-562: the loop reached a turn boundary while its region was hidden and stopped\n   *  advancing. A surface can say \"paused — this view is hidden\" instead of looking hung. */\n  onPause?(info: { turn: number }): void;\n  /** R3-562: the region was revealed (or the run was stopped) and the loop resumed. */\n  onResume?(info: { turn: number }): void;\n}\n\nexport interface RunAgentOptions {\n  client: ModelClient;\n  tools: AgentTool[];\n  execute: ToolExecutor;\n  system?: string;\n  /** Prior turns of this conversation, replayed before the new prompt so a\n   *  follow-up has context (the conversation stage seeds this from the store). */\n  history?: AgentMessage[];\n  /** The user's instruction that kicks off the loop. */\n  prompt: string;\n  /** Large safety-stop on model turns (default 100). No longer the primary bound —\n   *  a long task is bounded by `tokenBudget` + compaction; this just backstops a\n   *  pathological loop the budget/compaction somehow miss. */\n  maxTurns?: number;\n  /** Max consecutive \"you announced work but emitted no tool call\" nudges before\n   *  the loop gives up (default 1). GLM-over-OpenRouter intermittently ends a turn\n   *  with future-tense intent (\"I'll read the files…\") or an EMPTY turn right after\n   *  a tool error — no tool call, a silent stall (tutorial findings §2). One nudge\n   *  recovers most of these; the cap keeps a genuinely-finished model (which answers\n   *  the nudge with another call-free turn) from looping, and the budget resets on\n   *  any turn that DID call a tool, so a long task's later stall is still covered.\n   *  Set 0 to disable the backstop. */\n  maxNudges?: number;\n  // ---- R3-220 accounting / compaction (all inert unless `contextWindow` is set) ----\n  /** The resolved provider's context window (`describeChat().features.maxContextTokens`).\n   *  Compaction is disabled when this is absent/0 — the loop then behaves as before. */\n  contextWindow?: number;\n  /** Headroom left below the window before compacting (default: 25% of the window). */\n  reserveTokens?: number;\n  /** Recent messages kept verbatim across a compaction (default 8). */\n  keepRecentTurns?: number;\n  /** Cumulative token budget (input+output across turns). When exceeded the loop\n   *  stops — the runaway-cost guard that replaces the raw 12-turn cap. Off when unset. */\n  tokenBudget?: number;\n  /** Max consecutive truncated-tool-call re-prompts before giving up (default 2). */\n  maxTruncationRetries?: number;\n  /** R3-224 (§3.3): the stop button. When it fires the loop stops between turns AND\n   *  aborts the in-flight model turn (the host tears down the upstream provider\n   *  request and stops billing) — not merely the between-turn loop. The transcript so\n   *  far is returned; an abort is a clean stop, never a thrown error. */\n  signal?: AbortSignal;\n  /** R3-333: the mid-run steering queue. The loop drains it at every turn boundary\n   *  and folds each correction in as a `user` message, so the human can redirect a\n   *  run without restarting it and paying for the transcript again. Its `interrupt`\n   *  signal aborts the in-flight MODEL turn only — never a tool batch, which must\n   *  keep every `tool_use` paired with a `tool_result`. Absent ⇒ the loop behaves\n   *  exactly as before. */\n  steering?: SteerSource;\n  /**\n   * R3-562 (AGENT_RUN_DURABILITY_SPEC §7 R-ARD-20a): pause the run while nobody can see\n   * or stop it — the host has hidden this app's region but kept the frame mounted.\n   *\n   * Read at the TURN BOUNDARY only, so every `tool_use` still has its `tool_result` when\n   * the loop stops advancing. Nothing is torn down and nothing is injected: the run\n   * simply does not start its next turn until the region is revealed, then continues\n   * with no repair pass and no resume gate. Omitted ⇒ the loop never pauses, exactly as\n   * before.\n   */\n  pause?: PauseSource;\n  events?: AgentEvents;\n}\n\nconst textOf = (blocks: { type: string; text?: string }[]): string =>\n  blocks\n    .filter((b): b is TextBlock => b.type === 'text')\n    .map((b) => b.text)\n    .join('');\n\n// Terminal stops we must NOT nudge past. Only `max_tokens` survives the SDK→loop\n// mapping distinctly (chatModelClient `mapStop`: 'length'→'max_tokens', while\n// 'end'/'filtered'→'end_turn' and 'tool'→'tool_use'); a truncated turn is a\n// token-budget problem a nudge can't fix. An empty give-up after a tool error\n// arrives as 'end_turn', so it stays nudgeable.\nconst TERMINAL_STOPS = new Set(['max_tokens', 'refusal']);\n\n// Future-tense intent to ACT (\"I'll read…\", \"let me create…\", \"next I'll edit…\").\nconst INTENT_RE =\n  /\\b(i'?ll|i will|i'?m going to|going to|let me|let's|now,? i(?:'?ll| will)?|next,? i(?:'?ll| will)?)\\b[\\s\\S]{0,80}?\\b(read|write|edit|creat|add|updat|modif|regist|check|look|call|run|search|grep|list|open|fetch|inspect|review|explor|implement|fix|appl)/i;\n// A wrap-up marker → treat the turn as a genuine finish, never nudge.\nconst DONE_RE =\n  /\\b(done|complete|finished|all set|no (?:further|more) (?:changes|steps)|i(?:'| ha)ve (?:creat|add|updat|made|written|regist|edit|implement|fix|appli)|here'?s (?:a |the )?summ|to summ|in summ)/i;\n\n/**\n * Classify a NO-tool-call turn as a stall (nudge-worthy) vs a genuine finish.\n * GLM-over-OpenRouter intermittently (a) writes \"I'll read the files…\" then ends\n * with no call, or (b) returns an EMPTY turn after a tool error — both silent\n * give-ups (tutorial findings §2). Conservative on purpose: a real wrap-up (a\n * summary, \"Done\", \"I've created…\") returns null so the loop never nudges a\n * finished agent. Empty text is always a stall (there is nothing a finished agent\n * would say with zero words).\n */\nexport function detectStall(text: string): StallReason | null {\n  const t = text.trim();\n  if (!t) return 'empty';\n  if (DONE_RE.test(t)) return null;\n  if (INTENT_RE.test(t)) return 'announced-no-call';\n  return null;\n}\n\n// The single follow-up we inject to break a stall. Directive, short, and honest\n// about the two outcomes so a genuinely-finished model just confirms and stops\n// (→ another call-free turn, which the nudge cap then lets terminate). Exported so\n// the transcript renderer can recognise the injected turn and show it as a \"nudge\"\n// row (not a user message) when a persisted conversation is replayed.\nexport const NUDGE_TEXT =\n  \"You ended your turn without calling a tool. If the task is already complete, say so plainly in one line and stop. Otherwise don't just describe the next step — emit the tool call now.\";\n\n// ---- R3-220 token accounting + compaction ----------------------------------------\n\n/** Rough token estimate (~4 chars/token) over a message array, used only when the\n *  provider reports no `usage` delta. Conservative by design (over- not under-counts\n *  by treating structured blocks as their JSON length). */\nexport function estimateTokens(messages: AgentMessage[]): number {\n  let chars = 0;\n  for (const m of messages) {\n    for (const b of m.content) {\n      if (b.type === 'text') chars += b.text.length;\n      else if (b.type === 'tool_use') chars += JSON.stringify(b.input).length + b.name.length;\n      else if (b.type === 'tool_result') chars += b.content.length;\n      // R3-339: an image is large and MUST be accounted for, or it escapes exactly the\n      // budget the accounting exists to enforce. base64 is ~4/3 of the bytes, and the\n      // provider bills tokens per pixel area — the base64 length is the honest local\n      // proxy for \"this is big\", and over-counting is the safe direction.\n      else if (b.type === 'image') chars += b.data.length;\n      // R3-335: reasoning occupies the window like anything else. Not counting it would\n      // let a thinking model overrun the context the accounting exists to protect.\n      else if (b.type === 'reasoning') chars += b.text.length + (b.redactedData?.length ?? 0);\n    }\n  }\n  return Math.ceil(chars / 4);\n}\n\n/** Should the loop compact now? True once the running context passes\n *  `window − reserveTokens`. Disabled (false) when there is no window. */\nexport function shouldCompact(contextTokens: number, window: number | undefined, reserveTokens: number): boolean {\n  if (!window || window <= 0) return false;\n  return contextTokens > window - reserveTokens;\n}\n\n/** Prefix marking a `user` message as a compaction summary (not a real user turn),\n *  so the transcript renderer shows a \"compacted N turns\" affordance on replay. */\nexport const COMPACTION_MARKER = '␟[compacted-context]\\n';\n\nconst SUMMARY_SYSTEM =\n  'You are compacting a coding-agent transcript to fit the context window. Produce a ' +\n  'DENSE structured summary under these exact headings: Goal / Constraints / Progress / ' +\n  'Decisions / Next Steps / Critical Context. PRESERVE VERBATIM every file path, symbol/' +\n  'identifier, and error string that later steps will need — do not paraphrase them. Be ' +\n  'terse everywhere else. Output only the summary.';\nconst SUMMARY_INSTRUCTION =\n  'Summarize everything above into the structured block. Keep exact paths, symbols, and ' +\n  'error strings verbatim so work can continue from the summary alone.';\n\n/** Compact `messages` by folding the older head into a structured summary and keeping\n *  a verbatim recent tail. The tail is snapped to start at an `assistant` message so a\n *  `tool_use`/`tool_result` pair is never split (which would malform the next request).\n *  The taint tier is NOT modelled on messages (it is run-scoped host state, R-ASG-2):\n *  this is a pure content transform over the SAME session — it starts no new external\n *  read — so it cannot launder taint (F6). Returns the original array unchanged when\n *  there is nothing safe to summarize. */\nexport async function compactTranscript(\n  messages: AgentMessage[],\n  client: ModelClient,\n  keepRecentTurns: number,\n): Promise<{ messages: AgentMessage[]; summarizedCount: number }> {\n  if (messages.length <= keepRecentTurns + 1) return { messages, summarizedCount: 0 };\n\n  // Snap the tail boundary to an assistant message so tool_use/tool_result pairs stay\n  // together and the summary (a `user` turn) is followed by an `assistant` turn.\n  // Prefer the first assistant at/after the keep-recent boundary; fall back to the\n  // last assistant in the transcript so the tail is always well-formed.\n  const boundary = Math.max(1, messages.length - keepRecentTurns);\n  let tailStart = -1;\n  for (let i = boundary; i < messages.length; i++) {\n    if (messages[i].role === 'assistant') {\n      tailStart = i;\n      break;\n    }\n  }\n  if (tailStart === -1) {\n    for (let i = messages.length - 1; i >= 1; i--) {\n      if (messages[i].role === 'assistant') {\n        tailStart = i;\n        break;\n      }\n    }\n  }\n  if (tailStart <= 0) return { messages, summarizedCount: 0 };\n\n  const head = messages.slice(0, tailStart);\n  // Compaction DROPS both image parts (R3-339) and reasoning (R3-335) from the kept\n  // tail, each by an explicit rule — an implicit answer here is what corrupts a\n  // transcript quietly.\n  //\n  // IMAGES: the largest and least summarisable thing in a transcript, and the summary\n  // the head folds into is TEXT. The `tool_result` that named the image stays, so the\n  // model still knows it looked at `assets/mock.png` and what it concluded; it simply\n  // cannot look again without re-reading the file, which it can do.\n  //\n  // REASONING: only ever required by the turn that FOLLOWS it, and compaction rewrites\n  // at a turn boundary — so nothing after it is mid-chain and nothing needs the block\n  // replayed. Keeping them would spend the window on its most disposable content.\n  const tail = messages.map(dropImages).map(dropReasoning).slice(tailStart);\n\n  // Ask the model to summarize the head. Append the instruction to the final head\n  // message when it is a `user` turn (avoids introducing consecutive user turns).\n  const reqMessages: AgentMessage[] = head.map((m) => ({ role: m.role, content: [...m.content] }));\n  const lastMsg = reqMessages[reqMessages.length - 1];\n  if (lastMsg && lastMsg.role === 'user') {\n    lastMsg.content = [...lastMsg.content, { type: 'text', text: SUMMARY_INSTRUCTION }];\n  } else {\n    reqMessages.push({ role: 'user', content: [{ type: 'text', text: SUMMARY_INSTRUCTION }] });\n  }\n\n  let summaryText = '(summary unavailable)';\n  try {\n    const res = await client.createMessage({ system: SUMMARY_SYSTEM, messages: reqMessages, tools: [] });\n    summaryText = textOf(res.content).trim() || summaryText;\n  } catch {\n    // Summarization itself failed — keep the original transcript (caller will retry\n    // or hit the safety-stop). Better a longer context than a lost transcript.\n    return { messages, summarizedCount: 0 };\n  }\n\n  const summaryMsg: AgentMessage = {\n    role: 'user',\n    content: [{ type: 'text', text: COMPACTION_MARKER + summaryText }],\n  };\n  return { messages: [summaryMsg, ...tail], summarizedCount: head.length };\n}\n\n/** Strip blocks of one kind from a message, keeping everything else in order. A message\n *  left with no content at all keeps a single empty text block so the role sequence\n *  stays well-formed (a content-less message is rejected by most providers). */\nfunction dropBlocks(m: AgentMessage, kind: 'image' | 'reasoning'): AgentMessage {\n  if (!m.content.some((b) => b.type === kind)) return m;\n  const kept = m.content.filter((b) => b.type !== kind);\n  return { role: m.role, content: kept.length ? kept : [{ type: 'text', text: '' }] };\n}\n\n/** Compaction's image-drop rule (R3-339). */\nconst dropImages = (m: AgentMessage): AgentMessage => dropBlocks(m, 'image');\n/** Compaction's reasoning-drop rule (R3-335). */\nconst dropReasoning = (m: AgentMessage): AgentMessage => dropBlocks(m, 'reasoning');\n\n/**\n * The host's typed code for \"the conversation no longer fits\" — produced by site-main's\n * `PROVIDER_ERROR_CODES` vocabulary (`src/editor/llm/providerErrors.ts`): the host maps\n * BOTH a provider's own context overflow AND the relay's bound refusal to this one code.\n * The SDK cannot import the host's vocabulary, so the literal has exactly one home HERE,\n * pointed at its producer.\n */\nexport const HOST_CONTEXT_OVERFLOW_CODE = 'context-too-large';\n\n/** Does this thrown error look like a hard context-window overflow? Used to trigger\n *  recover-then-retry compaction (F3/exit-c) rather than a dead loop. */\nexport function isContextOverflow(e: unknown): boolean {\n  const msg = ((e as Error)?.message ?? String(e)).toLowerCase();\n  const code = String((e as { code?: unknown })?.code ?? '').toLowerCase();\n  return (\n    // The host's own typed code, matched EXACTLY: the host is the one place that\n    // decides what counts as an overflow (R3-588) — a relay `too-large` it did NOT\n    // translate must not sneak in as a substring of some message.\n    code === HOST_CONTEXT_OVERFLOW_CODE ||\n    code.includes('context_length') ||\n    code.includes('context-length') ||\n    /context (?:length|window)|maximum context|too many tokens|prompt is too long|reduce the length/.test(msg)\n  );\n}\n\n// The user turn injected when a truncated (`max_tokens`) turn emitted tool calls: we\n// fail the partial calls rather than execute them (F3), and tell the model to retry.\nconst TRUNCATED_RETRY_TEXT =\n  'That turn was cut off at the token limit mid tool-call, so the call was NOT executed. ' +\n  'Emit a smaller step: fewer/shorter tool calls, or a smaller file write.';\n\n/** The cumulative cache counters, as the event fields that carry them. A counter no\n *  provider has reported yet is OMITTED, never zeroed: \"the provider reports nothing\"\n *  and \"the provider cached nothing\" are different facts and the consumer must be able\n *  to tell them apart. Three event payloads state this rule; this is the one place it\n *  is spelled out. */\nconst cacheCounterFields = (\n  cacheReadTokens: number | undefined,\n  cacheWriteTokens: number | undefined,\n): { cacheReadTokens?: number; cacheWriteTokens?: number } => ({\n  ...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),\n  ...(cacheWriteTokens !== undefined ? { cacheWriteTokens } : {}),\n});\n\n/**\n * Drive the agent loop to completion. Returns the full message transcript\n * (including the kickoff user turn). Stops when the model returns without tool\n * calls (or a terminal stop reason), when the token budget is exhausted, or when\n * `maxTurns` (a large safety-stop) is reached. With a `contextWindow` set, the loop\n * accounts tokens and compacts automatically so it can run long.\n */\nexport async function runAgent(opts: RunAgentOptions): Promise<AgentMessage[]> {\n  const { client, tools, execute, system, prompt, events, signal, steering, pause } = opts;\n  const maxTurns = opts.maxTurns ?? 100;\n  const maxNudges = opts.maxNudges ?? 1;\n  const maxTruncationRetries = opts.maxTruncationRetries ?? 2;\n  const window = opts.contextWindow;\n  const reserveTokens = opts.reserveTokens ?? (window ? Math.floor(window * 0.25) : 0);\n  const keepRecentTurns = opts.keepRecentTurns ?? 8;\n\n  let messages: AgentMessage[] = [...(opts.history ?? []), { role: 'user', content: [{ type: 'text', text: prompt }] }];\n\n  // Consecutive-stall counter: how many times in a row we've nudged a no-tool-call\n  // turn. Reset to 0 by any turn that DOES call a tool, so the budget is per stall\n  // *episode*, not per run.\n  let nudges = 0;\n  let truncationRetries = 0;\n  // True when the previous iteration's model turn was cut short by an `interrupt`\n  // steer, so the injected correction can be reported as an interruption.\n  let interruptedLastTurn = false;\n  // Running context size (provider-reported when available) + cumulative spend.\n  let contextTokens = 0;\n  let spentTokens = 0;\n  // R3-336 — cumulative cache accounting. `undefined` until a provider reports\n  // something, so \"reports nothing\" stays distinguishable from \"cached nothing\".\n  let cacheReadTokens: number | undefined;\n  let cacheWriteTokens: number | undefined;\n\n  // Compact the transcript and, when anything was actually folded in, adopt the\n  // compacted messages, re-estimate the running context, and report it. Returns how\n  // many messages were summarized — 0 means \"there was nothing to compact\", which is\n  // what the overflow-recovery path below treats as unrecoverable. Both compaction\n  // sites (near-window before a request, and hard-overflow recovery) go through here,\n  // so they cannot drift on what a compaction updates or reports.\n  const compactAndReport = async (): Promise<number> => {\n    const { messages: compacted, summarizedCount } = await compactTranscript(messages, client, keepRecentTurns);\n    if (summarizedCount > 0) {\n      messages = compacted;\n      contextTokens = estimateTokens(messages);\n      events?.onCompact?.({ summarizedCount, ...cacheCounterFields(cacheReadTokens, cacheWriteTokens) });\n    }\n    return summarizedCount;\n  };\n\n  for (let turn = 0; turn < maxTurns; turn++) {\n    // R3-224 (§3.3): the stop button, checked between turns. Combined with the\n    // per-request `signal` below (which aborts the in-flight upstream turn), this\n    // halts \"the loop between tool calls AND aborts the in-flight LLM request\".\n    if (signal?.aborted) break;\n\n    // R3-562 (§7 R-ARD-20a): if the region is hidden, stop HERE — at the boundary, with\n    // the previous turn's tool batch fully paired — and wait for the reveal. Placed after\n    // the stop check and before the steer drain so a correction queued while hidden is\n    // applied on the way back in, as the very next turn, rather than a turn late.\n    if (pause?.isPaused()) {\n      events?.onPause?.({ turn });\n      await pause.whenResumed(signal);\n      events?.onResume?.({ turn });\n      // `whenResumed` also resolves on abort, so a run stopped while hidden lands here\n      // rather than awaiting a reveal that never comes.\n      if (signal?.aborted) break;\n    }\n\n    // R3-333: apply any queued corrections at the TURN BOUNDARY, before the next\n    // request, so the model's very next turn reflects them. Draining here (rather\n    // than at the point of arrival) is what makes a steer safe: whatever the loop\n    // was doing — streaming a turn, running a tool batch — has finished.\n    if (steering) {\n      const steers = steering.drain();\n      if (steers.length) {\n        messages.push({\n          role: 'user',\n          content: steers.map((m) => ({ type: 'text' as const, text: steerWireText(m) })),\n        });\n        events?.onSteer?.({ messages: steers, interrupted: interruptedLastTurn });\n      }\n      interruptedLastTurn = false;\n      steering.rearm();\n    }\n\n    // Compact BEFORE the next request when the running context is near the window.\n    if (shouldCompact(contextTokens, window, reserveTokens)) await compactAndReport();\n\n    // The in-flight turn is abortable by EITHER verb: STOP (ends the run) or an\n    // `interrupt`-mode STEER (ends the turn, keeps the run). They are composed into\n    // one per-turn signal, and told apart in the catch by asking which fired.\n    const turnAbort = anySignal([signal, steering?.interrupt]);\n    // Capture what the model had streamed when a steer cut in, so the interrupted\n    // turn is recorded as what actually happened rather than dropped.\n    let partialText = '';\n    const onTextDelta = (text: string): void => {\n      partialText += text;\n      events?.onAssistantDelta?.(text);\n    };\n    const sendTurn = () =>\n      client.createMessage({\n        system,\n        messages,\n        tools,\n        // R3-333's local `onTextDelta` (it captures the partial text a steer may cut\n        // short) — NOT `events.onAssistantDelta` directly.\n        onTextDelta,\n        // R3-335's reasoning stream rides alongside it.\n        onReasoningDelta: events?.onReasoningDelta,\n        // R3-333: STOP composed with the steer INTERRUPT, so either verb ends the turn.\n        signal: turnAbort.signal,\n      });\n    let res: ModelResponse;\n    try {\n      try {\n        res = await sendTurn();\n      } catch (e) {\n        // Recover-then-retry on a hard context-overflow (exit-c): compact once and\n        // re-send. If there is nothing to compact, or the retry also overflows, the\n        // error propagates — a bounded recovery, never a dead loop.\n        if (turnAbort.signal.aborted || !isContextOverflow(e)) throw e;\n        if ((await compactAndReport()) === 0) throw e;\n        res = await sendTurn();\n      }\n    } catch (e) {\n      // R3-224: a mid-turn abort surfaces as a thrown (Abort/Stream)Error. Treat it\n      // as a CLEAN stop — return the transcript so far — not a failure to bubble up.\n      if (signal?.aborted) {\n        turnAbort.dispose();\n        break;\n      }\n      // R3-333: the SAME thrown abort, but from a steer — the run continues. Record\n      // the turn the user cut short (an assistant message, so the transcript keeps\n      // strict role alternation and replay shows the interruption where it happened),\n      // then loop: the drain at the top of the next iteration injects the correction.\n      if (steering?.interrupt.aborted) {\n        turnAbort.dispose();\n        messages.push({\n          role: 'assistant',\n          content: [{ type: 'text', text: partialText.trim() || INTERRUPTED_TURN_TEXT }],\n        });\n        events?.onAssistantText?.(partialText.trim() || INTERRUPTED_TURN_TEXT);\n        interruptedLastTurn = true;\n        continue;\n      }\n      turnAbort.dispose();\n      throw e;\n    }\n    turnAbort.dispose();\n\n    // Token accounting (R3-220): prefer the provider `usage`, else estimate. `turnCost`\n    // is what this turn billed (input + output); `contextTokens` is the current window\n    // occupancy (drives compaction); `spentTokens` is cumulative run spend (input is\n    // re-billed every turn, so summing turnCost is the true cost signal).\n    const turnCost = res.usage\n      ? res.usage.inputTokens + res.usage.outputTokens\n      : estimateTokens(messages) + Math.ceil(textOf(res.content).length / 4);\n    contextTokens = turnCost;\n    spentTokens += turnCost;\n    if (res.usage?.cacheReadTokens !== undefined) {\n      cacheReadTokens = (cacheReadTokens ?? 0) + res.usage.cacheReadTokens;\n    }\n    if (res.usage?.cacheWriteTokens !== undefined) {\n      cacheWriteTokens = (cacheWriteTokens ?? 0) + res.usage.cacheWriteTokens;\n    }\n    events?.onUsage?.({\n      contextTokens,\n      window,\n      spentTokens,\n      ...cacheCounterFields(cacheReadTokens, cacheWriteTokens),\n    });\n\n    const assistantText = textOf(res.content);\n    if (assistantText) events?.onAssistantText?.(assistantText);\n    // R3-335: reasoning stays IN the message sequence — a provider that requires the\n    // block echoed back gets it from `messages`, not from a side channel.\n    for (const b of res.content) if (b.type === 'reasoning') events?.onReasoning?.(b);\n    messages.push({ role: 'assistant', content: res.content });\n\n    const toolUses = res.content.filter((b): b is ToolUseBlock => b.type === 'tool_use');\n\n    // Truncated-tool-call guard (F3): a `max_tokens` turn that emitted tool calls\n    // was cut off mid-call, so its args may be partial. Do NOT execute them — fail\n    // each with an error tool_result (keeps the conversation well-formed) and\n    // re-prompt for a smaller step, bounded by maxTruncationRetries.\n    if (res.stopReason === 'max_tokens' && toolUses.length > 0) {\n      events?.onTruncatedToolCall?.();\n      const failed: ContentBlock[] = toolUses.map((c) => ({\n        type: 'tool_result',\n        tool_use_id: c.id,\n        content: 'tool call truncated by the token limit — not executed',\n        is_error: true,\n      }));\n      failed.push({ type: 'text', text: TRUNCATED_RETRY_TEXT });\n      messages.push({ role: 'user', content: failed });\n      if (++truncationRetries > maxTruncationRetries) break;\n      continue;\n    }\n    truncationRetries = 0;\n\n    if (toolUses.length === 0) {\n      // No tool calls. Usually the model is genuinely done — but GLM/OpenRouter\n      // intermittently ends with \"I'll read the files…\" or an empty turn after a\n      // tool error and no call (findings §2). Nudge such a STALL back into action\n      // once (per episode), respecting terminal stops and a real wrap-up.\n      const stall = TERMINAL_STOPS.has(res.stopReason) ? null : detectStall(assistantText);\n      if (stall && nudges < maxNudges) {\n        nudges++;\n        events?.onNudge?.(stall);\n        messages.push({ role: 'user', content: [{ type: 'text', text: NUDGE_TEXT }] });\n        continue;\n      }\n      // R3-333 follow-up: the model is done, but the user queued something while it\n      // was working. Continue rather than end — the drain at the top of the next\n      // iteration turns the queued message into the next turn's prompt. This is the\n      // difference between a follow-up and a restart.\n      if (steering?.hasPending()) continue;\n      break;\n    }\n\n    nudges = 0; // a productive turn clears the stall budget\n\n    const results: ToolResultBlock[] = [];\n    // R3-339 — image parts produced by tools this turn. They ride in the SAME user\n    // message as the results (after them), because a `tool_result`'s content is a string\n    // on the wire; this is the shape both host adapters map to their provider.\n    const images: ImageBlock[] = [];\n    for (const call of toolUses) {\n      events?.onToolUse?.(call.name, call.input);\n      let outcome: ToolOutcome;\n      try {\n        outcome = await execute(call.name, call.input);\n      } catch (e) {\n        // A thrown executor error (e.g. host `forbidden`) becomes an error\n        // tool_result so the model sees the gate's verdict and can adapt.\n        const code = (e as { code?: string })?.code;\n        const msg = (e as Error)?.message ?? String(e);\n        outcome = { content: code ? `${code}: ${msg}` : msg, isError: true };\n      }\n      events?.onToolResult?.(call.name, outcome);\n      results.push({\n        type: 'tool_result',\n        tool_use_id: call.id,\n        content: outcome.content,\n        is_error: outcome.isError,\n      });\n      if (outcome.images?.length) images.push(...outcome.images);\n    }\n    messages.push({ role: 'user', content: [...results, ...images] });\n\n    // Runaway-cost guard: stop once cumulative spend passes the budget (the token/\n    // spend bound that replaces the old raw turn cap). Compaction keeps a single\n    // request small; this bounds the whole run.\n    if (opts.tokenBudget && spentTokens >= opts.tokenBudget) {\n      events?.onBudgetStop?.({ spentTokens, tokenBudget: opts.tokenBudget });\n      break;\n    }\n  }\n\n  return messages;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BA,2BAAqG;AAgOrG,MAAM,SAAS,CAAC,WACd,OACG,OAAO,CAAC,MAAsB,EAAE,SAAS,MAAM,EAC/C,IAAI,CAAC,MAAM,EAAE,IAAI,EACjB,KAAK,EAAE;AAOZ,MAAM,iBAAiB,oBAAI,IAAI,CAAC,cAAc,SAAS,CAAC;AAGxD,MAAM,YACJ;AAEF,MAAM,UACJ;AAWK,SAAS,YAAY,MAAkC;AAC5D,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,QAAQ,KAAK,CAAC,EAAG,QAAO;AAC5B,MAAI,UAAU,KAAK,CAAC,EAAG,QAAO;AAC9B,SAAO;AACT;AAOO,MAAM,aACX;AAOK,SAAS,eAAe,UAAkC;AAC/D,MAAI,QAAQ;AACZ,aAAW,KAAK,UAAU;AACxB,eAAW,KAAK,EAAE,SAAS;AACzB,UAAI,EAAE,SAAS,OAAQ,UAAS,EAAE,KAAK;AAAA,eAC9B,EAAE,SAAS,WAAY,UAAS,KAAK,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK;AAAA,eACxE,EAAE,SAAS,cAAe,UAAS,EAAE,QAAQ;AAAA,eAK7C,EAAE,SAAS,QAAS,UAAS,EAAE,KAAK;AAAA,eAGpC,EAAE,SAAS,YAAa,UAAS,EAAE,KAAK,UAAU,EAAE,cAAc,UAAU;AAAA,IACvF;AAAA,EACF;AACA,SAAO,KAAK,KAAK,QAAQ,CAAC;AAC5B;AAIO,SAAS,cAAc,eAAuB,QAA4B,eAAgC;AAC/G,MAAI,CAAC,UAAU,UAAU,EAAG,QAAO;AACnC,SAAO,gBAAgB,SAAS;AAClC;AAIO,MAAM,oBAAoB;AAEjC,MAAM,iBACJ;AAKF,MAAM,sBACJ;AAUF,eAAsB,kBACpB,UACA,QACA,iBACgE;AAChE,MAAI,SAAS,UAAU,kBAAkB,EAAG,QAAO,EAAE,UAAU,iBAAiB,EAAE;AAMlF,QAAM,WAAW,KAAK,IAAI,GAAG,SAAS,SAAS,eAAe;AAC9D,MAAI,YAAY;AAChB,WAAS,IAAI,UAAU,IAAI,SAAS,QAAQ,KAAK;AAC/C,QAAI,SAAS,CAAC,EAAE,SAAS,aAAa;AACpC,kBAAY;AACZ;AAAA,IACF;AAAA,EACF;AACA,MAAI,cAAc,IAAI;AACpB,aAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAI,SAAS,CAAC,EAAE,SAAS,aAAa;AACpC,oBAAY;AACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,MAAI,aAAa,EAAG,QAAO,EAAE,UAAU,iBAAiB,EAAE;AAE1D,QAAM,OAAO,SAAS,MAAM,GAAG,SAAS;AAaxC,QAAM,OAAO,SAAS,IAAI,UAAU,EAAE,IAAI,aAAa,EAAE,MAAM,SAAS;AAIxE,QAAM,cAA8B,KAAK,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;AAC/F,QAAM,UAAU,YAAY,YAAY,SAAS,CAAC;AAClD,MAAI,WAAW,QAAQ,SAAS,QAAQ;AACtC,YAAQ,UAAU,CAAC,GAAG,QAAQ,SAAS,EAAE,MAAM,QAAQ,MAAM,oBAAoB,CAAC;AAAA,EACpF,OAAO;AACL,gBAAY,KAAK,EAAE,MAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,oBAAoB,CAAC,EAAE,CAAC;AAAA,EAC3F;AAEA,MAAI,cAAc;AAClB,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,cAAc,EAAE,QAAQ,gBAAgB,UAAU,aAAa,OAAO,CAAC,EAAE,CAAC;AACnG,kBAAc,OAAO,IAAI,OAAO,EAAE,KAAK,KAAK;AAAA,EAC9C,QAAQ;AAGN,WAAO,EAAE,UAAU,iBAAiB,EAAE;AAAA,EACxC;AAEA,QAAM,aAA2B;AAAA,IAC/B,MAAM;AAAA,IACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,oBAAoB,YAAY,CAAC;AAAA,EACnE;AACA,SAAO,EAAE,UAAU,CAAC,YAAY,GAAG,IAAI,GAAG,iBAAiB,KAAK,OAAO;AACzE;AAKA,SAAS,WAAW,GAAiB,MAA2C;AAC9E,MAAI,CAAC,EAAE,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,EAAG,QAAO;AACpD,QAAM,OAAO,EAAE,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI;AACpD,SAAO,EAAE,MAAM,EAAE,MAAM,SAAS,KAAK,SAAS,OAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG,CAAC,EAAE;AACpF;AAGA,MAAM,aAAa,CAAC,MAAkC,WAAW,GAAG,OAAO;AAE3E,MAAM,gBAAgB,CAAC,MAAkC,WAAW,GAAG,WAAW;AAS3E,MAAM,6BAA6B;AAInC,SAAS,kBAAkB,GAAqB;AACrD,QAAM,OAAQ,GAAa,WAAW,OAAO,CAAC,GAAG,YAAY;AAC7D,QAAM,OAAO,OAAQ,GAA0B,QAAQ,EAAE,EAAE,YAAY;AACvE;AAAA;AAAA;AAAA;AAAA,IAIE,SAAS,8BACT,KAAK,SAAS,gBAAgB,KAC9B,KAAK,SAAS,gBAAgB,KAC9B,iGAAiG,KAAK,GAAG;AAAA;AAE7G;AAIA,MAAM,uBACJ;AAQF,MAAM,qBAAqB,CACzB,iBACA,sBAC6D;AAAA,EAC7D,GAAI,oBAAoB,SAAY,EAAE,gBAAgB,IAAI,CAAC;AAAA,EAC3D,GAAI,qBAAqB,SAAY,EAAE,iBAAiB,IAAI,CAAC;AAC/D;AASA,eAAsB,SAAS,MAAgD;AAC7E,QAAM,EAAE,QAAQ,OAAO,SAAS,QAAQ,QAAQ,QAAQ,QAAQ,UAAU,MAAM,IAAI;AACpF,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,uBAAuB,KAAK,wBAAwB;AAC1D,QAAM,SAAS,KAAK;AACpB,QAAM,gBAAgB,KAAK,kBAAkB,SAAS,KAAK,MAAM,SAAS,IAAI,IAAI;AAClF,QAAM,kBAAkB,KAAK,mBAAmB;AAEhD,MAAI,WAA2B,CAAC,GAAI,KAAK,WAAW,CAAC,GAAI,EAAE,MAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,OAAO,CAAC,EAAE,CAAC;AAKpH,MAAI,SAAS;AACb,MAAI,oBAAoB;AAGxB,MAAI,sBAAsB;AAE1B,MAAI,gBAAgB;AACpB,MAAI,cAAc;AAGlB,MAAI;AACJ,MAAI;AAQJ,QAAM,mBAAmB,YAA6B;AACpD,UAAM,EAAE,UAAU,WAAW,gBAAgB,IAAI,MAAM,kBAAkB,UAAU,QAAQ,eAAe;AAC1G,QAAI,kBAAkB,GAAG;AACvB,iBAAW;AACX,sBAAgB,eAAe,QAAQ;AACvC,cAAQ,YAAY,EAAE,iBAAiB,GAAG,mBAAmB,iBAAiB,gBAAgB,EAAE,CAAC;AAAA,IACnG;AACA,WAAO;AAAA,EACT;AAEA,WAAS,OAAO,GAAG,OAAO,UAAU,QAAQ;AAI1C,QAAI,QAAQ,QAAS;AAMrB,QAAI,OAAO,SAAS,GAAG;AACrB,cAAQ,UAAU,EAAE,KAAK,CAAC;AAC1B,YAAM,MAAM,YAAY,MAAM;AAC9B,cAAQ,WAAW,EAAE,KAAK,CAAC;AAG3B,UAAI,QAAQ,QAAS;AAAA,IACvB;AAMA,QAAI,UAAU;AACZ,YAAM,SAAS,SAAS,MAAM;AAC9B,UAAI,OAAO,QAAQ;AACjB,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,QAAiB,UAAM,oCAAc,CAAC,EAAE,EAAE;AAAA,QAChF,CAAC;AACD,gBAAQ,UAAU,EAAE,UAAU,QAAQ,aAAa,oBAAoB,CAAC;AAAA,MAC1E;AACA,4BAAsB;AACtB,eAAS,MAAM;AAAA,IACjB;AAGA,QAAI,cAAc,eAAe,QAAQ,aAAa,EAAG,OAAM,iBAAiB;AAKhF,UAAM,gBAAY,gCAAU,CAAC,QAAQ,UAAU,SAAS,CAAC;AAGzD,QAAI,cAAc;AAClB,UAAM,cAAc,CAAC,SAAuB;AAC1C,qBAAe;AACf,cAAQ,mBAAmB,IAAI;AAAA,IACjC;AACA,UAAM,WAAW,MACf,OAAO,cAAc;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA;AAAA;AAAA,MAGA;AAAA;AAAA,MAEA,kBAAkB,QAAQ;AAAA;AAAA,MAE1B,QAAQ,UAAU;AAAA,IACpB,CAAC;AACH,QAAI;AACJ,QAAI;AACF,UAAI;AACF,cAAM,MAAM,SAAS;AAAA,MACvB,SAAS,GAAG;AAIV,YAAI,UAAU,OAAO,WAAW,CAAC,kBAAkB,CAAC,EAAG,OAAM;AAC7D,YAAK,MAAM,iBAAiB,MAAO,EAAG,OAAM;AAC5C,cAAM,MAAM,SAAS;AAAA,MACvB;AAAA,IACF,SAAS,GAAG;AAGV,UAAI,QAAQ,SAAS;AACnB,kBAAU,QAAQ;AAClB;AAAA,MACF;AAKA,UAAI,UAAU,UAAU,SAAS;AAC/B,kBAAU,QAAQ;AAClB,iBAAS,KAAK;AAAA,UACZ,MAAM;AAAA,UACN,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,YAAY,KAAK,KAAK,2CAAsB,CAAC;AAAA,QAC/E,CAAC;AACD,gBAAQ,kBAAkB,YAAY,KAAK,KAAK,0CAAqB;AACrE,8BAAsB;AACtB;AAAA,MACF;AACA,gBAAU,QAAQ;AAClB,YAAM;AAAA,IACR;AACA,cAAU,QAAQ;AAMlB,UAAM,WAAW,IAAI,QACjB,IAAI,MAAM,cAAc,IAAI,MAAM,eAClC,eAAe,QAAQ,IAAI,KAAK,KAAK,OAAO,IAAI,OAAO,EAAE,SAAS,CAAC;AACvE,oBAAgB;AAChB,mBAAe;AACf,QAAI,IAAI,OAAO,oBAAoB,QAAW;AAC5C,yBAAmB,mBAAmB,KAAK,IAAI,MAAM;AAAA,IACvD;AACA,QAAI,IAAI,OAAO,qBAAqB,QAAW;AAC7C,0BAAoB,oBAAoB,KAAK,IAAI,MAAM;AAAA,IACzD;AACA,YAAQ,UAAU;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG,mBAAmB,iBAAiB,gBAAgB;AAAA,IACzD,CAAC;AAED,UAAM,gBAAgB,OAAO,IAAI,OAAO;AACxC,QAAI,cAAe,SAAQ,kBAAkB,aAAa;AAG1D,eAAW,KAAK,IAAI,QAAS,KAAI,EAAE,SAAS,YAAa,SAAQ,cAAc,CAAC;AAChF,aAAS,KAAK,EAAE,MAAM,aAAa,SAAS,IAAI,QAAQ,CAAC;AAEzD,UAAM,WAAW,IAAI,QAAQ,OAAO,CAAC,MAAyB,EAAE,SAAS,UAAU;AAMnF,QAAI,IAAI,eAAe,gBAAgB,SAAS,SAAS,GAAG;AAC1D,cAAQ,sBAAsB;AAC9B,YAAM,SAAyB,SAAS,IAAI,CAAC,OAAO;AAAA,QAClD,MAAM;AAAA,QACN,aAAa,EAAE;AAAA,QACf,SAAS;AAAA,QACT,UAAU;AAAA,MACZ,EAAE;AACF,aAAO,KAAK,EAAE,MAAM,QAAQ,MAAM,qBAAqB,CAAC;AACxD,eAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAC/C,UAAI,EAAE,oBAAoB,qBAAsB;AAChD;AAAA,IACF;AACA,wBAAoB;AAEpB,QAAI,SAAS,WAAW,GAAG;AAKzB,YAAM,QAAQ,eAAe,IAAI,IAAI,UAAU,IAAI,OAAO,YAAY,aAAa;AACnF,UAAI,SAAS,SAAS,WAAW;AAC/B;AACA,gBAAQ,UAAU,KAAK;AACvB,iBAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,WAAW,CAAC,EAAE,CAAC;AAC7E;AAAA,MACF;AAKA,UAAI,UAAU,WAAW,EAAG;AAC5B;AAAA,IACF;AAEA,aAAS;AAET,UAAM,UAA6B,CAAC;AAIpC,UAAM,SAAuB,CAAC;AAC9B,eAAW,QAAQ,UAAU;AAC3B,cAAQ,YAAY,KAAK,MAAM,KAAK,KAAK;AACzC,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,QAAQ,KAAK,MAAM,KAAK,KAAK;AAAA,MAC/C,SAAS,GAAG;AAGV,cAAM,OAAQ,GAAyB;AACvC,cAAM,MAAO,GAAa,WAAW,OAAO,CAAC;AAC7C,kBAAU,EAAE,SAAS,OAAO,GAAG,IAAI,KAAK,GAAG,KAAK,KAAK,SAAS,KAAK;AAAA,MACrE;AACA,cAAQ,eAAe,KAAK,MAAM,OAAO;AACzC,cAAQ,KAAK;AAAA,QACX,MAAM;AAAA,QACN,aAAa,KAAK;AAAA,QAClB,SAAS,QAAQ;AAAA,QACjB,UAAU,QAAQ;AAAA,MACpB,CAAC;AACD,UAAI,QAAQ,QAAQ,OAAQ,QAAO,KAAK,GAAG,QAAQ,MAAM;AAAA,IAC3D;AACA,aAAS,KAAK,EAAE,MAAM,QAAQ,SAAS,CAAC,GAAG,SAAS,GAAG,MAAM,EAAE,CAAC;AAKhE,QAAI,KAAK,eAAe,eAAe,KAAK,aAAa;AACvD,cAAQ,eAAe,EAAE,aAAa,aAAa,KAAK,YAAY,CAAC;AACrE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}