{"version":3,"file":"run-CsrBbtj_.mjs","names":[],"sources":["../src/workers/scope.ts","../src/workers/pane.ts","../src/workers/project-config.ts","../src/router/limits.ts","../src/workers/budget.ts","../src/workers/routing.ts","../src/workers/proxy/server.ts","../src/workers/tool-errors.ts","../src/workers/launch.ts","../src/workers/endreason.ts","../src/workers/slim-profile.ts","../src/workers/controls.ts","../src/workers/policy.ts","../src/workers/engines/image.ts","../src/workers/tree.ts","../src/workers/operator.ts","../src/workers/handoff.ts","../src/workers/waiting.ts","../src/workers/wait-on.ts","../src/workers/agentish.ts","../src/workers/report.ts","../src/workers/denials.ts","../src/workers/contract-addendum.ts","../src/workers/deadline.ts","../src/workers/browser-tools.ts","../src/workers/codex.ts","../src/workers/run.ts"],"sourcesContent":["/**\n * scope.ts — which workers \"belong\" to the terminal asking about them.\n *\n * Two identities, in priority order:\n *\n *   1. AIBroker's session registry (~/.aibroker/session-names.json): iTerm\n *      session UUID → session name. The launching session's UUID is stored in\n *      each worker's status file as `session.id`, so every pane of a named\n *      session sees its workers — regardless of tab layout.\n *   2. The iTerm tab key (`w<window>t<tab>` prefix of ITERM_SESSION_ID): the\n *      original heuristic, kept as the fallback when no registry entry\n *      matches (AIBroker absent, unnamed session, non-iTerm terminal).\n *\n * If neither is available, viewers fall back to \"all workers\".\n *\n * A third case: workers spawned from a Claude Code Bash tool (the\n * orchestrator pattern) inherit the orchestrator's `ITERM_SESSION_ID`, which\n * identifies the orchestrator's own pane, not a pane for the sub-worker —\n * so it does not key the sub-worker's tab. Those record `spawnerSession`,\n * the orchestrator's claude session id bridged through the status line's\n * session map (see below), so their tab still claims them.\n */\n\nimport { existsSync, readFileSync, renameSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { loadStatus, type WorkerStatus } from \"./status.js\";\n\nexport const AIBROKER_REGISTRY = join(homedir(), \".aibroker\", \"session-names.json\");\n\n/**\n * `w<window>t<tab>` prefix of an iTerm session id, \"\" if empty or malformed.\n * Panes split from the same tab share this prefix and differ only in `p<n>`.\n */\nexport function tabKey(term: string): string {\n  if (!term) return \"\";\n  const head = term.split(\"p\", 1)[0];\n  const parts = head.slice(1).split(\"t\");\n  if (head.startsWith(\"w\") && parts.length === 2 && parts.every((p) => /^\\d+$/.test(p))) {\n    return head;\n  }\n  return \"\";\n}\n\n/** Tab key of the iTerm tab this process runs in, \"\" if not inside iTerm2. */\nexport function currentTabKey(env: NodeJS.ProcessEnv = process.env): string {\n  return tabKey(env.ITERM_SESSION_ID ?? \"\");\n}\n\n/** The iTerm UUID part of an ITERM_SESSION_ID (after the last colon). */\nexport function itermUuid(term: string): string {\n  if (!term) return \"\";\n  return term.split(\":\").pop() ?? \"\";\n}\n\nexport interface SessionIdentity {\n  id: string;\n  name: string;\n}\n\n/**\n * Resolve the AIBroker session for an ITERM_SESSION_ID. Reads the persistent\n * name registry (the same store `aibroker_rename` writes); returns null when\n * AIBroker is absent or the session is not in it — callers then use the tab\n * key, which is the pre-AIBroker behaviour.\n */\nexport function resolveSession(\n  term: string,\n  registryPath: string = AIBROKER_REGISTRY\n): SessionIdentity | null {\n  const uuid = itermUuid(term);\n  if (!uuid) return null;\n  let names: Record<string, unknown>;\n  try {\n    if (!existsSync(registryPath)) return null;\n    names = JSON.parse(readFileSync(registryPath, \"utf8\")) as Record<string, unknown>;\n  } catch {\n    return null;\n  }\n  const name = names[uuid];\n  if (typeof name !== \"string\" || !name) return null;\n  return { id: uuid, name };\n}\n\n/**\n * True when `worker` was launched from `term` (the terminal asking about it).\n * Session-id match first; the tab key only when no registry entry matched —\n * a session without a name keeps the tab-scoped behaviour it always had.\n */\nexport function workerInScope(worker: WorkerStatus, term: string): boolean {\n  if (!term) return false;\n  const uuid = itermUuid(term);\n  if (worker.session?.id && uuid) return worker.session.id === uuid;\n  return tabKey(worker.term) === tabKey(term) && tabKey(term) !== \"\";\n}\n\n/**\n * Registry key for pane files: the session id when AIBroker knows this\n * terminal, else the tab key, else the raw iTerm UUID.\n */\nexport function scopeKey(term: string): string {\n  const session = resolveSession(term);\n  if (session) return session.id;\n  return currentTabKey() || itermUuid(term);\n}\n\n/** `[Name]` when the worker has an AIBroker session name, else \"\". */\nexport function sessionTag(worker: { session?: { name?: string } | null }): string {\n  return worker.session?.name ? `[${worker.session.name}]` : \"\";\n}\n\n/**\n * Who owns a worker, for machine-wide listings (every session must be able to\n * attribute every live worker): the AIBroker session name when the worker or\n * the registry knows it, else the owning tab key, else \"?\" — never nothing,\n * or the row would be exactly the kind of unattributable entry this exists\n * to prevent.\n */\nexport function ownerSessionName(\n  s: Pick<WorkerStatus, \"session\" | \"term\">,\n  registryPath: string = AIBROKER_REGISTRY\n): string {\n  if (s.session?.name) return s.session.name;\n  const uuid = s.session?.id || itermUuid(s.term ?? \"\");\n  if (uuid) {\n    const resolved = resolveSession(uuid, registryPath);\n    if (resolved?.name) return resolved.name;\n  }\n  const tab = tabKey(s.term ?? \"\");\n  return tab ? `tab ${tab}` : \"?\";\n}\n\n// ---------------------------------------------------------------------------\n// spawner sessions — attribution for workers launched from a Claude Code Bash\n// ---------------------------------------------------------------------------\n\n/**\n * Claude Code exports neither ITERM_SESSION_ID nor its own session id to the\n * Bash tool, so a `pai worker run` from an orchestrator has no terminal to\n * record and its status would be unattributable. The status line is the one\n * process that sees both identities at once — the payload's session_id and\n * the tab's ITERM_SESSION_ID — so it bridges them: every refresh writes this\n * cwd-keyed map, and the runner reads it back at spawn time.\n */\nexport interface SessionMapEntry {\n  session: string;\n  ts: number;\n  /**\n   * The tab's ITERM_SESSION_ID, when the status line could see one — the\n   * bridge back from a claude session id to the terminal it runs in\n   * (supervision uses it to push worker events into that terminal).\n   * Absent on entries written before the field existed.\n   */\n  term?: string;\n}\n\n/** How old a map entry may be for a spawn to adopt it (status lines refresh constantly while a session lives). */\nexport const SPAWNER_SESSION_TTL_MS = 10 * 60_000;\n\n/** Map entries not refreshed within this window are pruned on write. */\nconst SESSION_MAP_PRUNE_MS = 60 * 60_000;\n\nexport function sessionMapPath(logDir: string): string {\n  return join(logDir, \"claude-session-map.json\");\n}\n\n/**\n * Record that the claude session `session` renders its status line in `cwd`\n * (and, when known, in iTerm session `term`). Never throws — a broken map\n * must not break the bar. Prunes stale entries; skips the write when the\n * entry is unchanged and fresh.\n */\nexport function recordSessionMapEntry(\n  logDir: string,\n  cwd: string,\n  session: string,\n  term?: string,\n  now: number = Date.now()\n): void {\n  if (!cwd || !session) return;\n  const path = sessionMapPath(logDir);\n  let map: Record<string, SessionMapEntry> = {};\n  try {\n    if (existsSync(path)) {\n      map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n    }\n  } catch {\n    map = {}; // a damaged map is rewritten, never fatal\n  }\n  const prev = map[cwd];\n  if (\n    prev &&\n    prev.session === session &&\n    (prev.term ?? \"\") === (term ?? \"\") &&\n    now - prev.ts < 60_000\n  ) {\n    return;\n  }\n  const pruned: Record<string, SessionMapEntry> = {};\n  for (const [dir, e] of Object.entries(map)) {\n    if (now - e.ts < SESSION_MAP_PRUNE_MS) pruned[dir] = e;\n  }\n  pruned[cwd] = { session, ts: now, ...(term ? { term } : {}) };\n  try {\n    mkdirSync(logDir, { recursive: true });\n    const tmp = `${path}.tmp`;\n    writeFileSync(tmp, JSON.stringify(pruned), \"utf8\");\n    renameSync(tmp, path);\n  } catch {\n    // unwritable log dir: no attribution this round, nothing else breaks\n  }\n}\n\n/**\n * The claude session a new run was spawned by, when it can be known: a run\n * inside another worker inherits its spawner (chain stages keep the\n * orchestrator's tab that way); otherwise a fresh map entry for `cwd`.\n */\nexport function resolveSpawnerSession(\n  logDir: string,\n  cwd: string,\n  env: NodeJS.ProcessEnv = process.env,\n  now: number = Date.now()\n): string | null {\n  const parentId = env.PAI_WORKER_ID;\n  if (parentId) {\n    const inherited = loadStatus(logDir, parentId)?.spawnerSession;\n    if (inherited) return inherited;\n  }\n  const path = sessionMapPath(logDir);\n  if (!existsSync(path) || !cwd) return null;\n  try {\n    const entry = (JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>)[cwd];\n    if (entry && entry.session && now - entry.ts < SPAWNER_SESSION_TTL_MS) return entry.session;\n  } catch {\n    // a damaged map simply attributes nothing\n  }\n  return null;\n}\n\n/**\n * The iTerm UUID a claude session last rendered its status line in, when the\n * map knows one: the freshest entry naming that session. This is the reverse\n * of resolveSpawnerSession — worker → orchestrator there, orchestrator →\n * terminal here — and it exists so daemon-side pushes (supervision) can reach\n * a session that has no iTerm identity of its own in any worker status.\n */\nexport function itermForClaudeSession(\n  logDir: string,\n  claudeSession: string,\n  now: number = Date.now()\n): string | null {\n  const path = sessionMapPath(logDir);\n  if (!existsSync(path) || !claudeSession) return null;\n  let map: Record<string, SessionMapEntry>;\n  try {\n    map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n  } catch {\n    return null;\n  }\n  let best: SessionMapEntry | null = null;\n  for (const e of Object.values(map)) {\n    if (e.session !== claudeSession || !e.term) continue;\n    // a stale entry names a tab the session left; freshness orders them\n    if (now - e.ts >= SESSION_MAP_PRUNE_MS) continue;\n    if (!best || e.ts > best.ts) best = e;\n  }\n  const term = best?.term;\n  return term ? itermUuid(term) : null;\n}\n","/**\n * pane.ts — the per-worker follow pane in iTerm2.\n *\n * One small-font pane per worker, stacked in a right-hand column: the first\n * worker of a scope splits the launching session vertically, every further\n * one splits the lowest live worker pane horizontally, so panes stack top to\n * bottom. The split never sizes the new session — that would grow the whole\n * window; instead the window's bounds are read before the split and restored\n * right after, so the panes share the space the window already had. The follow\n * command is part of the split itself — iTerm creates the new session already\n * running it — so no text is ever typed into any session afterwards; a separate\n * typing step raced the operator's keystrokes (focus briefly sits on the new\n * pane after a split, and in one observed failure the command landed in the\n * operator's own shell). A split still makes the new session the tab's active\n * one — the scripts re-select the launching session right after, so a pane\n * opening never steals the keystrokes the operator is typing. Each\n * pane runs `pai worker follow <id> --auto-exit <n>` under the `pai-worker`\n * dynamic profile (Close Sessions On End), so panes disappear by themselves.\n *\n * Panes are tracked per scope (AIBroker session id, else tab key) in\n * <logDir>/panes/<key>/<worker id>.json (one file per worker), iTerm session unique id, newest first —\n * the split lands below the lowest live worker pane.\n *\n * The dynamic profile's font is the family of iTerm's DEFAULT profile (the\n * `Default Bookmark Guid` entry in New Bookmarks) at workers.pane.fontSize —\n * never the launching session's profile, which the Python version did and\n * which produced iTerm's \"unknown parent name\" dialog whenever the two\n * differed. `Dynamic Profile Parent Name` is only written when that name\n * actually exists in New Bookmarks; when iTerm's preferences cannot be read\n * the profile is still written, with font Menlo-Regular and no parent.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { execFileSync } from \"node:child_process\";\nimport {\n  existsSync,\n  mkdirSync,\n  readdirSync,\n  mkdtempSync,\n  readFileSync,\n  renameSync,\n  rmSync,\n  writeFileSync,\n} from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { WorkersConfig } from \"./config.js\";\nimport { panesDir, statusPath } from \"./paths.js\";\nimport { itermUuid, scopeKey } from \"./scope.js\";\n\nexport const PROFILE_NAME = \"pai-worker\";\n\n/** Where the dynamic profile lives (env override keeps tests off the real one). */\nexport function dynamicProfilePath(): string {\n  return process.env.PAI_WORKER_PROFILE ?? join(\n    homedir(),\n    \"Library\",\n    \"Application Support\",\n    \"iTerm2\",\n    \"DynamicProfiles\",\n    \"pai-worker.json\"\n  );\n}\n\n// ---------------------------------------------------------------------------\n// JXA scripts (arguments are passed as argv items, never interpolated).\n// JXA, not AppleScript: iTerm's AppleScript window collection can get stuck\n// failing every enumeration (\"every window doesn't understand the count\n// message\", -1708) while the JXA object model keeps answering.\n// ---------------------------------------------------------------------------\n\n// Shared prelude: locate the window/tab/session with a given unique id.\nconst FIND_SESSION = `function findSession(app, id) {\n    var ws = app.windows();\n    for (var i = 0; i < ws.length; i++) {\n        var ts = ws[i].tabs();\n        for (var j = 0; j < ts.length; j++) {\n            var ss = ts[j].sessions();\n            for (var k = 0; k < ss.length; k++) {\n                if (ss[k].id() === id) return { w: ws[i], t: ts[j], s: ss[k], all: ss };\n            }\n        }\n    }\n    return null;\n}`;\n\n// Smallest height a follow pane may be split down to (the visible-pane gate\n// refuses anything under 8 rows).\nconst MIN_ROWS = 8;\n\n// Equalise the follow panes' heights. iTerm exposes no pane position, so panes\n// are grouped by column count: panes stacked in one column share it, and a\n// group of equal width splits its summed rows evenly. ponytail: two columns of\n// equal width but unequal stack height are balanced as one group; upgrade to\n// real geometry if iTerm ever exposes it. Expects the `live` session list.\nconst REBALANCE = `var groups = {};\n    live.forEach(function (o) { var c = o.columns(); (groups[c] = groups[c] || []).push([o, o.rows()]); });\n    Object.keys(groups).forEach(function (c) {\n        var g = groups[c];\n        var want = Math.floor(g.reduce(function (a, e) { return a + e[1]; }, 0) / g.length);\n        g.forEach(function (e) { try { e[0].rows = want; } catch (x) {} });\n    });`;\n\n// One pane per worker: split the launching session vertically, or the lowest\n// live candidate horizontally. Returns \"<live ids>,|<new session id>\".\n// Exported for the script-content tests (window size, argv-only arguments).\nexport const WORKER_SPLIT_SCRIPT = `${FIND_SESSION}\nvar MIN_ROWS = ${MIN_ROWS};\nfunction run(argv) {\n    var targetID = argv[0], candList = argv[1], followCmd = argv[2], profileName = argv[3];\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"notrunning\";\n    var f = findSession(app, targetID);\n    if (!f) return \"notfound\";\n    // sizing the new session would grow the whole window: pin the window's\n    // bounds now and restore them after the split, so the panes share the\n    // existing space\n    var winBounds = f.w.bounds();\n    var sessIDs = f.all.map(function (o) { return o.id(); });\n    var lived = [];\n    var live = [];\n    (candList === \"\" ? [] : candList.split(\",\")).forEach(function (cid) {\n        var at = sessIDs.indexOf(cid);\n        if (at < 0) return;\n        lived.push(cid);\n        live.push(f.all[at]);\n    });\n    // split the LARGEST worker pane (by area), never the newest: halving the\n    // newest pane caps a tab at ~4 workers. Stack (horizontal split) while\n    // both halves keep >= MIN_ROWS rows, else add a column (vertical split).\n    var splitS = null;\n    live.forEach(function (o) { if (!splitS || o.columns() * o.rows() > splitS.columns() * splitS.rows()) splitS = o; });\n    var stack = splitS && Math.floor(splitS.rows() / 2) >= MIN_ROWS + 1;\n    // the follow command is part of the split itself: the pane is born already\n    // running it, so no text is ever typed into any session — a typing step\n    // raced the operator's keystrokes and could even land in the operator's\n    // own session\n    var newS;\n    if (!splitS || !stack) {\n        var base = splitS || f.s;\n        newS = profileName === \"\"\n            ? base.splitVerticallyWithDefaultProfile({ command: followCmd })\n            : base.splitVertically({ withProfile: profileName, command: followCmd });\n    } else {\n        newS = profileName === \"\"\n            ? splitS.splitHorizontallyWithDefaultProfile({ command: followCmd })\n            : splitS.splitHorizontally({ withProfile: profileName, command: followCmd });\n    }\n    live.push(newS);\n    ${REBALANCE}\n    // a split makes the new session the tab's active one: re-select the\n    // launching session so focus returns to where the operator was typing,\n    // never the new pane\n    try { f.s.select(); } catch (e) {}\n    try { f.w.bounds = winBounds; } catch (e) {}\n    return lived.map(function (l) { return l + \",\"; }).join(\"\") + \"|\" + newS.id();\n}`;\n\n// TTys of every session in the launching session's tab (no-worker pane variant),\n// comma-joined.\nconst TAB_TTYS_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"notrunning\";\n    var f = findSession(app, argv[0]);\n    if (!f) return \"notfound\";\n    return f.all.map(function (o) { return o.tty(); }).join(\",\");\n}`;\n\n// Bounds (x1, y1, x2, y2, comma-joined) of the window hosting one iTerm\n// session — read-only, for `pai worker pane <id> --check`. Exported for the\n// script-content tests (reads bounds, never sets them).\nexport const WINDOW_BOUNDS_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"notrunning\";\n    var f = findSession(app, argv[0]);\n    if (!f) return \"notfound\";\n    var b = f.w.bounds();\n    return [b.x, b.y, b.x + b.width, b.y + b.height].join(\", \");\n}`;\n\n// Split the launching session vertically and run followCmd in the new pane.\n// Exported for the script-content tests (focus stays on the launching session).\nexport const SPLIT_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n    var followCmd = argv[1];\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"notrunning\";\n    var f = findSession(app, argv[0]);\n    if (!f) return \"notfound\";\n    // the follow command is part of the split itself (see WORKER_SPLIT_SCRIPT):\n    // nothing is ever typed into a session afterwards\n    f.s.splitVerticallyWithDefaultProfile({ command: followCmd });\n    // keep the tab's active session where it was (see WORKER_SPLIT_SCRIPT)\n    try { f.s.select(); } catch (e) {}\n    return \"opened\";\n}`;\n\n// ---------------------------------------------------------------------------\n// osascript / ps / defaults helpers\n// ---------------------------------------------------------------------------\n\n/** Run a JXA script with argv. Rejects when osascript itself cannot run. */\nfunction osascript(script: string, args: string[]): Promise<{ stdout: string; stderr: string }> {\n  return new Promise((resolve, reject) => {\n    const proc = spawn(\"osascript\", [\"-l\", \"JavaScript\", \"-\", ...args], { stdio: [\"pipe\", \"pipe\", \"pipe\"] });\n    let out = \"\";\n    let err = \"\";\n    proc.stdout.on(\"data\", (c: Buffer) => (out += c.toString(\"utf8\")));\n    proc.stderr.on(\"data\", (c: Buffer) => (err += c.toString(\"utf8\")));\n    proc.on(\"error\", reject);\n    proc.on(\"close\", () => resolve({ stdout: out, stderr: err }));\n    proc.stdin.write(script);\n    proc.stdin.end();\n  });\n}\n\nfunction psOutput(format: string): string {\n  try {\n    return execFileSync(\"ps\", [\"-axo\", format], { encoding: \"utf8\" });\n  } catch {\n    return \"\";\n  }\n}\n\n/** TTys of running `worker follow` processes, normalised to /dev/ttysNNN. */\nfunction followTtys(): Set<string> {\n  const ttys = new Set<string>();\n  for (const line of psOutput(\"tty=,command=\").split(\"\\n\")) {\n    const parts = line.trim().split(/\\s+/, 2);\n    if (parts.length < 2 || parts[0] === \"ps\") continue;\n    if (!/(^|\\/)(worker-follow|pai worker follow|glm-ps follow)\\b/.test(parts[1])) continue;\n    if (parts[0].startsWith(\"ttys\")) ttys.add(`/dev/${parts[0]}`);\n  }\n  return ttys;\n}\n\n/** True while a `follow <wid>` process runs (its pane shows that worker). */\nexport function workerPaneOpen(wid: string): boolean {\n  const pat = new RegExp(`(?:worker follow|worker-follow|glm-ps follow) ${wid.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b`);\n  return psOutput(\"command=\").split(\"\\n\").some((ln) => pat.test(ln));\n}\n\n/** One entry of iTerm's New Bookmarks (the fields the pane cares about). */\nexport interface Bookmark {\n  Name?: string;\n  Guid?: string;\n  \"Normal Font\"?: string;\n}\n\n/** What reading iTerm's preferences yielded — `error` carries the reason. */\nexport interface PrefsRead {\n  bookmarks: Bookmark[];\n  defaultGuid: string | null;\n  error: string | null;\n}\n\n/** plutil's message for an ExecFileSync failure, else the exception text. */\nfunction toolErr(e: unknown): string {\n  const err = e as { stderr?: string | Buffer; message?: string };\n  const stderr = typeof err.stderr === \"string\" ? err.stderr : err.stderr?.toString(\"utf8\");\n  return (stderr || err.message || String(e)).trim();\n}\n\n/**\n * Read New Bookmarks and Default Bookmark Guid from an iTerm preferences\n * plist. Key-scoped `plutil -extract`, never a whole-file JSON conversion:\n * real iTerm preferences carry `<date>` objects (SULastCheckTime & friends)\n * which `plutil -convert json` rejects outright — \"Invalid object in plist\n * for JSON format\" — so the whole-file read always failed and the dynamic\n * profile was never written.\n */\nexport function readItermPlist(plistPath: string): PrefsRead {\n  let bookmarks: Bookmark[] = [];\n  try {\n    const out = execFileSync(\n      \"plutil\",\n      [\"-extract\", \"New Bookmarks\", \"json\", \"-o\", \"-\", plistPath],\n      { encoding: \"utf8\", timeout: 10_000 }\n    );\n    const parsed = JSON.parse(out) as unknown;\n    if (Array.isArray(parsed)) bookmarks = parsed as Bookmark[];\n  } catch (e) {\n    return { bookmarks: [], defaultGuid: null, error: `extracting New Bookmarks: ${toolErr(e)}` };\n  }\n  let defaultGuid: string | null = null;\n  try {\n    const out = execFileSync(\n      \"plutil\",\n      [\"-extract\", \"Default Bookmark Guid\", \"raw\", \"-o\", \"-\", plistPath],\n      { encoding: \"utf8\", timeout: 10_000 }\n    );\n    defaultGuid = out.trim() || null;\n  } catch {\n    // no default guid set — defaultBookmarkFrom() yields null, fine\n  }\n  return { bookmarks, defaultGuid, error: null };\n}\n\n/** iTerm's exported preferences, via a temp plist (no shell pipe involved). */\nexport function itermPrefs(): PrefsRead {\n  let dir: string | null = null;\n  try {\n    dir = mkdtempSync(join(tmpdir(), \"pai-iterm-\"));\n    const plist = join(dir, \"iterm2.plist\");\n    execFileSync(\"defaults\", [\"export\", \"com.googlecode.iterm2\", plist], {\n      encoding: \"utf8\",\n      timeout: 10_000,\n    });\n    return readItermPlist(plist);\n  } catch (e) {\n    return { bookmarks: [], defaultGuid: null, error: `defaults export: ${toolErr(e)}` };\n  } finally {\n    if (dir) {\n      try {\n        rmSync(dir, { recursive: true, force: true });\n      } catch {\n        // best effort cleanup of a temp dir\n      }\n    }\n  }\n}\n\n/** The New Bookmarks entry of that profile name, null if absent. */\nfunction bookmarkFrom(read: PrefsRead, named: string): Bookmark | null {\n  return read.bookmarks.find((b) => b.Name === named) ?? null;\n}\n\n/** The bookmark iTerm marks default, null when it is missing or unreadable. */\nfunction defaultBookmarkFrom(read: PrefsRead): Bookmark | null {\n  return read.defaultGuid\n    ? read.bookmarks.find((b) => b.Guid === read.defaultGuid) ?? null\n    : null;\n}\n\n/** The default profile, read fresh from iTerm's preferences. */\nexport function defaultBookmark(): Bookmark | null {\n  return defaultBookmarkFrom(itermPrefs());\n}\n\n/**\n * The pane profile's font: the family of iTerm's default profile at\n * `fontSize` points (\"MesloLGLNFM-Regular 18\" + 13 → \"MesloLGLNFM-Regular 13\"),\n * or \"Menlo-Regular <fontSize>\" when the default font cannot be read.\n */\nexport function paneFont(font: string | undefined, fontSize: number): string {\n  const idx = (font ?? \"\").lastIndexOf(\" \");\n  if (idx > 0) {\n    const family = font!.slice(0, idx);\n    if (!Number.isNaN(parseFloat(font!.slice(idx + 1)))) return `${family} ${fontSize}`;\n  }\n  return `Menlo-Regular ${fontSize}`;\n}\n\n/** Write the pai-worker dynamic profile; iTerm2 loads that directory itself. */\nexport function writeDynamicProfile(\n  parent: Bookmark | null,\n  fontSize: number,\n  read: () => PrefsRead = itermPrefs\n): void {\n  const profile: Record<string, unknown> = {\n    Name: PROFILE_NAME,\n    Guid: \"pai-worker-dynamic-profile\",\n    \"Normal Font\": paneFont(parent?.[\"Normal Font\"], fontSize),\n    \"Close Sessions On End\": true,\n  };\n  // Parent name only when iTerm actually has that profile loaded — a name it\n  // does not know makes every split open an error dialog instead of a pane.\n  if (parent?.Name && bookmarkFrom(read(), parent.Name)) {\n    profile[\"Dynamic Profile Parent Name\"] = parent.Name;\n  }\n  const path = dynamicProfilePath();\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  const tmp = `${path}.tmp`;\n  writeFileSync(tmp, JSON.stringify({ Profiles: [profile] }, null, 2) + \"\\n\", \"utf8\");\n  renameSync(tmp, path);\n}\n\nlet warnedPrefs = false;\n\n/**\n * Profile for new worker panes, creating the small-font one on demand.\n * \"\" means \"split with the default profile\" (also the fallback when the\n * dynamic profile does not show up in time).\n *\n * The profile file is written whenever it is missing — even when iTerm's\n * preferences cannot be read; then it gets font \"Menlo-Regular <fontSize>\"\n * and no parent, and the reason is logged to stderr once.\n */\nexport async function followProfile(\n  fontSize: number,\n  read: () => PrefsRead = itermPrefs\n): Promise<string> {\n  const first = read();\n  const parent = defaultBookmarkFrom(first);\n  const path = dynamicProfilePath();\n  const existed = existsSync(path);\n  if (!existed) writeDynamicProfile(parent, fontSize, () => first);\n  if (!parent) {\n    if (!warnedPrefs) {\n      warnedPrefs = true;\n      const reason = first.error ?? \"no profile is marked default\";\n      const what = existed\n        ? `keeping ${path} as-is`\n        : `wrote ${path} with Menlo-Regular ${fontSize} and no parent`;\n      process.stderr.write(\n        `pai worker pane: cannot read iTerm's default profile (${reason}) — ${what}\\n`\n      );\n    }\n    if (first.error) return \"\"; // polling cannot succeed against unreadable prefs\n  }\n  for (let i = 0; i < 10; i++) {\n    // iTerm2 picks DynamicProfiles up quickly; allow it 2 s\n    if (bookmarkFrom(read(), PROFILE_NAME)) return PROFILE_NAME;\n    await new Promise((r) => setTimeout(r, 200));\n  }\n  process.stderr.write(`pai worker pane: profile ${PROFILE_NAME} not visible, splitting with default\\n`);\n  return \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Pane registry\n// ---------------------------------------------------------------------------\n\nexport interface PaneEntry {\n  session: string;\n  worker: string;\n  opened: string;\n  /** the scope (registry key) the entry was opened under */\n  scope?: string;\n}\n\n/**\n * One file per worker, `<panes>/<scope>/<worker id>.json`: concurrent opens\n * for the same scope write different files, so none can be lost (the former\n * shared `<scope>.json` list was a read-modify-write that dropped entries when\n * workers started a second apart). Legacy `<scope>.json` lists are still read.\n */\nfunction readEntry(path: string, scope: string): PaneEntry[] {\n  try {\n    const v = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n    return (Array.isArray(v) ? v : [v])\n      .filter((e): e is PaneEntry => !!e && typeof (e as PaneEntry).session === \"string\" && typeof (e as PaneEntry).worker === \"string\")\n      .map((e) => ({ ...e, scope }));\n  } catch {\n    return [];\n  }\n}\n\n/** Registry entries of one scope (`scope` set) or of all scopes, oldest first. */\nexport function loadRegistry(logDir: string, scope?: string): PaneEntry[] {\n  const root = panesDir(logDir);\n  const out: PaneEntry[] = [];\n  let names: string[] = [];\n  try {\n    names = readdirSync(root);\n  } catch {\n    return out;\n  }\n  for (const name of names.sort()) {\n    const full = join(root, name);\n    if (name.endsWith(\".json\")) {\n      const sc = name.slice(0, -5);\n      if (!scope || sc === scope) out.push(...readEntry(full, sc));\n    } else if (!scope || name === scope) {\n      try {\n        for (const f of readdirSync(full)) if (f.endsWith(\".json\")) out.push(...readEntry(join(full, f), name));\n      } catch {\n        /* not a directory */\n      }\n    }\n  }\n  return out.sort((x, y) => x.opened.localeCompare(y.opened));\n}\n\nfunction entryPath(logDir: string, e: PaneEntry): string {\n  return join(panesDir(logDir), e.scope ?? \"\", `${e.worker}.json`);\n}\n\nfunction saveEntry(logDir: string, e: PaneEntry): void {\n  const path = entryPath(logDir, e);\n  mkdirSync(dirname(path), { recursive: true });\n  const tmp = `${path}.${process.pid}.tmp`;\n  writeFileSync(tmp, JSON.stringify({ session: e.session, worker: e.worker, opened: e.opened }, null, 1), \"utf8\");\n  renameSync(tmp, path);\n}\n\n/** Forget an entry (its per-worker file; a legacy list is rewritten without it). */\nfunction dropEntry(logDir: string, e: PaneEntry): void {\n  const legacy = join(panesDir(logDir), `${e.scope}.json`);\n  rmSync(entryPath(logDir, e), { force: true });\n  if (existsSync(legacy)) {\n    const rest = readEntry(legacy, e.scope ?? \"\").filter((x) => x.session !== e.session);\n    writeFileSync(legacy, JSON.stringify(rest.map(({ scope: _s, ...x }) => x), null, 1), \"utf8\");\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Ending panes of finished workers\n// ---------------------------------------------------------------------------\n\n// One listing for every caller. Each property is read on its own: iTerm 3.7 can\n// throw on name() for a session, which must not drop the others.\nconst LIST_SESSIONS_SCRIPT = `function run(argv) {\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"\";\n    function get(s, k) { try { return String(s[k]()); } catch (e) { return \"\"; } }\n    var out = \"\";\n    app.windows().forEach(function (w) {\n        w.tabs().forEach(function (t) {\n            t.sessions().forEach(function (s) {\n                var id = get(s, \"id\");\n                function v(n) { try { return String(s.variable({ named: n })); } catch (e) { return \"\"; } }\n                if (id) out += id + \"\\\\t\" + get(s, \"tty\") + \"\\\\t\" + v(\"jobPid\") + \"\\\\t\" + v(\"pid\") + \"\\\\t\" + get(s, \"name\") + \"\\\\n\";\n            });\n        });\n    });\n    return out;\n}`;\n\nexport interface ItermSession {\n  id: string;\n  tty: string;\n  /** Pid of the session's foreground job (variable \"jobPid\"), undefined when iTerm gave none. */\n  jobPid?: number;\n  /** Pid of the session's root process (variable \"pid\"). */\n  pid?: number;\n  /** Undefined when iTerm would not give the session's name. */\n  name?: string;\n}\n\n/** Every open iTerm session. [] when iTerm is not running; rejects when the script fails. */\nexport async function listItermSessions(\n  run: (script: string, args: string[]) => Promise<{ stdout: string }> = osascript\n): Promise<ItermSession[]> {\n  const p = await run(LIST_SESSIONS_SCRIPT, []);\n  return p.stdout\n    .split(\"\\n\")\n    .filter(Boolean)\n    .map((l) => {\n      const [id, tty, jobPid, pid, ...name] = l.split(\"\\t\");\n      return { id, tty, jobPid: Number(jobPid) || undefined, pid: Number(pid) || undefined, name: name.join(\"\\t\") || undefined };\n    });\n}\n\n// Ends one session by its unique id, never a window or the app.\nconst END_SESSION_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"notrunning\";\n    var f = findSession(app, argv[0]);\n    if (!f) return \"notfound\";\n    f.s.close();\n    return \"closed\";\n}`;\n\n// Re-balance the follow panes (argv[0]: comma-joined session ids) after a close.\nexport const REBALANCE_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n    var ids = argv[0].split(\",\");\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return \"notrunning\";\n    var f = null;\n    for (var i = 0; i < ids.length && !f; i++) f = findSession(app, ids[i]);\n    if (!f) return \"notfound\";\n    var winBounds = f.w.bounds();\n    var live = f.all.filter(function (o) { return ids.indexOf(o.id()) >= 0; });\n    ${REBALANCE}\n    try { f.w.bounds = winBounds; } catch (e) {}\n    return \"balanced\";\n}`;\n\n/** The real iTerm / tmux calls, swappable so tests never touch either. */\nexport interface PaneOps {\n  listIterm(): Promise<Array<{ id: string; name: string }>>;\n  closeIterm(id: string): Promise<boolean>;\n  listTmux(): Array<{ id: string; command: string }>;\n  closeTmux(id: string): boolean;\n  /** Equalise the heights of the given follow panes (optional: tests omit it). */\n  rebalanceIterm?(ids: string[]): Promise<void>;\n}\n\nexport const realPaneOps: PaneOps = {\n  async listIterm() {\n    if (process.platform !== \"darwin\") return [];\n    const sessions = await listItermSessions();\n    // iTerm renames a pane after its running command (\"node\"), so the follow\n    // command is looked up by the session's tty instead\n    const cmdByTty = new Map<string, string>();\n    for (const line of psOutput(\"tty=,command=\").split(\"\\n\")) {\n      const [tty, ...rest] = line.trim().split(/\\s+/);\n      if (tty && tty !== \"??\") cmdByTty.set(`/dev/${tty}`, rest.join(\" \"));\n    }\n    return sessions.map((x) => ({ id: x.id, name: `${x.name ?? \"\"} ${cmdByTty.get(x.tty) ?? \"\"}`.trim() }));\n  },\n  async closeIterm(id) {\n    const p = await osascript(END_SESSION_SCRIPT, [id]);\n    return p.stdout.trim() === \"closed\";\n  },\n  async rebalanceIterm(ids) {\n    if (ids.length) await osascript(REBALANCE_SCRIPT, [ids.join(\",\")]);\n  },\n  listTmux() {\n    try {\n      const out = execFileSync(\"tmux\", [\"list-panes\", \"-a\", \"-F\", \"#{pane_id}\\t#{pane_start_command}\"], {\n        encoding: \"utf8\",\n        stdio: [\"ignore\", \"pipe\", \"ignore\"],\n      });\n      return out.split(\"\\n\").filter(Boolean).map((l) => ({ id: l.split(\"\\t\")[0], command: l.split(\"\\t\").slice(1).join(\" \") }));\n    } catch {\n      return [];\n    }\n  },\n  closeTmux(id) {\n    try {\n      execFileSync(\"tmux\", [\"kill-pane\", \"-t\", id], { stdio: \"ignore\" });\n      return true;\n    } catch {\n      return false;\n    }\n  },\n};\n\nconst FOLLOW_ID = /worker[- ]follow (\\d{8}-\\d{6}-\\d+)/;\n\n/** End one worker's registered iTerm pane, found by worker id. True if a pane was closed. */\nexport async function closePaneForWorker(logDir: string, wid: string, ops: PaneOps = realPaneOps): Promise<boolean> {\n  let closed = false;\n  for (const e of loadRegistry(logDir).filter((x) => x.worker === wid)) {\n    try {\n      if (await ops.closeIterm(e.session)) closed = true;\n    } catch {\n      /* iTerm gone: the entry is stale either way */\n    }\n    dropEntry(logDir, e);\n  }\n  return closed;\n}\n\n/**\n * The follow pane closing its own iTerm session on exit, so a pane closes even\n * when the runner died and even under a profile without Close Sessions On End.\n * No-op outside iTerm.\n */\nexport async function closeOwnPane(env: NodeJS.ProcessEnv = process.env, ops: PaneOps = realPaneOps): Promise<boolean> {\n  const uid = itermUuid(env.ITERM_SESSION_ID ?? \"\");\n  if (!uid) return false;\n  return ops.closeIterm(uid).catch(() => false);\n}\n\nexport interface CloseEndedResult {\n  closed: string[];\n  kept: string[];\n}\n\n/**\n * Close every open follow pane whose worker has ended: registry entries first\n * (by worker id), then any iTerm session named after / tmux pane started with a\n * `worker follow <id>` command of an ended worker. `ended(id)` says whether\n * that worker is over; unknown ids are never closed. `dryRun` only reports.\n */\nexport async function closeEndedPanes(\n  logDir: string,\n  ended: (wid: string) => boolean,\n  dryRun: boolean,\n  ops: PaneOps = realPaneOps\n): Promise<CloseEndedResult> {\n  const res: CloseEndedResult = { closed: [], kept: [] };\n  const seen = new Set<string>();\n  const handle = async (backend: \"iterm\" | \"tmux\", id: string, worker: string, entry?: PaneEntry): Promise<void> => {\n    const key = `${backend}:${id}`;\n    if (seen.has(key)) return;\n    seen.add(key);\n    if (!ended(worker)) {\n      res.kept.push(`${worker} ${key} (running)`);\n      return;\n    }\n    if (!dryRun) {\n      const ok = backend === \"tmux\" ? ops.closeTmux(id) : await ops.closeIterm(id).catch(() => false);\n      if (entry) dropEntry(logDir, entry); // stale whether or not the pane still existed\n      if (!ok && !entry) return;\n    }\n    res.closed.push(`${worker} ${key}${dryRun ? \" (dry run)\" : \"\"}`);\n  };\n  // iTerm unreachable: no live-session view, registry entries are still handled\n  const sessions = await ops.listIterm().catch(() => []);\n  const open = new Set(sessions.map((s) => s.id));\n  for (const e of loadRegistry(logDir)) {\n    // iTerm answered and the session is gone: a stale entry, forgotten without a close\n    if (sessions.length && !open.has(e.session)) {\n      if (!dryRun) dropEntry(logDir, e);\n      continue;\n    }\n    await handle(\"iterm\", e.session, e.worker, e);\n  }\n  for (const s of sessions) {\n    const m = FOLLOW_ID.exec(s.name);\n    if (m) await handle(\"iterm\", s.id, m[1]);\n  }\n  for (const t of ops.listTmux()) {\n    const m = FOLLOW_ID.exec(t.command);\n    if (m) await handle(\"tmux\", t.id, m[1]);\n  }\n  // closed panes leave uneven neighbours: re-balance what is left\n  if (!dryRun && res.closed.length) {\n    await ops.rebalanceIterm?.(loadRegistry(logDir).map((e) => e.session)).catch(() => undefined);\n  }\n  return res;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry points\n// ---------------------------------------------------------------------------\n\n/**\n * The command a worker pane runs: one exec-able line with no shell syntax —\n * iTerm's split command parameter execs without a shell (proven live: panes\n * survive `sleep 30` and a single-quoted `/bin/sh -c`, die instantly on\n * `export …; …` or a redirect), so the command must be one exec-able line.\n * No --auto-exit: a finished worker's pane stays until the operator closes it\n * (exit the follow) or the next pane opened in the same scope replaces it.\n * Absolute node on the CLI entry file also sidesteps the panes' bare launchd\n * PATH (no pai, no node) and the `#!/usr/bin/env node` shebang.\n */\nexport function followCommand(wid: string): string {\n  return `${process.execPath} ${absoluteCliPath()} worker follow ${wid}`;\n}\n\n/** Absolute path of the running pai CLI; falls back to PATH lookup at spawn. */\nfunction absoluteCliPath(): string {\n  const argv1 = process.argv[1] ?? \"\";\n  if (argv1.endsWith(\"pai\")) return argv1;\n  return \"pai\";\n}\n\n// ---------------------------------------------------------------------------\n// Host session — which iTerm session launched this process\n// ---------------------------------------------------------------------------\n\n/** Lookups behind resolveHostSession, swappable so tests never touch ps or iTerm. */\nexport interface HostDeps {\n  /** [ppid, tty] of a pid, null when ps cannot say. tty is as ps prints it (\"ttys004\", \"??\"). */\n  psOf(pid: number): [number, string] | null;\n  /** Command line per pid, one batched ps call; pids ps does not know are absent. */\n  commandsOf(pids: number[]): Map<number, string>;\n  /** Every open iTerm session. Empty when iTerm is not running or unreachable. */\n  listSessions(): Promise<ItermSession[]>;\n}\n\nexport const realHostDeps: HostDeps = {\n  psOf(pid) {\n    try {\n      const out = execFileSync(\"ps\", [\"-o\", \"ppid=,tty=\", \"-p\", String(pid)], {\n        encoding: \"utf8\",\n        env: { ...process.env, LC_ALL: \"C\" },\n      }).trim();\n      const [ppid, tty] = out.split(/\\s+/);\n      return ppid && tty ? [Number(ppid), tty] : null;\n    } catch {\n      return null;\n    }\n  },\n  commandsOf(pids) {\n    const m = new Map<number, string>();\n    if (!pids.length) return m;\n    let out = \"\";\n    try {\n      out = execFileSync(\"ps\", [\"-o\", \"pid=,command=\", \"-p\", pids.join(\",\")], {\n        encoding: \"utf8\",\n        env: { ...process.env, LC_ALL: \"C\" },\n      });\n    } catch (e) {\n      out = (e as { stdout?: string }).stdout ?? \"\"; // ps exits 1 when some pid is gone\n    }\n    for (const l of out.split(\"\\n\")) {\n      const r = /^\\s*(\\d+)\\s+(.*)$/.exec(l);\n      if (r) m.set(Number(r[1]), r[2]);\n    }\n    return m;\n  },\n  async listSessions() {\n    if (process.platform !== \"darwin\") return [];\n    try {\n      return await listItermSessions();\n    } catch {\n      return [];\n    }\n  },\n};\n\n/** Pids from `start` up through its parents (self included), stopping at init or an unknown pid. */\nfunction ancestorPids(deps: HostDeps, start: number): number[] {\n  const pids: number[] = [];\n  for (let pid = start; pids.length < 64 && pid > 1; ) {\n    pids.push(pid);\n    const row = deps.psOf(pid);\n    if (!row) break;\n    pid = row[0];\n  }\n  return pids;\n}\n\n/** /dev/ttysNNN of the nearest ancestor (self included) that has a real tty, null when none. */\nexport function ancestorTty(deps: HostDeps = realHostDeps, start: number = process.pid): string | null {\n  let pid = start;\n  for (let i = 0; i < 64 && pid > 1; i++) {\n    const row = deps.psOf(pid);\n    if (!row) return null;\n    const [ppid, tty] = row;\n    if (tty && tty !== \"??\" && tty !== \"?\") return tty.startsWith(\"/dev/\") ? tty : `/dev/${tty}`;\n    pid = ppid;\n  }\n  return null;\n}\n\nexport interface HostSession {\n  /** iTerm unique id of the host session, \"\" when none was found. */\n  uid: string;\n  /** ITERM_SESSION_ID-shaped value for status/scope: the env value when it names the host, else the bare uid. \"\" when none. */\n  term: string;\n  /** Which step resolved it: \"foreground jobPid\", \"attach <short>\", \"tty\", \"env\", or \"none\". */\n  via: string;\n  reason?: string;\n}\n\nconst UUID_RE = \"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\";\n\n/** Claude session id from a Claude process command line: `--session-id <uuid>` or `--bg-pty-host …/pty/<short>.sock`. */\nfunction claudeIdOf(cmd: string): string | undefined {\n  return new RegExp(`--session-id[ =](${UUID_RE})`, \"i\").exec(cmd)?.[1] ?? /--bg-pty-host\\s+\\S*\\/pty\\/([0-9a-f]{8})\\.sock/i.exec(cmd)?.[1];\n}\n\n/**\n * The one place that decides which iTerm session hosts this process; first hit\n * wins:\n *  1. foreground: a session whose jobPid is CLAUDE_PID or an ancestor pid\n *  2. background (`claude attach <short>`): the Claude session id from env or an\n *     ancestor's command line, matched against each session's jobPid command;\n *     several matches are ambiguous and fail loudly\n *  3. the tty of the nearest ancestor that has one\n *  4. ITERM_SESSION_ID, only when it names a live session — a reattached session\n *     keeps its old id in the process env for good\n */\nexport async function resolveHostSession(\n  env: NodeJS.ProcessEnv = process.env,\n  deps: HostDeps = realHostDeps,\n  start: number = process.pid\n): Promise<HostSession> {\n  const sessions = await deps.listSessions();\n  const envTerm = env.ITERM_SESSION_ID ?? \"\";\n  const envUid = itermUuid(envTerm);\n  const found = (s: { id: string }, via: string): HostSession => ({ uid: s.id, term: s.id === envUid ? envTerm : s.id, via });\n  const notes: string[] = [];\n\n  const ancestors = ancestorPids(deps, start);\n  const jobPids = sessions.flatMap((s) => (s.jobPid ? [s.jobPid] : []));\n  const cmds = deps.commandsOf([...new Set([...ancestors, ...jobPids])]);\n\n  const fg = new Set(ancestors);\n  if (Number(env.CLAUDE_PID)) fg.add(Number(env.CLAUDE_PID));\n  const byJob = sessions.find((s) => s.jobPid && fg.has(s.jobPid));\n  if (byJob) return found(byJob, \"foreground jobPid\");\n  notes.push(`foreground: no session jobPid is CLAUDE_PID ${env.CLAUDE_PID || \"(unset)\"} or an ancestor`);\n\n  const cid = env.CLAUDE_CODE_SESSION_ID || ancestors.map((p) => claudeIdOf(cmds.get(p) ?? \"\")).find(Boolean);\n  if (cid) {\n    const short = cid.slice(0, 8);\n    const attach = new RegExp(`\\\\battach\\\\s+(${short}|${cid})\\\\b`, \"i\");\n    const hits = sessions.filter((s) => s.jobPid && attach.test(cmds.get(s.jobPid) ?? \"\"));\n    if (hits.length === 1) return found(hits[0], `attach ${short}`);\n    const why = hits.length ? `ambiguous, ${hits.length} sessions run attach ${short}: ${hits.map((s) => s.id).join(\", \")}` : `no session runs attach ${short}`;\n    if (hits.length) return { uid: \"\", term: \"\", via: \"none\", reason: [...notes, `attach: ${why}`].join(\"; \") };\n    notes.push(`attach: ${why}`);\n  } else {\n    notes.push(\"attach: no Claude session id in env or ancestors\");\n  }\n\n  const tty = ancestorTty(deps, start);\n  const byTty = tty ? sessions.find((s) => s.tty === tty) : undefined;\n  if (byTty) return found(byTty, \"tty\");\n  notes.push(tty ? `tty: ancestor tty ${tty} matched no session` : \"tty: no tty ancestor\");\n\n  if (envUid && sessions.some((s) => s.id === envUid)) return { uid: envUid, term: envTerm, via: \"env\" };\n  notes.push(`env: ITERM_SESSION_ID ${envTerm ? `${envTerm} is not live` : \"is unset\"}`);\n\n  const why = sessions.length === 0 ? \"iTerm2 not running or unreachable\" : \"no live iTerm2 session matched\";\n  return { uid: \"\", term: \"\", via: \"none\", reason: `${why}; ${notes.join(\"; \")}` };\n}\n\nexport type PaneBackend = \"tmux\" | \"iterm\" | \"none\";\n\n/** The one place that decides where a follow pane opens: tmux (any OS) > iTerm2 (macOS) > nowhere. */\nexport function paneBackend(\n  env: NodeJS.ProcessEnv = process.env,\n  platform: string = process.platform,\n  hostTerm: string = \"\"\n): PaneBackend {\n  if (env.TMUX) return \"tmux\";\n  if (platform === \"darwin\" && (env.ITERM_SESSION_ID || hostTerm)) return \"iterm\";\n  return \"none\";\n}\n\nconst NO_HOST = \"pai worker pane: no live iTerm2 host session (foreground, attach, tty lookups found none, ITERM_SESSION_ID not live)\";\n\nexport function noPaneMessage(wid?: string): string {\n  return `no pane support here, use: pai worker follow${wid ? ` ${wid}` : \"\"}`;\n}\n\n/** Split the current tmux window horizontally (right-hand column) and run cmd in it, focus stays put. */\nfunction tmuxSplit(cmd: string): void {\n  const target = process.env.TMUX_PANE ? [\"-t\", process.env.TMUX_PANE] : [];\n  try {\n    execFileSync(\"tmux\", [\"split-window\", \"-h\", \"-d\", \"-l\", \"40%\", ...target, cmd], { stdio: \"pipe\" });\n  } catch (e) {\n    const err = e as { stderr?: Buffer; message?: string };\n    throw new Error(`pai worker pane: tmux split-window failed: ${(err.stderr?.toString() || err.message || \"\").trim().slice(0, 200)}`);\n  }\n}\n\n/** Worker state straight from its status file (pane.ts cannot import status.ts: it imports us). */\nfunction workerEnded(logDir: string, wid: string): boolean {\n  try {\n    const st = JSON.parse(readFileSync(statusPath(logDir, wid), \"utf8\")) as { state?: string };\n    return !!st.state && st.state !== \"running\";\n  } catch {\n    return false;\n  }\n}\n\n/** The replacement rule: a new pane in a scope closes that scope's panes of ended workers. */\nasync function closeEndedInScope(logDir: string, scope: string, ops: PaneOps = realPaneOps): Promise<void> {\n  for (const e of loadRegistry(logDir, scope)) {\n    if (!workerEnded(logDir, e.worker)) continue;\n    await ops.closeIterm(e.session).catch(() => false);\n    dropEntry(logDir, e);\n  }\n}\n\n/** Outcome of opening a worker's follow pane: the report line plus, on a fresh open, the new iTerm session id. */\nexport interface PaneOpenResult {\n  message: string;\n  session: string | null;\n}\n\n/** Open (or only report on) the stacked follow pane of one worker. */\nexport async function openPaneForWorker(\n  logDir: string,\n  config: WorkersConfig,\n  wid: string,\n  term: string\n): Promise<PaneOpenResult> {\n  if (workerPaneOpen(wid)) return { message: `pane for ${wid} already open`, session: null };\n  const backend = paneBackend(process.env, process.platform, term);\n  if (backend === \"none\") return { message: noPaneMessage(wid), session: null };\n  if (backend === \"tmux\") {\n    tmuxSplit(followCommand(wid));\n    return { message: `pane opened for ${wid}`, session: null };\n  }\n  const uid = itermUuid(term);\n  if (!uid) throw new Error(NO_HOST);\n  const scope = scopeKey(term);\n  await closeEndedInScope(logDir, scope);\n  const reg = loadRegistry(logDir, scope);\n  const profile = await followProfile(config.pane.fontSize);\n  const cmd = followCommand(wid);\n  // registry newest first: the split lands below the lowest live worker pane\n  const cands = [...reg].reverse().map((e) => e.session).join(\",\");\n  const p = await osascript(WORKER_SPLIT_SCRIPT, [uid, cands, cmd, profile]);\n  const out = p.stdout.trim();\n  if (out === \"notfound\" || out === \"notrunning\") {\n    throw new Error(\n      out === \"notfound\"\n        ? \"pai worker pane: resolved iTerm2 session vanished\"\n        : \"pai worker pane: iTerm2 is not running\"\n    );\n  }\n  const bar = out.indexOf(\"|\");\n  if (bar < 0) {\n    throw new Error(\n      `pai worker pane: osascript failed: ${(p.stderr || out).slice(0, 200)}`\n    );\n  }\n  const live = new Set(out.slice(0, bar).split(\",\").filter(Boolean));\n  for (const e of reg) if (!live.has(e.session)) dropEntry(logDir, e);\n  saveEntry(logDir, {\n    session: out.slice(bar + 1),\n    worker: wid,\n    opened: new Date().toISOString().replace(\"T\", \" \").slice(0, 19),\n    scope,\n  });\n  return { message: `pane opened for ${wid}`, session: out.slice(bar + 1) };\n}\n\n/**\n * One `--check` line with the bounds of the window hosting `term`'s iTerm\n * session — the before/after pair that shows whether a split moved it.\n * Read-only; never touches the window.\n */\nasync function windowBoundsLine(term: string): Promise<string> {\n  const uid = itermUuid(term);\n  if (!uid) return \"window bounds: (not in iTerm2)\";\n  try {\n    const p = await osascript(WINDOW_BOUNDS_SCRIPT, [uid]);\n    const out = p.stdout.trim();\n    if (out && out !== \"notfound\" && out !== \"notrunning\") return `window bounds: ${out}`;\n    const why =\n      out === \"notfound\" ? \"iTerm2 session not found\"\n      : out === \"notrunning\" ? \"iTerm2 not running\"\n      : (p.stderr.trim() || \"no output\").slice(0, 120);\n    return `window bounds: (${why})`;\n  } catch (e) {\n    return `window bounds: (osascript: ${String((e as Error).message ?? e).slice(0, 120)})`;\n  }\n}\n\n/**\n * Report-only variant used by `pai worker pane <id> --check`: whether a pane\n * runs for the worker, the bounds of the window hosting the asking session,\n * plus the dynamic profile's path, its existence, and the font it contains\n * (or, when missing, would write).\n */\nexport async function checkPaneForWorker(wid: string, fontSize: number, term: string): Promise<string> {\n  const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\n  if (paneBackend(process.env, process.platform, term) !== \"iterm\") return lines.join(\"\\n\");\n  lines.push(await windowBoundsLine(term));\n  const path = dynamicProfilePath();\n  if (existsSync(path)) {\n    let font = \"(unreadable)\";\n    try {\n      const parsed = JSON.parse(readFileSync(path, \"utf8\")) as {\n        Profiles?: Array<{ \"Normal Font\"?: string }>;\n      };\n      font = parsed.Profiles?.[0]?.[\"Normal Font\"] ?? \"(none)\";\n    } catch {\n      // font stays \"(unreadable)\"\n    }\n    lines.push(`profile file: ${path} (exists)`);\n    lines.push(`profile font: ${font}`);\n  } else {\n    const parent = defaultBookmark();\n    lines.push(`profile file: ${path} (missing)`);\n    lines.push(`profile font (would write): ${paneFont(parent?.[\"Normal Font\"], fontSize)}`);\n  }\n  return lines.join(\"\\n\");\n}\n\n/**\n * Split this iTerm tab and run `pai worker follow` in the new pane, unless\n * one already runs here (detected by TTY — iTerm overwrites session names\n * with the running command, so names cannot serve as idempotence).\n */\nexport async function openFollowPane(\n  logDir: string,\n  _config: WorkersConfig,\n  term: string,\n  checkOnly: boolean\n): Promise<string> {\n  void logDir;\n  const backend = paneBackend(process.env, process.platform, term);\n  if (backend === \"none\") return noPaneMessage();\n  if (backend === \"tmux\") {\n    if (checkOnly) return \"no follow pane\";\n    tmuxSplit(`${process.execPath} ${absoluteCliPath()} worker follow`);\n    return \"follow pane opened\";\n  }\n  const uid = itermUuid(term);\n  if (!uid) throw new Error(NO_HOST);\n  const p = await osascript(TAB_TTYS_SCRIPT, [uid]);\n  const out = p.stdout.trim();\n  if (out === \"notfound\") throw new Error(\"pai worker pane: resolved iTerm2 session vanished\");\n  if (out === \"notrunning\") throw new Error(\"pai worker pane: iTerm2 is not running\");\n  const tabTtys = new Set(out.split(\",\").map((t) => t.trim()).filter(Boolean));\n  const overlap = [...tabTtys].filter((t) => followTtys().has(t));\n  if (overlap.length > 0) return \"follow pane already open\";\n  if (checkOnly) return \"no follow pane\";\n  // one exec-able line, no shell syntax — see followCommand()\n  const cmd = `${process.execPath} ${absoluteCliPath()} worker follow`;\n  const s = await osascript(SPLIT_SCRIPT, [uid, cmd]);\n  const sout = s.stdout.trim();\n  if (sout === \"opened\") return \"follow pane opened\";\n  if (sout === \"notfound\") throw new Error(\"pai worker pane: resolved iTerm2 session vanished\");\n  throw new Error(`pai worker pane: osascript failed: ${(s.stderr || sout).slice(0, 200)}`);\n}\n\n// ---------------------------------------------------------------------------\n// Fail-closed visibility: a headless worker runs only behind a verified pane\n// ---------------------------------------------------------------------------\n\n/**\n * The operator-only switch that lets a run go headless: this file's existence.\n * The PreToolUse Bash hook denies any command naming it, so a Claude session\n * cannot create it through Bash; only the operator's own shell can.\n */\nexport function headlessOverridePath(): string {\n  return process.env.PAI_HEADLESS_OK_FILE ?? join(homedir(), \".claude\", \"pai\", \"headless-ok\");\n}\n\n/**\n * Why this process may run a worker without a verified pane, null when it may\n * not. Three reasons only: it is itself a worker (PAI_WORKER=1: a child\n * spawned inside its parent's pane may stay headless), the operator set\n * PAI_WORKER_ALLOW_HIDDEN=1 in their own shell (buildRunEnv strips it from\n * every spawned worker, so the consent ends at the process it was set for),\n * or the operator created the override file.\n */\nexport const ALLOW_HIDDEN_ENV = \"PAI_WORKER_ALLOW_HIDDEN\";\n\nexport function headlessAllowed(env: NodeJS.ProcessEnv = process.env, file: string = headlessOverridePath()): string | null {\n  if (env.PAI_WORKER === \"1\") return \"PAI_WORKER=1 (child of a worker, runs inside its parent's pane)\";\n  if (env[ALLOW_HIDDEN_ENV] === \"1\") return `${ALLOW_HIDDEN_ENV}=1 (operator override)`;\n  if (existsSync(file)) return `operator override file ${file}`;\n  return null;\n}\n\n// Reports where the pane sits, without raising or focusing anything: a window\n// need not be frontmost, only visible (several sessions live in several windows). JSON on stdout; `fail` set when the pane is not a visible, usable\n// neighbour of the host session.\nexport const VERIFY_PANE_SCRIPT = `${FIND_SESSION}\nfunction run(argv) {\n    var app = Application(\"iTerm2\");\n    if (!app.running()) return JSON.stringify({ fail: \"iTerm2 is not running\" });\n    var h = findSession(app, argv[0]);\n    if (!h) return JSON.stringify({ fail: \"host session \" + argv[0] + \" is gone\" });\n    var p = findSession(app, argv[1]);\n    if (!p) return JSON.stringify({ fail: \"pane session \" + argv[1] + \" is gone (closed right after opening)\" });\n    function ids(ss) { return ss.map(function (o) { return o.id(); }); }\n    if (h.w.id() !== p.w.id()) return JSON.stringify({ fail: \"pane is in window \" + p.w.id() + \", host in window \" + h.w.id() });\n    if (ids(h.all).indexOf(argv[1]) < 0) return JSON.stringify({ fail: \"pane is not in the host's tab\" });\n    var tabs = h.w.tabs();\n    var tabIdx = 0;\n    for (var i = 0; i < tabs.length; i++) if (ids(tabs[i].sessions()).indexOf(argv[0]) >= 0) tabIdx = i + 1;\n    var selected = ids(h.w.currentTab().sessions()).indexOf(argv[1]) >= 0;\n    var b = h.w.bounds();\n    // On screen: the frame intersects a display (NSScreen is bottom-left origin, iTerm top-left).\n    ObjC.import(\"AppKit\");\n    var scr = $.NSScreen.screens, top = scr.objectAtIndex(0).frame.size.height, onScreen = false;\n    for (var k = 0; k < scr.count; k++) {\n        var f = scr.objectAtIndex(k).frame, fy = top - f.origin.y - f.size.height;\n        if (b.x < f.origin.x + f.size.width && b.x + b.width > f.origin.x && b.y < fy + f.size.height && b.y + b.height > fy) onScreen = true;\n    }\n    var hidden = false;\n    try { hidden = Application(\"System Events\").processes.byName(\"iTerm2\").visible() === false; } catch (e) {}\n    return JSON.stringify({\n        window: h.w.id(), bounds: [b.x, b.y, b.width, b.height], tab: tabIdx + \"/\" + tabs.length,\n        selectedTab: selected, onScreen: onScreen, hidden: hidden, miniaturized: h.w.miniaturized(),\n        rows: p.s.rows(), cols: p.s.columns(), tty: p.s.tty(), hostRows: h.s.rows(), hostCols: h.s.columns()\n    });\n}`;\n\nconst MIN_PANE_ROWS = 8;\nconst MIN_PANE_COLS = 40;\n\n/** True while a `worker follow <wid>` process runs on that tty. */\nfunction followRunsOn(tty: string, wid: string): boolean {\n  const short = tty.replace(/^\\/dev\\//, \"\");\n  return psOutput(\"tty=,command=\")\n    .split(\"\\n\")\n    .some((ln) => ln.trim().startsWith(`${short} `) && new RegExp(`worker follow ${wid}\\\\b`).test(ln));\n}\n\n/**\n * Check, and bring forward, the iTerm pane of a worker. Resolves with a line\n * naming window, tab and size; rejects with the reason when the pane is not\n * something the operator can see: gone, other window or tab, tab not selected,\n * window off screen, hidden or minimised, too small, or its follow command not running\n * (polled for up to ~7 s while the new session starts it).\n */\nexport async function verifyPane(\n  hostUid: string,\n  paneUid: string,\n  wid: string,\n  run: (script: string, args: string[]) => Promise<{ stdout: string; stderr: string }> = osascript,\n  running: (tty: string, wid: string) => boolean = followRunsOn,\n  tries = 25,\n  pollMs = 300\n): Promise<string> {\n  let last = \"\";\n  for (let i = 0; i < tries; i++) {\n    const p = await run(VERIFY_PANE_SCRIPT, [hostUid, paneUid]);\n    let r: Record<string, unknown>;\n    try {\n      r = JSON.parse(p.stdout.trim()) as Record<string, unknown>;\n    } catch {\n      throw new Error(`pane verification failed: osascript said ${(p.stderr || p.stdout || \"nothing\").trim().slice(0, 200)}`);\n    }\n    if (typeof r.fail === \"string\") throw new Error(`pane verification failed: ${r.fail}`);\n    const rows = Number(r.rows);\n    const cols = Number(r.cols);\n    const where = `window ${r.window} [${(r.bounds as number[]).join(\",\")}] tab ${r.tab}, pane ${cols}x${rows}, ${r.tty}`;\n    if (r.miniaturized === true) throw new Error(`pane verification failed: window is minimised (${where})`);\n    if (r.selectedTab !== true) throw new Error(`pane verification failed: its tab is not the selected one (${where})`);\n    if (r.hidden === true) throw new Error(`pane verification failed: iTerm2 is hidden (${where})`);\n    if (r.onScreen !== true) throw new Error(`pane verification failed: its window is off screen (${where})`);\n    if (rows < MIN_PANE_ROWS || cols < MIN_PANE_COLS) {\n      throw new Error(`pane verification failed: pane too small, ${cols}x${rows} < ${MIN_PANE_COLS}x${MIN_PANE_ROWS} (${where})`);\n    }\n    if (running(String(r.tty), wid)) return where;\n    last = where;\n    await new Promise((res) => setTimeout(res, pollMs));\n  }\n  throw new Error(`pane verification failed: \\`worker follow ${wid}\\` is not running in the pane (${last})`);\n}\n\n/**\n * Open the worker's pane and prove the operator can see it, or throw the\n * reason (the pane just opened is closed again). Needs a live iTerm2 host\n * session that ITERM_SESSION_ID itself names, or tmux; a stale or invented\n * id is refused, whatever the process-tree lookup would find.\n */\nexport async function openVerifiedPane(\n  logDir: string,\n  config: WorkersConfig,\n  wid: string,\n  term: string,\n  env: NodeJS.ProcessEnv = process.env\n): Promise<string> {\n  if (env.TMUX) {\n    const r = await openPaneForWorker(logDir, config, wid, term);\n    if (!r.message.startsWith(\"pane opened\")) throw new Error(`pane verification failed: ${r.message}`);\n    return \"tmux pane\";\n  }\n  const envUid = itermUuid(env.ITERM_SESSION_ID ?? \"\");\n  const hostUid = itermUuid(term);\n  if (!envUid) throw new Error(\"neither TMUX nor ITERM_SESSION_ID is set: this process does not run in a terminal pane the operator sees\");\n  if (envUid !== hostUid) {\n    throw new Error(`ITERM_SESSION_ID names ${envUid}, which is not a live iTerm2 session${hostUid ? ` (host found: ${hostUid})` : \"\"}`);\n  }\n  const r = await openPaneForWorker(logDir, config, wid, term);\n  if (!r.session) throw new Error(`pane verification failed: ${r.message}`);\n  try {\n    return `${await verifyPane(hostUid, r.session, wid)} (iTerm session ${r.session})`;\n  } catch (e) {\n    await closePaneForWorker(logDir, wid).catch(() => false);\n    throw e;\n  }\n}\n\n/** CLI entry check: --no-pane and PAI_WORKER_AUTOPANE=0 are refused unless headlessAllowed(). */\nexport function assertPaneNotDisabled(noPaneFlag: boolean, env: NodeJS.ProcessEnv = process.env): void {\n  const what = noPaneFlag ? \"--no-pane\" : env.PAI_WORKER_AUTOPANE === \"0\" ? \"PAI_WORKER_AUTOPANE=0\" : \"\";\n  if (what && !headlessAllowed(env)) {\n    throw new Error(\n      `${what} refused: every worker runs behind a visible pane ` +\n        `(only a worker's own children, the operator's ${ALLOW_HIDDEN_ENV}=1, ` +\n        `or the operator's ${headlessOverridePath()} may skip it)`\n    );\n  }\n}\n","/**\n * project-config.ts — the project registry's optional launch pin, read for\n * the interactive branch of run.ts only: which MCP servers and built-in\n * tools a project's supervisor session (`pai worker run`, no -p) loads. Set\n * with `pai project mcp` / `pai project tools` (src/cli/commands/project/\n * session-config.ts), stored in the same `projects.session_config` JSON\n * column as the rest of a project's launch config. Unset = today's\n * behavior: every server, every tool.\n *\n * A standalone SQL lookup rather than importing the CLI's detectProject:\n * workers/ is a dependency of cli/, not the other way around.\n */\n\nimport { resolve } from \"node:path\";\nimport { getRegistryBackend } from \"../storage/factory.js\";\nimport type { RegistryBackend } from \"../storage/registry-interface.js\";\nimport { readMcpServers } from \"./mcp.js\";\n\nexport interface ProjectLaunchConfig {\n  slug: string;\n  mcp?: string[];\n  tools?: string[];\n}\n\nfunction asStringArray(v: unknown): string[] | undefined {\n  if (!Array.isArray(v)) return undefined;\n  const out = v.filter((x): x is string => typeof x === \"string\");\n  return out.length ? out : undefined;\n}\n\n/**\n * The launch config of the project whose root_path is `cwd` or an ancestor\n * of it (longest match wins) — null when no registered project covers it,\n * or when the matching project has no mcp/tools pin set. `registry` is\n * injectable for tests; production callers take the process-wide backend.\n */\nexport async function projectLaunchConfig(\n  cwd: string,\n  registry?: RegistryBackend\n): Promise<ProjectLaunchConfig | null> {\n  const target = resolve(cwd);\n  const backend = registry ?? (await getRegistryBackend());\n  const rows = await backend.listProjectsByPathLengthDesc({ excludeArchived: true });\n  for (const row of rows) {\n    const root = resolve(row.root_path);\n    if (target !== root && !target.startsWith(root + \"/\")) continue;\n    if (!row.session_config) return null;\n    let parsed: { mcp?: unknown; tools?: unknown };\n    try {\n      parsed = JSON.parse(row.session_config);\n    } catch {\n      return null;\n    }\n    const mcp = asStringArray(parsed.mcp);\n    const tools = asStringArray(parsed.tools);\n    return mcp || tools ? { slug: row.slug, mcp, tools } : null;\n  }\n  return null;\n}\n\n/**\n * One line per active pin, so a pin never silently removes a server or tool:\n * shown by the SessionStart hook and by `pai worker run`'s interactive launch.\n * `claudeJson` is injectable for tests; read-only.\n */\nexport function pinNotices(launch: ProjectLaunchConfig | null, claudeJson?: string): string[] {\n  if (!launch) return [];\n  const lines: string[] = [];\n  if (launch.mcp) {\n    const all = Object.keys(readMcpServers(claudeJson));\n    const loaded = all.filter((n) => launch.mcp!.includes(n));\n    const missing = all.filter((n) => !launch.mcp!.includes(n));\n    lines.push(\n      `MCP pinned for ${launch.slug}: loading ${loaded.length} of ${all.length} servers; ` +\n        `not loaded: ${missing.join(\", \") || \"(none)\"} — \\`pai project mcp --clear\\` to load all`\n    );\n  }\n  if (launch.tools) {\n    lines.push(`Tools pinned for ${launch.slug}: ${launch.tools.join(\",\")} — \\`pai project tools --clear\\``);\n  }\n  return lines;\n}\n","/**\n * limits.ts — per-provider plan limits for the cockpit. Polled at most once a\n * minute and cached; keys and tokens stay in this process and never reach the\n * page or the JSON it is served.\n *\n *   anthropic  Max plan 5h/7d via the OAuth usage endpoint (token from the\n *              keychain, as the statusline does); on a failed poll (http 429)\n *              the last good reading is served with its age and the poll backs\n *              off, advisor-mode.json only when no last good reading exists\n *   others     the provider's workers.yaml `usage` block (jq expressions)\n *   local      up/down (TCP), RSS of the listener, tokens/s from accounting\n *   rest       counted requests vs a configured limit, labelled \"estimated\"\n */\n\nimport { execFile, spawnSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport net from \"node:net\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { resolveProviderKey, type ProviderUsage, type WorkerProvider } from \"../workers/config.js\";\nimport type { UsageRow } from \"./usage.js\";\n\nexport interface LimitWindow {\n  name: string;\n  /** used, 0-100 */\n  percent: number;\n  /** epoch ms of the next reset */\n  resetAt?: number;\n  estimated?: boolean;\n}\n\nexport interface ProviderLimits {\n  provider: string;\n  kind: \"plan\" | \"estimated\" | \"local\";\n  windows: LimitWindow[];\n  up?: boolean;\n  rssMb?: number;\n  /** model ids the local server lists */\n  served?: string[];\n  /** average output tokens per second over the last hour */\n  tokensPerSec?: number;\n  error?: string;\n  fetchedAt: number;\n}\n\nexport const ESTIMATE_FILE = join(homedir(), \".claude\", \"pai\", \"router-limits.json\");\nexport const ADVISOR_FILE = join(homedir(), \".claude\", \"pai\", \"advisor-mode.json\");\n/** Last good plan reading per provider, so a rate-limited poll shows an age, not \"reset unknown\". */\nexport const LASTGOOD_FILE = join(homedir(), \".claude\", \"pai\", \"logs\", \"router-limits-last.json\");\n/** A last good reading older than this is too stale to serve when the endpoint fails. */\nexport const PLAN_LASTGOOD_MAX_AGE = 2 * 3_600_000;\n/** Upper bound for the poll delay after repeated 429s. */\nexport const BACKOFF_MAX = 30 * 60_000;\n\n/** The windows of one provider's last successful poll. */\nexport interface LastGoodEntry {\n  windows: LimitWindow[];\n  fetchedAt: number;\n}\n\nexport function readLastGood(file: string = LASTGOOD_FILE): Record<string, LastGoodEntry> {\n  try {\n    const j = JSON.parse(readFileSync(file, \"utf8\")) as Record<string, Partial<LastGoodEntry>>;\n    const out: Record<string, LastGoodEntry> = {};\n    for (const [k, v] of Object.entries(j)) {\n      if (v && Array.isArray(v.windows) && typeof v.fetchedAt === \"number\") out[k] = { windows: v.windows, fetchedAt: v.fetchedAt };\n    }\n    return out;\n  } catch {\n    return {};\n  }\n}\n\nfunction writeLastGood(file: string, entries: Record<string, LastGoodEntry>): void {\n  try {\n    mkdirSync(dirname(file), { recursive: true });\n    writeFileSync(file, JSON.stringify(entries, null, 1));\n  } catch {\n    /* best-effort cache */\n  }\n}\n\n/**\n * The reading to show when a poll failed: the last good one, with its age in\n * the error field, while it is fresh enough to trust; undefined otherwise.\n */\nexport function staleReading(\n  provider: string,\n  entry: LastGoodEntry | undefined,\n  cause: string,\n  now: number,\n  maxAge: number = PLAN_LASTGOOD_MAX_AGE\n): ProviderLimits | undefined {\n  if (!entry || !entry.windows.length || now - entry.fetchedAt >= maxAge) return undefined;\n  const min = Math.round((now - entry.fetchedAt) / 60_000);\n  return { provider, kind: \"plan\", windows: entry.windows, fetchedAt: entry.fetchedAt, error: `stale ${min}min (${cause})` };\n}\n\n/** Per-provider poll backoff after a http 429: the interval doubles per strike, resets on success. */\nexport class Backoff {\n  private mult = new Map<string, number>();\n  private until = new Map<string, number>();\n\n  constructor(\n    private readonly baseMs: number,\n    private readonly maxMs: number = BACKOFF_MAX\n  ) {}\n\n  /** Record a 429 seen for `provider` at `now`; returns the delay before its next poll. */\n  penalize(provider: string, now: number): number {\n    const m = Math.min((this.mult.get(provider) ?? 1) * 2, this.maxMs / this.baseMs);\n    this.mult.set(provider, m);\n    this.until.set(provider, now + m * this.baseMs);\n    return m * this.baseMs;\n  }\n\n  /** ms until `provider` is due again; 0 when it may be polled now. */\n  remaining(provider: string, now: number): number {\n    return Math.max(0, (this.until.get(provider) ?? 0) - now);\n  }\n\n  /** A poll succeeded: forget the strikes. */\n  reset(provider: string): void {\n    this.mult.delete(provider);\n    this.until.delete(provider);\n  }\n}\n\nfunction toMs(v: unknown, unit: \"ms\" | \"s\" | \"iso\" | undefined): number | undefined {\n  if (v === null || v === undefined || v === \"\") return undefined;\n  if (unit === \"iso\" || (unit === undefined && typeof v === \"string\" && !/^\\d+(\\.\\d+)?$/.test(v))) {\n    const t = Date.parse(String(v));\n    return Number.isFinite(t) ? t : undefined;\n  }\n  const n = Number(v);\n  if (!Number.isFinite(n)) return undefined;\n  return unit === \"s\" ? n * 1000 : n;\n}\n\nfunction jq(expr: string, input: string): string | undefined {\n  const r = spawnSync(\"jq\", [\"-r\", expr], { input, encoding: \"utf8\", timeout: 3000 });\n  if (r.status !== 0) return undefined;\n  const out = r.stdout.trim().split(\"\\n\")[0];\n  return out === undefined || out === \"\" || out === \"null\" ? undefined : out;\n}\n\n/** Windows of a provider usage response, using its configured jq expressions. */\nexport function windowsFromJson(body: string, cfg: ProviderUsage): LimitWindow[] {\n  const out: LimitWindow[] = [];\n  for (const w of cfg.windows) {\n    const pct = Number(jq(w.percent, body));\n    if (!Number.isFinite(pct) || jq(w.percent, body) === undefined) continue;\n    const reset = w.resetAt ? toMs(jq(w.resetAt, body), w.resetUnit) : undefined;\n    out.push({ name: w.name, percent: Math.round(pct * 10) / 10, ...(reset ? { resetAt: reset } : {}) });\n  }\n  return out;\n}\n\n/** Anthropic OAuth usage response: five_hour / seven_day {utilization, resets_at ISO}. */\nexport function parseAnthropicUsage(body: string): LimitWindow[] {\n  let j: Record<string, { utilization?: number; resets_at?: string } | null>;\n  try {\n    j = JSON.parse(body);\n  } catch {\n    return [];\n  }\n  const out: LimitWindow[] = [];\n  for (const [key, name] of [[\"five_hour\", \"5h\"], [\"seven_day\", \"7d\"]] as const) {\n    const w = j[key];\n    if (!w || typeof w.utilization !== \"number\") continue;\n    const reset = toMs(w.resets_at, \"iso\");\n    out.push({ name, percent: w.utilization, ...(reset ? { resetAt: reset } : {}) });\n  }\n  return out;\n}\n\n/** Counted requests inside each window vs a configured cap: labelled estimated. */\nexport function estimateWindows(\n  rows: UsageRow[],\n  provider: string,\n  caps: Record<string, number>,\n  now: number\n): LimitWindow[] {\n  const spans: Record<string, number> = { \"5h\": 5 * 3_600_000, \"7d\": 7 * 86_400_000 };\n  const out: LimitWindow[] = [];\n  for (const [name, span] of Object.entries(spans)) {\n    const cap = caps[name];\n    if (!cap) continue;\n    const inWin = rows.filter((r) => r.provider === provider && Date.parse(r.ts) >= now - span);\n    const first = inWin.reduce((m, r) => Math.min(m, Date.parse(r.ts)), Infinity);\n    out.push({\n      name,\n      percent: Math.round((inWin.length / cap) * 1000) / 10,\n      ...(Number.isFinite(first) ? { resetAt: first + span } : {}),\n      estimated: true,\n    });\n  }\n  return out;\n}\n\nexport function readEstimateCaps(file: string = ESTIMATE_FILE): Record<string, Record<string, number>> {\n  try {\n    return existsSync(file) ? (JSON.parse(readFileSync(file, \"utf8\")) as Record<string, Record<string, number>>) : {};\n  } catch {\n    return {};\n  }\n}\n\n/** The OAuth usage endpoint the anthropic plan readings come from. */\nexport const ANTHROPIC_USAGE_URL = \"https://api.anthropic.com/api/oauth/usage\";\n\nexport function getJson(url: URL, headers: Record<string, string>): Promise<string> {\n  return new Promise((resolve, reject) => {\n    const mod = url.protocol === \"https:\" ? https : http;\n    const rq = mod.get(url, { headers, timeout: 5000 }, (res) => {\n      let t = \"\";\n      res.on(\"data\", (c) => (t += c));\n      res.on(\"end\", () => (res.statusCode === 200 ? resolve(t) : reject(new Error(`http ${res.statusCode}`))));\n    });\n    rq.on(\"timeout\", () => rq.destroy(new Error(\"timeout\")));\n    rq.on(\"error\", reject);\n  });\n}\n\nfunction sh(cmd: string, args: string[]): Promise<string> {\n  return new Promise((resolve) => execFile(cmd, args, { timeout: 4000 }, (_e, out) => resolve(String(out ?? \"\").trim())));\n}\n\nexport async function anthropicToken(): Promise<string | undefined> {\n  for (const args of [[\"-a\", process.env.USER ?? \"\"], []]) {\n    const raw = await sh(\"security\", [\"find-generic-password\", \"-s\", \"Claude Code-credentials\", ...args, \"-w\"]);\n    try {\n      const t = (JSON.parse(raw) as { claudeAiOauth?: { accessToken?: string } }).claudeAiOauth?.accessToken;\n      if (t) return t;\n    } catch {\n      /* try next */\n    }\n  }\n  return undefined;\n}\n\nasync function anthropicLimits(now: number, advisorFile: string, lastGood: LastGoodEntry | undefined): Promise<ProviderLimits> {\n  const base = { provider: \"anthropic\", kind: \"plan\" as const, fetchedAt: now };\n  try {\n    const token = await anthropicToken();\n    if (!token) throw new Error(\"no OAuth token in keychain\");\n    const body = await getJson(new URL(ANTHROPIC_USAGE_URL), {\n      authorization: `Bearer ${token}`,\n      \"anthropic-beta\": \"oauth-2025-04-20\",\n    });\n    const windows = parseAnthropicUsage(body);\n    if (windows.length) return { ...base, windows };\n    throw new Error(\"no usage windows in response\");\n  } catch (e) {\n    const stale = staleReading(\"anthropic\", lastGood, (e as Error).message, now);\n    if (stale) return stale;\n    try {\n      const a = JSON.parse(readFileSync(advisorFile, \"utf8\")) as { weeklyBudgetPercent?: number; asOf?: number };\n      if (typeof a.weeklyBudgetPercent === \"number\" && a.asOf && now / 1000 - a.asOf < 6 * 3600) {\n        return { ...base, windows: [{ name: \"7d\", percent: a.weeklyBudgetPercent }], error: `${(e as Error).message}; advisor-mode fallback` };\n      }\n    } catch {\n      /* no fallback */\n    }\n    return { ...base, windows: [], error: (e as Error).message };\n  }\n}\n\nasync function providerPlanLimits(name: string, p: WorkerProvider, now: number, lastGood: LastGoodEntry | undefined): Promise<ProviderLimits> {\n  const base = { provider: name, kind: \"plan\" as const, fetchedAt: now };\n  const u = p.usage!;\n  try {\n    const key = resolveProviderKey(p);\n    const [hn, ...pre] = (u.authHeader ?? \"Authorization: Bearer\").split(/:\\s*/);\n    const prefix = pre.join(\": \");\n    const headers: Record<string, string> = { [hn!.toLowerCase()]: prefix ? `${prefix} ${key}` : String(key) };\n    const body = await getJson(new URL(u.url), headers);\n    const windows = windowsFromJson(body, u);\n    return windows.length ? { ...base, windows } : { ...base, windows, error: \"no windows parsed\" };\n  } catch (e) {\n    const msg = (e as Error).message.replace(/Bearer\\s+\\S+/g, \"Bearer ***\");\n    return staleReading(name, lastGood, msg, now) ?? { ...base, windows: [], error: msg };\n  }\n}\n\nfunction probeTcp(port: number, host: string): Promise<boolean> {\n  return new Promise((resolve) => {\n    const s = net.connect({ port, host, timeout: 800 });\n    s.on(\"connect\", () => (s.destroy(), resolve(true)));\n    s.on(\"timeout\", () => (s.destroy(), resolve(false)));\n    s.on(\"error\", () => resolve(false));\n  });\n}\n\nasync function localLimits(name: string, p: WorkerProvider, rows: UsageRow[], now: number): Promise<ProviderLimits> {\n  const u = new URL(p.baseUrl);\n  const port = Number(u.port) || 80;\n  const listening = await probeTcp(port, u.hostname);\n  const served = listening ? await servedModels(u) : undefined;\n  const up = listening && modelServed(served, Object.values(p.models).filter((m): m is string => typeof m === \"string\"));\n  let rssMb: number | undefined;\n  if (up) {\n    const pid = (await sh(\"lsof\", [\"-ti\", `tcp:${port}`, \"-sTCP:LISTEN\"])).split(\"\\n\")[0];\n    const rss = pid ? Number(await sh(\"ps\", [\"-o\", \"rss=\", \"-p\", pid])) : NaN;\n    if (Number.isFinite(rss) && rss > 0) rssMb = Math.round(rss / 1024);\n  }\n  return { provider: name, kind: \"local\", windows: [], up, ...(rssMb ? { rssMb } : {}), ...(served ? { served } : {}), ...tokensPerSec(rows, name, now), fetchedAt: now };\n}\n\n/** Model ids the server at this base URL lists on GET /v1/models; undefined when it cannot say. */\nasync function servedModels(base: URL): Promise<string[] | undefined> {\n  try {\n    const body = JSON.parse(await getJson(new URL(\"/v1/models\", base), {})) as { data?: Array<{ id?: unknown }> };\n    const ids = (body.data ?? []).map((m) => m.id).filter((i): i is string => typeof i === \"string\");\n    return ids.length ? ids : undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n/** Several providers can share one port: the provider is up only if its model is the one served. Unknown list = port decides. */\nexport function modelServed(served: string[] | undefined, wanted: string[]): boolean {\n  if (!served) return true;\n  return wanted.some((w) => served.includes(w));\n}\n\n/** output tokens / generation seconds over the last hour (slow-path requests only). */\nexport function tokensPerSec(rows: UsageRow[], provider: string, now: number): { tokensPerSec?: number } {\n  let out = 0;\n  let ms = 0;\n  for (const r of rows) {\n    if (r.provider !== provider || Date.parse(r.ts) < now - 3_600_000 || r.status !== 200 || !r.output_tokens) continue;\n    out += r.output_tokens;\n    ms += r.ms;\n  }\n  return ms ? { tokensPerSec: Math.round((out / (ms / 1000)) * 10) / 10 } : {};\n}\n\nfunction isLocalUrl(u: string): boolean {\n  try {\n    const h = new URL(u).hostname;\n    return h === \"127.0.0.1\" || h === \"localhost\";\n  } catch {\n    return false;\n  }\n}\n\nexport interface LimitsDeps {\n  providers: () => Record<string, WorkerProvider>;\n  rows: () => UsageRow[];\n  advisorFile?: string;\n  estimateFile?: string;\n  /** Last-good store override (tests); defaults to LASTGOOD_FILE. */\n  lastGoodFile?: string;\n}\n\n/**\n * Polls all providers at most once per `ttlMs`; concurrent callers share one\n * poll. A provider whose poll answered http 429 is not fetched again until its\n * backoff (the interval doubled per strike, capped) has run out — its last\n * good reading is served with the current age instead.\n */\nexport class LimitsCache {\n  private value: ProviderLimits[] = [];\n  private at = 0;\n  private inflight: Promise<ProviderLimits[]> | undefined;\n  private backoff: Backoff;\n\n  constructor(private readonly deps: LimitsDeps, private readonly ttlMs = 60_000) {\n    this.backoff = new Backoff(ttlMs);\n  }\n\n  get(now: number = Date.now()): Promise<ProviderLimits[]> {\n    if (this.value.length && now - this.at < this.ttlMs) return Promise.resolve(this.value);\n    this.inflight ??= this.poll(now).then((v) => {\n      this.value = v;\n      this.at = now;\n      this.inflight = undefined;\n      return v;\n    });\n    return this.inflight;\n  }\n\n  private async poll(now: number): Promise<ProviderLimits[]> {\n    const rows = this.deps.rows();\n    let providers: Record<string, WorkerProvider> = {};\n    try {\n      providers = this.deps.providers();\n    } catch {\n      /* config unreadable: still report anthropic */\n    }\n    const caps = readEstimateCaps(this.deps.estimateFile);\n    const lastGoodFile = this.deps.lastGoodFile ?? LASTGOOD_FILE;\n    const lastGood = readLastGood(lastGoodFile);\n    const previous = new Map(this.value.map((v) => [v.provider, v]));\n    const jobs: Array<Promise<ProviderLimits>> = [];\n    const skipped: string[] = [];\n    /** Queue a plan poll unless the provider is backing off from a 429. */\n    const plan = (name: string, fn: (lg: LastGoodEntry | undefined) => Promise<ProviderLimits>): void => {\n      if (this.backoff.remaining(name, now) > 0) {\n        skipped.push(name);\n        return;\n      }\n      jobs.push(fn(lastGood[name]));\n    };\n    plan(\"anthropic\", (lg) => anthropicLimits(now, this.deps.advisorFile ?? ADVISOR_FILE, lg));\n    for (const [name, p] of Object.entries(providers)) {\n      if (p.native || name === \"anthropic\" || p.enabled === false || !p.baseUrl) continue;\n      if (isLocalUrl(p.baseUrl)) jobs.push(localLimits(name, p, rows, now));\n      else if (p.usage) plan(name, (lg) => providerPlanLimits(name, p, now, lg));\n      else if (caps[name]) {\n        jobs.push(Promise.resolve({ provider: name, kind: \"estimated\", windows: estimateWindows(rows, name, caps[name]!, now), fetchedAt: now }));\n      }\n    }\n    const results = await Promise.all(jobs);\n    const store = { ...lastGood };\n    let dirty = false;\n    for (const r of results) {\n      if (r.error) {\n        if (r.error.includes(\"http 429\")) this.backoff.penalize(r.provider, now);\n      } else {\n        this.backoff.reset(r.provider);\n        if (r.windows.length) {\n          store[r.provider] = { windows: r.windows, fetchedAt: r.fetchedAt };\n          dirty = true;\n        }\n      }\n    }\n    if (dirty) writeLastGood(lastGoodFile, store);\n    // Backed-off providers stay on the page: the previous reading, with a current age.\n    for (const name of skipped) {\n      const prev = previous.get(name);\n      const cause = prev?.error?.includes(\"http 429\") ? \"http 429\" : (prev?.error ?? \"http 429\");\n      results.push(\n        prev\n          ? { ...prev, error: `stale ${Math.max(0, Math.round((now - prev.fetchedAt) / 60_000))}min (${cause})` }\n          : { provider: name, kind: \"plan\", windows: [], error: cause, fetchedAt: now }\n      );\n    }\n    return results;\n  }\n}\n","/**\n * budget.ts — live budget probes for budget-aware routing (policy \"budget\").\n *\n * One reading per provider: {percent, resetAt} of its most-constrained usage\n * window, from the same endpoints the cockpit limits card polls — anthropic\n * via the OAuth usage endpoint (keychain token), every other provider via its\n * workers.yaml `usage` block. Readings are cached for BUDGET_TTL_MS so a\n * burst of spawns costs one poll, and a failed probe reads as \"unknown\",\n * never as \"full\": routing must not break because an endpoint did.\n *\n * The routing decision itself lives in routing.ts (wall skip, cooldown until\n * reset); this module only fetches and caches. Keys never leave this process\n * and never appear in a reading or an error.\n */\n\nimport {\n  ANTHROPIC_USAGE_URL,\n  anthropicToken,\n  getJson,\n  parseAnthropicUsage,\n  windowsFromJson,\n  type LimitWindow,\n} from \"../router/limits.js\";\nimport {\n  type WorkerProvider,\n  type WorkersConfig,\n  type WorkersRoutingConfig,\n  DEFAULT_BUDGET_WALL,\n  DEFAULT_BUDGET_WALLS,\n  nativeAnthropicProvider,\n  resolveProviderKey,\n} from \"./config.js\";\n\n/** One provider's budget reading: most-constrained window, or \"unknown\". */\nexport interface BudgetReading {\n  /** Used percent 0–100; null when no window could be read (never wall a provider on null). */\n  percent: number | null;\n  /** Epoch ms the window resets; null when the endpoint did not say. */\n  resetAt: number | null;\n  /** Epoch ms the reading was fetched. */\n  fetchedAt: number;\n}\n\nexport type BudgetMap = Record<string, BudgetReading>;\n\n/** How long a reading is trusted before the next spawn re-probes. */\nexport const BUDGET_TTL_MS = 5 * 60_000;\n\n/**\n * The most-constrained window of a usage response: highest percent, with its\n * reset. A provider is as full as its fullest window (a 96% 5h window is the\n * wall even when the 7d window reads 20%).\n */\nexport function pickBudgetWindow(windows: LimitWindow[], now: number): BudgetReading {\n  let best: LimitWindow | null = null;\n  for (const w of windows) if (!best || w.percent > best.percent) best = w;\n  if (!best) return { percent: null, resetAt: null, fetchedAt: now };\n  return { percent: best.percent, resetAt: best.resetAt ?? null, fetchedAt: now };\n}\n\n/**\n * Fetch one provider's budget reading. anthropic (native) reads the OAuth\n * usage endpoint with the keychain token; every other provider its\n * workers.yaml `usage` block. Any failure — no key, no network, bad JSON —\n * is \"unknown\", and unknown never walls a provider.\n */\nexport async function fetchBudgetReading(name: string, p: WorkerProvider): Promise<BudgetReading> {\n  const now = Date.now();\n  try {\n    if (p.native || name === \"anthropic\") {\n      const token = await anthropicToken();\n      if (!token) throw new Error(\"no OAuth token in keychain\");\n      const body = await getJson(new URL(ANTHROPIC_USAGE_URL), {\n        authorization: `Bearer ${token}`,\n        \"anthropic-beta\": \"oauth-2025-04-20\",\n      });\n      return pickBudgetWindow(parseAnthropicUsage(body), now);\n    }\n    if (!p.usage) return { percent: null, resetAt: null, fetchedAt: now };\n    const key = resolveProviderKey(p);\n    const [hn, ...pre] = (p.usage.authHeader ?? \"Authorization: Bearer\").split(/:\\s*/);\n    const prefix = pre.join(\": \");\n    const body = await getJson(new URL(p.usage.url), {\n      [hn!.toLowerCase()]: prefix ? `${prefix} ${key}` : String(key),\n    });\n    return pickBudgetWindow(windowsFromJson(body, p.usage), now);\n  } catch {\n    return { percent: null, resetAt: null, fetchedAt: now };\n  }\n}\n\n/**\n * Budget readings for spawn-time routing, at most one poll per TTL;\n * concurrent warmers share the inflight poll. `seed` lets tests (and a\n * caller with fresh readings of its own) put values in without a poll.\n */\nexport class BudgetCache {\n  private value: BudgetMap = {};\n  private at = 0;\n  private inflight: Promise<BudgetMap> | undefined;\n\n  constructor(\n    private readonly ttlMs: number = BUDGET_TTL_MS,\n    private readonly fetch: (name: string, p: WorkerProvider) => Promise<BudgetReading> = fetchBudgetReading\n  ) {}\n\n  /** Refresh every stale provider of `config`; resolves to the full map. */\n  async warm(config: WorkersConfig, now: number = Date.now()): Promise<BudgetMap> {\n    if (this.at && now - this.at < this.ttlMs) return this.value;\n    this.inflight ??= this.poll(config).then((v) => {\n      this.value = v;\n      this.at = now;\n      this.inflight = undefined;\n      return v;\n    });\n    return this.inflight;\n  }\n\n  private async poll(config: WorkersConfig): Promise<BudgetMap> {\n    const out: BudgetMap = { ...this.value };\n    const jobs: Array<Promise<void>> = [\n      // native anthropic has no config entry: probe it as the synthetic provider\n      this.fetch(\"anthropic\", nativeAnthropicProvider(config.nativeModels)).then((r) => {\n        out.anthropic = r;\n      }),\n    ];\n    for (const [name, p] of Object.entries(config.providers)) {\n      jobs.push(\n        this.fetch(name, p).then((r) => {\n          out[name] = r;\n        })\n      );\n    }\n    await Promise.all(jobs);\n    return out;\n  }\n\n  /** Synchronous read for routing (possibly absent — absence is \"unknown\"). */\n  get(name: string): BudgetReading | undefined {\n    return this.value[name];\n  }\n\n  snapshot(): BudgetMap {\n    return { ...this.value };\n  }\n\n  seed(map: BudgetMap, at: number = Date.now()): void {\n    this.value = { ...map };\n    this.at = at;\n  }\n\n  clear(): void {\n    this.value = {};\n    this.at = 0;\n    this.inflight = undefined;\n  }\n}\n\n/** The process-wide cache routing reads; run.ts warms it before resolving. */\nexport const budgetCache = new BudgetCache();\n\n/** A provider's wall: routing.budgetWalls[name], else the built-in default for it, else 95. */\nexport function budgetWall(routing: WorkersRoutingConfig, name: string): number {\n  return routing.budgetWalls[name] ?? DEFAULT_BUDGET_WALLS[name] ?? DEFAULT_BUDGET_WALL;\n}\n\n/** At or above its wall? Unknown (absent/null percent) is never full. */\nexport function budgetExceeded(r: BudgetReading | undefined, wall: number): boolean {\n  return !!r && r.percent !== null && r.percent >= wall;\n}\n\n/** Is this reading fresh enough to act on (wall cooldowns) at `now`? */\nexport function budgetFresh(r: BudgetReading, now: number, ttlMs: number = BUDGET_TTL_MS): boolean {\n  return now - r.fetchedAt < ttlMs;\n}\n\n/** \"08:02\" for a reading's reset, else now + 10 min — the reason line's clock. */\nexport function budgetUntilLabel(r: BudgetReading, now: Date = new Date()): string {\n  const t = r.resetAt && r.resetAt > now.getTime() ? r.resetAt : now.getTime() + 10 * 60_000;\n  const d = new Date(t);\n  return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n}\n\n/**\n * Why `chosen` is not the walled provider skipped ahead of it:\n * \"anthropic 96% until 08:02\" — the one-line switch reason.\n */\nexport function budgetSkipReason(name: string, r: BudgetReading, now: Date = new Date()): string {\n  return `${name} ${r.percent}% until ${budgetUntilLabel(r, now)}`;\n}\n","/**\n * routing.ts — provider selection: flag > class > active (possibly \"auto\").\n *\n * Auto-routing walks `workers.routing.order` — or the class's own `order` —\n * and takes the first provider that is enabled, out of cooldown, (when it\n * defines a quotaProbe) under its quotaSkipAt threshold, and — when the class\n * constrains it — within `maxCostTier` and carrying all `requireTags`. A run\n * that dies of a quota/rate error puts its provider in cooldown for\n * cooldownMinutes; when that happens before the first tool call and\n * retryOnQuota is set, the runner restarts the same task on the next provider\n * (ledger: WORKER-REROUTE).\n *\n * Policy \"budget\" (the default) orders candidates by routing.priority —\n * falling back to routing.order, else DEFAULT_BUDGET_PRIORITY — sorted by\n * percent ascending within equal priority, and additionally skips every\n * provider whose live budget reading (budget.ts) reads at or above its wall\n * (routing.budgetWalls). A walled provider gets a cooldown until\n * max(now + BUDGET_COOLDOWN_MIN, resetAt), so it stays skipped until its\n * window resets and re-enters on its own. The chosen target carries a\n * `switchReason` (\"glm: anthropic 96% until 08:02\") when budget skipped a\n * higher-priority provider. A run's provider is resolved once at spawn; a\n * running worker is never switched mid-flight.\n *\n * An explicit --provider or a class mapping that pins a provider always\n * bypasses all of this.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n  type WorkerEngine,\n  type WorkerProvider,\n  type WorkersConfig,\n  type ProviderTag,\n  WORKER_CLASSES,\n  ANTHROPIC_NATIVE,\n  DEFAULT_BUDGET_PRIORITY,\n  WorkersConfigError,\n  classModelCapability,\n  providerCostTier,\n  getProviderOrNative,\n  resolveCapability,\n} from \"./config.js\";\nimport {\n  type BudgetMap,\n  type BudgetReading,\n  budgetCache,\n  budgetExceeded,\n  budgetFresh,\n  budgetSkipReason,\n  budgetWall,\n} from \"./budget.js\";\nimport { routingStatePath } from \"./paths.js\";\n\nconst QUOTA_SKIP_DEFAULT = 95;\nconst PROBE_TIMEOUT_MS = 10_000;\n/** Minimum minutes a budget-walled provider stays in cooldown, even when its reset is closer. */\nexport const BUDGET_COOLDOWN_MIN = 10;\n\nexport interface RoutingState {\n  /** provider name → ISO timestamp when its cooldown ends */\n  cooldowns: Record<string, string>;\n}\n\nexport function readRoutingState(logDir: string): RoutingState {\n  const path = routingStatePath(logDir);\n  if (!existsSync(path)) return { cooldowns: {} };\n  try {\n    const parsed = JSON.parse(readFileSync(path, \"utf8\")) as RoutingState;\n    return { cooldowns: parsed.cooldowns ?? {} };\n  } catch {\n    return { cooldowns: {} };\n  }\n}\n\nexport function writeRoutingState(logDir: string, state: RoutingState): void {\n  const path = routingStatePath(logDir);\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  const tmp = `${path}.tmp`;\n  writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n  renameSync(tmp, path);\n}\n\nexport function cooldownRemaining(\n  state: RoutingState,\n  provider: string,\n  now: Date = new Date()\n): number {\n  const end = state.cooldowns[provider];\n  if (!end) return 0;\n  const ms = Date.parse(end) - now.getTime();\n  return ms > 0 ? ms : 0;\n}\n\nexport function setCooldown(\n  logDir: string,\n  provider: string,\n  minutes: number,\n  now: Date = new Date()\n): void {\n  const state = readRoutingState(logDir);\n  state.cooldowns[provider] = new Date(now.getTime() + minutes * 60_000).toISOString();\n  writeRoutingState(logDir, state);\n}\n\nexport function clearCooldown(logDir: string, provider: string): void {\n  const state = readRoutingState(logDir);\n  if (!(provider in state.cooldowns)) return;\n  delete state.cooldowns[provider];\n  writeRoutingState(logDir, state);\n}\n\n/**\n * The candidate order for auto-routing: the class's own order, else — policy\n * \"budget\" — routing.priority, falling back to routing.order, else\n * DEFAULT_BUDGET_PRIORITY. Policy \"order\" uses routing.order as before.\n */\nexport function autoOrder(config: WorkersConfig, cls?: { order?: string[] }): string[] {\n  if (cls?.order?.length) return cls.order;\n  if (config.routing.policy === \"order\") return config.routing.order;\n  if (config.routing.priority.length) return config.routing.priority;\n  if (config.routing.order.length) return config.routing.order;\n  return [...DEFAULT_BUDGET_PRIORITY];\n}\n\n/**\n * Run a provider's quotaProbe and return its percentage (0–100), or null when\n * there is no probe or it printed nothing usable. A probe must never break\n * routing: failures read as \"unknown\", not as \"full\".\n */\nexport function probeQuota(provider: WorkerProvider): number | null {\n  if (!provider.quotaProbe) return null;\n  try {\n    const out = execFileSync(\"/bin/sh\", [\"-c\", provider.quotaProbe], {\n      timeout: PROBE_TIMEOUT_MS,\n      encoding: \"utf8\",\n      stdio: [\"ignore\", \"pipe\", \"ignore\"],\n    });\n    const m = out.match(/(\\d+(?:\\.\\d+)?)/);\n    if (!m) return null;\n    return Math.min(100, Math.max(0, Math.round(parseFloat(m[1]))));\n  } catch {\n    return null;\n  }\n}\n\nexport function quotaSkipThreshold(provider: WorkerProvider): number {\n  return provider.quotaSkipAt ?? QUOTA_SKIP_DEFAULT;\n}\n\n/** Does this provider exceed its quota threshold right now? */\nexport function quotaExceeded(provider: WorkerProvider): boolean {\n  const used = probeQuota(provider);\n  return used !== null && used >= quotaSkipThreshold(provider);\n}\n\n/**\n * Put every provider whose fresh budget reading is at or above its wall into\n * a cooldown until max(now + BUDGET_COOLDOWN_MIN, resetAt), so the walk below\n * skips it until its window resets and it re-enters on its own. Never\n * shortens an existing cooldown (a quota-failure cooldown outlasting the\n * reset stays). Fresh readings only: a stale cache must not re-wall anyone.\n */\nexport function recordBudgetCooldowns(config: WorkersConfig, logDir: string, now: Date = new Date()): void {\n  if (config.routing.policy === \"order\") return;\n  const state = readRoutingState(logDir);\n  let dirty = false;\n  for (const [name, r] of Object.entries(budgetCache.snapshot()) as Array<[string, BudgetReading]>) {\n    if (r.percent === null || !budgetFresh(r, now.getTime())) continue;\n    if (!budgetExceeded(r, budgetWall(config.routing, name))) continue;\n    const until = Math.max(now.getTime() + BUDGET_COOLDOWN_MIN * 60_000, r.resetAt ?? 0);\n    const prev = state.cooldowns[name];\n    if (prev && Date.parse(prev) >= until) continue;\n    state.cooldowns[name] = new Date(until).toISOString();\n    dirty = true;\n  }\n  if (dirty) writeRoutingState(logDir, state);\n}\n\nexport interface ResolvedTarget {\n  providerName: string;\n  provider: WorkerProvider;\n  /** Model alias from the class (\"glm/fast\" → \"fast\"), null = provider default. */\n  modelAlias: string | null;\n  /** MCP servers (or set names) from the class target, null when it sets none. */\n  classMcp: string[] | null;\n  /** How the provider was chosen — names the bypass rule for rerouting. */\n  via: \"flag\" | \"class\" | \"active\" | \"auto\";\n  /**\n   * One line on why budget routing moved the run off a higher-priority\n   * provider (\"glm: anthropic 96% until 08:02\"); absent otherwise.\n   */\n  switchReason?: string;\n}\n\nexport class NoProviderError extends WorkersConfigError {}\n\nfunction mustExist(config: WorkersConfig, name: string): WorkerProvider {\n  const p = getProviderOrNative(config, name);\n  if (!p) {\n    throw new NoProviderError(\n      `no worker provider named \"${name}\". Configured: ` +\n        `${Object.keys(config.providers).join(\", \") || \"(none)\"}.` +\n        `\\nAdd one with: pai worker providers add <name> --base-url <url> --key-file <path> --model <model>`\n    );\n  }\n  return p;\n}\n\nfunction mustBeRunnable(name: string, p: WorkerProvider): WorkerProvider {\n  if (!p.enabled) {\n    throw new NoProviderError(\n      `provider \"${name}\" is disabled. Enable it with: pai worker providers enable ${name}`\n    );\n  }\n  return p;\n}\n\n/** Why one provider of a routing order did not qualify (message fragment). */\nfunction exclusionReason(\n  config: WorkersConfig,\n  state: RoutingState,\n  name: string,\n  cls?: { maxCostTier?: number; requireTags?: string[] },\n  budgets?: BudgetMap\n): string | null {\n  const p = getProviderOrNative(config, name);\n  if (!p) return \"not configured\";\n  if (!p.enabled) return \"disabled\";\n  if (cooldownRemaining(state, name) > 0) return \"cooldown\";\n  if (quotaExceeded(p)) return \"quota\";\n  if (budgets) {\n    const r = budgets[name];\n    // a fresh-enough reading at or above the provider's wall means full;\n    // unknown (no reading, null percent) never walls\n    if (r && r.percent !== null && budgetExceeded(r, budgetWall(config.routing, name))) {\n      return `budget ${r.percent}% ≥ wall ${budgetWall(config.routing, name)}`;\n    }\n  }\n  const tier = providerCostTier(p);\n  if (cls?.maxCostTier !== undefined && tier > cls.maxCostTier) {\n    return `cost tier ${tier} > max ${cls.maxCostTier}`;\n  }\n  if (cls?.requireTags?.length) {\n    const have = p.tags ?? [];\n    const missing = cls.requireTags.filter((t) => !have.includes(t as ProviderTag));\n    if (missing.length) return `missing tags: ${missing.join(\", \")}`;\n  }\n  return null;\n}\n\n/**\n * Resolve which provider (and model alias) a run uses.\n *\n * @param flagProvider --provider value, highest precedence\n * @param className    --class value (the old --role), looked up in workers.classes\n */\nexport function resolveTarget(\n  config: WorkersConfig,\n  logDir: string,\n  opts: { flagProvider?: string; className?: string } = {}\n): ResolvedTarget {\n  if (opts.flagProvider) {\n    return {\n      providerName: opts.flagProvider,\n      provider: mustBeRunnable(opts.flagProvider, mustExist(config, opts.flagProvider)),\n      modelAlias: null,\n      classMcp: null,\n      via: \"flag\",\n    };\n  }\n\n  // class constraints (present whether or not the target pins a provider)\n  const clsTarget = opts.className ? config.classes[opts.className] : undefined;\n  const cls =\n    typeof clsTarget === \"object\" && clsTarget !== null ? clsTarget : undefined;\n\n  if (opts.className && clsTarget !== undefined && typeof clsTarget !== \"object\") {\n    const [name, alias] = clsTarget.split(\"/\");\n    return {\n      providerName: name,\n      provider: mustBeRunnable(name, mustExist(config, name)),\n      modelAlias: alias ?? null,\n      classMcp: null,\n      via: \"class\",\n    };\n  }\n  if (opts.className && cls?.provider) {\n    const provider = mustBeRunnable(cls.provider, mustExist(config, cls.provider));\n    return {\n      providerName: cls.provider,\n      provider,\n      modelAlias: null,\n      classMcp: cls.mcp ?? null,\n      via: \"class\",\n    };\n  }\n  if (opts.className && clsTarget === undefined) {\n    // a standard class may simply be unconfigured (it then routes like a run\n    // without a class); anything else is a typo and must not pass silently\n    if (!(WORKER_CLASSES as readonly string[]).includes(opts.className)) {\n      throw new NoProviderError(\n        `no class named \"${opts.className}\". Standard classes: ${WORKER_CLASSES.join(\", \")}.` +\n          `Defined: ${Object.keys(config.classes).join(\", \") || \"(none)\"}.` +\n          `\\nSet one with: pai worker classes set ${opts.className}=<provider[/alias]>`\n      );\n    }\n  }\n\n  if (config.active !== \"auto\") {\n    // nothing active and no class entry: the built-in provider, no config written\n    const name = config.active ?? ANTHROPIC_NATIVE;\n    return {\n      providerName: name,\n      provider: mustBeRunnable(name, mustExist(config, name)),\n      modelAlias: null,\n      classMcp: null,\n      via: \"active\",\n    };\n  }\n\n  // auto: first provider in (class or global) order that is enabled,\n  // cooled-down-free, under quota and within the class constraints — policy\n  // \"budget\" additionally skips providers whose live budget is at/above\n  // their wall, preferring the fullest-budget candidate within equal priority\n  const state = readRoutingState(logDir);\n  const useBudget = config.routing.policy !== \"order\";\n  const order = autoOrder(config, cls);\n  const budgets: BudgetMap | undefined = useBudget ? budgetCache.snapshot() : undefined;\n  const candidates = useBudget\n    ? order\n        .map((name, idx) => ({ name, idx, percent: budgets?.[name]?.percent }))\n        .sort((a, b) => a.idx - b.idx || (a.percent ?? 0) - (b.percent ?? 0))\n        .map((c) => c.name)\n    : order;\n  const excluded: string[] = [];\n  let budgetSkipped: string | null = null;\n  for (const name of candidates) {\n    const p = getProviderOrNative(config, name);\n    const why = exclusionReason(config, state, name, cls, budgets);\n    if (why) {\n      excluded.push(`${name}: ${why}`);\n      if (useBudget && budgetSkipped === null && why.startsWith(\"budget\") && p) {\n        budgetSkipped = budgetSkipReason(name, budgets![name]);\n      }\n      continue;\n    }\n    // exclusionReason already returned null (no exclusion) above, which is\n    // only possible when getProviderOrNative found a real provider\n    return {\n      providerName: name,\n      provider: p!,\n      modelAlias: null,\n      classMcp: cls?.mcp ?? null,\n      via: \"auto\",\n      ...(budgetSkipped ? { switchReason: `${name}: ${budgetSkipped}` } : {}),\n    };\n  }\n  throw new NoProviderError(\n    `auto-routing found no usable provider${opts.className ? ` for class \"${opts.className}\"` : \"\"} ` +\n      `(order: [${order.join(\", \")}]).` +\n      `${excluded.length ? `\\nExcluded: ${excluded.join(\"; \")}.` : \"\"}` +\n      `\\nClear a cooldown with: pai worker providers enable <name>; widen the class with: pai worker classes set ${opts.className ?? \"<class>\"}=<target>`\n  );\n}\n\n/**\n * Next provider after `from` in auto order, applying the same filters —\n * including the budget wall under policy \"budget\", so a reroute never moves\n * a run onto a provider with no budget left. Used by rerouting; null when\n * the order is exhausted.\n */\nexport function nextAutoProvider(\n  config: WorkersConfig,\n  logDir: string,\n  from: string,\n  now: Date = new Date()\n): string | null {\n  const state = readRoutingState(logDir);\n  const useBudget = config.routing.policy !== \"order\";\n  const order = autoOrder(config);\n  const budgets: BudgetMap | undefined = useBudget ? budgetCache.snapshot() : undefined;\n  const start = order.indexOf(from);\n  for (let i = start + 1; i < order.length; i++) {\n    const name = order[i];\n    const p = getProviderOrNative(config, name);\n    if (!p || !p.enabled) continue;\n    const end = state.cooldowns[name];\n    if (end && Date.parse(end) > now.getTime()) continue;\n    if (quotaExceeded(p)) continue;\n    if (budgets) {\n      const r = budgets[name];\n      if (r && budgetExceeded(r, budgetWall(config.routing, name))) continue;\n    }\n    return name;\n  }\n  return null;\n}\n\n/**\n * Was this failure a quota/rate failure? Detected from the result text the\n * endpoint produced (HTTP status is not visible in the stream events).\n */\nexport function isQuotaFailure(resultText: string): boolean {\n  return /usage limit reached|rate limit|quota/i.test(resultText);\n}\n\nexport interface CapabilityRunResolution {\n  providerName: string;\n  provider: WorkerProvider;\n  model: string;\n  engine: WorkerEngine;\n  fellBack: boolean;\n  capability: string;\n}\n\n/**\n * Whether a run should resolve through `resolveCapability` instead of\n * `resolveTarget`: an explicit `--capability` always does. A `--class`\n * whose implied capability is not \"default\" (e.g. `image`) does too, but\n * only when the class itself names no provider — a class already pinned to\n * one (`image: glm/image`) always wins unchanged, same as any other class.\n */\nexport function capabilityForRun(\n  config: WorkersConfig,\n  opts: { capabilityFlag?: string; className?: string }\n): string | null {\n  if (opts.capabilityFlag) return opts.capabilityFlag;\n  if (!opts.className) return null;\n  const capability = classModelCapability(opts.className);\n  if (capability === \"default\") return null;\n  const clsTarget = config.classes[opts.className];\n  const namesProvider = typeof clsTarget === \"string\" ? true : Boolean(clsTarget?.provider);\n  return namesProvider ? null : capability;\n}\n\n/**\n * resolveCapability, turned into a runnable provider (NoProviderError-shaped\n * on failure). Unlike resolveCapability itself — which falls back to the\n * active provider's default model so `describeCapabilities` has something to\n * report — an actual run refuses the fallback: silently running a normal\n * model on a prompt written for a capability it does not have (an image\n * prompt fed to a text model, say) produces a confusing non-answer instead\n * of a clear error naming the config key to set.\n */\nexport function resolveCapabilityRun(config: WorkersConfig, capability: string): CapabilityRunResolution {\n  const r = resolveCapability(config, capability);\n  if (r.fellBack) {\n    throw new NoProviderError(\n      `no provider declares the \"${capability}\" capability — configure one with: ` +\n        `pai worker capability ${capability} <provider>[,<provider>…], or ` +\n        `pai worker model ${capability} <model-id> --provider <name>`\n    );\n  }\n  // \"image\" specifically promises real pixel output — a provider whose only\n  // claim to it is a `models.image` entry from the classes-based idiom\n  // (`image: glm/image`, a plain model choice on an unchanged claude/codex\n  // engine) cannot deliver that through this cross-provider run path, so a\n  // run refuses it rather than spawning claude on a prompt it cannot answer.\n  if (capability === \"image\" && r.engine !== \"image\") {\n    throw new NoProviderError(\n      `provider \"${r.provider}\" has no image engine (it would run \"${r.model}\" on its normal ` +\n        `${r.engine} engine, not generate an image) — configure an engine=image provider with: ` +\n        `pai worker providers add <name> --engine image --url <url> --key <key> --model <id>, then ` +\n        `pai worker capability image <name>`\n    );\n  }\n  const provider = getProviderOrNative(config, r.provider);\n  if (!provider) {\n    throw new NoProviderError(`capability \"${capability}\" resolved to unknown provider \"${r.provider}\"`);\n  }\n  return {\n    providerName: r.provider,\n    provider,\n    model: r.model,\n    engine: r.engine,\n    fellBack: r.fellBack,\n    capability,\n  };\n}\n","/**\n * server.ts — the PAI worker proxy: Anthropic Messages API on the front,\n * OpenAI Chat Completions on the back, loopback only.\n *\n * One proxy serves every OpenAI-protocol provider: Claude Code points\n * ANTHROPIC_BASE_URL at `http://127.0.0.1:8797/<provider>` and the provider\n * name in the path selects the upstream (`upstreamUrl` + `keyFile` from the\n * workers config, re-read per request so config edits apply without a\n * restart). `run` starts the proxy on demand (detached, pid file under the\n * logDir) and `pai worker proxy stop` stops it again.\n *\n * All translation lives in translate.ts; this file is only HTTP plumbing.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { createServer, type Server } from \"node:http\";\nimport { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from \"node:fs\";\nimport { connect } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { readWorkersSection, resolveProviderKey, type WorkerProvider } from \"../config.js\";\nimport { workersLogDir } from \"../paths.js\";\nimport {\n  anthropicError,\n  anthropicToOpenAi,\n  openAiToAnthropic,\n  OpenAiStreamTranslator,\n  type AnthropicRequest,\n} from \"./translate.js\";\n\nexport const DEFAULT_PROXY_PORT = 8797;\nconst HOST = \"127.0.0.1\";\n\n/** How providers are looked up — swapped out by the tests. */\nexport type ProviderResolver = () => Record<string, WorkerProvider>;\n\nconst configProviders: ProviderResolver = () => readWorkersSection().workers.providers;\n\nexport function proxyPidPath(logDir: string): string {\n  return join(logDir, \"proxy.pid\");\n}\n\n// ---------------------------------------------------------------------------\n// HTTP server\n// ---------------------------------------------------------------------------\n\ninterface ProxyServerOptions {\n  port?: number;\n  resolveProvider?: ProviderResolver;\n  /** Test hook: called with every (translated) upstream request body. */\n  onUpstreamRequest?: (url: string, body: unknown) => void;\n}\n\n/**\n * Create (but not start) the proxy server. Routes:\n *   GET  /healthz              → 200 ok\n *   POST /<provider>/v1/messages (and /<provider>/messages) → translated call\n */\nexport function createProxyServer(opts: ProxyServerOptions = {}): Server {\n  const resolveProvider = opts.resolveProvider ?? configProviders;\n  return createServer((req, res) => {\n    const url = new URL(req.url ?? \"/\", `http://${HOST}`);\n    if (req.method === \"GET\" && url.pathname === \"/healthz\") {\n      res.writeHead(200, { \"content-type\": \"text/plain\" });\n      res.end(\"ok\");\n      return;\n    }\n    const m = url.pathname.match(/^\\/([a-zA-Z0-9_-]+)\\/(v1\\/)?messages$/);\n    if (req.method !== \"POST\" || !m) {\n      res.writeHead(404, { \"content-type\": \"application/json\" });\n      res.end(JSON.stringify(anthropicError(404, `no such route: ${req.method} ${url.pathname}`)));\n      return;\n    }\n    const providerName = m[1];\n    const chunks: Buffer[] = [];\n    req.on(\"data\", (c: Buffer) => chunks.push(c));\n    req.on(\"end\", () => {\n      void handleMessages(providerName, Buffer.concat(chunks).toString(\"utf8\"), resolveProvider, res, opts);\n    });\n    req.on(\"error\", () => {\n      res.writeHead(400, { \"content-type\": \"application/json\" });\n      res.end(JSON.stringify(anthropicError(400, \"request body read failed\")));\n    });\n  });\n}\n\nasync function handleMessages(\n  providerName: string,\n  bodyText: string,\n  resolveProvider: ProviderResolver,\n  res: import(\"node:http\").ServerResponse,\n  opts: ProxyServerOptions\n): Promise<void> {\n  const fail = (status: number, message: string) => {\n    res.writeHead(status, { \"content-type\": \"application/json\" });\n    res.end(JSON.stringify(anthropicError(status, JSON.stringify({ message }))));\n  };\n  let reqBody: AnthropicRequest;\n  try {\n    reqBody = JSON.parse(bodyText) as AnthropicRequest;\n  } catch {\n    fail(400, \"request body is not valid JSON\");\n    return;\n  }\n  const provider = resolveProvider()[providerName];\n  if (!provider) {\n    fail(404, `no worker provider named \"${providerName}\"`);\n    return;\n  }\n  if (provider.protocol !== \"openai\" || !provider.upstreamUrl) {\n    fail(400, `provider \"${providerName}\" is not an openai-protocol provider`);\n    return;\n  }\n\n  const model = provider.models.default;\n  const openaiBody = anthropicToOpenAi(reqBody, model);\n  opts.onUpstreamRequest?.(`${provider.upstreamUrl}/chat/completions`, openaiBody);\n\n  let upstream: Response;\n  try {\n    const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n    let token: string | null;\n    try {\n      token = resolveProviderKey(provider);\n    } catch (e) {\n      fail(500, e instanceof Error ? e.message : String(e));\n      return;\n    }\n    if (token) headers.authorization = `Bearer ${token}`;\n    upstream = await fetch(`${provider.upstreamUrl}/chat/completions`, {\n      method: \"POST\",\n      headers,\n      body: JSON.stringify(openaiBody),\n    });\n  } catch (e) {\n    fail(502, `upstream unreachable: ${e instanceof Error ? e.message : String(e)}`);\n    return;\n  }\n\n  if (!upstream.ok) {\n    const text = await upstream.text().catch(() => \"\");\n    res.writeHead(upstream.status, { \"content-type\": \"application/json\" });\n    res.end(JSON.stringify(anthropicError(upstream.status, text)));\n    return;\n  }\n\n  if (reqBody.stream) {\n    res.writeHead(200, {\n      \"content-type\": \"text/event-stream\",\n      \"cache-control\": \"no-cache\",\n      connection: \"keep-alive\",\n    });\n    const translator = new OpenAiStreamTranslator(model);\n    let buf = \"\";\n    if (!upstream.body) {\n      res.end(translator.finish());\n      return;\n    }\n    const reader = upstream.body.getReader();\n    try {\n      for (;;) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        buf += new TextDecoder().decode(value, { stream: true });\n        let nl: number;\n        while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n          const line = buf.slice(0, nl).trim();\n          buf = buf.slice(nl + 1);\n          if (!line.startsWith(\"data:\")) continue;\n          const payload = line.slice(5).trim();\n          if (payload === \"[DONE]\") {\n            res.write(translator.finish());\n          } else {\n            try {\n              res.write(translator.feed(JSON.parse(payload) as Parameters<typeof translator.feed>[0]));\n            } catch {\n              // skip a malformed chunk rather than kill the stream\n            }\n          }\n        }\n      }\n    } finally {\n      res.write(translator.finish()); // no-op when [DONE] already finished it\n      res.end();\n    }\n    return;\n  }\n\n  const json = (await upstream.json().catch(() => null)) as unknown;\n  if (!json) {\n    fail(502, \"upstream returned a non-JSON body\");\n    return;\n  }\n  res.writeHead(200, { \"content-type\": \"application/json\" });\n  res.end(JSON.stringify(openAiToAnthropic(json as Parameters<typeof openAiToAnthropic>[0], model)));\n}\n\n// ---------------------------------------------------------------------------\n// Start / stop\n// ---------------------------------------------------------------------------\n\n/** Listen on host:port (loopback). Resolves with the bound port. */\nexport function listenProxy(server: Server, port = DEFAULT_PROXY_PORT): Promise<number> {\n  return new Promise((resolve, reject) => {\n    server.once(\"error\", reject);\n    server.listen(port, HOST, () => {\n      const addr = server.address();\n      resolve(typeof addr === \"object\" && addr ? addr.port : port);\n    });\n  });\n}\n\n/** True when something already accepts connections on the port. */\nexport function proxyListening(port = DEFAULT_PROXY_PORT, timeoutMs = 400): Promise<boolean> {\n  return new Promise((resolve) => {\n    const sock = connect({ port, host: HOST });\n    const done = (ok: boolean) => {\n      sock.removeAllListeners();\n      sock.destroy();\n      resolve(ok);\n    };\n    sock.setTimeout(timeoutMs, () => done(false));\n    sock.once(\"connect\", () => done(true));\n    sock.once(\"error\", () => done(false));\n  });\n}\n\n/**\n * Where the standalone proxy artefact lives, found from this module's own\n * location (dist/cli bundle → ../hooks/, dev checkout → <repo>/dist/hooks).\n */\nexport function standaloneProxyPath(): string | null {\n  let dir = dirname(fileURLToPath(import.meta.url));\n  for (let i = 0; i < 6; i++) {\n    for (const cand of [\n      join(dir, \"worker-proxy.mjs\"),\n      join(dir, \"hooks\", \"worker-proxy.mjs\"),\n      join(dir, \"dist\", \"hooks\", \"worker-proxy.mjs\"),\n    ]) {\n      if (existsSync(cand)) return cand;\n    }\n    const parent = dirname(dir);\n    if (parent === dir) break;\n    dir = parent;\n  }\n  return null;\n}\n\n/**\n * Ensure a proxy is listening on the port; start the detached standalone when\n * nothing answers. Returns the base URL (`http://127.0.0.1:<port>`).\n */\nexport async function ensureProxyRunning(port: number, logDir: string): Promise<string> {\n  if (await proxyListening(port)) return `http://${HOST}:${port}`;\n  const script = standaloneProxyPath();\n  if (!script) {\n    throw new Error(\n      `the PAI worker proxy is not running and its build was not found — ` +\n        `run \\`bun run build\\` (or start one with: pai worker proxy --port ${port})`\n    );\n  }\n  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n  const child = spawn(process.execPath, [script, \"--port\", String(port)], {\n    detached: true,\n    stdio: \"ignore\",\n  });\n  child.unref();\n  writeFileSync(proxyPidPath(logDir), `${child.pid}\\n`, \"utf8\");\n  for (let i = 0; i < 40; i++) {\n    if (await proxyListening(port)) return `http://${HOST}:${port}`;\n    await new Promise((r) => setTimeout(r, 250));\n  }\n  throw new Error(`the PAI worker proxy did not come up on port ${port} (see ${proxyPidPath(logDir)})`);\n}\n\n/** `pai worker proxy stop`: SIGTERM the pid from the pid file. */\nexport function stopProxy(logDir: string): string {\n  const path = proxyPidPath(logDir);\n  if (!existsSync(path)) return \"no proxy pid file — nothing to stop\";\n  const pid = parseInt(readFileSync(path, \"utf8\").trim(), 10);\n  unlinkSync(path);\n  if (!Number.isFinite(pid)) return \"stale proxy pid file removed\";\n  try {\n    process.kill(pid, \"SIGTERM\");\n  } catch {\n    return `proxy pid ${pid} was not running (pid file removed)`;\n  }\n  return `proxy pid ${pid} stopped`;\n}\n","/**\n * tool-errors.ts — systematic worker tool errors.\n *\n * The runner records every failed tool_result as one JSONL line\n * (<logDir>/tool-errors.jsonl) under a normalised signature. Supervision's\n * tick reads that file on two routes (see runSupervisionTick): while a worker\n * is RUNNING, the first occurrence of each signature — all classes — is\n * pushed to the session that spawned it (detectRunningWorkerErrors);\n * everything else keeps the actionable-only thresholds (detectToolErrors,\n * the same signature repeating tells the orchestrator once per 30 min).\n * `pai worker errors` lists the signatures grouped.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, openSync, readSync, closeSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport interface ToolErrorRecord {\n  /** Epoch ms. */\n  ts: number;\n  worker: string;\n  provider: string;\n  label: string;\n  tool: string;\n  signature: string;\n  raw: string;\n  /** What the tool was asked to do: path, pattern or command head. */\n  input?: string;\n}\n\nexport const TOOL_ERRORS_FILE = \"tool-errors.jsonl\";\nexport const SAME_WORKER_MIN = 3;\nexport const CROSS_WORKER_MIN = 2;\nexport const CROSS_WORKER_WINDOW_MS = 30 * 60_000;\nexport const SAME_WORKER_WINDOW_MS = 2 * 60 * 60_000;\nexport const RESEND_MS = 30 * 60_000;\nconst SIGNATURE_MAX = 160;\nconst RAW_MAX = 300;\nconst INPUT_MAX = 1000;\nconst TAIL_BYTES = 400_000;\n\nexport type ErrorClass = \"harness\" | \"model\" | \"unknown\";\n\nexport interface Classification {\n  cls: ErrorClass;\n  /** One-line hint naming what the error is. */\n  hint: string;\n  /** Model-only: same-worker count that makes it an alert (Infinity = never). */\n  loopAt?: number;\n}\n\nconst cwdOf = (raw: string): string | undefined => raw.match(/working directory (?:is )?(\\/[^\\s\"'`),;]*)/i)?.[1]?.replace(/[.]+$/, \"\");\nconst inputPath = (r: ToolErrorRecord): string | undefined => r.input?.split(\" \")[0];\n\n/**\n * Deterministic classification, first match wins. The one table to extend:\n * model rules are recorded but alert only at loopAt (Infinity: never);\n * harness rules alert at the usual thresholds; anything else is unknown.\n */\nconst RULES: Array<{ test: (r: ToolErrorRecord) => boolean; c: Classification }> = [\n  { test: (r) => /no matches/i.test(r.raw) && /^(Grep|Bash)$/.test(r.tool), c: { cls: \"model\", hint: \"search found nothing\", loopAt: Infinity } },\n  {\n    test: (r) => {\n      const cwd = cwdOf(r.raw), p = inputPath(r);\n      return r.tool === \"Read\" && /File does not exist/i.test(r.raw) && !!cwd && !!p && (p === cwd || p.startsWith(cwd + \"/\"));\n    },\n    c: { cls: \"model\", hint: \"Read of a missing file inside the worker cwd\", loopAt: Infinity },\n  },\n  { test: (r) => /exactly the same|String to replace not found/i.test(r.raw), c: { cls: \"model\", hint: \"Edit with a wrong old_string\", loopAt: 5 } },\n  { test: (r) => /no AI attribution/i.test(r.raw), c: { cls: \"model\", hint: \"commit guard: AI attribution denied\", loopAt: 3 } },\n  { test: (r) => /git checkout .*blocked|checkout .* blocked/i.test(r.raw), c: { cls: \"model\", hint: \"guard: git checkout blocked\", loopAt: 3 } },\n  { test: (r) => /sleep .*blocked/i.test(r.raw), c: { cls: \"model\", hint: \"guard: sleep blocked\", loopAt: 3 } },\n  { test: (r) => /Bash timeout .*blocked/i.test(r.raw), c: { cls: \"model\", hint: \"guard: Bash timeout blocked\", loopAt: 3 } },\n  {\n    test: (r) => {\n      if (!/hook error/i.test(r.raw)) return false;\n      const cwd = cwdOf(r.raw);\n      const paths = r.raw.match(/(?:^|[\\s(=:])(\\/[^\\s\"'`),;]+)/g)?.map((x) => x.replace(/^[\\s(=:]+/, \"\")) ?? [];\n      return paths.some((p) => (!cwd || !(p === cwd || p.startsWith(cwd + \"/\"))) && existsSync(p));\n    },\n    c: { cls: \"harness\", hint: \"hook error on an existing path outside the worker cwd\" },\n  },\n  { test: (r) => /^empty-response/i.test(r.raw), c: { cls: \"model\", hint: \"empty first reply — model stopped at once (seen with short spec prompts); one automatic relaunch already ran\" } },\n  { test: (r) => /Working directory .* was deleted/i.test(r.raw), c: { cls: \"harness\", hint: \"worker cwd deleted\" } },\n  { test: (r) => /Failed to authenticate|API Error|\\b(401|500)\\b/.test(r.raw), c: { cls: \"harness\", hint: \"API/auth error\" } },\n  { test: (r) => /No such tool available/i.test(r.raw), c: { cls: \"harness\", hint: \"tool missing in the worker\" } },\n  { test: (r) => /\\b5\\d\\d\\b/.test(r.raw) && /localhost|127\\.0\\.0\\.1/.test(r.raw), c: { cls: \"harness\", hint: \"HTTP 5xx from a local server\" } },\n];\n\nexport function classifyToolError(r: ToolErrorRecord): Classification {\n  return RULES.find((x) => x.test(r))?.c ?? { cls: \"unknown\", hint: \"unclassified\" };\n}\n\nexport function toolErrorsPath(logDir: string): string {\n  return join(logDir, TOOL_ERRORS_FILE);\n}\n\n/** Tool name + error text with quoted strings, paths, ids and numbers replaced by placeholders. */\nexport function normalizeError(tool: string, text: string): string {\n  const body = text\n    .replace(/\\s+/g, \" \")\n    .trim()\n    .replace(/([\"'`])[^\"'`]*\\1/g, \"<S>\")\n    .replace(/(^|[\\s(=:])(~?\\/[^\\s\"'`),;]*)/g, \"$1<P>\")\n    .replace(/\\b[0-9a-f]{8}-[0-9a-f-]{27,}\\b/gi, \"<ID>\")\n    .replace(/\\b\\d{8}-\\d{6}-\\d+\\b/g, \"<ID>\")\n    .replace(/\\b\\d+\\b/g, \"<N>\");\n  return `${tool}|${body.slice(0, SIGNATURE_MAX)}`;\n}\n\n/** Path / pattern / command head of a tool_use input, for the error record. */\nexport function summarizeToolInput(input: unknown): string | undefined {\n  if (!input || typeof input !== \"object\") return undefined;\n  const i = input as Record<string, unknown>;\n  const str = (k: string) => (typeof i[k] === \"string\" ? (i[k] as string) : \"\");\n  const s = [str(\"file_path\") || str(\"path\"), str(\"pattern\"), str(\"command\")].filter(Boolean).join(\" \");\n  return s ? s.replace(/\\s+/g, \" \").slice(0, INPUT_MAX) : undefined;\n}\n\nexport function recordToolError(\n  logDir: string,\n  rec: { worker: string; provider: string; label: string; tool: string; text: string; input?: unknown },\n  now = Date.now()\n): void {\n  try {\n    if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n    const line: ToolErrorRecord = {\n      ts: now,\n      worker: rec.worker,\n      provider: rec.provider,\n      label: rec.label,\n      tool: rec.tool,\n      signature: normalizeError(rec.tool, rec.text),\n      raw: rec.text.replace(/\\s+/g, \" \").trim().slice(0, RAW_MAX),\n    };\n    const input = summarizeToolInput(rec.input);\n    if (input) line.input = input;\n    appendFileSync(toolErrorsPath(logDir), JSON.stringify(line) + \"\\n\", \"utf8\");\n  } catch {\n    // supervision data must never break a run\n  }\n}\n\n/** Records of the file's tail (bounded read), oldest first; damaged lines skipped. */\nexport function readToolErrors(logDir: string, sinceMs = 0): ToolErrorRecord[] {\n  const path = toolErrorsPath(logDir);\n  if (!existsSync(path)) return [];\n  let text: string;\n  const size = statSync(path).size;\n  if (size > TAIL_BYTES) {\n    const fd = openSync(path, \"r\");\n    const buf = Buffer.alloc(TAIL_BYTES);\n    readSync(fd, buf, 0, TAIL_BYTES, size - TAIL_BYTES);\n    closeSync(fd);\n    text = buf.toString(\"utf8\").split(\"\\n\").slice(1).join(\"\\n\");\n  } else {\n    text = readFileSync(path, \"utf8\");\n  }\n  const out: ToolErrorRecord[] = [];\n  for (const l of text.split(\"\\n\")) {\n    if (!l) continue;\n    try {\n      const r = JSON.parse(l) as ToolErrorRecord;\n      if (r.ts >= sinceMs && typeof r.signature === \"string\") out.push(r);\n    } catch {\n      /* skip */\n    }\n  }\n  return out;\n}\n\nexport interface ToolErrorGroup {\n  signature: string;\n  count: number;\n  workers: string[];\n  providers: string[];\n  first: number;\n  last: number;\n  sample: string;\n  sampleInput?: string;\n  /** The worker with the newest record. */\n  lastWorker: string;\n  cls: ErrorClass;\n  hint: string;\n}\n\nconst pick = (r: ToolErrorRecord) => {\n  const { cls, hint } = classifyToolError(r);\n  return { cls, hint };\n};\n\nexport function groupToolErrors(records: ToolErrorRecord[]): ToolErrorGroup[] {\n  const m = new Map<string, ToolErrorGroup>();\n  for (const r of records) {\n    let g = m.get(r.signature);\n    if (!g) {\n      g = { signature: r.signature, count: 0, workers: [], providers: [], first: r.ts, last: r.ts, sample: r.raw, sampleInput: r.input, lastWorker: r.worker, ...pick(r) };\n      m.set(r.signature, g);\n    }\n    g.count += 1;\n    if (!g.workers.includes(r.worker)) g.workers.push(r.worker);\n    if (!g.providers.includes(r.provider)) g.providers.push(r.provider);\n    g.first = Math.min(g.first, r.ts);\n    if (r.ts >= g.last) {\n      g.last = r.ts;\n      g.lastWorker = r.worker;\n      g.sample = r.raw;\n      g.sampleInput = r.input;\n    }\n  }\n  return [...m.values()].sort((a, b) => b.last - a.last);\n}\n\n/**\n * Signatures to report now. Rules: (a) ≥3 in one worker within 2h, (b) ≥2\n * workers within 30 min, (c) a hook-generated error twice in one worker (a guard that blocks once and teaches the fix is not systematic; two workers is rule b).\n * `sent` (signature → last send epoch ms) holds each signature to one message\n * per RESEND_MS and hides records up to that send; it is updated for the returned groups.\n */\nexport function detectToolErrors(\n  records: ToolErrorRecord[],\n  now: number,\n  sent: Record<string, number>\n): ToolErrorGroup[] {\n  const out: ToolErrorGroup[] = [];\n  const bySig = new Map<string, ToolErrorRecord[]>();\n  for (const r of records) bySig.set(r.signature, [...(bySig.get(r.signature) ?? []), r]);\n  for (const [sig, all] of bySig) {\n    if (now - (sent[sig] ?? 0) < RESEND_MS) continue;\n    // records at or before the last alert were already reported\n    const recs = all.filter((r) => r.ts > (sent[sig] ?? 0) && classifyToolError(r).loopAt !== Infinity);\n    if (!recs.length) continue;\n    const loopAt = classifyToolError(recs[0]).loopAt;\n    const perWorker = new Map<string, number>();\n    for (const r of recs) if (now - r.ts <= SAME_WORKER_WINDOW_MS) perWorker.set(r.worker, (perWorker.get(r.worker) ?? 0) + 1);\n    if (loopAt !== undefined) {\n      // model-only: alert just when one worker loops on it\n      const n = Math.max(0, ...perWorker.values());\n      if (n < loopAt) continue;\n      sent[sig] = now;\n      out.push(groupToolErrors(recs)[0]);\n      continue;\n    }\n    const a = [...perWorker.values()].some((n) => n >= SAME_WORKER_MIN);\n    const recent = recs.filter((r) => now - r.ts <= CROSS_WORKER_WINDOW_MS);\n    const b = new Set(recent.map((r) => r.worker)).size >= CROSS_WORKER_MIN;\n    const c = recs[0].raw.includes(\"hook error\") && [...perWorker.values()].some((n) => n >= 2);\n    if (!(a || b || c)) continue;\n    sent[sig] = now;\n    // only rule (b) fired: report just the workers inside its window\n    out.push(groupToolErrors(a || c ? recs : recent)[0]);\n  }\n  return out;\n}\n\nexport function toolErrorText(g: ToolErrorGroup): string {\n  const what = `${g.signature} ×${g.count} in ${g.workers.join(\",\")} — sample: ${g.sample}${g.sampleInput ? ` (input: ${g.sampleInput})` : \"\"}`;\n  const tail = \"harness → stop runs it spoils, fix the cause in PAI/AIBroker, prove it, commit; model → counts against the model, stop the run if it loops; details: pai worker errors\";\n  if (g.cls === \"harness\") return `[harness] ${g.hint}: ${what} — fix at the owner. ${tail}`;\n  if (g.cls === \"model\") return `[model ×${g.count}] ${g.hint}: ${what} — counts against the model; stop the run if it loops. ${tail}`;\n  return `[unknown] ${what} — ${tail}`;\n}\n\n/**\n * A running worker whose spawner is known: errors go to that session.\n */\nexport interface RunningWorkerRef {\n  id: string;\n  /** Owning session key (the spawner's claude session id, else the iTerm uuid). */\n  session: string;\n}\n\nexport interface RunningErrorPush {\n  worker: string;\n  session: string;\n  signature: string;\n  group: ToolErrorGroup;\n}\n\n/**\n * Stable short tag of a signature, for claim ids: once-per-(worker,signature)\n * delivery that survives a lost state file.\n */\nexport function toolErrorSigTag(signature: string): string {\n  let h = 5381;\n  for (let i = 0; i < signature.length; i++) h = ((h * 33) ^ signature.charCodeAt(i)) >>> 0;\n  return h.toString(36);\n}\n\n/**\n * While a worker is RUNNING, its tool errors are pushed to the session that\n * spawned it — the FIRST occurrence of each signature, ALL classes. 2026-10-17:\n * a run doomed from its first tool call (worktree of the wrong repo → every\n * path error [model]-class) was silently classified away by the\n * actionable-only filter, and the spawner learned of it only by polling\n * `pai worker errors`. Actionability is not the daemon's to guess: the\n * spawner wrote the spec and knows what a spoiled run looks like. Later\n * occurrences of a pushed signature are counted (the group's count, and the\n * run-end result report carries the totals), never re-pushed; nothing is\n * pushed after the run ends. `sent` maps worker id → signature → push time\n * and is updated for the returned pushes.\n */\nexport function detectRunningWorkerErrors(\n  running: readonly RunningWorkerRef[],\n  records: readonly ToolErrorRecord[],\n  sent: Record<string, Record<string, number>>,\n  now: number\n): RunningErrorPush[] {\n  const sessions = new Map(running.map((r) => [r.id, r.session] as const));\n  const byWorker = new Map<string, Map<string, ToolErrorRecord[]>>();\n  for (const r of records) {\n    if (!sessions.has(r.worker)) continue;\n    let bySig = byWorker.get(r.worker);\n    if (!bySig) byWorker.set(r.worker, (bySig = new Map()));\n    bySig.set(r.signature, [...(bySig.get(r.signature) ?? []), r]);\n  }\n  const out: RunningErrorPush[] = [];\n  for (const [worker, bySig] of byWorker) {\n    const pushed = (sent[worker] ??= {});\n    for (const [sig, all] of bySig) {\n      if (pushed[sig] !== undefined) continue; // first occurrence per run, never re-pushed\n      pushed[sig] = now;\n      out.push({ worker, session: sessions.get(worker)!, signature: sig, group: groupToolErrors(all)[0] });\n    }\n  }\n  return out;\n}\n\n/** \"30m\", \"2h\", \"1d\" → ms; null when unparsable. */\nexport function parseSince(s: string): number | null {\n  const m = s.match(/^(\\d+)([mhd])$/);\n  return m ? Number(m[1]) * { m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2] as \"m\" | \"h\" | \"d\"] : null;\n}\n\nfunction hhmm(ts: number): string {\n  const d = new Date(ts);\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  return `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;\n}\n\nexport function errorsTable(groups: ToolErrorGroup[]): string {\n  if (!groups.length) return \"no tool errors recorded\";\n  const rows = [[\"SIGNATURE\", \"COUNT\", \"WORKERS\", \"PROVIDERS\", \"FIRST\", \"LAST\"]];\n  for (const g of [...groups].sort((a, b) => b.count - a.count)) {\n    rows.push([g.signature, String(g.count), String(g.workers.length), g.providers.join(\",\"), hhmm(g.first), hhmm(g.last)]);\n  }\n  const w = rows[0].map((_, i) => Math.max(...rows.map((r) => r[i].length)));\n  return rows.map((r) => r.map((c, i) => c.padEnd(w[i])).join(\"  \")).join(\"\\n\");\n}\n","/**\n * launch.ts — start the Claude Code harness directly on any provider/model\n * configured in workers.yaml, for an interactive session (`pai launch`).\n *\n * A running claude process cannot switch provider: base URL and auth are\n * fixed at start, so \"switch provider\" always means \"start a new session\".\n * Provider/model resolution and the spawn environment are NOT re-derived\n * here — this reuses getProviderOrNative (config.ts, the same lookup\n * routing.ts's resolveTarget uses) and buildRunEnv (run-env.ts, the exact\n * env every worker spawns with). Adding a provider to workers.yaml therefore\n * makes it appear here with no code change.\n */\n\nimport {\n  ANTHROPIC_NATIVE,\n  MODEL_CAPABILITIES,\n  WorkersConfigError,\n  getProviderOrNative,\n  maskKey,\n  nativeAnthropicProvider,\n  type ModelCapability,\n  type WorkerProvider,\n  type WorkersConfig,\n} from \"./config.js\";\nimport { buildRunEnv, pinnedSettingsArgs } from \"./run-env.js\";\nimport { DEFAULT_PROXY_PORT, ensureProxyRunning } from \"./proxy/server.js\";\n\nexport class LaunchError extends WorkersConfigError {}\n\nexport interface LaunchRow {\n  index: number;\n  provider: string;\n  model: string;\n  capability: ModelCapability;\n  native: boolean;\n  /** This is workers.yaml's configured `active:` provider (not the running\n   *  session's actual provider — see detectCurrentProviderName for that). */\n  configActive: boolean;\n}\n\n/** Providers `pai launch` will start a session on: the built-in anthropic\n *  provider plus every ENABLED configured provider — same population\n *  auto-routing considers (routing.ts), disabled providers excluded. */\nexport function launchableProviderNames(workers: Pick<WorkersConfig, \"providers\">): string[] {\n  return [\n    ANTHROPIC_NATIVE,\n    ...Object.entries(workers.providers)\n      .filter(([, p]) => p.enabled)\n      .map(([name]) => name),\n  ];\n}\n\nfunction configActiveName(active: string | null): string {\n  return active ?? ANTHROPIC_NATIVE;\n}\n\n/**\n * One row per provider x configured model capability (default/fast/image),\n * built-in anthropic first, in workers.yaml provider order. Disabled\n * providers are skipped entirely — this is the sole source `--list` prints\n * and `--provider`/interactive selection accept.\n */\nexport function buildLaunchRows(\n  workers: Pick<WorkersConfig, \"active\" | \"providers\" | \"nativeModels\">\n): LaunchRow[] {\n  const rows: LaunchRow[] = [];\n  let i = 1;\n  const activeName = configActiveName(workers.active);\n  const native = nativeAnthropicProvider(workers.nativeModels);\n  for (const cap of MODEL_CAPABILITIES) {\n    const id = native.models[cap];\n    if (!id) continue;\n    rows.push({\n      index: i++,\n      provider: ANTHROPIC_NATIVE,\n      model: id,\n      capability: cap,\n      native: true,\n      configActive: activeName === ANTHROPIC_NATIVE,\n    });\n  }\n  for (const [name, p] of Object.entries(workers.providers)) {\n    if (!p.enabled) continue;\n    for (const cap of MODEL_CAPABILITIES) {\n      const id = p.models[cap];\n      if (!id) continue;\n      rows.push({\n        index: i++,\n        provider: name,\n        model: id,\n        capability: cap,\n        native: false,\n        configActive: activeName === name,\n      });\n    }\n  }\n  return rows;\n}\n\n/**\n * Which configured provider a running claude process's ANTHROPIC_BASE_URL\n * belongs to. No base URL at all is the plain Anthropic login; a base URL\n * that matches no configured provider is treated the same way rather than\n * guessed at. Anthropic-protocol providers match by exact base URL;\n * openai-protocol providers run through the local PAI proxy, whose URL ends\n * in \"/<providerName>\" (see buildLaunchPlan).\n */\nexport function detectCurrentProviderName(\n  workers: Pick<WorkersConfig, \"providers\">,\n  env: NodeJS.ProcessEnv = process.env\n): string {\n  const base = env.ANTHROPIC_BASE_URL;\n  if (!base) return ANTHROPIC_NATIVE;\n  for (const [name, p] of Object.entries(workers.providers)) {\n    if (p.protocol === \"anthropic\" && p.baseUrl && p.baseUrl === base) return name;\n  }\n  for (const [name, p] of Object.entries(workers.providers)) {\n    if (p.protocol === \"openai\" && base.endsWith(`/${name}`)) return name;\n  }\n  return ANTHROPIC_NATIVE;\n}\n\n/**\n * Best-effort current model id from the environment a `pai launch` session\n * carries. ANTHROPIC_DEFAULT_SONNET_MODEL is set (to the provider's default\n * model id) for every non-native provider by buildRunEnv, but NOT to\n * whichever capability (default/fast/image) was actually chosen — so this\n * is only ever the provider's default model id, never a positive\n * confirmation of the exact running model. Returns null (not a guess) when\n * nothing was set, e.g. the native anthropic provider, which sets no\n * DEFAULT_*_MODEL vars at all.\n */\nexport function detectCurrentModel(env: NodeJS.ProcessEnv = process.env): string | null {\n  return env.ANTHROPIC_DEFAULT_SONNET_MODEL || null;\n}\n\n/** Provider by name, or a LaunchError naming the launchable ones. */\nexport function resolveLaunchProvider(\n  workers: Pick<WorkersConfig, \"providers\" | \"nativeModels\">,\n  name: string\n): WorkerProvider {\n  const valid = launchableProviderNames(workers);\n  const p = getProviderOrNative(workers, name);\n  if (!p) {\n    throw new LaunchError(`no provider named \"${name}\". Configured: ${valid.join(\", \")}`);\n  }\n  if (!p.enabled) {\n    throw new LaunchError(`provider \"${name}\" is disabled. Configured: ${valid.join(\", \")}`);\n  }\n  return p;\n}\n\n/** The model for a launch: `modelName` if given (must be one of this\n *  provider's configured model ids), else the provider's default. */\nexport function resolveLaunchModel(provider: WorkerProvider, modelName?: string): string {\n  if (!modelName) return provider.models.default;\n  const valid = Object.values(provider.models);\n  if (!valid.includes(modelName)) {\n    throw new LaunchError(`model \"${modelName}\" is not configured for this provider. Configured: ${valid.join(\", \")}`);\n  }\n  return modelName;\n}\n\nexport interface LaunchPlan {\n  providerName: string;\n  provider: WorkerProvider;\n  model: string;\n  /** Full claude argv (argv[0] \"claude\" excluded). */\n  argv: string[];\n  env: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve provider + model and build the exact argv/env `pai launch` execs\n * claude with. Starts the PAI proxy on demand for an openai-protocol\n * provider — the same on-demand start the worker runner uses\n * (run.ts's executeRun).\n */\nexport async function buildLaunchPlan(\n  workers: Pick<WorkersConfig, \"providers\" | \"nativeModels\">,\n  providerName: string,\n  modelName: string | undefined,\n  extraArgs: string[],\n  logDir: string\n): Promise<LaunchPlan> {\n  const provider = resolveLaunchProvider(workers, providerName);\n  if (provider.engine === \"codex\") {\n    throw new LaunchError(\n      `provider \"${providerName}\" runs through the Codex CLI (engine: codex) — pai launch starts the Claude Code harness only`\n    );\n  }\n  const model = resolveLaunchModel(provider, modelName);\n\n  let proxyUrl: string | undefined;\n  if (provider.protocol === \"openai\") {\n    const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n    proxyUrl = `${base}/${providerName}`;\n  }\n  const env = buildRunEnv(provider, false, proxyUrl);\n  // buildRunEnv only strips worker identity when headless — an interactive\n  // launch is never headless, so a `pai launch` run FROM inside an existing\n  // worker (PAI_WORKER=1 already in this process's own environment) would\n  // otherwise hand its child that ancestor's marker. PAI_WORKER=1 gates every\n  // per-session hook (autosave, stop bookkeeping, the edit-delegation guard —\n  // isWorkerSession(), src/hooks/ts/lib/worker-session.ts) into treating a\n  // brand-new interactive session as a disposable worker, and PAI_WORKER_ID\n  // would misattribute it as a sub-worker of whatever spawned `pai launch`\n  // (scope.ts's parent-chain lookup). This IS a new session, not a worker.\n  delete env.PAI_WORKER;\n  delete env.PAI_WORKER_ID;\n  // One rule for every spawn: pinnedSettingsArgs decides the route (provider\n  // URL + token, else the router when enabled, else api.anthropic.com).\n  const pin = pinnedSettingsArgs(env);\n  const argv = [...pin, \"--model\", model, ...extraArgs];\n  return { providerName, provider, model, argv, env };\n}\n\n/** `****abcd`-style masking for `--dry-run` env printing — never the full\n *  token, even the placeholder-free ones (\"local\" is left as-is: it is not\n *  a secret). */\nexport function maskLaunchEnv(env: NodeJS.ProcessEnv): Record<string, string> {\n  const masked: Record<string, string> = {};\n  for (const [k, v] of Object.entries(env)) {\n    if (v === undefined) continue;\n    masked[k] = k === \"ANTHROPIC_AUTH_TOKEN\" && v !== \"local\" ? maskKey(v) : v;\n  }\n  return masked;\n}\n","/**\n * Who or what ended a worker. One place decides the reason; the runner's\n * signal handlers and the deadline record it, `pai worker kill` and the daemon\n * leave a kill-request marker (`<logDir>/<id>.kill`) before signalling so the\n * runner can tell their stops from an external one.\n */\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { appendLedger } from \"./ledger.js\";\n\nexport type EndReason = \"exit\" | \"deadline\" | \"pai-worker-kill\" | \"parent-gone\" | \"external-signal\";\n\nexport interface EndInfo {\n  reason: EndReason;\n  by?: string;\n  note?: string;\n}\n\nexport interface KillRequest {\n  ts: string;\n  by: string;\n  pid: number;\n  ppid: number;\n  cwd: string;\n}\n\nexport const killMarkerPath = (logDir: string, id: string): string => join(logDir, `${id}.kill`);\n\n/** Best description of the invoking session: PAI parent/AIBroker session env, else the Claude Code session id. */\nexport function callerDescription(env: NodeJS.ProcessEnv = process.env): string {\n  const name =\n    env.PAI_WORKER_PARENT ?? env.PAI_SESSION_NAME ?? env.AIBROKER_SESSION_NAME ?? env.AIBROKER_SESSION;\n  if (name) return name;\n  if (env.CLAUDE_CODE_SESSION_ID) return `claude-session:${env.CLAUDE_CODE_SESSION_ID}`;\n  return env.CLAUDECODE ? \"claude-code\" : \"shell\";\n}\n\n/** Write the kill-request marker and the WORKER-KILL ledger line; call right before signalling. */\nexport function requestKill(logDir: string, ledger: string, id: string, by: string = callerDescription()): void {\n  const req: KillRequest = {\n    ts: new Date().toISOString(),\n    by,\n    pid: process.pid,\n    ppid: process.ppid,\n    cwd: process.cwd(),\n  };\n  writeFileSync(killMarkerPath(logDir, id), JSON.stringify(req));\n  appendLedger(ledger, \"WORKER-KILL\", { id, by, pid: req.pid, ppid: req.ppid, cwd: req.cwd });\n}\n\nfunction readMarker(logDir: string, id: string): KillRequest | null {\n  const p = killMarkerPath(logDir, id);\n  if (!existsSync(p)) return null;\n  try {\n    return JSON.parse(readFileSync(p, \"utf8\")) as KillRequest;\n  } catch {\n    return { ts: \"\", by: \"unknown\", pid: 0, ppid: 0, cwd: \"\" };\n  } finally {\n    try {\n      unlinkSync(p);\n    } catch {\n      /* already gone */\n    }\n  }\n}\n\nexport function pidAlive(pid: number): boolean {\n  try {\n    process.kill(pid, 0);\n    return true;\n  } catch (e) {\n    return (e as NodeJS.ErrnoException).code === \"EPERM\";\n  }\n}\n\n/** Command name of a pid (`ps -o comm=`), or undefined when it is gone or ps fails. */\nexport function commOf(pid: number): string | undefined {\n  try {\n    return execFileSync(\"ps\", [\"-o\", \"comm=\", \"-p\", String(pid)], { encoding: \"utf8\" }).trim() || undefined;\n  } catch {\n    return undefined;\n  }\n}\n\n/** Parent facts recorded at start, for the end report (\"parent: claude\"). */\nexport function parentInfo(): { parentPid: number; parentComm?: string; parentBackground?: boolean } {\n  const parentPid = process.ppid;\n  return {\n    parentPid,\n    parentComm: commOf(parentPid),\n    // a Claude Code Bash task carries CLAUDECODE; the runner strips it from children only\n    parentBackground: process.env.CLAUDECODE ? true : undefined,\n  };\n}\n\n/**\n * The single decision. `signal` = the signal the runner received (absent for a\n * deadline stop or a normal exit); `timedOut` = the deadline fired. Reads (and\n * consumes) the kill marker.\n */\nexport function decideEndReason(a: {\n  logDir: string;\n  id: string;\n  signal?: string;\n  timedOut?: boolean;\n  parentPid?: number;\n  parentComm?: string;\n  parentBackground?: boolean;\n  isAlive?: (pid: number) => boolean;\n}): EndInfo {\n  if (a.timedOut) return { reason: \"deadline\" };\n  if (!a.signal) return { reason: \"exit\" };\n  const marker = readMarker(a.logDir, a.id);\n  if (marker) return { reason: \"pai-worker-kill\", by: marker.by, note: `${a.signal} requested by pid ${marker.pid}` };\n  const alive = (a.isAlive ?? pidAlive)(a.parentPid ?? process.ppid);\n  const parent = `parent ${a.parentPid ?? \"?\"}${a.parentComm ? ` (${a.parentComm})` : \"\"}`;\n  if (!alive) return { reason: \"parent-gone\", note: `${a.signal}; ${parent} not alive` };\n  return {\n    reason: \"external-signal\",\n    note:\n      `${a.signal}; sender unknown (not deadline, not pai worker kill); a Claude Code background task stop ` +\n      `(TaskStop, Bash timeout, session end) is the usual source; ${parent} alive` +\n      (a.parentBackground ? \"; started from a Claude Code session\" : \"\"),\n  };\n}\n\n/** The status/ledger fields for an end. */\nexport function endFields(e: EndInfo): { reason: EndReason; by?: string } {\n  return { reason: e.reason, ...(e.by ? { by: e.by } : {}) };\n}\n\n/** \"external-signal\", \"pai-worker-kill by <caller>\" for a finished worker; null when nothing was recorded. */\nexport function endLabel(s: { endReason?: string; endBy?: string }): string | null {\n  return s.endReason ? `${s.endReason}${s.endBy ? ` by ${s.endBy}` : \"\"}` : null;\n}\n","/**\n * slim-profile.ts — what a headless worker loads of the operator's setup.\n *\n * A default `claude -p` run reads the user settings: ~/.claude/CLAUDE.md, the\n * enabled plugins' SessionStart context, every SessionStart/UserPromptSubmit\n * injection (Whispr, caveman, handover...), skills, output style. Measured\n * 2026-10-16 on a \"reply OK\" worker, roughly a third of the first request.\n * A worker needs none of it, only the PreToolUse guards and its contract.\n *\n * The slim profile drops the user setting source (`--setting-sources\n * project,local`) and passes a generated `--settings` file instead: the user's\n * env/permissions plus only the guard hooks. The project CLAUDE.md stays.\n * Opt out with PAI_WORKER_FULL_PROFILE=1 or by passing your own\n * --settings/--setting-sources.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Hook commands kept for a worker; everything else is context or bookkeeping. */\nconst GUARD_HOOKS = [\n  \"terminal_guard.py\",\n  \"route-agents-to-worker\",\n  \"block-taskoutput-wait\",\n  \"route-edits-to-worker\",\n  \"security-validator\",\n  \"ztk-auto.sh\",\n  \"caseleaf-warm-builds\",\n  \"block-sleep-poll\",\n  \"worker-guard\",\n  \"worker-path-repair\",\n  \"worker-tool-name-repair\",\n  \"ab-levers\",\n];\n\n/** The text a worker needs in place of the user CLAUDE.md. */\nexport const WORKER_BRIEF = [\n  \"You are a headless worker: do the task in this process, then report.\",\n  \"- Commit or push only if the spec says so; no AI attribution, no --no-verify.\",\n  \"- Keep personal data (names, accounts, home paths) out of code, docs and commits.\",\n  \"- Stay in the working directory; do not touch other checkouts or the user's shell.\",\n  \"- Never sleep-poll: no sleep over 60 s; long jobs run detached and are checked with a bounded wait.\",\n  \"- No screen/osascript automation; no email or messaging.\",\n  \"- Nothing is done without proof: run the tests, show before/after.\",\n  \"- A hook denial is a rule, not a glitch: adjust, do not retry verbatim.\",\n  \"- End with the report format given in the system prompt.\",\n].join(\"\\n\");\n\nconst PROVIDER_KEYS = [\"ANTHROPIC_BASE_URL\", \"ANTHROPIC_AUTH_TOKEN\", \"ANTHROPIC_API_KEY\"];\n\ntype HookGroup = { matcher?: string; hooks?: Array<{ command?: string }> };\n\n/** The user settings reduced to env, permissions and the guard hooks. */\nexport function slimSettings(\n  user: Record<string, unknown>,\n  procEnv: NodeJS.ProcessEnv = {},\n): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const k of [\"env\", \"permissions\", \"model\"]) if (user[k] !== undefined) out[k] = user[k];\n  // Settings env overrides the process env, so a provider's endpoint and token\n  // (set by the spawner) must not be shadowed by the user's own.\n  if (out.env && typeof out.env === \"object\") {\n    const e = { ...(out.env as Record<string, unknown>) };\n    for (const k of PROVIDER_KEYS) if (procEnv[k]) delete e[k];\n    out.env = e;\n  }\n  const hooks: Record<string, HookGroup[]> = {};\n  for (const [event, groups] of Object.entries((user.hooks ?? {}) as Record<string, HookGroup[]>)) {\n    const kept = groups\n      .map((g) => ({ ...g, hooks: (g.hooks ?? []).filter((h) => GUARD_HOOKS.some((n) => h.command?.includes(n))) }))\n      .filter((g) => g.hooks.length > 0);\n    if (kept.length) hooks[event] = kept;\n  }\n  out.hooks = hooks;\n  // Empty strings switch off Claude Code's Co-Authored-By / \"Generated with\" lines.\n  out.attribution = { commit: \"\", pr: \"\" };\n  return out;\n}\n\n/**\n * Args for the slim profile, or [] when it does not apply (caller brought\n * their own settings, opted out, or there is no user settings file to slim).\n */\nexport function slimProfileArgs(\n  logDir: string,\n  wid: string,\n  rest: string[],\n  env: NodeJS.ProcessEnv = process.env,\n  userSettingsPath = join(homedir(), \".claude\", \"settings.json\"),\n): string[] {\n  if (env.PAI_WORKER_FULL_PROFILE === \"1\") return [];\n  if (rest.some((a) => a === \"--settings\" || a.startsWith(\"--settings=\") || a.startsWith(\"--setting-sources\"))) return [];\n  if (!existsSync(userSettingsPath)) return [];\n  let user: Record<string, unknown>;\n  try {\n    user = JSON.parse(readFileSync(userSettingsPath, \"utf8\"));\n  } catch {\n    return [];\n  }\n  mkdirSync(logDir, { recursive: true });\n  const path = join(logDir, `${wid}.settings.json`);\n  writeFileSync(path, JSON.stringify(slimSettings(user, env), null, 2) + \"\\n\", \"utf8\");\n  return [\"--setting-sources\", \"project,local\", \"--settings\", path];\n}\n","/**\n * controls.ts — the clickr argv for handing desktop control to a worker or\n * taking it back, shared by `pai worker controls` and the follow pane's\n * \"your controls\" / \"my controls\" chat action, so both go through clickr the\n * same way.\n *\n * The grant is exclusive to one worker at a time: clickr's MCP server checks\n * PAI_WORKER_ID/pid against the `--agent <id> --pid <pid>` it was handed\n * control for. Crash paths are covered by clickr's own pid check; a clean\n * exit is covered by returnControlsIfHeld below, called from every exit path\n * of run.ts's runWorker.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { loadStatus, saveStatus } from \"./status.js\";\n\n/** clickr argv for `who`: `you` binds the grant to `id` (and its runner pid, when known); `me` has no target. */\nexport function clickrControlsArgv(id: string, who: \"you\" | \"me\", pid?: number): string[] {\n  if (who === \"me\") return [\"controls\", \"me\"];\n  const argv = [\"controls\", \"you\", \"--agent\", id];\n  if (pid !== undefined) argv.push(\"--pid\", String(pid));\n  return argv;\n}\n\n/** clickr argv to hand a worker's grant back on its exit. */\nexport function clickrControlsReturnArgv(id: string): string[] {\n  return [\"controls\", \"return\", \"--agent\", id];\n}\n\n/** Record that `id` was granted clickr desktop controls (`who: \"you\"` succeeded). */\nexport function markControlsHeld(logDir: string, id: string): void {\n  const status = loadStatus(logDir, id);\n  if (!status) return;\n  status.controlsHeld = true;\n  saveStatus(logDir, status);\n}\n\n/**\n * Best-effort hand desktop controls back on a worker's exit, when it ever\n * held them. Never throws and never hangs past clickr's own exit or a 3s\n * cap — a missing clickr binary or a stuck process must not hold up the\n * worker's own exit.\n */\nexport async function returnControlsIfHeld(logDir: string, id: string | undefined): Promise<void> {\n  if (!id) return;\n  const status = loadStatus(logDir, id);\n  if (!status?.controlsHeld) return;\n  await new Promise<void>((resolve) => {\n    let done = false;\n    const finish = () => {\n      if (done) return;\n      done = true;\n      resolve();\n    };\n    try {\n      const proc = spawn(\"clickr\", clickrControlsReturnArgv(id), { stdio: \"ignore\" });\n      proc.on(\"close\", finish);\n      proc.on(\"error\", finish);\n    } catch {\n      finish();\n    }\n    setTimeout(finish, 3000).unref();\n  });\n}\n","/**\n * policy.ts — where workers run: one persistent policy plus per-run hints.\n *\n *   cloud-only   never local (battery, on the road)\n *   local-only   never cloud (conserve tokens); fails when no local server answers\n *   prefer-local local first, falls through to cloud on failure\n *   prefer-cloud cloud first (fast turnaround), local only when cloud is unavailable\n *   auto         prefer-local; cloud-only on battery; prefer-cloud for an --urgent run\n *\n * The policy lives in <logDir>/routing-policy.json (not workers.yaml: a runtime\n * switch with an expiry, not provider configuration). An explicit --provider\n * (or --capability) bypasses it entirely. chooseRoute and fallThroughReason\n * are pure; the I/O (battery, ping, quota) is gathered by routeRun.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n  ANTHROPIC_NATIVE,\n  WorkersConfigError,\n  getProviderOrNative,\n  type WorkersConfig,\n} from \"./config.js\";\nimport { cooldownRemaining, quotaExceeded, readRoutingState } from \"./routing.js\";\n\nexport const POLICY_MODES = [\"cloud-only\", \"local-only\", \"prefer-local\", \"prefer-cloud\", \"auto\"] as const;\nexport type PolicyMode = (typeof POLICY_MODES)[number];\n\nexport interface PolicyState {\n  mode: PolicyMode;\n  /** ISO stamp after which the policy no longer applies. */\n  until?: string;\n  set: string;\n}\n\n/** How a run was routed: shown by `pai worker ps`, kept in the worker status. */\nexport interface RouteInfo {\n  policy: PolicyMode;\n  reason: string;\n  local: boolean;\n}\n\nexport function policyPath(logDir: string): string {\n  return join(logDir, \"routing-policy.json\");\n}\n\n/** The active policy, or null when none is set or it has expired. */\nexport function readPolicy(logDir: string, now: Date = new Date()): PolicyState | null {\n  const path = policyPath(logDir);\n  if (!existsSync(path)) return null;\n  try {\n    const p = JSON.parse(readFileSync(path, \"utf8\")) as PolicyState;\n    if (!(POLICY_MODES as readonly string[]).includes(p.mode)) return null;\n    if (p.until && Date.parse(p.until) <= now.getTime()) return null;\n    return p;\n  } catch {\n    return null;\n  }\n}\n\nexport function writePolicy(logDir: string, mode: PolicyMode, forMs?: number, now: Date = new Date()): PolicyState {\n  const state: PolicyState = {\n    mode,\n    ...(forMs ? { until: new Date(now.getTime() + forMs).toISOString() } : {}),\n    set: now.toISOString(),\n  };\n  const tmp = `${policyPath(logDir)}.tmp`;\n  writeFileSync(tmp, JSON.stringify(state, null, 2) + \"\\n\", \"utf8\");\n  renameSync(tmp, policyPath(logDir));\n  return state;\n}\n\nexport function clearPolicy(logDir: string): void {\n  if (existsSync(policyPath(logDir))) unlinkSync(policyPath(logDir));\n}\n\n/** \"30m\", \"2h\", \"1d\" → milliseconds. */\nexport function parseDuration(s: string): number {\n  const m = /^(\\d+)([mhd])$/.exec(s.trim());\n  if (!m) throw new WorkersConfigError(`--for: expected a duration like 30m, 2h or 1d, got \"${s}\"`);\n  return Number(m[1]) * { m: 60_000, h: 3_600_000, d: 86_400_000 }[m[2] as \"m\" | \"h\" | \"d\"];\n}\n\nexport function onBattery(): boolean {\n  try {\n    return /Battery Power/.test(execFileSync(\"pmset\", [\"-g\", \"batt\"], { encoding: \"utf8\", timeout: 3000 }));\n  } catch {\n    return false;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// route selection\n// ---------------------------------------------------------------------------\n\nexport interface RouteInputs {\n  mode: PolicyMode;\n  urgent: boolean;\n  localOk: boolean;\n  cloud: boolean;\n  battery: boolean;\n  /** Name of an enabled local provider that answered, or null. */\n  local: string | null;\n  /** Why `local` is null: \"no local provider\" (none enabled) or \"local down\". */\n  localWhy: string;\n  /** The cloud provider is in cooldown or over its quota. */\n  cloudUnavailable: boolean;\n}\n\nexport type RouteChoice = { local: string; reason: string } | { local: null; reason: string };\n\nexport function chooseRoute(i: RouteInputs): RouteChoice {\n  const cloud = (reason: string): RouteChoice => ({ local: null, reason });\n  const useLocal = (reason: string): RouteChoice => ({ local: i.local!, reason });\n  if (i.mode === \"cloud-only\") return cloud(\"policy cloud-only\");\n  if (i.mode === \"local-only\") {\n    if (!i.local) {\n      throw new WorkersConfigError(`policy local-only: ${i.localWhy} — start a local server or change the policy (pai worker policy)`);\n    }\n    return useLocal(\"policy local-only\");\n  }\n  if (i.mode === \"prefer-cloud\") {\n    if (i.localOk && i.local && !i.cloud) return useLocal(\"--local-ok\");\n    if (i.cloudUnavailable && i.local) return useLocal(\"cloud unavailable\");\n    return cloud(\"policy prefer-cloud\");\n  }\n  // prefer-local and auto\n  if (i.mode === \"auto\" && i.battery) return cloud(\"battery\");\n  if (i.urgent) return cloud(\"urgent\");\n  if (i.cloud) return cloud(\"--cloud\");\n  if (!i.local) return cloud(i.localWhy);\n  return useLocal(`policy ${i.mode}`);\n}\n\n/** Does this provider's endpoint live on this machine? */\nexport function isLocalProvider(baseUrl: string): boolean {\n  try {\n    const h = new URL(baseUrl).hostname;\n    return h === \"127.0.0.1\" || h === \"localhost\" || h === \"::1\" || h === \"[::1]\";\n  } catch {\n    return false;\n  }\n}\n\n/** One cheap models request; any answer below 500 (a 401 included) means the server is up. */\nexport async function pingLocal(baseUrl: string, timeoutMs = 4000): Promise<boolean> {\n  try {\n    const r = await fetch(`${baseUrl.replace(/\\/+$/, \"\")}/v1/models`, { signal: AbortSignal.timeout(timeoutMs) });\n    return r.status < 500;\n  } catch {\n    return false;\n  }\n}\n\n/** First enabled, loopback, answering provider in config order. */\nexport async function findLocal(\n  config: WorkersConfig,\n  logDir: string,\n  ping: (url: string) => Promise<boolean> = pingLocal\n): Promise<{ name: string | null; why: string }> {\n  const state = readRoutingState(logDir);\n  const candidates = Object.entries(config.providers).filter(\n    ([name, p]) => p.enabled && p.engine !== \"image\" && isLocalProvider(p.baseUrl) && cooldownRemaining(state, name) === 0\n  );\n  if (!candidates.length) return { name: null, why: \"no local provider\" };\n  for (const [name, p] of candidates) if (await ping(p.baseUrl)) return { name, why: \"\" };\n  return { name: null, why: \"local down\" };\n}\n\nexport interface RouteFlags {\n  urgent?: boolean;\n  localOk?: boolean;\n  cloud?: boolean;\n}\n\n/**\n * The policy decision for one run: null when no policy is set (class/active\n * routing stands), else the provider to pin (null = cloud, normal routing)\n * and the RouteInfo to record.\n */\nexport async function routeRun(\n  config: WorkersConfig,\n  logDir: string,\n  flags: RouteFlags\n): Promise<{ providerFlag: string | null; route: RouteInfo } | null> {\n  const policy = readPolicy(logDir);\n  if (!policy) return null;\n  const mode = policy.mode;\n  const needLocal = mode !== \"cloud-only\";\n  const found = needLocal ? await findLocal(config, logDir) : { name: null, why: \"\" };\n  const native = getProviderOrNative(config, ANTHROPIC_NATIVE);\n  const cloudUnavailable =\n    mode === \"prefer-cloud\" &&\n    (cooldownRemaining(readRoutingState(logDir), ANTHROPIC_NATIVE) > 0 || (native ? quotaExceeded(native) : false));\n  const choice = chooseRoute({\n    mode,\n    urgent: flags.urgent ?? false,\n    localOk: flags.localOk ?? false,\n    cloud: flags.cloud ?? false,\n    battery: mode === \"auto\" ? onBattery() : false,\n    local: found.name,\n    localWhy: found.why,\n    cloudUnavailable,\n  });\n  return { providerFlag: choice.local, route: { policy: mode, reason: choice.reason, local: choice.local !== null } };\n}\n\n// ---------------------------------------------------------------------------\n// fall-through\n// ---------------------------------------------------------------------------\n\nexport interface EndedRun {\n  reportValid?: boolean;\n  timedOut?: boolean;\n  rc?: number | null;\n  state?: string;\n}\n\n/**\n * Why a finished local run should be repeated once on the cloud, or null.\n * Reads the stored result: an invalid report (reportValid false covers a\n * failed AG2 check and the proof-check downgrade), r=- or r=!, a timeout, a\n * crash with no report. A killed run (rc 143) is the operator's call, not a\n * model failure.\n */\nexport function fallThroughReason(\n  route: RouteInfo | undefined,\n  status: EndedRun,\n  report: { result?: string } | null\n): string | null {\n  if (!route?.local || route.policy === \"local-only\") return null;\n  if (status.timedOut) return \"max-minutes\";\n  if (status.rc === 143) return null;\n  if (status.reportValid === false) return \"invalid report\";\n  if (report?.result === \"-\" || report?.result === \"!\") return `r=${report.result}`;\n  if (!report && status.state === \"failed\") return \"no report\";\n  return null;\n}\n","/**\n * engines/image.ts — the `engine: image` worker.\n *\n * No claude-code process, no worktree, no MCP: `pai worker run --capability\n * image` POSTs one OpenAI-compatible images-generations request straight to\n * the provider's `url` and writes the PNG it gets back. One request, no\n * retries beyond the fetch's own timeout — a caller that wants another try\n * just runs again.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { resolveProviderKey, WorkersConfigError, type WorkerProvider } from \"../config.js\";\n\nexport interface ImageRunOptions {\n  providerName: string;\n  provider: WorkerProvider;\n  model: string;\n  prompt: string;\n  /** Where the PNG is written; caller resolves the default path. */\n  outPath: string;\n  /** \"WIDTHxHEIGHT\", default 1024x1024. */\n  size?: string;\n  /** Default 120000 (2 minutes). */\n  timeoutMs?: number;\n}\n\nexport interface ImageRunResult {\n  ok: true;\n  path: string;\n  model: string;\n  provider: string;\n  durationMs: number;\n  bytes: number;\n}\n\nconst DEFAULT_SIZE = \"1024x1024\";\nexport const DEFAULT_IMAGE_TIMEOUT_MS = 120_000;\n\ninterface ImagesGenerationsResponse {\n  data?: { b64_json?: string }[];\n}\n\n/**\n * `POST {url}/images/generations` with `{model, prompt, size, n: 1,\n * response_format: \"b64_json\"}` and the provider's bearer token, decode the\n * base64 PNG it returns, and write it to `outPath`.\n */\nexport async function runImageCapability(opts: ImageRunOptions): Promise<ImageRunResult> {\n  if (!opts.provider.baseUrl) {\n    throw new WorkersConfigError(\n      `provider \"${opts.providerName}\" (engine image) has no \"url\" configured — set one with: ` +\n        `pai worker providers update ${opts.providerName} --base-url <url>`\n    );\n  }\n  const token = resolveProviderKey(opts.provider);\n  const url = `${opts.provider.baseUrl.replace(/\\/+$/, \"\")}/images/generations`;\n  const timeoutMs = opts.timeoutMs ?? DEFAULT_IMAGE_TIMEOUT_MS;\n  const controller = new AbortController();\n  const timer = setTimeout(() => controller.abort(), timeoutMs);\n  const t0 = Date.now();\n  let res: Response;\n  try {\n    res = await fetch(url, {\n      method: \"POST\",\n      headers: {\n        \"content-type\": \"application/json\",\n        ...(token ? { authorization: `Bearer ${token}` } : {}),\n      },\n      body: JSON.stringify({\n        model: opts.model,\n        prompt: opts.prompt,\n        size: opts.size ?? DEFAULT_SIZE,\n        n: 1,\n        response_format: \"b64_json\",\n      }),\n      signal: controller.signal,\n    });\n  } catch (e) {\n    if (e instanceof Error && e.name === \"AbortError\") {\n      throw new WorkersConfigError(\n        `provider \"${opts.providerName}\": image request timed out after ${timeoutMs}ms`\n      );\n    }\n    throw new WorkersConfigError(\n      `provider \"${opts.providerName}\": image request failed: ${e instanceof Error ? e.message : String(e)}`\n    );\n  } finally {\n    clearTimeout(timer);\n  }\n  const durationMs = Date.now() - t0;\n  const text = await res.text();\n  if (!res.ok) {\n    throw new WorkersConfigError(\n      `provider \"${opts.providerName}\": image request failed (${res.status}): ${text.slice(0, 500)}`\n    );\n  }\n  let parsed: ImagesGenerationsResponse;\n  try {\n    parsed = JSON.parse(text) as ImagesGenerationsResponse;\n  } catch {\n    throw new WorkersConfigError(`provider \"${opts.providerName}\": image response was not JSON`);\n  }\n  const b64 = parsed.data?.[0]?.b64_json;\n  if (!b64) {\n    throw new WorkersConfigError(`provider \"${opts.providerName}\": image response had no data[0].b64_json`);\n  }\n  const bytes = Buffer.from(b64, \"base64\");\n  mkdirSync(dirname(opts.outPath), { recursive: true });\n  writeFileSync(opts.outPath, bytes);\n  return {\n    ok: true,\n    path: opts.outPath,\n    model: opts.model,\n    provider: opts.providerName,\n    durationMs,\n    bytes: bytes.length,\n  };\n}\n","/**\n * tree.ts — sub-workers: parent detection and the tree caps.\n *\n * A worker launched by another worker (the runner exports PAI_WORKER_ID in\n * every worker's environment) carries `parent` in its status, so the worker\n * forest is visible in `ps`, the status line and the ledger. Two caps keep\n * the tree from growing without bound, both under `workers.tree`:\n *\n *   - maxDepth (default 2): how deep sub-workers may nest. A top-level worker\n *     sits at depth 0; its children at 1; grandchildren at 2 — one level past\n *     the cap refuses with a clear message.\n *   - maxChildren (default 4): how many children of one parent may run at the\n *     same time. Finished children do not count; a planner works through its\n *     sub-tasks in waves of this size.\n *\n * Chain stages also carry `parent` (the chain id), but chains are not workers\n * and never get a status file — a parent without a status is not capped.\n */\n\nimport type { WorkersTreeConfig } from \"./config.js\";\nimport { isLive, loadStatus, loadStatuses, saveStatus, type WorkerStatus } from \"./status.js\";\n\n/** The env var the runner sets in every worker's environment. */\nexport const WORKER_ID_ENV = \"PAI_WORKER_ID\";\n\n/** The worker this process runs inside, when it runs inside one. */\nexport function parentFromEnv(env: NodeJS.ProcessEnv = process.env): string | null {\n  const id = env[WORKER_ID_ENV];\n  return typeof id === \"string\" && id.trim() ? id.trim() : null;\n}\n\n/**\n * Depth of `id` in the worker forest: 0 for a top-level worker, 1 + the\n * parent's depth for a sub-worker. Parents without a status file (chain ids,\n * unknown ids) count as roots; a cycle reads as its own depth and is cut off\n * after the statuses it walked. `chatIsRoot` stops one level short of the\n * chat-pane tracker (origin \"chat\"): the status line treats the pane as the\n * bar itself, so the workers it spawned are its top level.\n */\nexport function workerDepth(\n  statuses: WorkerStatus[],\n  id: string,\n  opts?: { chatIsRoot?: boolean }\n): number {\n  const byId = new Map(statuses.map((s) => [s.id, s]));\n  let depth = 0;\n  let cur = byId.get(id);\n  const seen = new Set<string>([id]);\n  while (cur?.parent && !seen.has(cur.parent)) {\n    seen.add(cur.parent);\n    const next = byId.get(cur.parent);\n    if (!next) break; // chain id or stale parent: a root, not a level\n    if (!(opts?.chatIsRoot && next.origin === \"chat\")) depth += 1;\n    cur = next;\n  }\n  return depth;\n}\n\n/** Children of `parent` that are still running, oldest first. */\nexport function runningChildren(statuses: WorkerStatus[], parent: string): WorkerStatus[] {\n  return statuses.filter((s) => s.parent === parent && isLive(s));\n}\n\n/**\n * Would starting a child of `parent` stay within the caps? Throws with a\n * clear message when it would not; returns silently when `parent` has no\n * status file (a chain id or an unknown id — not a worker, not capped).\n */\nexport function assertChildAllowed(\n  logDir: string,\n  parent: string,\n  caps: WorkersTreeConfig,\n  statuses: WorkerStatus[] = loadStatuses(logDir)\n): void {\n  if (!statuses.some((s) => s.id === parent)) return;\n  const depth = workerDepth(statuses, parent);\n  if (depth + 1 > caps.maxDepth) {\n    throw new Error(\n      `worker tree: ${parent} sits at depth ${depth} and ` +\n        `workers.tree.maxDepth is ${caps.maxDepth} — it cannot start another level of sub-workers. ` +\n        `Hand the task up instead: pai worker handoff '{\"kind\":\"proposal\",\"text\":\"…\"}'`\n    );\n  }\n  const kids = runningChildren(statuses, parent);\n  if (kids.length >= caps.maxChildren) {\n    throw new Error(\n      `worker tree: ${parent} already has ${kids.length} running sub-workers ` +\n        `(${kids.map((k) => k.id).join(\", \")}) and workers.tree.maxChildren is ${caps.maxChildren} — ` +\n        `wait for one to finish, or raise the cap in the workers config`\n    );\n  }\n}\n\n/**\n * The parent a launch should record: an explicit one (chain stage) wins, else\n * the worker this process runs inside. Returns null for top-level runs.\n */\nexport function launchParent(explicit: string | undefined, env: NodeJS.ProcessEnv = process.env): string | null {\n  return explicit ?? parentFromEnv(env);\n}\n\n/** Does a status file exist for `id` (i.e. is it a worker rather than a chain)? */\nexport function isWorkerId(logDir: string, id: string): boolean {\n  return loadStatus(logDir, id) !== null;\n}\n\n/**\n * A worker owns its children's results (the worker contract says so): if\n * `parent` exits leaving sub-workers unaccounted for, its own run must not\n * report success. Two shapes count, both read off `runningChildren` and the\n * raw statuses, never a second live-child lookup:\n *\n *   - still live: left running untouched (killing another process's work\n *     here would be its own bug) but named in the reason.\n *   - marked \"running\" with a dead pid (the child never got to write its own\n *     terminal state before its parent vanished from under it): finalised\n *     here as failed, so `ps` stops showing a corpse as live.\n *\n * Returns the reason string for the parent's own report, or null when every\n * child it started is accounted for.\n */\nexport function checkOrphanedChildren(logDir: string, parent: string): string | null {\n  const statuses = loadStatuses(logDir);\n  const live = runningChildren(statuses, parent);\n  const abandoned = statuses.filter(\n    (s) => s.parent === parent && s.state === \"running\" && !isLive(s)\n  );\n  for (const child of abandoned) {\n    saveStatus(logDir, { ...child, state: \"failed\", last: `parent ${parent} exited` });\n  }\n  const ids = [...live, ...abandoned].map((s) => s.id);\n  if (!ids.length) return null;\n  return (\n    `exited with ${ids.length} sub-worker${ids.length === 1 ? \"\" : \"s\"} still running ` +\n    `(${ids.join(\", \")}); they were not waited for — run children in the foreground or wait for them before finishing`\n  );\n}\n","/**\n * operator.ts — talking to a running worker.\n *\n * Headless workers run with `--input-format stream-json` and their stdin held\n * open, so a run is a conversation: every line sent to the per-worker Unix\n * socket `<logDir>/<id>.sock` is forwarded to the child as a user message and\n * mirrored into the transcript as an `operator` event (rendered with a »\n * marker). Once the worker has finished its turn, stdin closes 2 s later\n * unless a new message arrives — after that, `say` refuses and `resume`\n * continues the same Claude session with the worker's context intact.\n */\n\nimport { createServer, connect, type Socket } from \"node:net\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { loadStatus, isLive } from \"./status.js\";\n\nexport function operatorSocketPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.sock`);\n}\n\n/**\n * The runner's side: listen on the worker socket, hand every received line to\n * `onLine`. It acknowledges with \"ok\" only when `onLine` returns without a\n * string; a returned string (or a throw) is the failure reason, sent back as\n * `err <reason>` so `say` never reports ok for a message nobody received. Returns the server (close it when the run ends; the socket file is\n * unlinked on close, best effort).\n */\nexport function createOperatorServer(\n  logDir: string,\n  id: string,\n  onLine: (text: string) => unknown\n): import(\"node:net\").Server {\n  const path = operatorSocketPath(logDir, id);\n  try {\n    if (existsSync(path)) unlinkSync(path);\n  } catch {\n    // a stale socket from a crashed run must not block the new one\n  }\n  const server = createServer((socket: Socket) => {\n    let buf = \"\";\n    socket.on(\"error\", () => {});\n    socket.on(\"data\", async (chunk: Buffer) => {\n      buf += chunk.toString(\"utf8\");\n      let nl: number;\n      while ((nl = buf.indexOf(\"\\n\")) >= 0) {\n        const line = buf.slice(0, nl).replace(/\\r$/, \"\");\n        buf = buf.slice(nl + 1);\n        let failure: unknown;\n        if (line.trim()) {\n          try {\n            failure = await onLine(line);\n          } catch (e) {\n            failure = (e as Error).message;\n          }\n        }\n        if (!socket.destroyed) socket.write(typeof failure === \"string\" && failure ? `err ${failure.replace(/\\n/g, \" \")}\\n` : \"ok\\n\");\n      }\n    });\n  });\n  server.listen(path);\n  server.on(\"close\", () => {\n    try {\n      if (existsSync(path)) unlinkSync(path);\n    } catch {\n      // already gone\n    }\n  });\n  return server;\n}\n\n/**\n * `pai worker say <id> \"<text>\"`: forward one line to a running worker.\n * Resolves \"ok\" only once the worker acknowledged delivery; rejects with the reason otherwise.\n */\nexport function sayToWorker(logDir: string, id: string, text: string, timeoutMs = 4000): Promise<string> {\n  const status = loadStatus(logDir, id);\n  if (!status) {\n    return Promise.reject(new Error(`no worker named \"${id}\"`));\n  }\n  if (!isLive(status)) {\n    return Promise.reject(\n      new Error(\n        `worker ${id} is not running (state: ${status.state}) — ` +\n          `continue it instead with: pai worker resume ${id} \"<text>\"`\n      )\n    );\n  }\n  const path = operatorSocketPath(logDir, id);\n  if (!existsSync(path)) {\n    return Promise.reject(\n      new Error(`worker ${id} has no operator socket (${path}) — it may predate this PAI version`)\n    );\n  }\n  return new Promise((resolve, reject) => {\n    const sock = connect(path);\n    const fail = (e: Error) => {\n      sock.destroy();\n      reject(new Error(`cannot talk to worker ${id}: ${e.message}`));\n    };\n    sock.setTimeout(timeoutMs, () => fail(new Error(\"timeout\")));\n    sock.once(\"error\", (e: Error) => fail(e));\n    sock.once(\"connect\", () => {\n      sock.write(text.replace(/\\n/g, \" \") + \"\\n\");\n    });\n    sock.once(\"data\", (d: Buffer) => {\n      sock.end();\n      const reply = d.toString(\"utf8\").trim();\n      if (reply === \"ok\") resolve(\"ok\");\n      else reject(new Error(`worker ${id} did not take the message: ${reply.replace(/^err /, \"\") || \"no acknowledgement\"}`));\n    });\n    sock.once(\"close\", () => fail(new Error(\"connection closed before the worker acknowledged\")));\n  });\n}\n","/**\n * handoff.ts — upward messages between workers.\n *\n * Coordination in the worker tree is upward only: a child appends a handoff\n * to `<logDir>/<parent>.inbox.jsonl` and (when the parent is a running\n * worker) the same text is said to it, so it lands in the parent's\n * conversation as `[handoff from <child>] …`. The parent's pane renders the\n * inbox as `◆` lines, `ps`/`worker_status` show an inbox count, and a child\n * that finishes delivers its structured report automatically as a\n * `kind: \"result\"` handoff. There is no downward or sideways path — telling\n * a worker something is the operator's job (`pai worker say`).\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { isLive, loadStatus } from \"./status.js\";\nimport { sayToWorker } from \"./operator.js\";\n\nexport const HANDOFF_KINDS = [\"proposal\", \"result\", \"question\", \"blocker\"] as const;\n\nexport type HandoffKind = (typeof HANDOFF_KINDS)[number];\n\nexport interface Handoff {\n  /** Sending worker id. */\n  from: string;\n  /** Receiving worker id (the parent). */\n  to: string;\n  kind: HandoffKind;\n  text: string;\n  /** Structured payload (a report, a proposal's fields, …). */\n  data?: Record<string, unknown>;\n  /** ISO stamp, attached on append. */\n  _ts?: string;\n}\n\n/** Where a worker's handoffs land: <logDir>/<id>.inbox.jsonl. */\nexport function inboxPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.inbox.jsonl`);\n}\n\nexport function isHandoffKind(v: unknown): v is HandoffKind {\n  return typeof v === \"string\" && (HANDOFF_KINDS as readonly string[]).includes(v);\n}\n\n/**\n * Normalize a raw parsed value into a Handoff, or explain what is missing.\n * `from`/`to` may be preset by the caller (the CLI fills them from the\n * environment); everything else must be present and well-formed.\n */\nexport function parseHandoff(\n  v: unknown,\n  preset: Partial<Pick<Handoff, \"from\" | \"to\">> = {}\n): Handoff {\n  if (typeof v !== \"object\" || v === null || Array.isArray(v)) {\n    throw new Error(\"handoff payload must be a JSON object\");\n  }\n  const o = v as Record<string, unknown>;\n  const from = typeof o.from === \"string\" && o.from ? o.from : preset.from;\n  const to = typeof o.to === \"string\" && o.to ? o.to : preset.to;\n  if (!from) throw new Error(`handoff needs \"from\" (the sending worker id)`);\n  if (!to) throw new Error(`handoff needs \"to\" (the parent worker id)`);\n  if (!isHandoffKind(o.kind)) {\n    throw new Error(`handoff \"kind\" must be one of: ${HANDOFF_KINDS.join(\", \")}`);\n  }\n  const text = typeof o.text === \"string\" ? o.text : \"\";\n  if (!text.trim()) throw new Error(`handoff needs a non-empty \"text\"`);\n  const data =\n    typeof o.data === \"object\" && o.data !== null && !Array.isArray(o.data)\n      ? (o.data as Record<string, unknown>)\n      : undefined;\n  return { from, to, kind: o.kind, text, ...(data ? { data } : {}) };\n}\n\n/** Append one handoff to its recipient's inbox, stamped now. */\nexport function appendHandoff(logDir: string, h: Handoff, now: Date = new Date()): void {\n  const path = inboxPath(logDir, h.to);\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  appendFileSync(path, JSON.stringify({ ...h, _ts: now.toISOString() }) + \"\\n\", \"utf8\");\n}\n\n/** Every handoff in a worker's inbox, oldest first; damaged lines skip. */\nexport function readInbox(logDir: string, id: string): Handoff[] {\n  const path = inboxPath(logDir, id);\n  if (!existsSync(path)) return [];\n  const out: Handoff[] = [];\n  for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n    if (!line.trim()) continue;\n    try {\n      out.push(JSON.parse(line) as Handoff);\n    } catch {\n      // a half-written line is not worth a crash; the rest still reads\n    }\n  }\n  return out;\n}\n\n/** How the say path prefixes a handoff so the parent model knows its source. */\nexport function handoffMessage(h: Pick<Handoff, \"from\" | \"kind\" | \"text\">): string {\n  return `[handoff from ${h.from}] (${h.kind}) ${h.text.replace(/\\s+/g, \" \").trim()}`;\n}\n\n/** True for the exact shape of handoffMessage() — the runner marks the mirror. */\nexport function isHandoffMessage(text: string): boolean {\n  return new RegExp(`^\\\\[handoff from \\\\S+\\\\] \\\\((${HANDOFF_KINDS.join(\"|\")})\\\\) `).test(text);\n}\n\n/** Test seam for deliverHandoff: the say path, overridable with a mock. */\nexport interface HandoffDeps {\n  say?: (id: string, text: string) => Promise<string>;\n}\n\n/**\n * Deliver one handoff: append it to the parent's inbox, then — when the\n * parent is a running worker with a live operator socket — say it so it\n * enters the parent's conversation. The say is best effort: a parent that is\n * busy, finished or gone still has the inbox line, and nothing here may make\n * a finishing child's exit path fail.\n */\nexport async function deliverHandoff(\n  logDir: string,\n  h: Handoff,\n  deps: HandoffDeps = {},\n  now: Date = new Date()\n): Promise<Handoff> {\n  appendHandoff(logDir, h, now);\n  const say = deps.say ?? ((id: string, text: string) => sayToWorker(logDir, id, text));\n  const parent = loadStatus(logDir, h.to);\n  if (parent && isLive(parent)) {\n    try {\n      await say(h.to, handoffMessage(h));\n    } catch {\n      // the inbox line is the durable record; a failed say is not an error\n    }\n  }\n  return h;\n}\n\n/**\n * Send a handoff from inside a worker (the `pai worker handoff` CLI and the\n * MCP tool both land here): the sender comes from PAI_WORKER_ID, the\n * recipient from its status file's `parent`. Rejects with a clear message\n * outside a worker or under a parentless worker.\n */\nexport async function handoffFromInside(\n  logDir: string,\n  env: NodeJS.ProcessEnv,\n  payload: unknown,\n  deps: HandoffDeps = {}\n): Promise<Handoff> {\n  const mine = env.PAI_WORKER_ID;\n  if (!mine) {\n    throw new Error(\n      \"not inside a worker — handoffs are sent by workers (PAI_WORKER_ID unset). \" +\n        \"Talk to a running worker with: pai worker say <id> \\\"<text>\\\"\"\n    );\n  }\n  const st = loadStatus(logDir, mine);\n  if (!st) throw new Error(`no status for this worker (${mine}) — cannot find its parent`);\n  if (!st.parent || !loadStatus(logDir, st.parent)) {\n    throw new Error(\n      `worker ${mine} has no worker parent to hand off to ` +\n        `(parent: ${st.parent ?? \"(none)\"}) — handoffs go up the worker tree only`\n    );\n  }\n  const h = parseHandoff(payload, { from: mine, to: st.parent });\n  return deliverHandoff(logDir, h, deps);\n}\n","/**\n * waiting.ts — what a worker is waiting on, in the job's own words.\n *\n * `pai worker wait-on <pid>` records `waitingOn` in the worker's status; the\n * ps table, the follow pane (ticker, footer) and the transcript all render it\n * through waitingLine/detachedLine so the operator reads the job\n * (\"tools/batch.sh --land …\"), not the mechanism (a pid file).\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { shortText } from \"./args.js\";\n\n/** What `wait-on` writes into WorkerStatus.waitingOn. */\nexport interface WaitingOn {\n  pid: number;\n  /** Raw command line of the pid, captured when the wait began (survives its exit). */\n  cmd: string;\n  /** Epoch ms the pid started (ps lstart), null when unknown. */\n  startedAt: number | null;\n  log?: string | null;\n}\n\n/** A live process: its command line and start time; null from `proc` means gone. */\nexport interface ProcInfo {\n  cmd: string;\n  startedAt: number | null;\n}\n\nexport interface WaitDeps {\n  proc: (pid: number) => ProcInfo | null;\n  /** Last non-empty line of a log file, \"\" when there is none. */\n  logTail: (file: string) => string;\n}\n\nconst PS_LINE = /^\\s*(\\w{3} \\w{3}\\s+\\d+ [\\d:]{8} \\d{4})\\s+(.*)$/;\n\nexport function realProc(pid: number): ProcInfo | null {\n  try {\n    const m = PS_LINE.exec(\n      execFileSync(\"ps\", [\"-o\", \"lstart=\", \"-o\", \"command=\", \"-p\", String(pid)], { encoding: \"utf8\", env: { ...process.env, LC_ALL: \"C\" } }).split(\"\\n\")[0] ?? \"\"\n    );\n    if (!m) return null;\n    const t = Date.parse(m[1]);\n    return { cmd: m[2], startedAt: Number.isFinite(t) ? t : null };\n  } catch {\n    return null;\n  }\n}\n\nexport function lastLogLine(file: string): string {\n  if (!file || !existsSync(file)) return \"\";\n  try {\n    const lines = readFileSync(file, \"utf8\").split(\"\\n\");\n    for (let i = lines.length - 1; i >= 0; i--) if (lines[i].trim()) return lines[i].trim();\n  } catch {\n    // unreadable log: show nothing\n  }\n  return \"\";\n}\n\nexport const realWaitDeps: WaitDeps = { proc: realProc, logTail: lastLogLine };\n\n/** `4m12s` — the runtime shape shared by the status row and the waiting line. */\nexport function fmtElapsed(secs: number): string {\n  const s = Math.max(0, Math.floor(secs));\n  return `${Math.floor(s / 60)}m${String(s % 60).padStart(2, \"0\")}s`;\n}\n\n/**\n * The job behind a command line: strips `nohup`, the shell wrapper\n * (`sh -c '…'`), an interpreter prefix (`node`, `bash`) and the `; echo $? > rc`\n * trailer of the documented recipe, then shortens with an ellipsis at the end.\n */\nexport function shortCmd(raw: string, max = 50): string {\n  let c = raw.trim();\n  for (let prev = \"\"; prev !== c; ) {\n    prev = c;\n    c = c.replace(/^nohup\\s+/, \"\").replace(/^(?:\\/\\S*\\/)?(?:ba|z)?sh\\s+(?:-\\w+\\s+)*/, \"\");\n    c = c.replace(/^([\"'])([\\s\\S]*)\\1$/, \"$2\").trim();\n  }\n  c = c.replace(/\\s*;\\s*echo\\s+\\$\\?\\s*>.*$/, \"\").replace(/^(?:\\/\\S*\\/)?(?:node|python3?)\\s+/, \"\");\n  return shortText(c, max);\n}\n\n/**\n * `waiting on: <cmd> · running 4m12s · log: <last line>` — running time is\n * the pid's own age (not the wait's), re-read at render time. A pid that is\n * gone (or was reused by another process) reads `· ended`.\n */\nexport function waitingLine(w: WaitingOn, now = Date.now(), d: WaitDeps = realWaitDeps, width = 50): string {\n  const head = `waiting on: ${shortCmd(w.cmd, width)}`;\n  const p = d.proc(w.pid);\n  const reused = p && p.startedAt !== null && w.startedAt !== null && Math.abs(p.startedAt - w.startedAt) > 2000;\n  if (!p || reused) return `${head} · ended`;\n  const parts = [head];\n  const since = w.startedAt ?? p.startedAt;\n  if (since !== null) parts.push(`running ${fmtElapsed((now - since) / 1000)}`);\n  const tail = w.log ? d.logTail(w.log) : \"\";\n  if (tail) parts.push(`log: ${shortText(tail, 60)}`);\n  return parts.join(\" · \");\n}\n\n/** The pid and `--log` of a `pai worker wait-on` Bash command; the pid may be `$(cat file)`. */\nexport function parseWaitOn(\n  command: string,\n  readPid: (file: string) => number | null = (f) => {\n    const n = Number(readFileOr(f).trim());\n    return Number.isInteger(n) && n > 0 ? n : null;\n  }\n): { pid: number | null; log: string | null } | null {\n  const m = /\\bpai\\s+worker\\s+wait-on\\s+(?:(\\d+)|\"?\\$\\(cat\\s+(\\S+?)\\)\"?)(?:\\s|$)/.exec(command);\n  if (!m) return null;\n  const log = /--log[ =](\\S+)/.exec(command)?.[1] ?? null;\n  return { pid: m[1] ? Number(m[1]) : readPid(m[2]), log };\n}\n\nfunction readFileOr(f: string): string {\n  try {\n    return readFileSync(f, \"utf8\");\n  } catch {\n    return \"\";\n  }\n}\n\n/** Transcript line for a Bash call that blocks in wait-on; null when `command` is not one. */\nexport function waitOnCallLine(command: string, now = Date.now(), d: WaitDeps = realWaitDeps, readPid?: (f: string) => number | null): string | null {\n  const w = parseWaitOn(command, readPid);\n  if (!w) return null;\n  const p = w.pid !== null ? d.proc(w.pid) : null;\n  if (w.pid === null || !p) return `waiting on: ${w.pid === null ? \"a background job\" : `pid ${w.pid}`} · ended`;\n  return waitingLine({ pid: w.pid, cmd: p.cmd, startedAt: p.startedAt, log: w.log }, now, d);\n}\n\n/** Transcript line for a result of `wait-on`: `done (exit 7) after 6m03s`, or the still-running note. */\nexport function waitOnResultLine(text: string): string | null {\n  const done = /^pid \\d+ gone(?: after (\\d+m\\d+s))?(?:, exit code (\\S+))?/.exec(text);\n  if (done) return `done${done[2] !== undefined ? ` (exit ${done[2]})` : \"\"}${done[1] ? ` after ${done[1]}` : \"\"}`;\n  const still = /^still running after (\\S+) \\(pid (\\d+)\\)/.exec(text);\n  return still ? `still running after ${still[1]} (pid ${still[2]}), waiting again` : null;\n}\n\n/** `started in the background: <cmd>` for a Bash call that detaches a job (`nohup … &`); null otherwise. */\nexport function detachedLine(command: string): string | null {\n  const m = /\\bnohup\\s+([\\s\\S]*?)(?:\\s+>\\s*\\S+(?:\\s+2>&1)?)?\\s*(?<![>&])&(?![&\\d])/.exec(command);\n  return m ? `started in the background: ${shortCmd(m[1])}` : null;\n}\n","/**\n * wait-on.ts — bounded wait for a detached job, the worker alternative to a\n * long blocking foreground call (which starves operator messages and\n * supervision). Recipe: `nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid`,\n * then `pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` until exit 0.\n *\n * Exit codes: 0 pid gone, 3 still running at --max, 2 pid never existed or not ours.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { loadStatus, saveStatus } from \"./status.js\";\nimport { fmtElapsed, realProc, type ProcInfo, type WaitingOn } from \"./waiting.js\";\n\nexport const WAIT_DEFAULT_SECS = 110;\nexport const WAIT_CAP_SECS = 115;\nconst POLL_MS = 2000;\nconst HEARTBEAT_MS = 20_000;\n/** Slack between the first observation and a process start time (ps lstart has 1 s resolution). */\nconst REUSE_SLACK_MS = 2000;\n\nexport interface WaitOnOpts {\n  pid: number;\n  log?: string;\n  maxSecs?: number;\n  tail?: number;\n}\n\nexport interface WaitOnDeps {\n  /** \"alive\" | \"gone\" | \"foreign\" (exists, not ours). */\n  probe: (pid: number) => \"alive\" | \"gone\" | \"foreign\";\n  /** Process start time in ms epoch, null when unknown. */\n  startTime: (pid: number) => number | null;\n  now: () => number;\n  sleep: (ms: number) => Promise<void>;\n  out: (line: string) => void;\n  /** Command line + start of the awaited pid; absent = not recorded. */\n  proc?: (pid: number) => ProcInfo | null;\n  /** Record (or, with null, clear) what this worker waits on in its status. */\n  mark?: (w: WaitingOn | null) => void;\n}\n\n/** A `mark` that writes `waitingOn` into worker `id`'s status file; a no-op for an unknown id. */\nexport function statusMarker(logDir: string, id: string): (w: WaitingOn | null) => void {\n  return (w) => {\n    const s = loadStatus(logDir, id);\n    if (!s) return;\n    s.waitingOn = w;\n    saveStatus(logDir, s);\n  };\n}\n\nexport const realDeps: WaitOnDeps = {\n  probe(pid) {\n    try {\n      process.kill(pid, 0);\n      return \"alive\";\n    } catch (e) {\n      return (e as NodeJS.ErrnoException).code === \"EPERM\" ? \"foreign\" : \"gone\";\n    }\n  },\n  startTime(pid) {\n    try {\n      const t = Date.parse(execFileSync(\"ps\", [\"-o\", \"lstart=\", \"-p\", String(pid)], { encoding: \"utf8\", env: { ...process.env, LC_ALL: \"C\" } }).trim());\n      return Number.isFinite(t) ? t : null;\n    } catch {\n      return null;\n    }\n  },\n  now: () => Date.now(),\n  sleep: (ms) => new Promise((r) => setTimeout(r, ms)),\n  out: (l) => console.log(l),\n  proc: realProc,\n};\n\nconst readLines = (file: string | undefined): string[] => {\n  if (!file || !existsSync(file)) return [];\n  const t = readFileSync(file, \"utf8\");\n  return t === \"\" ? [] : t.replace(/\\n$/, \"\").split(\"\\n\");\n};\n\nfunction rcOf(pid: number, log?: string): string | null {\n  for (const f of [log && `${log}.rc`, log && log.replace(/\\.[^./]+$/, \".rc\"), `/tmp/${pid}.rc`]) {\n    if (f && existsSync(f)) return readFileSync(f, \"utf8\").trim();\n  }\n  return null;\n}\n\nexport async function waitOn(o: WaitOnOpts, d: WaitOnDeps = realDeps): Promise<number> {\n  const maxMs = Math.min(o.maxSecs ?? WAIT_DEFAULT_SECS, WAIT_CAP_SECS) * 1000;\n  const tail = o.tail ?? 20;\n  const t0 = d.now();\n  const baseLines = readLines(o.log).length;\n\n  let ranSince: number | null = null;\n  const finish = (): number => {\n    const rc = rcOf(o.pid, o.log);\n    const after = ranSince !== null ? ` after ${fmtElapsed((d.now() - ranSince) / 1000)}` : \"\";\n    d.out(`pid ${o.pid} gone${after}${rc !== null ? `, exit code ${rc}` : \"\"}`);\n    if (tail > 0) for (const l of readLines(o.log).slice(-tail)) d.out(l);\n    return 0;\n  };\n\n  if (!Number.isInteger(o.pid) || o.pid <= 0) {\n    d.out(`wait-on: invalid pid ${o.pid}`);\n    return 2;\n  }\n  const first = d.probe(o.pid);\n  if (first === \"foreign\") {\n    d.out(`wait-on: pid ${o.pid} is not owned by this user`);\n    return 2;\n  }\n  if (first === \"gone\") {\n    // A job that finished between two wait-on calls leaves an rc or a log; neither means the pid never existed.\n    if (rcOf(o.pid, o.log) !== null || (o.log && existsSync(o.log))) return finish();\n    d.out(`wait-on: pid ${o.pid} does not exist`);\n    return 2;\n  }\n\n  const p = d.proc?.(o.pid) ?? null;\n  ranSince = p?.startedAt ?? null;\n  d.mark?.({ pid: o.pid, cmd: p?.cmd ?? \"\", startedAt: p?.startedAt ?? null, log: o.log ?? null });\n  try {\n    let nextBeat = t0 + HEARTBEAT_MS;\n    while (d.now() - t0 < maxMs) {\n      await d.sleep(Math.min(POLL_MS, Math.max(0, maxMs - (d.now() - t0))));\n      const st = d.startTime(o.pid);\n      if (d.probe(o.pid) !== \"alive\" || (st !== null && st > t0 + REUSE_SLACK_MS)) return finish();\n      if (d.now() >= nextBeat) {\n        d.out(`running ${Math.round((d.now() - t0) / 1000)}s, log +${readLines(o.log).length - baseLines} lines`);\n        nextBeat += HEARTBEAT_MS;\n      }\n    }\n    d.out(`still running after ${Math.round((d.now() - t0) / 1000)}s (pid ${o.pid}); call wait-on again`);\n    if (tail > 0) for (const l of readLines(o.log).slice(-tail)) d.out(l);\n    return 3;\n  } finally {\n    d.mark?.(null);\n  }\n}\n\n/** The documented long-job recipe, quoted by the worker contract and every deny message. */\nexport const WAIT_RECIPE =\n  \"`nohup sh -c 'CMD; echo $? > /tmp/job.rc' > /tmp/job.log 2>&1 & echo $! > /tmp/job.pid`, then repeat \" +\n  \"`pai worker wait-on $(cat /tmp/job.pid) --log /tmp/job.log` (each call returns within 2 min; do other work between calls) until it exits 0\";\n","/**\n * agentish.ts — Agentish v2 (AG2), the wire format a worker's final report\n * uses by default: a kind line plus one-letter k=v lines instead of JSON\n * prose. Spec and validator live in the `aibroker` CLI\n * (see /Users/i052341/Daten/Cloud/Development/ai/AIBroker/docs/agentish.md);\n * this module shells out to it when present and falls back to a frozen copy\n * of the spec text otherwise, so a worker still gets a contract when\n * `aibroker` is not installed on its box.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport type { WorkerReport } from \"./report.js\";\n\n/** Frozen copy of `aibroker agentish spec`'s first line, for when the CLI is absent. */\nexport const AG2_SPEC_FALLBACK =\n  \"AG2. msg=kind line+k=v lines. kinds T R S Q A X. keys i id g goal o own n forbid d steps p proof u out l limits r res c changes t tests G gate I inst m images # nums w worst x next z note(<200ch). sep |. outcomes + - ~ ? !. @n=path declared once then reused; @n:12=file:line. tests as Name+ Name-. no prose, no articles, never restate, unknown=?. r=+ only if all t +.\";\n\n/** Frozen copy of the `y` (why) extension line. */\nexport const AG2_EXTENSIONS_FALLBACK = \"y why(≤600ch)\";\n\ninterface Ag2SpecResult {\n  spec: string;\n  extensions: string;\n  source: \"aibroker\" | \"builtin\";\n}\n\nlet cachedSpec: Ag2SpecResult | null = null;\n\n/**\n * The AG2 spec + extensions lines: from `aibroker agentish spec` when the\n * CLI runs and its output looks right, else the frozen fallback. Memoised —\n * every caller in one process gets the same spec without re-spawning aibroker.\n */\nexport function ag2Spec(): Ag2SpecResult {\n  if (cachedSpec) return cachedSpec;\n  try {\n    const out = execFileSync(\"aibroker\", [\"agentish\", \"spec\"], {\n      timeout: 3000,\n      stdio: [\"ignore\", \"pipe\", \"ignore\"],\n      encoding: \"utf8\",\n    });\n    const lines = out.split(\"\\n\").filter((l) => l.trim());\n    if (lines[0]?.startsWith(\"AG2.\")) {\n      cachedSpec = { spec: lines[0], extensions: lines[1] ?? AG2_EXTENSIONS_FALLBACK, source: \"aibroker\" };\n      return cachedSpec;\n    }\n  } catch {\n    // aibroker missing, not runnable, or timed out — fall through\n  }\n  cachedSpec = { spec: AG2_SPEC_FALLBACK, extensions: AG2_EXTENSIONS_FALLBACK, source: \"builtin\" };\n  return cachedSpec;\n}\n\n/** Only for tests: drop the memoised spec so the next ag2Spec() re-probes. */\nexport function resetAg2SpecCache(): void {\n  cachedSpec = null;\n}\n\n/**\n * Strip what workers wrap around a report: one surrounding ``` fence (opening\n * line may carry a language tag) and a leading `[YYYY-MM-DD HH:MM]` stamp line.\n */\nexport function unwrapAg2(text: string): string {\n  let t = (text ?? \"\").trim();\n  const fenced = t.match(/^```[^\\n]*\\n([\\s\\S]*?)\\n?```$/);\n  if (fenced) t = fenced[1].trim();\n  return t.replace(/^\\[\\d{4}-\\d{2}-\\d{2}[^\\]\\n]*\\][ \\t]*\\n/, \"\").trim();\n}\n\nexport interface ParsedAg2 {\n  kind: string;\n  fields: Record<string, string>;\n  symbols: Record<string, string>;\n}\n\n/**\n * Parse one AG2 message: first non-empty line is the kind (T R S Q A X,\n * optionally followed by trailing text — only the first character is the\n * kind), later lines are `key=value` or `@n=path` symbol declarations.\n * ```-fenced lines are ignored so a message pasted inside a code fence still\n * parses. Returns null when the first non-empty line is not a known kind.\n */\nexport function parseAg2Report(text: string): ParsedAg2 | null {\n  const kinds = new Set([\"T\", \"R\", \"S\", \"Q\", \"A\", \"X\"]);\n  const lines = unwrapAg2(text)\n    .split(\"\\n\")\n    .map((l) => l.trim())\n    .filter((l) => l && !l.startsWith(\"```\"));\n  if (!lines.length) return null;\n  const kind = lines[0][0];\n  if (!kinds.has(kind)) return null;\n\n  const fields: Record<string, string> = {};\n  const symbols: Record<string, string> = {};\n  for (const line of lines.slice(1)) {\n    const eq = line.indexOf(\"=\");\n    if (eq < 0) continue;\n    const key = line.slice(0, eq).trim();\n    const value = line.slice(eq + 1).trim();\n    if (key.startsWith(\"@\")) {\n      symbols[key.slice(1)] = value;\n    } else {\n      fields[key] = value;\n    }\n  }\n  return { kind, fields, symbols };\n}\n\n/** true iff the value ends with the AG2 pass outcome character. */\nfunction passed(entry: string): boolean {\n  return entry.endsWith(\"+\");\n}\n\n/**\n * Map a parsed AG2 report onto the runner's WorkerReport shape, so the\n * viewer and `printResult` can render an AG2 `R` exactly like a JSON one.\n */\nexport function ag2ToWorkerReport(parsed: ParsedAg2): WorkerReport {\n  const f = parsed.fields;\n  const out: WorkerReport = { format: \"ag2\" };\n\n  if (f.c) {\n    out.changed = f.c\n      .split(\"|\")\n      .map((s) => s.trim())\n      .filter(Boolean)\n      .map((entry) => {\n        const colon = entry.indexOf(\":\");\n        const space = entry.indexOf(\" \");\n        const cut = colon >= 0 && (space < 0 || colon < space) ? colon : space;\n        if (cut < 0) return { path: entry, summary: \"\" };\n        return { path: entry.slice(0, cut).trim(), summary: entry.slice(cut + 1).trim() };\n      });\n  }\n\n  if (f.t) {\n    const tokens = f.t.split(/\\s+/).filter(Boolean);\n    const isGrammar = tokens.length > 0 && tokens.every((tok) => /^\\S+[+\\-~?!]$/.test(tok));\n    out.checks = isGrammar\n      ? tokens.map((entry) => ({\n          name: entry.slice(0, -1),\n          ok: passed(entry),\n          detail: entry.slice(-1),\n        }))\n      : [{ name: \"tests\", detail: f.t }];\n  }\n\n  if (f.p) out.commands = f.p.split(\"|\").map((s) => s.trim()).filter(Boolean);\n\n  const open: string[] = [];\n  if (f.x) open.push(f.x);\n  for (const [k, v] of Object.entries(f)) {\n    if ((v.endsWith(\"!\") || v.endsWith(\"?\")) && k !== \"r\" && k !== \"res\") open.push(`${k}: ${v}`);\n  }\n  if (open.length) out.open = open;\n\n  if (f.z) out.notes = f.z;\n  const res = f.r ?? f.res;\n  if (res === \"+\" || res === \"-\" || res === \"~\" || res === \"?\" || res === \"!\") out.result = res;\n  if (f.y) out.why = f.y;\n\n  return out;\n}\n\nexport interface Ag2ValidationResult {\n  ok: boolean;\n  errors: string[];\n  validator: \"aibroker\" | \"none\";\n}\n\ninterface AibrokerCheckJson {\n  ok?: boolean;\n  errors?: Array<{ code?: string; message?: string; line?: number }>;\n}\n\n/**\n * Validate an AG2 message with `aibroker agentish check - --json`. Never\n * fails the caller: a missing/broken CLI reports ok:true, validator \"none\" —\n * a worker's run is never blocked on a local tooling gap, only its\n * `reportValid` marker goes unset.\n */\nexport function validateAg2(text: string): Ag2ValidationResult {\n  try {\n    const out = execFileSync(\"aibroker\", [\"agentish\", \"check\", \"-\", \"--json\"], {\n      input: unwrapAg2(text),\n      timeout: 5000,\n      stdio: [\"pipe\", \"pipe\", \"ignore\"],\n      encoding: \"utf8\",\n    });\n    const parsed = JSON.parse(out) as AibrokerCheckJson;\n    const errors = (parsed.errors ?? []).map((e) => e.message ?? e.code ?? \"unknown error\");\n    return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n  } catch (e) {\n    // execFileSync throws on non-zero exit too — stdout still carries the JSON\n    const out = (e as { stdout?: Buffer | string }).stdout;\n    if (out) {\n      try {\n        const parsed = JSON.parse(out.toString()) as AibrokerCheckJson;\n        const errors = (parsed.errors ?? []).map((er) => er.message ?? er.code ?? \"unknown error\");\n        return { ok: parsed.ok !== false, errors, validator: \"aibroker\" };\n      } catch {\n        // fall through to \"no validator\" below\n      }\n    }\n    return { ok: true, errors: [], validator: \"none\" };\n  }\n}\n","/**\n * report.ts — the worker contract: terse, structured final reports.\n *\n * Headless workers run with an appended system prompt that fixes their output\n * shape: act, verify, then finish with ONE final message — AG2 (see\n * agentish.ts) by default, JSON as the pre-AG2 fallback. The runner parses it\n * out of the result; the viewer renders it as a compact block instead of a\n * wall of text.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { statSync } from \"node:fs\";\nimport { isAbsolute, relative, resolve } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { WAIT_RECIPE } from \"./wait-on.js\";\nimport type { Paint } from \"./render.js\";\nimport type { Denial } from \"./denials.js\";\nimport { ag2Spec, ag2ToWorkerReport, parseAg2Report } from \"./agentish.js\";\n\n/** The marker the runner puts on operator messages the worker must answer. */\nexport const OPERATOR_MARK = \"[operator]\";\n\n/** Classes that get the short contract — cheap, bounded, single-answer tasks. */\nconst SHORT_CONTRACT_CLASSES = new Set([\"spotcheck\", \"simple\"]);\n\n/**\n * The AG2 report block appended to a contract when format is \"ag2\" — field\n * set matches `aibroker agentish check`'s real R schema (verified 2026-09-20:\n * i/r/t/c are required, not optional as an early draft of this contract had\n * it; r=+ needs G=+ and non-empty p too, not just all-t-passing; R has no\n * `u`/out field at all — u is T-only — so the answer goes in z instead).\n */\nfunction ag2ReportBlock(): string[] {\n  const spec = ag2Spec();\n  return [\n    spec.spec,\n    spec.extensions,\n    \"Final message: one AG2 `R` message only — no prose, no code fence, no markdown.\",\n    \"R requires i (id), r (+ - ~ ? !), t (tests, Name+ Name-), c (path summary|path summary).\",\n    \"r=+ requires G=+ (gate) and p non-empty (proof command), and every t entry +. z= the answer/note,\",\n    \"one line, always present. x= what is left (omit if nothing). y= one line why when r is not +.\",\n    \"Issue claims (`pai issue claim`) are released at end unless r=~ (work continues).\",\n    \"Example:\",\n    \"R\",\n    \"i=count-audit-ts\",\n    \"r=+\",\n    \"G=+\",\n    \"c=src/audit summary of files counted\",\n    \"t=Count+\",\n    \"p=wc -l src/audit/*.ts\",\n    \"z=19 files, 2523 lines\",\n  ];\n}\n\n/** The JSON report block appended to a contract when format is \"json\" (pre-AG2 fallback). */\nfunction jsonReportBlock(lead: string): string[] {\n  return [\n    lead,\n    '{\"changed\":[{\"path\":\"…\",\"summary\":\"…\"}],\"commands\":[\"…\"],\"checks\":[{\"name\":\"…\",\"ok\":true,\"detail\":\"…\"}],\"open\":[\"…\"],\"notes\":\"one line\"}',\n  ];\n}\n\n/** The two sentences that stop a worker from breaking on shell quoting — kept in both contracts. */\nfunction fileNotInlineBlock(): string[] {\n  return [\n    \"NEVER put a multi-line or quoted payload inline in a shell command — no heredocs (<<EOF), no long\",\n    \"-p '…' specs for child workers, no inline JSON, no inline scripts. ALWAYS write it to a file with\",\n    \"the Write tool first and pass the file: -p \\\"$(cat spec.txt)\\\", --file spec.txt, or < spec.txt.\",\n    \"Inline quoting fails silently or dies with a parse error and burns a worker launch; there are no\",\n    \"exceptions.\",\n    \"Never inline code one-liners (python3 -c, node -e, ruby -e and friends) — the permission layer\",\n    \"denies them (\\\"ztk: command denied by permission rules\\\"); write the script to a file with the\",\n    \"Write tool and run it (python3 script.py), or use jq for JSON inspection.\",\n  ];\n}\n\n/** The file-not-inline rule, condensed for the short contract's tight length budget. */\nfunction fileNotInlineBlockShort(): string[] {\n  return [\n    \"NEVER put a multi-line or quoted payload inline in a shell command (no heredocs, no inline\",\n    \"scripts): write it to a file with the Write tool and pass it, e.g. -p \\\"$(cat spec.txt)\\\".\",\n    \"Never inline code (python3 -c and friends) — write a script file and run it, or use jq.\",\n  ];\n}\n\n/** spotcheck/simple: headless, bounded, no delegation language — under 1500 chars in ag2 format. */\nfunction shortContractPrompt(format: \"ag2\" | \"json\"): string {\n  const lines = [\n    \"You are a headless implementation worker, run non-interactively.\",\n    \"This task is yours: answer the question or do the bounded task now. Run at most one command\",\n    \"to confirm a result and never re-derive a number you already have. No child workers, no handoff.\",\n    ...fileNotInlineBlockShort(),\n  ];\n  lines.push(\n    ...(format === \"ag2\"\n      ? ag2ReportBlock()\n      : jsonReportBlock(\n          \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n        ))\n  );\n  return lines.join(\"\\n\");\n}\n\n/** Every other class (and no class): the full worker/orchestrator contract. */\nfunction fullContractPrompt(format: \"ag2\" | \"json\"): string {\n  const lines = [\n    \"You are a headless implementation worker, run non-interactively by an orchestrating session.\",\n    \"You are the worker: this task is yours to finish in this process. Never hand your whole task to a\",\n    \"single child worker and step back — that is delegation, not work. Child workers are for two things\",\n    \"only: (1) PARALLELISING independent parts of your task, each part a self-contained spec; (2) CHEAPER\",\n    \"bounded sub-tasks — probes, renders, test runs, lookups — run one tier down with --class spotcheck or\",\n    \"--class simple. Always pin --provider anthropic on children.\",\n    \"You own your children's results: run them in the foreground or poll until each has finished, read\",\n    \"their result, verify it, and fold it into your own report. When your turn ends this run ends and any\",\n    \"running child is killed, so never end with a child still running.\",\n    \"Ending your turn ends this run. There is no later wake-up: never call ScheduleWakeup, never say you\",\n    \"will pick something up later, never leave a background command as your final action. Finish, then\",\n    \"report.\",\n    \"No narration, no timestamps, no greetings, no summaries of what you read; act, verify, then stop.\",\n    \"Never sleep-poll: no `sleep N` over 60 seconds, anywhere in a command — standalone, after `&&`/`||`/\",\n    \"`;`/`|`, or inside a loop or subshell — and the sleeps of one command add up (`sleep 58; sleep 58` is\",\n    \"116 s). Ending your turn ends this process, so a background job gets no later wake-up here:\",\n    \"run_in_background is denied. Foreground Bash calls stay under 2 min (longer ones block operator messages and supervision and are denied);\",\n    `Prefer the project's own blocking command for builds and tests (e.g. a build script's --wait, repeated while it says to call again); for a long job that has none, run it detached: ${WAIT_RECIPE}.`,\n    \"Kill only processes you started, by PID (`cmd & echo $! > /tmp/x.pid`, then `kill $(cat /tmp/x.pid)`); never `pkill`/`killall`/`kill $(pgrep ...)` — other workers' command lines contain your spec text.\",\n    ...fileNotInlineBlock(),\n    \"\",\n    `Operator messages: a user turn starting with ${OPERATOR_MARK} was typed by the operator while you`,\n    \"run (the prompt of a resumed run is the operator's too). Answer it FIRST, in one or two plain lines —\",\n    \"a question gets its answer, an instruction gets one line naming what will change — then continue the\",\n    \"task you were on.\",\n    \"\",\n    \"Sub-workers: you may start your own workers with `pai worker run --class <class> -p '<prompt>'`\",\n    \"(Bash tool). Your children run on their own provider and report back to you automatically when\",\n    \"they finish. Send a handoff UP instead of doing work yourself when a cheaper provider would\",\n    \"suffice, the task is out of your scope, or a decision is needed:\",\n    \"`pai worker handoff '{\\\"kind\\\":\\\"proposal\\\",\\\"text\\\":\\\"…\\\",\\\"data\\\":{…}}'` (kinds: proposal,\",\n    \"question, blocker; results are sent for you when you finish). Sibling workers are not a\",\n    \"coordination path — there is no sideways channel; everything goes up to your parent.\",\n    \"\",\n  ];\n  lines.push(\n    ...(format === \"ag2\"\n      ? ag2ReportBlock()\n      : jsonReportBlock(\n          \"Your ONE final message is a JSON object and nothing else — no prose before or after, no code fence:\"\n        ))\n  );\n  if (format === \"json\") {\n    lines.push(\n      \"changed: files you touched (path + one-line summary). commands: the commands that verify the work.\",\n      \"checks: each with ok true/false and the evidence in detail. open: what you could not finish, if anything.\",\n      \"notes: one line, the headline a reviewer reads first.\"\n    );\n  }\n  return lines.join(\"\\n\");\n}\n\n/**\n * The system prompt appended to every headless run, chosen by task class and\n * report format. `spotcheck`/`simple` get a short contract (no delegation\n * language, under 1200 chars in json format / 1500 in ag2); every other class (and no class) gets the\n * full contract. Both end with the AG2 report block unless `format` is\n * \"json\", the pre-AG2 fallback.\n */\nexport function workerContractPrompt(cls: string | undefined, format: \"ag2\" | \"json\"): string {\n  return SHORT_CONTRACT_CLASSES.has(cls ?? \"\") ? shortContractPrompt(format) : fullContractPrompt(format);\n}\n\n/**\n * The line appended to a headless prompt's own text (never the system\n * prompt): models weight an instruction at the end of the user turn more\n * than one buried in a long system-prompt paragraph, and this is the\n * difference between a worker that returns a valid AG2/JSON report and one\n * that answers in prose despite the contract (measured 2026-09-20 on both\n * Haiku and Sonnet with the AG2 contract alone).\n */\nexport function promptTrailer(format: \"ag2\" | \"json\"): string {\n  return format === \"ag2\"\n    ? \"\\n\\nFinal message: one AG2 R message only, no prose (format in system prompt).\"\n    : \"\\n\\nFinal message: a JSON object only, no prose before or after, no code fence (format in system prompt).\";\n}\n\n/** The exact text sent to resend a final message that failed AG2 validation. */\nexport const AG2_REASK_TEXT =\n  \"Your final message was not a valid AG2 R message. Resend it now as a single AG2 R message only, \" +\n  \"no prose, no code fence. Minimal example (one key=value per line, first line R):\\n\" +\n  \"R\\ni=task-id\\nr=+\\nG=+\\nc=path summary\\nt=Name+\\np=proof command\\nz=the answer, one line\\n\" +\n  \"(r is + - ~ ? !; r=+ needs G=+ and p; when r is not +, add y=why.)\";\n\n/** The full-contract AG2 prompt — the keepalive path's identical-construction baseline. */\nexport const WORKER_CONTRACT_PROMPT = fullContractPrompt(\"ag2\");\n\nexport interface WorkerReport {\n  changed?: Array<{ path?: string; summary?: string }>;\n  commands?: string[];\n  checks?: Array<{ name?: string; ok?: boolean; detail?: string }>;\n  open?: string[];\n  notes?: string;\n  /** AG2 `r` (res): the single outcome character, when the report was AG2. */\n  result?: \"+\" | \"-\" | \"~\" | \"?\" | \"!\";\n  /** AG2 `y` (why): one line explaining a non-\"+\" result. */\n  why?: string;\n  /** Runner-side: untracked files the worker left behind (AG2 `u`). */\n  untracked?: string[];\n  /** Runner-side: hook/permission denials the run hit (never written by the worker). */\n  denials?: Denial[];\n  /** Which wire format the final message was parsed as. */\n  format?: \"ag2\" | \"json\";\n}\n\n/**\n * Extract the report from a worker's final message: AG2 first (an `R`\n * message, see agentish.ts), then the JSON contract as a fallback for\n * workers still on the pre-AG2 shape. Accepts a bare JSON object, a\n * ```json fenced block, or an object embedded in surrounding text — only\n * objects that look like the contract count (at least one of changed /\n * checks / notes) — any other JSON falls through to null so the raw text is\n * kept as-is.\n */\nexport function parseWorkerReport(text: string): WorkerReport | null {\n  const trimmed = (text ?? \"\").trim();\n  if (!trimmed) return null;\n\n  const ag2 = parseAg2Report(trimmed);\n  if (ag2 && ag2.kind === \"R\") return ag2ToWorkerReport(ag2);\n\n  const candidates: string[] = [];\n  const fence = trimmed.match(/```(?:json)?\\s*(\\{[\\s\\S]*?\\})\\s*```/);\n  if (fence) candidates.push(fence[1]);\n  if (trimmed.startsWith(\"{\")) {\n    // a bare object may still carry trailing punctuation/newlines\n    candidates.push(trimmed);\n  }\n  // object embedded in prose: first \"{\" to the matching last \"}\"\n  const first = trimmed.indexOf(\"{\");\n  const last = trimmed.lastIndexOf(\"}\");\n  if (first >= 0 && last > first) candidates.push(trimmed.slice(first, last + 1));\n  for (const cand of candidates) {\n    try {\n      const v = JSON.parse(cand) as unknown;\n      if (typeof v === \"object\" && v !== null && !Array.isArray(v) && looksLikeReport(v)) {\n        return { ...(v as WorkerReport), format: \"json\" };\n      }\n    } catch {\n      // try the next candidate\n    }\n  }\n  return null;\n}\n\n/** The contract's fingerprint — guards against unrelated JSON in a reply. */\nfunction looksLikeReport(v: object): boolean {\n  const o = v as WorkerReport;\n  return Array.isArray(o.changed) || Array.isArray(o.checks) || typeof o.notes === \"string\";\n}\n\n/**\n * Render a parsed report as the compact block the viewer shows: changed paths\n * with summaries, checks with ✓/✗, open items, notes. Empty report → [].\n */\nexport function renderReport(c: Paint, prefix: string, r: WorkerReport, cwd = \"\"): string[] {\n  const out: string[] = [];\n  const has = (xs: unknown[] | undefined) => (xs ?? []).length > 0;\n  if (has(r.changed)) {\n    out.push(`${prefix}${c(\"bold\", \"changed\")}`);\n    for (const ch of r.changed!) {\n      out.push(`${prefix}  ${relShort(ch.path ?? \"?\", cwd)} — ${shortText(ch.summary ?? \"\", 90)}`);\n    }\n  }\n  if (has(r.checks)) {\n    out.push(`${prefix}${c(\"bold\", \"checks\")}`);\n    for (const ck of r.checks!) {\n      const mark = ck.ok === false ? c(\"red\", \"✗\") : ck.ok === true ? c(\"green\", \"✓\") : c(\"dim\", \"·\");\n      const detail = ck.detail ? c(\"dim\", ` · ${shortText(ck.detail, 80)}`) : \"\";\n      out.push(`${prefix}  ${mark} ${ck.name ?? \"?\"}${detail}`);\n    }\n  }\n  if (has(r.commands)) {\n    out.push(`${prefix}${c(\"bold\", \"commands\")}`);\n    for (const cmd of r.commands!) out.push(`${prefix}  ${c(\"dim\", shortText(cmd, 100))}`);\n  }\n  if (has(r.open)) {\n    out.push(`${prefix}${c(\"bold\", \"open\")}`);\n    for (const o of r.open!) out.push(`${prefix}  ${c(\"yellow\", shortText(o, 100))}`);\n  }\n  if (r.notes) out.push(`${prefix}${c(\"bold\", \"notes\")}  ${shortText(r.notes, 120)}`);\n  return out;\n}\n\nexport interface ReportVerifyFailure {\n  path: string;\n  reason: \"missing\" | \"not-deleted\" | \"stale\";\n}\n\nexport interface ReportVerifyResult {\n  ok: boolean;\n  failures: ReportVerifyFailure[];\n}\n\n/** A summary that names its own deletion — the path is expected to be gone. */\nconst DELETED_RE = /\\b(delete|deleted|deleting|remove|removed|removing)\\b/i;\n\n/**\n * Check every path a report's `changed` list claims against the filesystem:\n * it must exist (mtime at or after `startMs`, the run's start) unless its\n * summary says it was deleted, in which case it must be absent. Mechanical\n * only — counts, row totals and other free-text claims are not checked (a\n * worker's R report is trusted as written; a 2026-09-23 report claimed \"181\n * rows saved\" while it had written 31, and only its listed paths are\n * something the filesystem can confirm or refute).\n */\nexport function verifyReportChanges(\n  changed: Array<{ path?: string; summary?: string }> | undefined,\n  cwd: string,\n  startMs: number\n): ReportVerifyResult {\n  const failures: ReportVerifyFailure[] = [];\n  for (const ch of changed ?? []) {\n    const raw = (ch.path ?? \"\").trim();\n    if (!raw) continue;\n    const abs = isAbsolute(raw) ? raw : resolve(cwd, raw);\n    let mtimeMs: number | null;\n    try {\n      mtimeMs = statSync(abs).mtimeMs;\n    } catch {\n      mtimeMs = null;\n    }\n    const deleted = DELETED_RE.test(ch.summary ?? \"\");\n    if (deleted) {\n      if (mtimeMs !== null) failures.push({ path: raw, reason: \"not-deleted\" });\n    } else if (mtimeMs === null) {\n      failures.push({ path: raw, reason: \"missing\" });\n    } else if (mtimeMs < startMs) {\n      failures.push({ path: raw, reason: \"stale\" });\n    }\n  }\n  return { ok: failures.length === 0, failures };\n}\n\n/** The one-line note appended to a report/ledger when verify fails. */\nexport function verifyFailNote(v: ReportVerifyResult): string {\n  const paths = v.failures.map((f) => f.path).slice(0, 5).join(\", \");\n  return `verify: ${v.failures.length} claimed change${v.failures.length === 1 ? \"\" : \"s\"} not confirmed: ${paths}`;\n}\n\n/** repo-relative display of a changed path when it lies under cwd. */\nfunction relShort(p: string, cwd: string): string {\n  if (!cwd) return p;\n  const r = relative(cwd, p);\n  return r && !r.startsWith(\"..\") ? r : p;\n}\n\n/** One Bash call the worker made: the command and whether its result was a failure. */\nexport interface BashRun {\n  command: string;\n  failed: boolean;\n}\n\nconst squeeze = (s: string): string => s.replace(/\\s+/g, \" \").trim();\n\n/**\n * Reduce a shell command to what it runs: drop leading `cd <dir> &&|;` and\n * `VAR=value` assignments, trailing `| tail|head …`, `2>&1`, `; echo rc=$?`.\n */\nfunction normaliseCommand(cmd: string): string {\n  let c = squeeze(cmd).replace(/\\bgit -C (?:\"[^\"]*\"|'[^']*'|\\S+) /g, \"git \");\n  for (let prev = \"\"; prev !== c; ) {\n    prev = c;\n    c = c.replace(/^cd\\s+\\S+\\s*(&&|;)\\s*/, \"\").replace(/^[A-Za-z_]\\w*=\\S*\\s+/, \"\");\n  }\n  for (let prev = \"\"; prev !== c; ) {\n    prev = c;\n    c = c.replace(/\\s*;\\s*echo\\s+[\"']?rc=\\$\\?[\"']?\\s*$/, \"\").replace(/\\s*\\|\\s*(grep|wc|tail|head|cut|sort|uniq|awk|sed\\s+-n|tee)\\b[^|]*$/, \"\").replace(/\\s*2>&1(?=\\s*(\\||$))/g, \"\");\n  }\n  return c.trim();\n}\n\n/**\n * Mechanical check of an AG2 report's claimed proof (2026-10-14: a local model\n * reported r=+ with `p=git diff HEAD`, a command that proves nothing, and the\n * hidden test then failed). r=+ stands only if the p= command appears inside a\n * Bash call the worker really made and that call's result was not a failure;\n * otherwise the verdict becomes r=~ with a y= line. `u` lists stray untracked\n * files and is appended either way. Non-AG2 text and non-\"+\" verdicts pass\n * through (apart from u). `rewritten` is true when the verdict was downgraded.\n */\nexport function checkProof(\n  text: string,\n  runs: BashRun[],\n  untracked: string[] = []\n): { text: string; rewritten: boolean; proof?: string } {\n  // a stamp glued to the kind line (\"[2026-10-14 20:14] R\") is dropped, as unwrapAg2 does for a stamp line\n  text = text.replace(/^\\s*\\[\\d{4}-\\d{2}-\\d{2}[^\\]\\n]*\\][ \\t]*(?=[TRSQAX]\\s*(\\n|$))/, \"\");\n  const parsed = parseAg2Report(text);\n  let out = text.trimEnd();\n  let rewritten = false;\n  let proof: string | undefined;\n  if (parsed?.kind === \"R\" && parsed.fields.r === \"+\" && parsed.fields.p) {\n    const claimed = parsed.fields.p;\n    // a compound claim (`a; b`, `a && b`) stands when every part ran, in order or not, in some passing call\n    const parts = claimed.split(/;|&&/).map(normaliseCommand).filter(Boolean);\n    const ok = runs.filter((r) => !r.failed).map((r) => ({ raw: squeeze(r.command), norm: normaliseCommand(r.command) }));\n    const ran = ok.some((r) => r.raw.includes(squeeze(claimed))) || (parts.length > 0 && parts.every((c) => ok.some((r) => r.norm.includes(c) || r.raw.includes(c))));\n    if (!ran) {\n      rewritten = true;\n      proof = parsed.fields.p;\n      out = out\n        .split(\"\\n\")\n        .filter((l) => !/^\\s*y=/.test(l))\n        .map((l) => (/^\\s*r=\\+\\s*$/.test(l) ? \"r=~\" : l))\n        .concat(`y=claimed proof not run or failed: ${parsed.fields.p}`)\n        .join(\"\\n\");\n    }\n  }\n  if (parsed && untracked.length) out += `\\nu=${untracked.join(\"|\")}`;\n  return { text: out, rewritten, proof };\n}\n\n/** Untracked (`??`) paths under cwd that were not in `before`; [] when cwd is not a git tree. */\nexport function newUntracked(cwd: string, before: ReadonlySet<string>): string[] {\n  return untrackedPaths(cwd).filter((p) => !before.has(p));\n}\n\nexport function untrackedPaths(cwd: string): string[] {\n  try {\n    return execFileSync(\"git\", [\"status\", \"--porcelain\", \"-uall\"], { cwd, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] })\n      .split(\"\\n\")\n      .filter((l) => l.startsWith(\"?? \"))\n      .map((l) => l.slice(3));\n  } catch {\n    return [];\n  }\n}\n","/**\n * denials.ts — hook/permission denials a worker ran into, recorded by the\n * runner so orchestrators can fix specs that contradict the worker contract\n * and turn recurring ones into deterministic rules.\n *\n * Detection happens on the stream-json tool_result (see run.ts); this file is\n * pure: text → denial, denial list → specConflict flags.\n */\n\nexport interface Denial {\n  at: string;\n  tool: string;\n  hook: string;\n  /** First ~120 chars of the denied input (command, file_path, …). */\n  input: string;\n  /** First ~200 chars of the denial reason. */\n  reason: string;\n  specConflict: boolean;\n}\n\n/** Entries kept per run; `denialCount` on the status keeps the true total. */\nexport const MAX_DENIALS = 50;\nconst MIN_SPEC_OVERLAP = 20;\n\n/**\n * Claude Code's denial texts: `PreToolUse:<Tool> hook error: [<hook>]: <reason>`\n * (also \"hook blocking error from command\"), the `<prog>: command denied by\n * permission rules` shape, and the plain permission-denied sentences.\n */\nexport function parseDenialText(\n  text: string,\n  fallbackTool = \"?\"\n): { tool: string; hook: string; reason: string } | null {\n  const pre = text.match(/^PreToolUse:(\\S+) hook (?:blocking )?error(?: from command)?:\\s*(?:\\[([^\\]]*)\\]:?\\s*)?([\\s\\S]*)/);\n  if (pre) return { tool: pre[1], hook: pre[2] ?? \"hook\", reason: pre[3].trim() };\n  const rules = text.match(/(?:^|\\n)(\\S+): command denied by permission rules/);\n  if (rules) return { tool: fallbackTool, hook: rules[1], reason: text.trim() };\n  if (/Permission to use \\S+ has been denied|requested permissions to use \\S+, but you haven't granted/.test(text)) {\n    return { tool: fallbackTool, hook: \"permission\", reason: text.trim() };\n  }\n  return null;\n}\n\n/** The denied input as one short line: command / file_path / path / pattern / url, else JSON. */\nexport function denialInput(input: unknown): string {\n  if (typeof input !== \"object\" || input === null) return \"\";\n  const o = input as Record<string, unknown>;\n  const key = [\"command\", \"file_path\", \"path\", \"pattern\", \"url\"].find((k) => typeof o[k] === \"string\");\n  return (key ? String(o[key]) : JSON.stringify(o)).replace(/\\s+/g, \" \").slice(0, 120);\n}\n\nconst normalizeReason = (r: string): string => r.toLowerCase().replace(/\\d+/g, \"#\").replace(/\\s+/g, \" \").trim();\n\n/**\n * specConflict: the denial is likely the orchestrator's spec contradicting the\n * worker contract, not a worker mistake. True when\n *  (a) the same (hook, tool, normalized reason) was denied 2+ times in the run, or\n *  (b) the denied input shares a >= 20 char verbatim substring with the spec/prompt.\n * Sets the flag on every entry it matches, so the first occurrence of a\n * repeated denial is flagged too. Cheap enough: O(input × spec) on ≤120 chars.\n */\nexport function markSpecConflicts(denials: Denial[], spec: string | null): void {\n  const key = (d: Denial): string => `${d.hook}\\0${d.tool}\\0${normalizeReason(d.reason)}`;\n  const seen = new Map<string, number>();\n  for (const d of denials) seen.set(key(d), (seen.get(key(d)) ?? 0) + 1);\n  for (const d of denials) {\n    d.specConflict = (seen.get(key(d)) ?? 0) >= 2 || sharesSubstring(d.input, spec ?? \"\");\n  }\n}\n\nfunction sharesSubstring(input: string, spec: string): boolean {\n  for (let i = 0; i + MIN_SPEC_OVERLAP <= input.length; i++) {\n    if (spec.includes(input.slice(i, i + MIN_SPEC_OVERLAP))) return true;\n  }\n  return false;\n}\n\n/** Status fields the recorder owns. */\nexport interface DenialStatus {\n  denials?: Denial[];\n  denialCount?: number;\n}\n\n/**\n * Record one tool_result block when it is a denial. `uses` maps tool_use ids to\n * the tool name and input. Returns whether the status changed.\n */\nexport function recordDenial(\n  status: DenialStatus,\n  block: { tool_use_id?: string; is_error?: boolean; content?: unknown },\n  text: string,\n  uses: Map<string, { name: string; input: unknown }>,\n  spec: string | null,\n  at: string\n): boolean {\n  if (block.is_error !== true) return false;\n  const use = block.tool_use_id ? uses.get(block.tool_use_id) : undefined;\n  const p = parseDenialText(text, use?.name ?? \"?\");\n  if (!p) return false;\n  const list = (status.denials ??= []);\n  status.denialCount = (status.denialCount ?? 0) + 1;\n  if (list.length < MAX_DENIALS) {\n    list.push({\n      at,\n      tool: use?.name ?? p.tool,\n      hook: p.hook,\n      input: denialInput(use?.input),\n      reason: p.reason.slice(0, 200),\n      specConflict: false,\n    });\n  }\n  markSpecConflicts(list, spec);\n  return true;\n}\n\n/** `denied N` plus `spec?` when any entry looks like a spec conflict; \"\" when none. */\nexport function denialMark(s: DenialStatus): string {\n  const n = s.denialCount ?? s.denials?.length ?? 0;\n  if (n <= 0) return \"\";\n  return `denied ${n}${s.denials?.some((d) => d.specConflict) ? \" spec?\" : \"\"}`;\n}\n\n/** Replay lines: one per recorded denial, `!` marking a likely spec conflict. */\nexport function denialLines(s: DenialStatus): string[] {\n  const list = s.denials ?? [];\n  if (!list.length) return [];\n  const more = (s.denialCount ?? list.length) - list.length;\n  return [\n    `  ${denialMark(s)}:`,\n    ...list.map((d) => `  ${d.specConflict ? \"!\" : \" \"} ${d.at.slice(11, 19)} ${d.tool} [${d.hook}] ${d.input} — ${d.reason}`),\n    ...(more > 0 ? [`    … ${more} more`] : []),\n  ];\n}\n","/**\n * Per-provider worker-contract addendum: lessons learned for a model class,\n * appended to the system prompt right after the generic contract. File is\n * `providers.<name>.contract` from the config, else `<PAI_HOME>/contracts/<name>.md`.\n * Missing or empty: no addendum, behaviour unchanged.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { paiHomePath } from \"../config/pai-home.js\";\n\nexport interface ContractAddendum {\n  path: string;\n  text: string;\n  sha: string;\n}\n\nexport function loadContractAddendum(provider: string, override?: string): ContractAddendum | null {\n  const path = override ?? paiHomePath(\"contracts\", `${provider}.md`);\n  if (!existsSync(path)) return null;\n  const text = readFileSync(path, \"utf8\").trim();\n  if (!text) return null;\n  return { path, text, sha: createHash(\"sha256\").update(text).digest(\"hex\").slice(0, 8) };\n}\n","/**\n * deadline.ts — the time gate of a worker run: resolving --max-minutes /\n * --deadline / workers.defaultMaxMinutes into one absolute deadline, the\n * prompt line that tells the worker, the ps label, and the timer that stops\n * the child (SIGTERM, then SIGKILL after a grace period).\n */\n\nimport type { WorkerStatus } from \"./status.js\";\n\nexport interface RunLimit {\n  /** Epoch ms the run is stopped at. */\n  deadlineAt: number;\n  /** Minutes from launch to the deadline. */\n  minutes: number;\n}\n\n/** Local HH:MM → epoch ms: today, or tomorrow when already past. */\nexport function parseDeadline(hhmm: string, now: Date = new Date()): number {\n  const m = /^(\\d{1,2}):(\\d{2})$/.exec(hhmm.trim());\n  if (!m || Number(m[1]) > 23 || Number(m[2]) > 59) {\n    throw new Error(`--deadline: expected HH:MM (local time, 00:00-23:59), got \"${hhmm}\"`);\n  }\n  const at = new Date(now);\n  at.setHours(Number(m[1]), Number(m[2]), 0, 0);\n  if (at.getTime() <= now.getTime()) at.setDate(at.getDate() + 1);\n  return at.getTime();\n}\n\n/** The run's limit, or null when disabled (--max-minutes 0 / default 0). */\nexport function resolveLimit(\n  flags: { maxMinutes?: number; deadline?: string },\n  defaultMinutes: number,\n  now: Date = new Date()\n): RunLimit | null {\n  if (flags.maxMinutes !== undefined && flags.deadline !== undefined) {\n    throw new Error(\"--max-minutes and --deadline are mutually exclusive\");\n  }\n  if (flags.deadline !== undefined) {\n    const deadlineAt = parseDeadline(flags.deadline, now);\n    return { deadlineAt, minutes: Math.max(1, Math.round((deadlineAt - now.getTime()) / 60_000)) };\n  }\n  const minutes = flags.maxMinutes ?? defaultMinutes;\n  if (!Number.isFinite(minutes) || minutes < 0) throw new Error(`--max-minutes: expected a non-negative number, got ${minutes}`);\n  return minutes === 0 ? null : { deadlineAt: now.getTime() + minutes * 60_000, minutes };\n}\n\nconst pad = (n: number): string => String(n).padStart(2, \"0\");\n\n/** The one line injected into the worker's system prompt. */\nexport function deadlinePromptLine(limit: RunLimit): string {\n  const d = new Date(limit.deadlineAt);\n  return (\n    `Hard deadline ${pad(d.getHours())}:${pad(d.getMinutes())} (${limit.minutes} min). ` +\n    \"Finish and report before it; at the deadline the run is stopped and whatever you have is salvaged.\"\n  );\n}\n\n/** ps column: \"36s left\", \"12m left\", \"1h05m left\" for running; \"OVERDUE ...\" for overdue; \"timed out\" for stopped; null otherwise. */\nexport function deadlineLabel(\n  s: Pick<WorkerStatus, \"state\" | \"deadlineAt\" | \"timedOut\">,\n  now: number = Date.now()\n): string | null {\n  if (s.timedOut) return \"timed out\";\n  if (s.state !== \"running\" || !s.deadlineAt) return null;\n  const secs = Math.abs(s.deadlineAt - now) / 1_000;\n  let label: string;\n  if (secs < 60) {\n    label = `${Math.floor(secs)}s`;\n  } else if (secs < 3600) {\n    label = `${Math.floor(secs / 60)}m`;\n  } else {\n    const hours = Math.floor(secs / 3600);\n    const mins = Math.floor((secs % 3600) / 60);\n    label = `${hours}h${String(mins).padStart(2, \"0\")}m`;\n  }\n  return s.deadlineAt >= now ? `${label} left` : `OVERDUE ${label}`;\n}\n\n/** Grace between SIGTERM and SIGKILL. */\nexport const DEADLINE_GRACE_MS = 10_000;\n\n/**\n * Stop `proc` at the deadline: `onFire`, SIGTERM, SIGKILL after `graceMs`\n * when it is still alive. Returns the disarm function.\n */\nexport function armDeadline(\n  proc: { kill(sig?: NodeJS.Signals): boolean; exitCode: number | null; signalCode: NodeJS.Signals | null },\n  limit: RunLimit,\n  onFire: () => void,\n  graceMs: number = DEADLINE_GRACE_MS\n): () => void {\n  let kill: NodeJS.Timeout | null = null;\n  const t = setTimeout(() => {\n    onFire();\n    try {\n      proc.kill(\"SIGTERM\");\n    } catch {\n      /* already gone */\n    }\n    kill = setTimeout(() => {\n      if (proc.exitCode === null && proc.signalCode === null) {\n        try {\n          proc.kill(\"SIGKILL\");\n        } catch {\n          /* already gone */\n        }\n      }\n    }, graceMs);\n    kill.unref();\n  }, Math.max(0, limit.deadlineAt - Date.now()));\n  return () => {\n    clearTimeout(t);\n    if (kill) clearTimeout(kill);\n  };\n}\n","/**\n * browser-tools.ts — what `--no-browser` strips: --chrome, every browser MCP\n * tool grant (mcp__claude-in-chrome__*, browsr, playwright, …) and browser\n * server names, from the caller's claude args. The PreToolUse worker guard\n * denies the same tools (hooks/ts/lib/worker-guard.ts) when\n * PAI_WORKER_NO_BROWSER=1.\n */\n\nconst BROWSER_SERVER = /chrome|browsr|browser|playwright|puppeteer/i;\n\n/** A browser MCP server name (bare, e.g. \"browsr\") or tool name (mcp__browsr__open). */\nexport function isBrowserTool(name: string): boolean {\n  const server = /^mcp__(.+?)(__|$)/.exec(name)?.[1] ?? name;\n  return BROWSER_SERVER.test(server) && (name.startsWith(\"mcp__\") || !name.includes(\"__\"));\n}\n\nconst keepNonBrowser = (list: string): string[] =>\n  list.split(\",\").map((s) => s.trim()).filter((s) => s && !isBrowserTool(s));\n\n/** claude args without --chrome and browser tool grants (an emptied --allowedTools goes too). */\nexport function stripBrowserArgs(rest: string[]): string[] {\n  const out: string[] = [];\n  for (let i = 0; i < rest.length; i++) {\n    const a = rest[i];\n    if (a === \"--chrome\") continue;\n    if (a === \"--allowedTools\" && i + 1 < rest.length && !rest[i + 1].startsWith(\"-\")) {\n      const kept = keepNonBrowser(rest[++i]);\n      if (kept.length) out.push(a, kept.join(\",\"));\n    } else if (a.startsWith(\"--allowedTools=\")) {\n      const kept = keepNonBrowser(a.slice(\"--allowedTools=\".length));\n      if (kept.length) out.push(`--allowedTools=${kept.join(\",\")}`);\n    } else out.push(a);\n  }\n  return out;\n}\n\n/** Comma-list entries (--mcp values, class MCP) without browser servers. */\nexport function stripBrowserNames(names: string[]): string[] {\n  return names.map((n) => keepNonBrowser(n).join(\",\")).filter(Boolean);\n}\n","/**\n * codex.ts — the \"codex\" runner engine.\n *\n * A ChatGPT plan gives no API key, only Codex CLI access, so a provider may\n * set `engine: \"codex\"`: `run` then shells out to `codex exec --json <prompt>`\n * (non-interactive) instead of Claude Code. The JSONL event stream is parsed\n * into the same status-file fields (turns, tools, last) and ledger lines as a\n * Claude run, and is normalised into claude-code-shaped events in the\n * worker's .jsonl so follow/replay/the pane render it unchanged.\n *\n * Implemented against the documented `codex exec --json` interface\n * (thread.started / item.completed / turn.completed / turn.failed lines);\n * verify against the installed CLI when one is present.\n * `--allowedTools` has no Codex equivalent and is dropped (ledgered).\n */\n\nimport { spawn, execFileSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { providerKeyPath, resolveProviderKey, type WorkerProvider } from \"./config.js\";\n\n/** Build the codex exec argument vector (without the binary itself). */\nexport function buildCodexArgs(prompt: string, model: string | undefined): string[] {\n  return [\n    \"exec\",\n    \"--json\",\n    \"--skip-git-repo-check\",\n    ...(model ? [\"-m\", model] : []),\n    \"--\",\n    prompt,\n  ];\n}\n\n/** Env for a codex run: caller's env, Anthropic vars stripped, key applied. */\nexport function buildCodexEnv(provider: WorkerProvider): NodeJS.ProcessEnv {\n  const env: NodeJS.ProcessEnv = { ...process.env };\n  delete env.ANTHROPIC_API_KEY;\n  delete env.ANTHROPIC_BASE_URL;\n  delete env.ANTHROPIC_AUTH_TOKEN;\n  delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;\n  delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;\n  delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;\n  // API-key providers: OpenAI env from key/keyFile/baseUrl; ChatGPT-login\n  // codex (neither set) keeps its own auth from ~/.codex. A key_file that\n  // does not exist is tolerated here (unlike resolveProviderKey's other\n  // callers) — that is the ChatGPT-login shape, not a broken config.\n  const keyPath = providerKeyPath(provider);\n  if (provider.key || (keyPath && existsSync(keyPath))) {\n    env.OPENAI_API_KEY = resolveProviderKey(provider) ?? \"\";\n  }\n  if (provider.upstreamUrl) env.OPENAI_BASE_URL = provider.upstreamUrl;\n  for (const [k, v] of Object.entries(provider.env)) env[k] = v;\n  env.PAI_WORKER = \"1\";\n  return env;\n}\n\n/** Codex \"not applicable\" flags the runner drops with a ledger note. */\nexport function codexDroppedFlags(argv: string[]): string[] {\n  const dropped: string[] = [];\n  for (let i = 0; i < argv.length; i++) {\n    const a = argv[i];\n    if (a === \"--allowedTools\" || a === \"--disallowedTools\" || a === \"--mcp-config\" || a === \"--mcp\") {\n      dropped.push(a);\n      if (i + 1 < argv.length && !argv[i + 1].startsWith(\"-\")) i++; // and its value\n    } else if (a.startsWith(\"--allowedTools=\") || a.startsWith(\"--mcp-config=\")) {\n      dropped.push(a.split(\"=\")[0]);\n    }\n  }\n  return dropped;\n}\n\n// ---------------------------------------------------------------------------\n// Event stream parsing (pure — tested with recorded JSONL lines)\n// ---------------------------------------------------------------------------\n\n/** One normalised claude-code-shaped event + its effect on the status. */\nexport interface CodexEventResult {\n  events: Array<Record<string, unknown>>;\n  turns: number;\n  tools: number;\n  last: string | null;\n  isError: boolean;\n  contextTokens: number | null;\n  finalText: string | null;\n  threadId: string | null;\n}\n\nexport const emptyCodexResult = (): CodexEventResult => ({\n  events: [],\n  turns: 0,\n  tools: 0,\n  last: null,\n  isError: false,\n  contextTokens: null,\n  finalText: null,\n  threadId: null,\n});\n\n/**\n * Fold one parsed `codex exec --json` line into the running result: appends\n * normalised transcript events and updates the counters in place.\n */\nexport function foldCodexLine(line: unknown, r: CodexEventResult): void {\n  if (typeof line !== \"object\" || line === null) return;\n  const e = line as Record<string, unknown>;\n  const type = typeof e.type === \"string\" ? e.type : \"\";\n\n  if (type === \"thread.started\" && typeof e.thread_id === \"string\") {\n    r.threadId = e.thread_id;\n    return;\n  }\n  if (type === \"item.completed\" && typeof e.item === \"object\" && e.item !== null) {\n    const item = e.item as Record<string, unknown>;\n    const kind = typeof item.type === \"string\" ? item.type : \"\";\n    if (kind === \"agent_message\" && typeof item.text === \"string\") {\n      r.turns += 1;\n      r.last = `says: ${item.text.slice(0, 70)}`;\n      r.finalText = item.text;\n      r.events.push({\n        type: \"assistant\",\n        message: { content: [{ type: \"text\", text: item.text }] },\n      });\n    } else if (kind === \"command_execution\") {\n      r.tools += 1;\n      const cmd = typeof item.command === \"string\" ? item.command : \"?\";\n      const rc = typeof item.exit_code === \"number\" ? item.exit_code : 0;\n      r.last = `Bash: ${cmd.slice(0, 70)}`;\n      r.events.push({\n        type: \"assistant\",\n        message: { content: [{ type: \"tool_use\", name: \"Bash\", input: { command: cmd } }] },\n      });\n      r.events.push({\n        type: \"user\",\n        message: {\n          content: [\n            {\n              type: \"tool_result\",\n              tool_use_id: \"\",\n              is_error: rc !== 0,\n              content: item.aggregated_output ?? \"\",\n            },\n          ],\n        },\n      });\n    } else if (kind === \"file_change\") {\n      r.tools += 1;\n      const changes = Array.isArray(item.changes) ? item.changes : [];\n      const files = changes\n        .map((c) => (typeof c === \"object\" && c !== null && typeof (c as { path?: unknown }).path === \"string\" ? (c as { path: string }).path : \"?\"))\n        .join(\", \");\n      r.last = `files: ${files.slice(0, 70)}`;\n      r.events.push({\n        type: \"assistant\",\n        message: { content: [{ type: \"tool_use\", name: \"Write\", input: { file_path: files } }] },\n      });\n    } else if (kind === \"mcp_tool_call\") {\n      r.tools += 1;\n      r.last = `mcp: ${String(item.tool ?? \"?\")}`;\n    }\n    return;\n  }\n  if (type === \"turn.completed\") {\n    const usage = (typeof e.usage === \"object\" && e.usage !== null ? e.usage : {}) as {\n      input_tokens?: number;\n      output_tokens?: number;\n      cached_input_tokens?: number;\n    };\n    const tokens =\n      (usage.input_tokens ?? 0) + (usage.cached_input_tokens ?? 0) + (usage.output_tokens ?? 0);\n    if (tokens > 0) r.contextTokens = tokens;\n    return;\n  }\n  if (type === \"turn.failed\" || type === \"error\") {\n    r.isError = true;\n    const err = typeof e.error === \"object\" && e.error !== null ? e.error : {};\n    r.last = String((err as { message?: unknown }).message ?? e.message ?? \"codex turn failed\");\n  }\n}\n\n/** Parse one JSONL line; null for blanks and non-JSON noise. */\nexport function parseCodexLine(line: string): unknown | null {\n  const t = line.trim();\n  if (!t.startsWith(\"{\")) return null;\n  try {\n    return JSON.parse(t) as unknown;\n  } catch {\n    return null;\n  }\n}\n\n// ---------------------------------------------------------------------------\n// The engine probe used by `providers test`\n// ---------------------------------------------------------------------------\n\n/** True when the Codex CLI is on PATH (test reports \"not installed\" else). */\nexport function codexInstalled(): boolean {\n  try {\n    execFileSync(\"codex\", [\"--version\"], { timeout: 5000, stdio: \"ignore\" });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/** Spawn helper shared by run and test; caller wires stdout. */\nexport function spawnCodex(args: string[], env: NodeJS.ProcessEnv, cwd: string) {\n  return spawn(\"codex\", args, { env, cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n}\n","/**\n * run.ts — the worker runner (port of the glm / glm-run pair, provider-neutral).\n *\n * One claude-code process per call, pointed at the chosen provider:\n *\n *   - env: ANTHROPIC_BASE_URL/AUTH_TOKEN from the provider (token from its\n *     key file, never from the environment), the three DEFAULT_*_MODEL vars,\n *     the provider's extra env, nonessential traffic off, and ANTHROPIC_API_KEY\n *     stripped so nothing can fall back to Anthropic billing. Headless runs\n *     also drop the spawner's session identity (messaging socket, session id,\n *     nesting markers) — inherited, a worktree child starts tool-blind.\n *     OpenAI-protocol providers point at the PAI proxy instead (started on\n *     demand, the provider name in the URL path); codex-engine providers run\n *     the Codex CLI.\n *   - headless (-p): strict empty MCP config unless the caller brings one or\n *     names servers via --mcp / a role (then a filtered <id>.mcp.json), the\n *     core tool grants the caller did not bring (a headless run cannot\n *     approve a permission-gated tool mid-flight — with no grant at all,\n *     claude drops the file/shell tools entirely and the worker is\n *     tool-blind), PAI_WORKER=1 so PAI's per-session hooks leave it alone,\n *     the worker\n *     contract appended to the system prompt, `--input-format stream-json`\n *     with the prompt as the first stdin user message (the operator socket\n *     can add more mid-run), stream-json mirroring (every line stamped `_ts`)\n *     into <logDir>/<id>.jsonl, a live <id>.status for ps/follow/status line\n *     (context meter included), ledger lines, result printed in the caller's\n *     --output-format (json adds the parsed `report`), and the follow pane\n *     (unless --no-pane).\n *   - interactive: no MCP/tool restriction by default, no pane,\n *     ENABLE_TOOL_SEARCH=true. A project that pinned a list with `pai\n *     project mcp` / `pai project tools` gets `--strict-mcp-config\n *     --mcp-config <filtered>` / `--tools <list>` built the same way a\n *     headless run's allowlist is; an explicit --mcp or --tools on this\n *     command line still wins over the project's pin.\n *\n * Auto-routed runs that die of a quota error before the first tool call are\n * restarted on the next provider in routing order (WORKER-REROUTE ledger line).\n */\n\nimport { recordToolError } from \"./tool-errors.js\";\nimport { spawn } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { join } from \"node:path\";\nimport {\n  existsSync,\n  mkdirSync,\n  openSync,\n  writeFileSync,\n  closeSync,\n  writeSync,\n} from \"node:fs\";\nimport { appendSystemPromptArgs, parseRunnerArgs, shortText, stripPromptValues } from \"./args.js\";\nimport {\n  ANTHROPIC_NATIVE,\n  assertProviderRunnable,\n  classModelCapability,\n  isModelCapability,\n  NATIVE_ANTHROPIC_MODELS,\n  readWorkersSection,\n  resolveModelCapability,\n  type WorkerProvider,\n} from \"./config.js\";\nimport { buildRunEnv, claudeCommand } from \"./run-env.js\";\nimport { detectCurrentModel } from \"./launch.js\";\n\nexport { buildRunEnv } from \"./run-env.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { decideEndReason, endFields, parentInfo, type EndInfo } from \"./endreason.js\";\nimport {\n  ensureNoMcpConfig,\n  eventsPath,\n  ledgerPath,\n  noMcpConfigPath,\n  workersLogDir,\n} from \"./paths.js\";\nimport { slimProfileArgs, WORKER_BRIEF } from \"./slim-profile.js\";\nimport {\n  type WorkerStatus,\n  newWorkerId,\n  saveStatus,\n  describeTool,\n  nowStamp,\n  parseSleepSecs,\n  UNLABELED,\n} from \"./status.js\";\nimport { certainContextWindow } from \"../utils/model-window.js\";\nimport { resolveSession, resolveSpawnerSession } from \"./scope.js\";\nimport { returnControlsIfHeld } from \"./controls.js\";\nimport { fallThroughReason, routeRun, type RouteInfo } from \"./policy.js\";\nimport {\n  autoOrder,\n  capabilityForRun,\n  isQuotaFailure,\n  nextAutoProvider,\n  recordBudgetCooldowns,\n  resolveCapabilityRun,\n  resolveTarget,\n  setCooldown,\n  type CapabilityRunResolution,\n} from \"./routing.js\";\nimport { budgetCache } from \"./budget.js\";\nimport { runImageCapability } from \"./engines/image.js\";\nimport { ALLOW_HIDDEN_ENV, headlessAllowed, headlessOverridePath, openPaneForWorker, openVerifiedPane, paneBackend, resolveHostSession } from \"./pane.js\";\nimport { assertChildAllowed, checkOrphanedChildren, isWorkerId, launchParent } from \"./tree.js\";\nimport { deliverHandoff, isHandoffMessage } from \"./handoff.js\";\nimport {\n  addWorktree,\n  adoptTree,\n  markResumed,\n  salvageOnExit,\n  gcWorktreesThrottled,\n  inPlaceSystemPrompt,\n  recordWorktree,\n  worktreeSystemPrompt,\n  worktreeWanted,\n  type WorktreeInfo,\n} from \"./worktree.js\";\nimport {\n  OPERATOR_MARK,\n  AG2_REASK_TEXT,\n  promptTrailer,\n  workerContractPrompt,\n  parseWorkerReport,\n  verifyReportChanges,\n  verifyFailNote,\n  checkProof,\n  newUntracked,\n  untrackedPaths,\n  type BashRun,\n  type WorkerReport,\n} from \"./report.js\";\nimport { recordDenial } from \"./denials.js\";\nimport { validateAg2 } from \"./agentish.js\";\nimport { loadContractAddendum, type ContractAddendum } from \"./contract-addendum.js\";\nimport { loadPreamble, preambleBlock, preambleLine, type Preamble } from \"./preamble.js\";\nimport { armDeadline, deadlinePromptLine, resolveLimit, type RunLimit } from \"./deadline.js\";\nimport { stripBrowserArgs, stripBrowserNames } from \"./browser-tools.js\";\nimport { expandMcpNames, grantsChrome, mcpServersFromToolGrants, writeMcpConfig } from \"./mcp.js\";\nimport { pinNotices, projectLaunchConfig, type ProjectLaunchConfig } from \"./project-config.js\";\nimport { createOperatorServer } from \"./operator.js\";\nimport { DEFAULT_PROXY_PORT, ensureProxyRunning } from \"./proxy/server.js\";\nimport {\n  buildCodexArgs,\n  buildCodexEnv,\n  codexDroppedFlags,\n  codexInstalled,\n  emptyCodexResult,\n  foldCodexLine,\n  parseCodexLine,\n} from \"./codex.js\";\nimport {\n  poolIneligibleReason,\n  poolSettings,\n  poolSlotCwd,\n  postTaskToPool,\n  type PoolTaskResponse,\n} from \"./pool.js\";\n\nexport interface RunOptions {\n  providerFlag?: string;\n  /** --class value (the old --role): a task class from workers.classes. */\n  className?: string;\n  modelFlag?: string;\n  label?: string;\n  noPane?: boolean;\n  /** --spec value, resolved for display/recording (\"-\" for stdin). */\n  specPath?: string;\n  /** --mcp value: server/set names, comma-separated. */\n  mcpFlag?: string;\n  /** Everything after `--` (the claude args). */\n  claudeArgs: string[];\n  /** Working directory for the run (default: this process's cwd). */\n  cwd?: string;\n  /** Chain stage bookkeeping: the chain id this stage belongs to. */\n  parent?: string;\n  /** Chain stage bookkeeping: the class name of this stage. */\n  stage?: string;\n  /** Suppress result printing (MCP worker_run: its stdout is the RPC channel). */\n  quiet?: boolean;\n  /** Internal: notified with the worker id once it exists (resume uses it). */\n  onWorkerStart?: (wid: string) => void;\n  /** Internal: preset worker id (the planner mints its id before phase 1). */\n  id?: string;\n  /** --worktree/--no-worktree; undefined lets the class default decide. */\n  worktreeFlag?: boolean;\n  /** Resume: the id whose worktree this run takes over (see adoptTree). */\n  resumeOf?: string;\n  /** --max-minutes: stop the run after N minutes (0 = no limit; default workers.defaultMaxMinutes). */\n  maxMinutes?: number;\n  /** --deadline HH:MM (local): stop the run then; exclusive with maxMinutes. */\n  deadline?: string;\n  /** --browser / --no-browser: undefined follows workers.noBrowserByDefault. */\n  browser?: boolean;\n  /** --print-cmd: print the assembled claude argv as JSON and exit, no spawn. */\n  printCmd?: boolean;\n  /** --urgent: wants a fast turnaround (policy auto/prefer-local route to the cloud). */\n  urgent?: boolean;\n  /** --local-ok: this run may go local under policy prefer-cloud. */\n  localOk?: boolean;\n  /** --cloud: this run goes to the cloud under policy prefer-local/auto. */\n  cloud?: boolean;\n  /** Internal: the policy already decided (fall-through re-run); carries its route and the run it repeats. */\n  _route?: RouteInfo;\n  _fallThroughOf?: string;\n  /** Internal: suppress recursion depth on reroute. */\n  _reroutes?: number;\n  /** Internal: this run is the one automatic re-launch after an empty model response. */\n  _emptyRetried?: boolean;\n  /** Internal: this run is the planner's phase-1 worker, not a new orchestration. */\n  _planner?: boolean;\n  /** --report json|ag2: which contract/parser the run uses (default: ag2, or PAI_WORKER_REPORT). */\n  reportFormatFlag?: \"json\" | \"ag2\";\n  /** --no-report-retry: skip the one bounded re-ask on an invalid AG2 final message. */\n  noReportRetry?: boolean;\n  /** --capability/--for: run through resolveCapability instead of resolveTarget. */\n  capabilityFlag?: string;\n  /** --out: image-engine output path (default: <logDir>/<id>.png). */\n  imageOut?: string;\n  /** --size: image-engine \"WIDTHxHEIGHT\" (default: 1024x1024). */\n  imageSize?: string;\n  /** --timeout-ms: image-engine request timeout (default: 120000). */\n  imageTimeoutMs?: number;\n}\n\n/**\n * ag2 unless the caller passed --report json or PAI_WORKER_REPORT=json is\n * set in the environment; an explicit flag always wins over the env default.\n */\nexport function resolveReportFormat(\n  flag: \"json\" | \"ag2\" | undefined,\n  env: NodeJS.ProcessEnv = process.env\n): \"json\" | \"ag2\" {\n  if (flag) return flag;\n  return env.PAI_WORKER_REPORT === \"json\" ? \"json\" : \"ag2\";\n}\n\n/** The strict empty MCP config for headless workers, written on demand. */\nexport { ensureNoMcpConfig } from \"./paths.js\";\n\n/** A stream-json user message for the child's stdin. */\nexport function stdinUserMessage(text: string): string {\n  return JSON.stringify({ type: \"user\", message: { role: \"user\", content: text } });\n}\n\n/** An operator line as the worker sees it: carrying the contract's marker. */\nexport function operatorUserText(text: string): string {\n  return `${OPERATOR_MARK} ${text}`;\n}\n\n/**\n * ISO stamp with seconds, attached to every mirrored event (2g): local time\n * with its offset (`2026-09-17T14:19:23+02:00`), so the viewer can render the\n * wall clock the operator lives in. `offMin` is east-positive minutes —\n * injectable so tests do not depend on the machine's zone.\n */\nexport function isoStamp(d = new Date(), offMin = -d.getTimezoneOffset()): string {\n  const t = new Date(d.getTime() + offMin * 60_000);\n  const sign = offMin < 0 ? \"-\" : \"+\";\n  const abs = Math.abs(offMin);\n  const hh = String(Math.floor(abs / 60)).padStart(2, \"0\");\n  const mm = String(abs % 60).padStart(2, \"0\");\n  return `${t.toISOString().slice(0, 19)}${sign}${hh}:${mm}`;\n}\n\nexport interface UsageBlock {\n  input_tokens?: number;\n  output_tokens?: number;\n  cache_read_input_tokens?: number;\n  cache_creation_input_tokens?: number;\n}\n\n// a type alias (not an interface): it must stay assignable to\n// Record<string, unknown> when written into the event transcript\nexport type StreamEvent = {\n  type?: string;\n  subtype?: string;\n  session_id?: string;\n  model?: string;\n  cwd?: string;\n  context_window?: number;\n  model_info?: { context_window?: number } | null;\n  message?: {\n    content?: Array<{\n      type?: string;\n      text?: string;\n      name?: string;\n      id?: string;\n      tool_use_id?: string;\n      input?: unknown;\n      /** tool_result blocks: the result payload (string or text blocks). */\n      content?: unknown;\n      is_error?: boolean;\n    }>;\n    usage?: UsageBlock;\n  };\n  usage?: UsageBlock;\n  result?: string;\n  is_error?: boolean;\n  is_compact?: boolean;\n  num_turns?: number;\n  duration_ms?: number;\n  /** API time of the final turn — the TTFT proxy on zeroed per-turn usage. */\n  duration_api_ms?: number;\n};\n\n/** Context tokens of an assistant/result usage block (input+cache+output). */\nexport function usageContextTokens(u: UsageBlock | undefined): number | null {\n  if (!u) return null;\n  const t =\n    (u.input_tokens ?? 0) +\n    (u.cache_read_input_tokens ?? 0) +\n    (u.cache_creation_input_tokens ?? 0) +\n    (u.output_tokens ?? 0);\n  return t > 0 ? t : null;\n}\n\n/** Current context fill of one assistant turn: input + cache read + cache creation (no output). */\nexport function usageContextUsed(u: UsageBlock | undefined): number | null {\n  if (!u) return null;\n  const t = (u.input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0);\n  return t > 0 ? t : null;\n}\n\n/**\n * Context never shrinks mid-segment, so the status keeps the high-water\n * mark of the usage it has seen: a smaller later reading (short reply,\n * sidechain answer) must not drag the meter down. Compaction is the one\n * legitimate drop — see `resetContextTokensOnCompact`.\n */\nexport function bumpContextTokens(status: Pick<WorkerStatus, \"contextTokens\">, tokens: number | null): void {\n  if (tokens === null || tokens <= 0) return;\n  status.contextTokens = Math.max(status.contextTokens ?? 0, tokens);\n}\n\n/**\n * A compact boundary (`system`/`compact_boundary`, the shape Claude Code\n * writes with `compactMetadata.preTokens`; `compact` kept as the older\n * spelling) legitimately restarts the context at a lower size: the floor\n * drops to the event's own usage — usually none — so the next usage\n * reading re-seeds the meter at the fresh, smaller context.\n */\nexport function resetContextTokensOnCompact(status: Pick<WorkerStatus, \"contextTokens\">, e: StreamEvent): void {\n  status.contextTokens = usageContextTokens(e.usage) ?? null;\n}\n\n/** A compact boundary in a worker's stream, in either event spelling. */\nexport function isCompactBoundary(e: StreamEvent): boolean {\n  return e.type === \"system\" && (e.subtype === \"compact_boundary\" || e.subtype === \"compact\");\n}\n\n/** The command string of a Bash tool_use input, \"\" when there is none. */\nfunction bashCommand(input: unknown): string {\n  if (typeof input === \"object\" && input !== null && typeof (input as { command?: unknown }).command === \"string\") {\n    return (input as { command: string }).command;\n  }\n  return \"\";\n}\n\nexport function toolResultText(content: unknown): string {\n  if (typeof content === \"string\") return content;\n  if (Array.isArray(content)) {\n    return content\n      .map((c) =>\n        typeof c === \"object\" && c !== null && typeof (c as { text?: unknown }).text === \"string\"\n          ? (c as { text: string }).text\n          : \"\"\n      )\n      .join(\"\\n\");\n  }\n  return \"\";\n}\n\n/**\n * Whether a tool_result block is a failed execution: the harness's own\n * is_error flag, or — the older Bash spelling — an \"Exit code N\" line with a\n * non-zero N in the result text. Feeds the consecFails counter supervision's\n * thrashing detector reads.\n */\nexport function toolResultFailed(block: { is_error?: boolean; content?: unknown }): boolean {\n  if (block.is_error === true) return true;\n  const m = toolResultText(block.content).match(/Exit code (\\d+)/);\n  return m !== null && Number(m[1]) !== 0;\n}\n\n/** CLI aliases the built-in provider hands to `--model` (NATIVE_ANTHROPIC_MODELS'\n *  values): a bare name like \"sonnet\", not a concrete id, so the init event's\n *  resolved id is always the more useful thing to display. */\nconst NATIVE_MODEL_ALIASES = new Set(Object.values(NATIVE_ANTHROPIC_MODELS));\n\n/**\n * The model the init event announces, adopted into the status when the spawn\n * could not name one (a caller that passed `--model` itself in the claude\n * args, or a provider whose table has no entry for the capability) or named\n * only a CLI alias (the built-in provider's \"sonnet\"/\"haiku\", which tracks\n * whatever release is current — the init event says which one actually ran).\n * The run's real model is then only knowable from the first event it sends.\n * Never overwrites a concrete resolved or pinned model — that stays the\n * recorded truth.\n */\nexport function adoptInitModel(status: Pick<WorkerStatus, \"model\">, e: StreamEvent): void {\n  if (status.model && !NATIVE_MODEL_ALIASES.has(status.model)) return;\n  const m = (e.model ?? \"\").trim();\n  if (m) status.model = m;\n}\n\n/**\n * The model a run goes out on: an explicit `--model` wins; else the class\n * target's alias (\"glm/fast\") names the capability; else the capability the\n * class implies (image → image model, spotcheck/simple → fast, everything\n * else → default), resolved against the provider's model table. The built-in\n * anthropic provider resolves through the same table as any other, so a\n * worker never inherits the orchestrator session's model.\n */\nexport function resolveRunModel(\n  target: { provider: WorkerProvider; modelAlias: string | null },\n  className?: string,\n  modelFlag?: string\n): string {\n  if (modelFlag) return modelFlag;\n  const alias = target.modelAlias;\n  const capability =\n    alias && isModelCapability(alias) ? alias : classModelCapability(className);\n  return resolveModelCapability(target.provider, capability);\n}\n\n/**\n * The `--model` part of the claude argv. Skipped when the caller already\n * pinned one in the claude args (it must not be clobbered) or when nothing\n * resolved — `--model \"\"` would break the spawn.\n */\nexport function modelArgs(model: string, callerPinned: boolean): string[] {\n  return !callerPinned && model ? [\"--model\", model] : [];\n}\n\n/**\n * Two model ids name the same model when their canonical forms do: a\n * bracketed suffix is a context-window variant, not another model\n * (\"glm-5.3[1m]\" IS \"glm-5.3\", announced with a 1M window — the same\n * collision the init event's adoptInitModel sees).\n */\nexport function canonicalModelId(id: string): string {\n  return id.trim().replace(/\\[[^\\]]*\\]$/, \"\");\n}\n\nexport interface ModelStepDown {\n  /** The model id the run goes out on. */\n  model: string;\n  /** One launch-preamble line, or null when resolution is unchanged. */\n  notice: string | null;\n}\n\n/**\n * The model step-down: a worker never runs on the orchestrator's own model —\n * enforced here, not remembered (config.ts states the principle). Until\n * 2026-10-17 it was only enforced where a model id is named explicitly: with\n * no --model, the class map's default tier resolved glm's default model —\n * glm-5.3[1m], the very model the orchestrating session itself ran — and\n * nothing detected the equality. Mechanical now: when the resolved id equals\n * the orchestrating session's (detected from the environment exactly the way\n * `pai launch --list --current` does it, launch.ts's detectCurrentModel), the\n * same provider's fast model replaces it. An explicit operator --model always\n * wins, including \"use your own model\" — the runner's own flag and a --model\n * pinned in the claude args both count as explicit. A provider without a\n * distinct fast model keeps the resolved model with a warning; a run is never\n * refused over this. An undetectable orchestrator model changes nothing.\n */\nexport function stepDownOrchestratorModel(opts: {\n  resolved: string;\n  providerName: string;\n  provider: Pick<WorkerProvider, \"models\">;\n  /** Explicit --model (the runner's own flag): the operator's choice wins. */\n  modelFlag?: string;\n  /** The caller pinned --model in the claude args: also explicit. */\n  callerPinned?: boolean;\n  env?: NodeJS.ProcessEnv;\n}): ModelStepDown {\n  if (opts.modelFlag || opts.callerPinned) return { model: opts.resolved, notice: null };\n  const orchestrator = detectCurrentModel(opts.env ?? process.env);\n  if (!orchestrator) return { model: opts.resolved, notice: null };\n  if (canonicalModelId(opts.resolved) !== canonicalModelId(orchestrator)) {\n    return { model: opts.resolved, notice: null };\n  }\n  const fast = opts.provider.models.fast;\n  if (!fast || canonicalModelId(fast) === canonicalModelId(opts.resolved)) {\n    return {\n      model: opts.resolved,\n      notice:\n        `pai: worker model == orchestrator model — provider \"${opts.providerName}\" has no distinct ` +\n        `fast model, keeping ${opts.resolved} (workers never inherit the orchestrator's model)`,\n    };\n  }\n  return {\n    model: fast,\n    notice:\n      `pai: worker model == orchestrator model — stepped down to ${fast} ` +\n      `(workers never inherit the orchestrator's model); pass --model to override`,\n  };\n}\n\n/**\n * Whether the run's assembled cmd gets a forced --model. Headless workers\n * always take the resolved class/provider model — that table exists so\n * spawned subagents stay cheap. An interactive launch (`pai worker run` with\n * no -p) IS the chat pane, not a subagent: with no --model of the caller's\n * own it gets none here either, so Claude Code's own settings.json model\n * applies, exactly as launching `claude` by hand would (2026-09-20: an\n * interactive supervisor came up on the class model instead of the\n * settings.json [1m] variant, then needed a manual /model switch that\n * rebuilt the whole cache). A caller's own --model, headless or not, is\n * already in the passthrough args and must not be duplicated here.\n */\nexport function modelFlagArgs(headless: boolean, model: string, callerPinned: boolean): string[] {\n  return headless ? modelArgs(model, callerPinned) : [];\n}\n\n/**\n * The prompt text actually sent to the worker and whether the trailer got\n * appended. An instruction at the END of the user turn outweighs one buried\n * in the system prompt (measured 2026-09-20: the AG2 contract alone was\n * ignored by both Haiku and Sonnet). Never applied to an interactive launch\n * (no -p) or when the caller brought its own --append-system-prompt — the\n * trailer's \"format in system prompt\" would then point at nothing.\n */\nexport function headlessPromptText(\n  prompt: string | null,\n  headless: boolean,\n  callerSystemPrompt: boolean,\n  format: \"ag2\" | \"json\"\n): { text: string | null; applied: boolean } {\n  const applied = headless && prompt !== null && !callerSystemPrompt;\n  return { text: applied ? prompt! + promptTrailer(format) : prompt, applied };\n}\n\n/**\n * Whether an invalid final report earns the one bounded re-ask: only when a\n * real validator caught the problem (a missing/broken aibroker must never\n * block the run further), the caller did not opt out, and there is a session\n * to resume into. Cap of exactly one is enforced by the caller never calling\n * this twice for the same run.\n */\nexport function shouldRetryReport(\n  validation: { ok: boolean; validator: \"aibroker\" | \"none\" },\n  noReportRetry: boolean,\n  hasSession: boolean\n): boolean {\n  return !validation.ok && validation.validator === \"aibroker\" && !noReportRetry && hasSession;\n}\n\n/** Context window announced by the init event, when the endpoint sends one. */\n/**\n * An empty model response: the model \"answered\" without producing anything —\n * no tool ran and the result text is empty. A local model can stop at once with an empty reply;\n * it is a harness failure, not a finished run (rc 0 and a result record\n * notwithstanding). Usage plays no part: local servers report 0/0 on every\n * message, normal ones too.\n */\nexport function isEmptyResponse(resultText: string | undefined, tools: number): boolean {\n  return tools === 0 && (resultText ?? \"\").trim() === \"\";\n}\n\n/**\n * Did the run end well? A headless run must have produced a stream-json\n * result event that is not an error; an interactive launch IS the chat pane\n * and never emits one, so its clean exit (rc 0) is the whole verdict —\n * without this every interactive session that ends normally was recorded\n * as `failed rc=0` and supervision pinged the spawner about a non-failure.\n */\nexport function runSucceeded(\n  headless: boolean,\n  rc: number,\n  resultEvent: Pick<StreamEvent, \"is_error\"> | null,\n): boolean {\n  if (rc !== 0) return false;\n  if (!headless) return true;\n  return resultEvent !== null && !resultEvent.is_error;\n}\n\nexport function initContextWindow(e: StreamEvent): number | null {\n  if (typeof e.context_window === \"number\" && e.context_window > 0) return e.context_window;\n  if (e.model_info && typeof e.model_info.context_window === \"number\" && e.model_info.context_window > 0) {\n    return e.model_info.context_window;\n  }\n  // the `[1m]` model variant announces a 1M-token window by suffix\n  if (/\\[1m\\]$/.test((e.model ?? \"\").trim())) return 1_000_000;\n  return null;\n}\n\n/**\n * Run one worker. Returns the process exit code to pass through.\n * Throws WorkersConfigError-shaped Errors for configuration problems.\n */\nexport async function runWorker(opts: RunOptions): Promise<number> {\n  const { raw: _raw, workers: config } = readWorkersSection();\n  void _raw;\n  if (!config.enabled) {\n    throw new Error(\n      `workers are off. Turn them on with: pai worker on` +\n        `\\n(then the Agent-tool hook stops denying Anthropic subagents only when you do)`\n    );\n  }\n  const logDir = workersLogDir(config);\n  mkdirSync(logDir, { recursive: true });\n  try {\n    const gc = gcWorktreesThrottled(logDir)?.filter((r) => r.archive);\n    if (gc?.length) process.stderr.write(`pai worker: archived ${gc.length} leftover worktree(s) (pai worker gc)\\n`);\n  } catch {\n    // best effort\n  }\n\n  // class plan is not one worker but the planner orchestration (planner.ts)\n  if (opts.className === \"plan\" && !opts._planner) {\n    const { runPlanner } = await import(\"./planner.js\");\n    return runPlanner(opts);\n  }\n\n  // Sub-worker bookkeeping: an explicit parent (chain stage, planner child)\n  // wins, else the worker this process runs inside (PAI_WORKER_ID). Both\n  // caps from workers.tree apply to parents that are workers themselves.\n  const parent = launchParent(opts.parent);\n  if (parent) assertChildAllowed(logDir, parent, config.tree);\n\n  // An explicit --provider always bypasses capability resolution entirely;\n  // otherwise --capability, or a class whose implied capability is not\n  // \"default\" and names no provider of its own, resolves cross-provider.\n  const capability = opts.providerFlag\n    ? null\n    : capabilityForRun(config, { capabilityFlag: opts.capabilityFlag, className: opts.className });\n  const capabilityResolution: CapabilityRunResolution | null = capability\n    ? resolveCapabilityRun(config, capability)\n    : null;\n\n  // Worker routing policy (policy.ts): picks local or cloud unless the caller named a provider.\n  let route = opts._route;\n  let providerFlag = opts.providerFlag;\n  if (!route && !providerFlag && !capability && opts.className !== \"plan\" && parseRunnerArgs(opts.claudeArgs).headless) {\n    const routed = await routeRun(config, logDir, opts);\n    if (routed) {\n      route = routed.route;\n      providerFlag = routed.providerFlag ?? undefined;\n    }\n  }\n\n  // Budget-aware fallback: probe live budgets (5-min cache) and cooldown any\n  // provider at/above its wall before the walk, so resolveTarget picks the\n  // first priority provider that still has room. Probing must never block a\n  // spawn: failures read as \"unknown\" inside the cache, never as \"full\".\n  if (!providerFlag && !capabilityResolution && config.routing.policy !== \"order\") {\n    try {\n      await budgetCache.warm(config);\n      recordBudgetCooldowns(config, logDir);\n    } catch {\n      /* no readings: budget routing degrades to plain order */\n    }\n  }\n\n  const target = capabilityResolution\n    ? {\n        providerName: capabilityResolution.providerName,\n        provider: capabilityResolution.provider,\n        modelAlias: null,\n        classMcp: null,\n        via: \"class\" as const,\n      }\n    : resolveTarget(config, logDir, {\n        flagProvider: providerFlag,\n        className: opts.className,\n      });\n  assertProviderRunnable(target.providerName, target.provider);\n\n  const noBrowser = opts.browser === undefined ? config.noBrowserByDefault : !opts.browser;\n  const parsed = parseRunnerArgs(noBrowser ? stripBrowserArgs(opts.claudeArgs) : opts.claudeArgs);\n  // The config default is a headless-run guard; an interactive launch is the operator's own session and is\n  // limited only by an explicit --max-minutes / --deadline.\n  const limit = resolveLimit(opts, parsed.headless ? config.defaultMaxMinutes : 0);\n  const label =\n    opts.label ??\n    shortText(parsed.prompt ?? UNLABELED, 70);\n\n  let model = capabilityResolution\n    ? capabilityResolution.model\n    : resolveRunModel(target, opts.className, opts.modelFlag);\n  if (!capabilityResolution) {\n    // capability runs pin their model on purpose (an image prompt on the\n    // image model); the step-down guards the class/provider resolution only\n    const step = stepDownOrchestratorModel({\n      resolved: model,\n      providerName: target.providerName,\n      provider: target.provider,\n      modelFlag: opts.modelFlag,\n      callerPinned: parsed.callerModel,\n    });\n    model = step.model;\n    if (step.notice && !opts.quiet) process.stderr.write(`${step.notice}\\n`);\n  }\n  const reportFormat = resolveReportFormat(opts.reportFormatFlag);\n\n  // tracked across every engine (including a quota reroute's recursive\n  // runWorker call) so the exit hook below knows which status to check,\n  // whatever opts.id/onWorkerStart the caller did or didn't pass\n  let workerId: string | undefined = opts.id;\n  const trackStart = (wid: string) => {\n    workerId = wid;\n    opts.onWorkerStart?.(wid);\n  };\n\n  try {\n    let rc: number;\n    if (target.provider.engine === \"image\") {\n      rc = await executeImageRun({\n        config,\n        logDir,\n        target,\n        model,\n        label,\n        parsed,\n        capability: capability ?? \"image\",\n        outPath: opts.imageOut,\n        size: opts.imageSize,\n        timeoutMs: opts.imageTimeoutMs,\n        cwd: opts.cwd,\n        specPath: opts.specPath,\n        parent: parent ?? undefined,\n        stage: opts.stage,\n        quiet: opts.quiet,\n        onWorkerStart: trackStart,\n        id: opts.id,\n      });\n    } else if (target.provider.engine === \"codex\") {\n      rc = await executeCodexRun({\n        config,\n        logDir,\n        target,\n        model,\n        label,\n        parsed,\n        claudeArgs: opts.claudeArgs,\n        noPane: opts.noPane ?? false,\n        cwd: opts.cwd,\n        specPath: opts.specPath,\n        parent: parent ?? undefined,\n        stage: opts.stage,\n        quiet: opts.quiet,\n        onWorkerStart: trackStart,\n        id: opts.id,\n        worktreeFlag: opts.worktreeFlag,\n        className: opts.className,\n        reportFormat,\n        capability: capability ?? undefined,\n        limit,\n        noBrowser,\n      });\n    } else {\n      const execArgs: ExecuteArgs = {\n        config,\n        logDir,\n        target,\n        model,\n        label,\n        parsed,\n        claudeArgs: opts.claudeArgs,\n        noPane: opts.noPane ?? false,\n        mcpFlag: opts.mcpFlag,\n        cwd: opts.cwd,\n        specPath: opts.specPath,\n        parent: parent ?? undefined,\n        stage: opts.stage,\n        quiet: opts.quiet,\n        onWorkerStart: trackStart,\n        id: opts.id,\n        capability: capability ?? undefined,\n        worktreeFlag: opts.worktreeFlag,\n        resumeOf: opts.resumeOf,\n        className: opts.className,\n        reroutes: opts._reroutes ?? 0,\n        emptyRetried: opts._emptyRetried ?? false,\n        route,\n        fallThroughOf: opts._fallThroughOf,\n        printCmd: opts.printCmd,\n        reportFormat,\n        noReportRetry: opts.noReportRetry ?? false,\n        limit,\n        noBrowser,\n        opts,\n      };\n      // the session pool (workers.pool) gets first crack: an eligible task\n      // runs as one more turn of a warm claude instead of a fresh spawn\n      const pooledRc = await tryPoolRun(execArgs);\n      if (pooledRc !== null) {\n        await returnControlsIfHeld(logDir, workerId);\n        return pooledRc;\n      }\n      rc = await executeRun(execArgs);\n    }\n    await returnControlsIfHeld(logDir, workerId);\n    return rc;\n  } catch (e) {\n    await returnControlsIfHeld(logDir, workerId);\n    if (e instanceof Error && e.message.startsWith(\"key file\")) {\n      throw new Error(\n        `provider \"${target.providerName}\": ${e.message}` +\n          `\\nPut the token in that file (chmod 600) or point keyFile elsewhere.`\n      );\n    }\n    throw e;\n  }\n}\n\n/**\n * The core tools a headless run grants when the caller brings none of its\n * own. Permission-gated tools (Bash, Read, Write, …) are never offered to a\n * headless session that has no allow rule for them — the child starts with\n * only the tools that never ask (tool search, web, cron, tasks) and cannot\n * touch a file or shell (2026-09-18). A caller's own --allowedTools is a\n * deliberate restriction and passes through untouched.\n */\nexport const DEFAULT_WORKER_TOOLS = \"Read,Edit,Write,Bash,Grep,Glob\";\n\n/** The --allowedTools args a headless run needs; [] when the caller granted. */\nexport function headlessToolGrants(allowedTools: string[]): string[] {\n  return allowedTools.length ? [] : [\"--allowedTools\", DEFAULT_WORKER_TOOLS];\n}\n\n/**\n * `--tools` narrows which built-in tool *schemas* claude loads into the\n * system prompt — distinct from `--allowedTools`, which narrows which of\n * those loaded tools may run without asking. A headless worker with a small\n * grant (say, just Read) still pays for every built-in schema (Bash, Web*,\n * NotebookEdit, …) it never uses: ~9k tokens per spawn (29,270 -> 20,352,\n * measured 2026-09-20). Passing --tools built from the same grant removes\n * the unused schemas.\n *\n * MCP grants (`mcp__server__tool` / `mcp__server__*`) are left out of the\n * --tools list: they are not built-in tool names, --tools does not\n * recognize them, and the MCP server's tools load through --mcp-config\n * regardless. Tested 2026-09-20: with an --allowedTools grant of\n * `Read,mcp__pai__memory_search`, passing `--tools Read` (mcp name\n * omitted) still let the worker call memory_search successfully; the tool\n * loads from the filtered MCP config, not from --tools.\n */\nexport function headlessToolsFlag(allowedTools: string[]): string[] {\n  const names: string[] = [];\n  for (const entry of allowedTools) {\n    for (const raw of entry.split(\",\").map((s) => s.trim()).filter(Boolean)) {\n      if (raw.startsWith(\"mcp__\")) continue; // loads via --mcp-config, not --tools\n      const patterned = raw.match(/^([A-Za-z]+)\\(.*\\)$/); // e.g. Bash(git *) -> Bash\n      const name = patterned ? patterned[1] : raw;\n      if (!names.includes(name)) names.push(name);\n    }\n  }\n  return names.length ? [\"--tools\", names.join(\",\")] : [];\n}\n\n/**\n * A `--tools` value with no ToolSearch silently disables MCP tool deferral\n * (measured 2026-09-20: a run with `--tools \"Bash,Read,Grep,Glob,Agent\"` paid\n * 65,300 first-turn tokens with 5 MCP servers, 230,448 with 16, versus\n * 24,889 / 37,133 with ToolSearch present). Applied to the fully-assembled\n * argv so it catches a `--tools` from any source — the computed\n * `headlessToolsFlag`/project-pin flag, or one the caller put directly on\n * the command line.\n */\nexport function ensureToolSearch(cmd: string[]): string[] {\n  const flagIdx = cmd.findIndex((a) => a === \"--tools\");\n  if (flagIdx >= 0 && flagIdx + 1 < cmd.length) {\n    const value = cmd[flagIdx + 1];\n    if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n      const out = [...cmd];\n      out[flagIdx + 1] = `${value},ToolSearch`;\n      process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n      return out;\n    }\n    return cmd;\n  }\n  const eqIdx = cmd.findIndex((a) => a.startsWith(\"--tools=\"));\n  if (eqIdx >= 0) {\n    const value = cmd[eqIdx].slice(\"--tools=\".length);\n    if (value && !value.split(\",\").includes(\"ToolSearch\")) {\n      const out = [...cmd];\n      out[eqIdx] = `--tools=${value},ToolSearch`;\n      process.stderr.write(\"pai: added ToolSearch to --tools (keeps MCP tool deferral on)\\n\");\n      return out;\n    }\n  }\n  return cmd;\n}\n\n/**\n * The claude flag that turns on the browser bridge, when the run asked for it.\n *\n * The bridge is not an MCP server — it rides the Chrome native-host channel,\n * is absent from `mcpServers`, and is off in a spawned claude until `--chrome`\n * is passed. A grant such as `mcp__claude-in-chrome__tabs_context_mcp` is\n * therefore a request for that flag, not for a server to load; without this\n * the grant names a tool that never exists. `rest` is the caller's own argv:\n * a `--chrome` they passed themselves is kept rather than duplicated.\n */\nexport function chromeGrantArgs(wanted: string[], rest: string[] = []): string[] {\n  if (rest.includes(\"--chrome\")) return [];\n  return grantsChrome(wanted) ? [\"--chrome\"] : [];\n}\n\n/**\n * Which MCP server names and --tools list an interactive launch (`pai\n * worker run`, no -p) uses: an explicit --mcp on this command line beats\n * the project's `pai project mcp` pin; an explicit --tools beats `pai\n * project tools`. Neither given: [] — today's \"everything loads\" default.\n */\nexport function interactiveMcpTools(\n  explicitMcp: string[],\n  callerTools: boolean,\n  projectLaunch: Omit<ProjectLaunchConfig, \"slug\"> | null\n): { mcpNames: string[]; tools: string[] } {\n  return {\n    mcpNames: explicitMcp.length ? explicitMcp : (projectLaunch?.mcp ?? []),\n    tools: !callerTools && projectLaunch?.tools?.length ? projectLaunch.tools : [],\n  };\n}\n\n// ---------------------------------------------------------------------------\n// image engine (2i) — no process spawn: one HTTP request, one file written.\n// ---------------------------------------------------------------------------\n\ninterface ImageExecuteArgs {\n  logDir: string;\n  target: ReturnType<typeof resolveTarget>;\n  model: string;\n  label: string;\n  parsed: ReturnType<typeof parseRunnerArgs>;\n  capability: string;\n  outPath?: string;\n  size?: string;\n  timeoutMs?: number;\n  cwd?: string;\n  specPath?: string;\n  parent?: string;\n  stage?: string;\n  quiet?: boolean;\n  onWorkerStart?: (wid: string) => void;\n  id?: string;\n}\n\ninterface FinaliseArgs {\n  logDir: string;\n  ledger: string;\n  status: WorkerStatus;\n  /** The process's real outcome; recordWorktree sees this, never the orphan verdict. */\n  ok: boolean;\n  rc: number;\n  secs: number;\n  /** Engine-specific WORKER-END fields (provider, mode, engine, model, label, ...). */\n  end: Record<string, unknown>;\n  report: WorkerReport | null;\n  /** Handoff text when the report has no notes. */\n  fallbackText?: string;\n  worktree?: WorktreeInfo | null;\n  /** The deadline stopped the run after this many minutes. */\n  timedOut?: number;\n  /**\n   * Runs after the worktree outcome is recorded, before the parent handoff\n   * (print the result, quota reroute). A returned number ends the run with\n   * it, skipping the handoff — the reroute's own run reports instead.\n   */\n  beforeHandoff?: (status: WorkerStatus, report: WorkerReport | null) => Promise<number | void> | number | void;\n}\n\n/**\n * The finalisation every engine shares: orphaned-children check, state, the\n * WORKER-END ledger line, worktree outcome, parent handoff, final rc. A worker\n * owns its children's results (the worker contract says so): if it\n * backgrounded a sub-worker and exited without waiting, the run must not\n * report success, whatever the process returned.\n */\n/** Per-git-call bound for the salvage a kill signal runs before exiting. */\nconst SIGNAL_SALVAGE_MS = 5_000;\n\n/** The deadline fired: record it on the status and in the ledger (the caller then stops the child). */\nexport function fireDeadline(logDir: string, status: WorkerStatus, ledger: string, limit: RunLimit): void {\n  status.timedOut = true;\n  status.last = `deadline reached after ${limit.minutes} min`;\n  saveStatus(logDir, status);\n  appendLedger(ledger, \"WORKER-DEADLINE\", { id: status.id, minutes: limit.minutes });\n}\n\n/** Decide why a signal ended the run and put it on the status; the caller writes WORKER-END. */\nexport function recordSignalEnd(logDir: string, status: WorkerStatus, signal: string): EndInfo {\n  const end = decideEndReason({\n    logDir,\n    id: status.id,\n    signal,\n    timedOut: status.timedOut,\n    parentPid: status.parentPid,\n    parentComm: status.parentComm,\n    parentBackground: status.parentBackground,\n  });\n  status.endReason = end.reason;\n  status.endBy = end.by;\n  status.endNote = end.note;\n  return end;\n}\n\nexport async function finaliseRun(f: FinaliseArgs): Promise<number> {\n  const { logDir, ledger, ok, rc } = f;\n  let { status, report } = f;\n  const wid = status.id;\n  // first, whatever the outcome (rc 143 included): uncommitted edits in the\n  // worktree must reach the branch before anything can drop the worktree\n  const salvaged = f.worktree\n    ? salvageOnExit(ledger, wid, status.label || UNLABELED, f.worktree.dir)\n    : [];\n  if (salvaged.length) {\n    const msg = `WORKER-SALVAGE: committed uncommitted edits (${salvaged.join(\", \")})`;\n    report = { ...(report ?? {}), notes: [report?.notes ?? f.fallbackText, msg].filter(Boolean).join(\"\\n\"), result: report?.result ?? \"-\" };\n  }\n  if (f.timedOut) {\n    const reason = `deadline reached after ${f.timedOut} min`;\n    status.timedOut = true;\n    status.last = reason;\n    // partial state = the salvage summary (r=~: stopped, not failed outright)\n    report = { ...(report ?? {}), result: \"~\", why: reason, notes: [reason, report?.notes ?? f.fallbackText].filter(Boolean).join(\"\\n\") };\n  }\n  const orphanReason = checkOrphanedChildren(logDir, wid);\n  const succeeded = ok && !orphanReason && !f.timedOut;\n  status.state = succeeded ? \"done\" : \"failed\";\n  status.rc = rc;\n  status.secs = f.secs;\n  if (orphanReason) {\n    status.last = shortText(orphanReason, 90);\n    report = { ...(report ?? {}), notes: orphanReason, result: \"-\" };\n    appendLedger(ledger, \"WORKER-ORPHAN-CHILDREN\", { id: wid, reason: orphanReason });\n  }\n  const end = decideEndReason({ logDir, id: wid, timedOut: f.timedOut !== undefined });\n  status.endReason = end.reason;\n  // r=~ (partial) means the work continues: saveStatus then keeps the issue claims\n  status.claimsContinue = report?.result === \"~\";\n  saveStatus(logDir, status);\n  appendLedger(ledger, \"WORKER-END\", { id: wid, ...f.end, rc, secs: f.secs, ...endFields(end) });\n\n  if (f.worktree) status = recordWorktree(logDir, status, f.worktree, ok);\n\n  const early = await f.beforeHandoff?.(status, report);\n  if (typeof early === \"number\") return early;\n\n  const finalRc = rc !== 0 ? rc : succeeded ? 0 : 1;\n  if (status.parent && isWorkerId(logDir, status.parent)) {\n    try {\n      await deliverHandoff(logDir, {\n        from: wid,\n        to: status.parent,\n        kind: \"result\",\n        text: shortText(report?.notes ?? f.fallbackText ?? (succeeded ? \"done\" : \"failed\"), 400),\n        data: {\n          rc: finalRc,\n          ok: succeeded,\n          ...(status.branch ? { branch: status.branch, commits: status.commits ?? 0 } : {}),\n          ...(report ? { report } : {}),\n        },\n      });\n    } catch {\n      // the inbox line is best effort; it must never fail the exit path\n    }\n  }\n  return finalRc;\n}\n\nasync function executeImageRun(a: ImageExecuteArgs & { config: ReturnType<typeof readWorkersSection>[\"workers\"] }): Promise<number> {\n  const { logDir, target, model, label, parsed } = a;\n  if (!parsed.headless || parsed.prompt === null) {\n    throw new Error(\n      `provider \"${target.providerName}\" (engine image) supports headless runs only: ` +\n        `pass the task with -p '<prompt>'`\n    );\n  }\n  const wid = a.id ?? newWorkerId();\n  const cwd = a.cwd ?? process.cwd();\n  const term = process.env.ITERM_SESSION_ID ?? \"\";\n  const outPath = a.outPath ?? join(logDir, `${wid}.png`);\n\n  const status: WorkerStatus = {\n    id: wid,\n    pid: process.pid,\n    label,\n    cwd,\n    term,\n    provider: target.providerName,\n    model,\n    state: \"running\",\n    started: nowStamp(),\n    updated: nowStamp(),\n    turns: 0,\n    tools: 0,\n    last: \"generating image\",\n    rc: null,\n    secs: null,\n    origin: \"spawn\",\n    outputFormat: parsed.outputFormat,\n    reportFormat: \"json\",\n    ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n    ...(a.specPath ? { spec: a.specPath } : {}),\n  };\n  saveStatus(logDir, status);\n  a.onWorkerStart?.(wid);\n  const ledger = ledgerPath(logDir);\n  appendLedger(ledger, \"WORKER-START\", {\n    id: wid,\n    provider: target.providerName,\n    mode: \"headless\",\n    engine: \"image\",\n    model,\n    cwd,\n    label,\n    capability: a.capability,\n    ...(a.specPath ? { spec: a.specPath } : {}),\n  });\n\n  const imageEnd = {\n    provider: target.providerName,\n    mode: \"headless\",\n    engine: \"image\",\n    model,\n    label,\n    capability: a.capability,\n  };\n  const t0 = Date.now();\n  try {\n    const result = await runImageCapability({\n      providerName: target.providerName,\n      provider: target.provider,\n      model,\n      prompt: parsed.prompt,\n      outPath,\n      size: a.size,\n      timeoutMs: a.timeoutMs,\n    });\n    const secs = Math.round((Date.now() - t0) / 1000);\n    status.last = `wrote ${result.path}`;\n    return await finaliseRun({\n      logDir,\n      ledger,\n      status,\n      ok: true,\n      rc: 0,\n      secs,\n      end: imageEnd,\n      report: null,\n      beforeHandoff: () => {\n        if (!a.quiet) process.stdout.write(JSON.stringify(result) + \"\\n\");\n      },\n    });\n  } catch (e) {\n    const secs = Math.round((Date.now() - t0) / 1000);\n    status.last = e instanceof Error ? e.message : String(e);\n    await finaliseRun({ logDir, ledger, status, ok: false, rc: 1, secs, end: imageEnd, report: null });\n    throw e;\n  }\n}\n\n/**\n * The fail-closed pane gate: a headless run proceeds only behind a pane that\n * was opened and verified visible (see openVerifiedPane). Runs BEFORE the\n * worker has an id on disk, so a refusal leaves nothing in `pai worker ps`.\n *\n * Deterministic (no hidden workers): the ONLY skips are headlessAllowed() —\n * a worker's own child (PAI_WORKER=1), the operator's PAI_WORKER_ALLOW_HIDDEN=1\n * or their override file. `dryRun` (--print-cmd audits) skips too: it spawns\n * nothing. An explicit hidden request (noPane from a daemon, keepalive or an\n * in-process caller) without that consent is refused, not silently honoured —\n * a spawning session with no window (daemon) has no pane to attach to and\n * cannot run workers.\n */\nexport async function requireVisiblePane(\n  logDir: string,\n  config: ReturnType<typeof readWorkersSection>[\"workers\"],\n  wid: string,\n  term: string,\n  noPane: boolean,\n  dryRun: boolean,\n  ledger: string\n): Promise<boolean> {\n  if (dryRun) return false;\n  const allow = headlessAllowed();\n  if (allow) {\n    process.stderr.write(`[pai worker] hidden run allowed for ${wid}: ${allow}\\n`);\n    return false;\n  }\n  const how = `hidden runs need the operator's ${ALLOW_HIDDEN_ENV}=1 or ${headlessOverridePath()}`;\n  if (noPane) {\n    appendLedger(ledger, \"WORKER-REFUSED\", { id: wid, reason: `headless requested, no override (${how})`.slice(0, 300) });\n    throw new Error(`worker not started, no visible pane: headless requested but nothing may run hidden (${how})`);\n  }\n  try {\n    const where = await openVerifiedPane(logDir, config, wid, term);\n    process.stderr.write(`[pai worker] pane verified for ${wid}: ${where}\\n`);\n    return true;\n  } catch (e) {\n    const reason = e instanceof Error ? e.message : String(e);\n    appendLedger(ledger, \"WORKER-REFUSED\", { id: wid, reason: `${reason} (${how})`.slice(0, 300) });\n    throw new Error(`worker not started, no visible pane: ${reason} (${how})`);\n  }\n}\n\n/**\n * Follow pane, made observable on stderr (stdout stays pure JSON for\n * `--output-format json`): one line naming the outcome — opened, skipped\n * (with reason), or failed (with reason, alongside the existing PANE-FAIL\n * ledger line). Fire-and-forget, never blocks the worker.\n */\nexport async function announcePane(\n  logDir: string,\n  config: ReturnType<typeof readWorkersSection>[\"workers\"],\n  wid: string,\n  term: string,\n  noPane: boolean,\n  ledger: string,\n  status?: WorkerStatus\n): Promise<void> {\n  const setPane = (p: NonNullable<WorkerStatus[\"pane\"]>): void => {\n    if (status) status.pane = p;\n  };\n  const skip = !noPane\n    ? !config.pane.enabled\n      ? \"workers.pane.enabled is false\"\n      : process.env.PAI_WORKER_AUTOPANE === \"0\"\n        ? \"PAI_WORKER_AUTOPANE=0\"\n        : paneBackend(process.env, process.platform, term) === \"none\"\n          ? \"no tmux ($TMUX) or iTerm2 (ITERM_SESSION_ID) here\"\n          : null\n    : \"--no-pane\";\n  if (skip) {\n    process.stderr.write(`[pai worker] no pane for ${wid}: ${skip}\\n`);\n    setPane({ opened: false, reason: skip });\n    return;\n  }\n  try {\n    const r = await openPaneForWorker(logDir, config, wid, term);\n    const suffix = r.session ? ` (iTerm session ${r.session})` : \"\";\n    process.stderr.write(`[pai worker] pane opened for ${wid}${suffix}\\n`);\n    setPane({ opened: true, ...(r.session ? { session: r.session } : {}) });\n  } catch (e) {\n    const message = e instanceof Error ? e.message : String(e);\n    process.stderr.write(`[pai worker] no pane for ${wid}: ${message.slice(0, 200)}\\n`);\n    appendLedger(ledger, \"PANE-FAIL\", { id: wid, error: message.slice(0, 200) });\n    setPane({ opened: false, reason: message.slice(0, 200) });\n  }\n}\n\nexport interface ExecuteArgs {\n  config: ReturnType<typeof readWorkersSection>[\"workers\"];\n  logDir: string;\n  target: ReturnType<typeof resolveTarget>;\n  model: string;\n  label: string;\n  parsed: ReturnType<typeof parseRunnerArgs>;\n  claudeArgs: string[];\n  noPane: boolean;\n  mcpFlag?: string;\n  cwd?: string;\n  specPath?: string;\n  parent?: string;\n  stage?: string;\n  quiet?: boolean;\n  onWorkerStart?: (wid: string) => void;\n  id?: string;\n  worktreeFlag?: boolean;\n  resumeOf?: string;\n  className?: string;\n  reroutes: number;\n  emptyRetried?: boolean;\n  route?: RouteInfo;\n  fallThroughOf?: string;\n  printCmd?: boolean;\n  reportFormat: \"json\" | \"ag2\";\n  noReportRetry?: boolean;\n  /** Capability name this run resolved through (--capability, or an implied class capability), when it did. */\n  capability?: string;\n  /** Time limit of the run; null = none. */\n  limit: RunLimit | null;\n  /** Browser tools are stripped from this run (and denied by the worker guard). */\n  noBrowser: boolean;\n  /** The options this run was launched with; a relaunch starts from these (see relaunchOptions). */\n  opts: RunOptions;\n}\n\n/**\n * The worker session pool attempt (src/workers/pool.ts), behind\n * workers.pool.enabled: an eligible headless task is shipped to the pool\n * host and runs as the next user turn of a warm claude slot, so the\n * provider prompt cache carries over. Returns the exit code when the task\n * ran pooled; null sends the caller down the normal fresh-spawn path (host\n * unreachable, or the task needs a setup a slot cannot provide — worktree,\n * own system prompt, MCP, another cwd, native Anthropic).\n */\nexport async function tryPoolRun(a: ExecuteArgs): Promise<number | null> {\n  const settings = poolSettings(a.config);\n  if (!settings.enabled) return null;\n  const reason = poolIneligibleReason({\n    headless: a.parsed.headless,\n    prompt: a.parsed.prompt,\n    providerName: a.target.providerName,\n    provider: a.target.provider,\n    worktreeFlag: a.worktreeFlag,\n    resumeOf: a.resumeOf,\n    printCmd: a.printCmd,\n    parsed: a.parsed,\n    mcpFlag: a.mcpFlag,\n    noBrowser: a.noBrowser,\n    outputFormat: a.parsed.outputFormat,\n    cwd: a.cwd,\n    slotCwd: poolSlotCwd(a.config),\n  });\n  if (reason) {\n    if (!a.quiet) process.stderr.write(`[pai worker] pool: not pooled (${reason}) — spawning fresh\\n`);\n    return null;\n  }\n  const wid = a.id ?? newWorkerId();\n  const limitMinutes = a.limit && a.limit.minutes > 0 ? a.limit.minutes : null;\n  // the socket bounds connect+send only; the answer takes as long as the task\n  const timeoutMs = limitMinutes ? limitMinutes * 2 * 60_000 + 120_000 : 7_200_000;\n  let resp: PoolTaskResponse | null;\n  try {\n    resp = await postTaskToPool(\n      settings.port,\n      {\n        id: wid,\n        label: a.label,\n        prompt: a.parsed.prompt ?? \"\",\n        provider: a.target.providerName,\n        model: a.model,\n        className: a.className,\n        capability: a.capability,\n        specPath: a.specPath,\n        parent: a.parent,\n        stage: a.stage,\n        reportFormat: a.reportFormat,\n        outputFormat: a.parsed.outputFormat === \"json\" ? \"json\" : \"text\",\n        maxMinutes: limitMinutes,\n        trailer: promptTrailer(a.reportFormat),\n      },\n      timeoutMs\n    );\n  } catch {\n    resp = null;\n  }\n  if (resp === null) {\n    process.stderr.write(`[pai worker] pool: host unreachable on port ${settings.port} — spawning fresh\\n`);\n    return null;\n  }\n  if (!resp.pooled) {\n    if (!a.quiet) process.stderr.write(`[pai worker] pool: not pooled (${resp.reason ?? \"refused\"}) — spawning fresh\\n`);\n    return null;\n  }\n  a.onWorkerStart?.(wid);\n  const rc = resp.rc ?? 1;\n  if (!a.quiet) {\n    printResult(a.parsed.outputFormat, resp.resultEvent ?? null, rc, a.logDir, wid, resp.report);\n  }\n  return rc;\n}\n\nasync function executeRun(a: ExecuteArgs): Promise<number> {\n  const { config, logDir, target, model, label, parsed, noPane, limit, noBrowser } = a;\n  const headless = parsed.headless;\n\n  // openai-protocol providers run through the local proxy (started on demand)\n  let proxyUrl: string | undefined;\n  if (target.provider.protocol === \"openai\") {\n    const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n    proxyUrl = `${base}/${target.providerName}`;\n  }\n  const env = buildRunEnv(target.provider, headless, proxyUrl, target.providerName);\n\n  const wid = a.id ?? newWorkerId();\n  // a resume takes over the old id's tree and runs against its repo\n  const adopted = a.resumeOf ? adoptTree(logDir, a.resumeOf) : null;\n  const cwd = adopted?.cwd ?? a.cwd ?? process.cwd();\n  const term = (await resolveHostSession()).term;\n  const paneVerified = headless && (await requireVisiblePane(logDir, config, wid, term, noPane, a.printCmd === true, ledgerPath(logDir)));\n  const session = resolveSession(term);\n  // orchestrator Bash children have no terminal identity (see scope.ts) — the\n  // session map supplies the claude session they were spawned by instead\n  const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n  // One worktree per run whose tools can write (Edit/Write/Bash or no\n  // restriction), in a git cwd; --worktree/--no-worktree override. A git refusal\n  // degrades to an in-place run — the worker itself must still run.\n  // Skipped for --print-cmd: it is a dry-run and must not touch git.\n  let worktree: WorktreeInfo | null = adopted;\n  if (\n    !adopted &&\n    headless &&\n    !a.printCmd &&\n    worktreeWanted(a.worktreeFlag, { cwd, allowedTools: parsed.allowedTools })\n  ) {\n    try {\n      worktree = addWorktree(logDir, wid, cwd);\n    } catch (e) {\n      const why = (e as Error).message;\n      process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n      appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n    }\n  }\n  // project preamble (.pai/worker.md): headless only, oversize refuses the run\n  let preamble: Preamble | null = null;\n  if (headless) {\n    const loaded = loadPreamble(cwd);\n    if (\"refused\" in loaded) {\n      process.stderr.write(`pai worker: preamble refused: ${loaded.refused}\\n`);\n      appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `preamble refused: ${loaded.refused}` });\n      return 1;\n    }\n    preamble = loaded.preamble;\n    if (!a.quiet) process.stderr.write(preambleLine(preamble) + \"\\n\");\n  }\n  // provider contract addendum (<PAI_HOME>/contracts/<provider>.md): headless, only with the generic contract\n  const addendum: ContractAddendum | null =\n    headless && !parsed.callerSystemPrompt ? loadContractAddendum(target.providerName, target.provider.contract) : null;\n  if (addendum && !a.quiet) process.stderr.write(`contract addendum: ${addendum.path} sha ${addendum.sha}\\n`);\n  // sub-workers detect themselves (and their parent) through this variable\n  env.PAI_WORKER_ID = wid;\n  if (noBrowser) env.PAI_WORKER_NO_BROWSER = \"1\";\n\n  const mcpFlag = noBrowser ? stripBrowserNames(a.mcpFlag ? [a.mcpFlag] : [])[0] : a.mcpFlag;\n  const classMcp = noBrowser && target.classMcp ? stripBrowserNames(target.classMcp) : target.classMcp;\n  if (noBrowser) parsed.mcp = stripBrowserNames(parsed.mcp);\n  const chromeArgs = noBrowser\n    ? []\n    : chromeGrantArgs(\n        [...(mcpFlag ? [mcpFlag] : []), ...parsed.mcp, ...(classMcp ?? []), ...parsed.allowedTools],\n        parsed.rest\n      );\n\n  // MCP / tools: caller config > allowlist (--mcp flag / --mcp args / role /\n  // mcp__ grants in --allowedTools) > project pin (interactive only, `pai\n  // project mcp` / `pai project tools`) > the strict empty set (headless) /\n  // everything (interactive, today's default).\n  let mcpArgs: string[] = [];\n  // headless without a grant or --tools of its own: load only the default core schemas\n  let toolsFlag: string[] = headless ? headlessToolsFlag(parsed.allowedTools) : [];\n  if (headless && !parsed.allowedTools.length && !parsed.callerTools) {\n    toolsFlag = [\"--tools\", DEFAULT_WORKER_TOOLS];\n  }\n  if (headless && !parsed.callerMcpConfig) {\n    const wanted = [\n      ...(mcpFlag ? [mcpFlag] : []),\n      ...parsed.mcp,\n      ...(classMcp ?? []),\n      ...mcpServersFromToolGrants(parsed.allowedTools),\n    ];\n    if (wanted.length) {\n      const names = expandMcpNames(wanted, config); // unknown names fail fast\n      mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n    } else {\n      mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", ensureNoMcpConfig(logDir)];\n    }\n  } else if (!headless && !parsed.callerMcpConfig) {\n    const explicitMcp = [...(mcpFlag ? [mcpFlag] : []), ...parsed.mcp];\n    const projectLaunch = await projectLaunchConfig(cwd);\n    const picked = interactiveMcpTools(explicitMcp, parsed.callerTools, projectLaunch);\n    // applied pins only: an explicit --mcp / --tools overrides its pin\n    const applied = projectLaunch && {\n      ...projectLaunch,\n      mcp: explicitMcp.length ? undefined : projectLaunch.mcp,\n      tools: picked.tools.length ? projectLaunch.tools : undefined,\n    };\n    for (const line of pinNotices(applied)) console.error(line);\n    if (picked.mcpNames.length) {\n      const names = expandMcpNames(picked.mcpNames, config);\n      mcpArgs = [\"--strict-mcp-config\", \"--mcp-config\", writeMcpConfig(logDir, wid, names)];\n    }\n    if (picked.tools.length) toolsFlag = [\"--tools\", picked.tools.join(\",\")];\n  }\n\n  // In stdin mode the prompt moves to the first user message on stdin, so it\n  // must come off the command line (bare -p stays: stream-json needs --print).\n  const restArgs = headless ? stripPromptValues(parsed.rest) : parsed.rest;\n  const toolArgs = headless ? headlessToolGrants(parsed.allowedTools) : [];\n  const { text: promptText, applied: trailerApplied } = headlessPromptText(\n    parsed.prompt,\n    headless,\n    parsed.callerSystemPrompt,\n    a.reportFormat\n  );\n  const claudeCmd = claudeCommand(env, config.caveman);\n  let cmd: string[] = [...claudeCmd];\n  cmd.push(...modelFlagArgs(headless, model, Boolean(parsed.callerModel)));\n  const slimArgs = headless ? slimProfileArgs(logDir, wid, parsed.rest, env) : [];\n  cmd.push(...chromeArgs, ...mcpArgs, ...toolArgs, ...toolsFlag, ...slimArgs, ...restArgs);\n  if (headless) {\n    cmd.push(\"--output-format\", \"stream-json\", \"--verbose\", \"--input-format\", \"stream-json\");\n  }\n  // ONE flag: contract (unless the caller brought their own), placement, caller text\n  cmd.push(\n    ...appendSystemPromptArgs([\n      ...(headless && !parsed.callerSystemPrompt ? [workerContractPrompt(a.className, a.reportFormat)] : []),\n      ...(addendum ? [addendum.text] : []),\n      ...(slimArgs.length ? [WORKER_BRIEF] : []),\n      ...(preamble ? [preambleBlock(preamble)] : []),\n      ...(headless\n        ? [worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) : inPlaceSystemPrompt()]\n        : []),\n      ...(headless && limit ? [deadlinePromptLine(limit)] : []),\n      ...parsed.callerSystemPrompts,\n    ])\n  );\n  cmd = ensureToolSearch(cmd);\n\n  // Dry run: the argv audit, no status/ledger/pane, no spawn.\n  if (a.printCmd) {\n    console.log(\n      JSON.stringify({\n        cmd,\n        cwd: worktree?.dir ?? cwd,\n        browser: noBrowser ? \"off\" : \"on\",\n        ...(limit ? { deadline: new Date(limit.deadlineAt).toISOString(), maxMinutes: limit.minutes } : { limit: \"none\" }),\n      })\n    );\n    return 0;\n  }\n\n  let status: WorkerStatus = {\n    id: wid,\n    pid: process.pid,\n    ...parentInfo(),\n    label,\n    cwd,\n    term,\n    provider: target.providerName,\n    model,\n    state: \"running\",\n    started: nowStamp(),\n    updated: nowStamp(),\n    turns: 0,\n    tools: 0,\n    last: headless ? \"starting\" : \"interactive\",\n    rc: null,\n    secs: null,\n    // interactive runs ARE the chat pane, not a spawned subagent of it\n    origin: headless ? \"spawn\" : \"chat\",\n    outputFormat: parsed.outputFormat,\n    ...(a.route ? { route: a.route } : {}),\n    ...(target.via === \"auto\" && target.switchReason ? { switchReason: target.switchReason } : {}),\n    ...(a.fallThroughOf ? { fallThroughOf: a.fallThroughOf } : {}),\n    ...(headless ? { reportFormat: a.reportFormat } : {}),\n    ...(trailerApplied ? { promptTrailer: true } : {}),\n    ...(preamble ? { preamble: { path: preamble.path, hash: preamble.hash } } : {}),\n    ...(addendum ? { contractAddendum: { path: addendum.path, sha: addendum.sha } } : {}),\n    ...(session ? { session } : {}),\n    ...(spawnerSession ? { spawnerSession } : {}),\n    // no window seed: contextWindow comes from the init event only, and the\n    // meter stays hidden until one is announced (never a guessed default)\n    ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n    ...(worktree\n      ? {\n          worktreeDir: worktree.dir,\n          branch: worktree.branch,\n          worktreeBase: worktree.base,\n          worktreeSnapshot: worktree.snapshot,\n        }\n      : {}),\n    ...(adopted ? { resumedFrom: a.resumeOf } : {}),\n    ...(a.specPath ? { spec: a.specPath } : {}),\n    ...(limit ? { deadlineAt: limit.deadlineAt, maxMinutes: limit.minutes } : {}),\n  };\n  saveStatus(logDir, status);\n  if (adopted && a.resumeOf) markResumed(logDir, a.resumeOf, wid);\n  a.onWorkerStart?.(wid);\n  const ledger = ledgerPath(logDir);\n  appendLedger(ledger, \"WORKER-START\", {\n    id: wid,\n    provider: target.providerName,\n    mode: headless ? \"headless\" : \"interactive\",\n    model,\n    cwd,\n    label,\n    ...(a.capability ? { capability: a.capability } : {}),\n    ...(a.specPath ? { spec: a.specPath } : {}),\n    ...(target.switchReason ? { switch: target.switchReason } : {}),\n  });\n\n  // Follow pane: headless only (interactive runs ARE the chat pane). Verified\n  // above when the gate applies; best effort otherwise.\n  if (paneVerified) status.pane = { opened: true };\n  else if (headless) void announcePane(logDir, config, wid, term, noPane, ledger, status);\n\n  const t0 = Date.now();\n  const proc = spawn(cmd[0], cmd.slice(1), {\n    env,\n    cwd: worktree?.dir ?? cwd,\n    stdio: headless ? [\"pipe\", \"pipe\", \"inherit\"] : \"inherit\",\n  });\n\n  // --- stdin lifecycle (2i): prompt in, socket forwards, close 2 s after result\n  let operatorInFlight = 0;\n  let closeTimer: NodeJS.Timeout | null = null;\n  const armStdinClose = () => {\n    if (closeTimer) clearTimeout(closeTimer);\n    closeTimer = setTimeout(() => {\n      if (operatorInFlight === 0) {\n        try {\n          proc.stdin?.end();\n        } catch {\n          /* already closed */\n        }\n      }\n    }, 2_000);\n  };\n\n  let eventsFd: number | null = null;\n  const writeEvent = (obj: Record<string, unknown>): void => {\n    if (eventsFd === null) return;\n    try {\n      writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n    } catch {\n      // a full disk must not take the worker transcript's process down\n    }\n  };\n\n  const operatorServer = headless\n    ? createOperatorServer(logDir, wid, async (text) => {\n        const stdin = proc.stdin;\n        if (!stdin || !stdin.writable || stdin.writableEnded) {\n          return \"the worker's stdin is already closed (its turn ended) — use: pai worker resume\";\n        }\n        operatorInFlight += 1;\n        if (closeTimer) {\n          clearTimeout(closeTimer);\n          closeTimer = null;\n        }\n        // a handoff delivery's mirror carries a flag: the viewer shows the\n        // inbox ◆ line instead, never both\n        writeEvent({ type: \"operator\", text, handoff: isHandoffMessage(text) });\n        return new Promise<void | string>((resolve) => {\n          try {\n            stdin.write(stdinUserMessage(operatorUserText(text)) + \"\\n\", (e) =>\n              resolve(e ? `write to the worker failed: ${e.message}` : undefined)\n            );\n          } catch (e) {\n            resolve(`write to the worker failed: ${(e as Error).message}`);\n          }\n        });\n      })\n    : null;\n\n  let killed = false;\n  const stopDeadline = limit ? armDeadline(proc, limit, () => fireDeadline(logDir, status, ledger, limit)) : null;\n  const cleanup = () => {\n    stopDeadline?.();\n    if (closeTimer) clearTimeout(closeTimer);\n    operatorServer?.close();\n  };\n  const onSignal = (sig: string) => {\n    killed = true;\n    status.state = \"killed\";\n    status.rc = 143;\n    status.secs = Math.floor((Date.now() - t0) / 1000);\n    const end = recordSignalEnd(logDir, status, sig);\n    status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : \"\"})`;\n    saveStatus(logDir, status);\n    appendLedger(ledger, \"WORKER-END\", {\n      id: wid,\n      provider: target.providerName,\n      mode: \"headless\",\n      model,\n      rc: 143,\n      secs: status.secs,\n      killed: 1,\n      ...endFields(end),\n      note: end.note,\n      label,\n    });\n    // a killed worktree run leaves nothing to merge — drop its worktree and\n    // branch too, or every kill strands them for hand-pruning (2026-09-18)\n    if (worktree) {\n      salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);\n      try {\n        recordWorktree(logDir, status, worktree, false);\n      } catch {\n        /* best effort: the status already says killed */\n      }\n    }\n    cleanup();\n    try {\n      proc.kill();\n    } catch {\n      /* already gone */\n    }\n    process.exit(143);\n  };\n  process.once(\"SIGTERM\", () => onSignal(\"SIGTERM\"));\n  process.once(\"SIGINT\", () => onSignal(\"SIGINT\"));\n  process.once(\"SIGHUP\", () => onSignal(\"SIGHUP\"));\n\n  // Holder for the last result event + its parsed report: assigned inside the\n  // readline callback below, read after the await.\n  const ctx: {\n    resultEvent: StreamEvent | null;\n    resultReport: WorkerReport | null;\n  } = {\n    resultEvent: null,\n    resultReport: null,\n  };\n  // every Bash call + outcome, and the untracked files present at spawn: the\n  // end-of-run proof check (checkProof) and stray-file listing read these\n  const bashRuns: BashRun[] = [];\n  const untrackedAtStart = new Set(headless ? untrackedPaths(worktree?.dir ?? cwd) : []);\n\n  if (headless) {\n    // the first user message carries the prompt (the -p value was stripped)\n    if (promptText !== null) {\n      try {\n        proc.stdin!.write(stdinUserMessage(promptText) + \"\\n\");\n      } catch {\n        /* child died instantly; the close handler reports it */\n      }\n    }\n    eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n    writeEvent({ type: \"preamble\", text: preambleLine(preamble), ...(preamble ? { hash: preamble.hash } : {}) });\n    if (addendum) writeEvent({ type: \"contract-addendum\", text: `${addendum.path} sha ${addendum.sha}`, sha: addendum.sha });\n    const rl = createInterface({ input: proc.stdout! });\n    const toolUses = new Map<string, { name: string; input: unknown }>();\n    rl.on(\"line\", (line) => {\n      if (parsed.outputFormat === \"stream-json\") {\n        process.stdout.write(line + \"\\n\");\n      }\n      if (!line.startsWith(\"{\")) return;\n      let e: StreamEvent;\n      try {\n        e = JSON.parse(line) as StreamEvent;\n      } catch {\n        return;\n      }\n      writeEvent(e as Record<string, unknown>);\n      if (e.type === \"system\" && e.subtype === \"init\") {\n        if (e.session_id) status.claudeSession = e.session_id;\n        adoptInitModel(status, e);\n        const cw =\n          initContextWindow(e) ?? target.provider.contextWindow ?? certainContextWindow(status.model);\n        if (cw) status.contextWindow = cw;\n        saveStatus(logDir, status);\n      } else if (isCompactBoundary(e)) {\n        resetContextTokensOnCompact(status, e);\n        saveStatus(logDir, status);\n      } else if (e.type === \"assistant\") {\n        status.turns += 1;\n        bumpContextTokens(status, usageContextTokens(e.message?.usage));\n        status.contextUsed = usageContextUsed(e.message?.usage) ?? status.contextUsed;\n        for (const block of e.message?.content ?? []) {\n          if (block.type === \"tool_use\") {\n            status.tools += 1;\n            if (block.id) toolUses.set(block.id, { name: block.name ?? \"?\", input: block.input });\n            status.last = describeTool(block.name ?? \"?\", block.input);\n            // a long single sleep is worth seeing while it runs (supervision's\n            // \"sleeping\" kind) — set when issued, cleared when it completes\n            if (block.name === \"Bash\") status.sleepSec = parseSleepSecs(bashCommand(block.input));\n          } else if (block.type === \"text\" && (block.text ?? \"\").trim()) {\n            status.last = \"says: \" + shortText(block.text, 70);\n          }\n        }\n        saveStatus(logDir, status);\n      } else if (e.type === \"user\") {\n        // tool results arrive as user events: each one advances (or resets)\n        // the consecutive-failure counter supervision's thrashing detector\n        // reads, and retires a pending sleep observation. Plain user turns\n        // (operator input) have no tool_result blocks and change nothing.\n        const blocks = e.message?.content;\n        if (Array.isArray(blocks)) {\n          let sawResult = false;\n          for (const block of blocks) {\n            if (block.type !== \"tool_result\") continue;\n            sawResult = true;\n            status.consecFails = toolResultFailed(block) ? (status.consecFails ?? 0) + 1 : 0;\n            status.sleepSec = null;\n            recordDenial(status, block, toolResultText(block.content), toolUses, parsed.prompt, new Date().toISOString());\n            if (block.is_error === true) {\n              recordToolError(logDir, {\n                worker: wid,\n                provider: target.providerName,\n                label,\n                tool: toolUses.get(block.tool_use_id ?? \"\")?.name ?? \"?\",\n                text: toolResultText(block.content),\n                input: toolUses.get(block.tool_use_id ?? \"\")?.input,\n              });\n            }\n            const use = block.tool_use_id ? toolUses.get(block.tool_use_id) : undefined;\n            if (use?.name === \"Bash\") bashRuns.push({ command: bashCommand(use.input), failed: toolResultFailed(block) });\n          }\n          if (sawResult) saveStatus(logDir, status);\n        }\n      } else if (e.type === \"result\") {\n        const tokens = usageContextTokens(e.usage);\n        // a compact result legitimately restarts the context lower\n        if (e.is_compact) status.contextTokens = tokens ?? status.contextTokens;\n        else bumpContextTokens(status, tokens);\n        const report = parseWorkerReport(e.result ?? \"\");\n        if (report?.notes) status.last = shortText(report.notes, 90);\n        else if (e.result) status.last = shortText(e.result, 90);\n        saveStatus(logDir, status);\n        operatorInFlight = 0;\n        armStdinClose();\n        ctx.resultEvent = e;\n        ctx.resultReport = report;\n      }\n    });\n  }\n\n  const rc = await new Promise<number>((resolve, reject) => {\n    proc.on(\"error\", reject);\n    proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n  });\n  if (eventsFd !== null) closeSync(eventsFd);\n  cleanup();\n\n  const secs = Math.floor((Date.now() - t0) / 1000);\n  const resultEvent = ctx.resultEvent;\n  const empty = headless && rc === 0 && resultEvent !== null && isEmptyResponse(resultEvent.result, status.tools);\n  const ok = !empty && runSucceeded(headless, rc, resultEvent);\n  if (resultEvent) status.last = shortText(resultEvent.result ?? \"\", 90);\n  if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n  if (empty) {\n    status.last = \"harness: empty-response (model returned nothing)\";\n    recordToolError(logDir, {\n      worker: wid,\n      provider: target.providerName,\n      label,\n      tool: \"model\",\n      text: \"empty-response: model answered with zero usage and no tool call\",\n    });\n  }\n  if (headless && a.reportFormat === \"ag2\" && resultEvent?.result) {\n    const v = validateAg2(resultEvent.result);\n    status.reportValid = v.ok;\n    status.reportErrors = v.errors;\n    // one bounded re-ask, cap exactly one — see shouldRetryReport\n    if (shouldRetryReport(v, a.noReportRetry ?? false, Boolean(status.claudeSession))) {\n      status.reportRetried = true;\n      saveStatus(logDir, status);\n      appendLedger(ledger, \"WORKER-REPORT-RETRY\", { id: wid, reason: v.errors.join(\"; \") || \"invalid AG2 message\" });\n      try {\n        const reaskText = await reaskAg2Report({\n          env,\n          cwd: worktree?.dir ?? cwd,\n          model,\n          callerPinnedModel: Boolean(parsed.callerModel),\n          chromeArgs,\n          mcpArgs,\n          toolArgs,\n          toolsFlag,\n          slimArgs,\n          sessionId: status.claudeSession!,\n          claudeCmd,\n        });\n        const v2 = validateAg2(reaskText);\n        status.reportValid = v2.ok;\n        status.reportErrors = v2.errors;\n        if (v2.ok) {\n          resultEvent.result = reaskText;\n          ctx.resultReport = parseWorkerReport(reaskText);\n          if (ctx.resultReport?.notes) status.last = shortText(ctx.resultReport.notes, 90);\n        }\n      } catch {\n        // the re-ask is best effort; the original invalid report stands\n      }\n    }\n  }\n  if (headless && a.reportFormat === \"ag2\" && resultEvent?.result) {\n    const stray = newUntracked(worktree?.dir ?? cwd, untrackedAtStart);\n    const chk = checkProof(resultEvent.result, bashRuns, stray);\n    if (chk.rewritten || stray.length) {\n      resultEvent.result = chk.text;\n      ctx.resultReport = parseWorkerReport(chk.text);\n      if (ctx.resultReport) ctx.resultReport.untracked = stray.length ? stray : undefined;\n    }\n    if (chk.rewritten) {\n      status.reportValid = false;\n      status.last = shortText(`claimed proof not run or failed: ${chk.proof}`, 90);\n      appendLedger(ledger, \"REPORT-PROOF-UNVERIFIED\", { id: wid, proof: chk.proof });\n    }\n    if (stray.length) process.stderr.write(`pai worker: warning: ${stray.length} untracked file(s) left by the worker: ${stray.join(\", \")}\\n`);\n  }\n  if (ctx.resultReport && status.denials?.length) ctx.resultReport.denials = status.denials;\n  if (headless && ctx.resultReport?.changed?.length) {\n    const verify = verifyReportChanges(ctx.resultReport.changed, worktree?.dir ?? cwd, t0);\n    if (!verify.ok) {\n      status.reportValid = false;\n      const note = verifyFailNote(verify);\n      status.last = shortText(note, 90);\n      ctx.resultReport.notes = ctx.resultReport.notes ? `${ctx.resultReport.notes} — ${note}` : note;\n      appendLedger(ledger, \"REPORT-UNVERIFIED\", {\n        id: wid,\n        failures: verify.failures.length,\n        paths: verify.failures.map((f) => f.path).slice(0, 5).join(\",\"),\n      });\n    }\n  }\n  return finaliseRun({\n    logDir,\n    ledger,\n    status,\n    ok,\n    rc,\n    secs,\n    end: {\n      provider: target.providerName,\n      mode: headless ? \"headless\" : \"interactive\",\n      model,\n      turns: status.turns,\n      tools: status.tools,\n      label,\n    },\n    report: ctx.resultReport,\n    fallbackText: resultEvent?.result,\n    worktree,\n    timedOut: status.timedOut ? limit?.minutes : undefined,\n    beforeHandoff: async (st, rep) => {\n      // Fall-through: a local run that failed its own report is repeated once on the cloud.\n      const why = headless ? fallThroughReason(a.route, st, rep) : null;\n      if (why && a.route) {\n        appendLedger(ledger, \"WORKER-FALLTHROUGH\", { id: wid, reason: why });\n        process.stderr.write(`pai worker: ${wid} on ${target.providerName} ended badly (${why}) — re-running on the cloud\\n`);\n        return runWorker({\n          providerFlag: ANTHROPIC_NATIVE,\n          label: `${label} (fall-through)`,\n          noPane: a.noPane,\n          mcpFlag: a.mcpFlag,\n          cwd: a.cwd,\n          specPath: a.specPath,\n          parent: a.parent,\n          stage: a.stage,\n          claudeArgs: a.claudeArgs,\n          quiet: a.quiet,\n          worktreeFlag: a.worktreeFlag,\n          className: a.className,\n          _route: { policy: a.route.policy, reason: `fall-through (${why})`, local: false },\n          _fallThroughOf: wid,\n          onWorkerStart: (id2) => {\n            st.fallThroughTo = id2;\n            saveStatus(logDir, st);\n            a.onWorkerStart?.(id2);\n          },\n        });\n      }\n      if (headless && !a.quiet) {\n        printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n      }\n\n    // Empty response: one automatic re-launch of the same spec in the same cwd.\n    if (empty && !a.emptyRetried) {\n      appendLedger(ledger, \"WORKER-EMPTY-RETRY\", { id: wid, provider: target.providerName });\n      return runWorker(\n        relaunchOptions(a.opts, {\n          providerFlag: target.providerName,\n          onWorkerStart: a.onWorkerStart,\n          _emptyRetried: true,\n        })\n      );\n    }\n\n    // Quota reroute: only auto-routed runs, dead before the first tool call.\n    const resultText = resultEvent?.result ?? \"\";\n    if (\n      !ok &&\n      headless &&\n      a.target.via === \"auto\" &&\n      config.routing.retryOnQuota &&\n      status.turns <= 1 &&\n      status.tools === 0 &&\n      isQuotaFailure(resultText)\n    ) {\n      setCooldown(logDir, target.providerName, config.routing.cooldownMinutes);\n      const next = nextAutoProvider(config, logDir, target.providerName);\n      if (next && a.reroutes < autoOrder(config).length) {\n        appendLedger(ledger, \"WORKER-REROUTE\", {\n          from: target.providerName,\n          to: next,\n          reason: \"quota\",\n        });\n        return runWorker(\n          relaunchOptions(a.opts, {\n            providerFlag: next,\n            modelFlag: undefined,\n            onWorkerStart: a.onWorkerStart,\n            _reroutes: a.reroutes + 1,\n          })\n        );\n      }\n    }\n    },\n  });\n}\n\n/**\n * Options for a relaunch (empty-response retry, quota reroute): the original\n * run's options, minus the preset id (the relaunch is a new worker), with the\n * given changes applied. Nothing is copied field by field, so a new RunOptions\n * field is carried over by default.\n */\nexport function relaunchOptions(orig: RunOptions, changes: Partial<RunOptions>): RunOptions {\n  return { ...orig, id: undefined, ...changes };\n}\n\n/** The worktree fields printResult adds to a json payload, when there is one. */\nexport function worktreeExtras(s: WorkerStatus): Record<string, unknown> | undefined {\n  return s.branch ? { branch: s.branch, commits: s.commits ?? 0 } : undefined;\n}\n\nexport interface Ag2ReaskArgs {\n  env: NodeJS.ProcessEnv;\n  cwd: string;\n  model: string;\n  callerPinnedModel: boolean;\n  chromeArgs: string[];\n  mcpArgs: string[];\n  toolArgs: string[];\n  toolsFlag: string[];\n  /** Slim-profile args of the original launch, so the resumed turn loads the same setup. */\n  slimArgs?: string[];\n  sessionId: string;\n  timeoutMs?: number;\n  /** Argv head of the original launch (claudeCommand); defaults to plain claude. */\n  claudeCmd?: string[];\n}\n\n/**\n * The one bounded re-ask, cap exactly one: `claude --resume <session>` with\n * AG2_REASK_TEXT, single-shot `--output-format json` (one turn, no streaming\n * plumbing needed). Same env/model/mcp/tool flags as the original launch —\n * only --resume and the fixed re-ask prompt are new — so the resumed turn\n * sees the same grants the worker ran with.\n */\nexport async function reaskAg2Report(a: Ag2ReaskArgs): Promise<string> {\n  let cmd: string[] = [...(a.claudeCmd ?? [\"claude\"])];\n  cmd.push(...modelFlagArgs(true, a.model, a.callerPinnedModel));\n  cmd.push(...a.chromeArgs, ...a.mcpArgs, ...a.toolArgs, ...a.toolsFlag, ...(a.slimArgs ?? []));\n  cmd.push(\"--resume\", a.sessionId, \"-p\", AG2_REASK_TEXT, \"--output-format\", \"json\");\n  cmd = ensureToolSearch(cmd);\n  const proc = spawn(cmd[0], cmd.slice(1), { env: a.env, cwd: a.cwd, stdio: [\"ignore\", \"pipe\", \"inherit\"] });\n  const timer = setTimeout(() => {\n    try {\n      proc.kill(\"SIGKILL\");\n    } catch {\n      /* already gone */\n    }\n  }, a.timeoutMs ?? 60_000);\n  let out = \"\";\n  proc.stdout.on(\"data\", (chunk: Buffer) => {\n    out += chunk.toString(\"utf8\");\n  });\n  await new Promise<void>((resolve) => {\n    proc.on(\"error\", () => resolve());\n    proc.on(\"close\", () => resolve());\n  });\n  clearTimeout(timer);\n  return resultFromOutput(out);\n}\n\n// ---------------------------------------------------------------------------\n// codex engine (2d)\n// ---------------------------------------------------------------------------\n\ninterface CodexArgs extends Omit<ExecuteArgs, \"reroutes\" | \"mcpFlag\" | \"opts\"> {}\n\nasync function executeCodexRun(a: CodexArgs): Promise<number> {\n  const { config, logDir, target, model, label, parsed, noPane } = a;\n  if (!parsed.headless || parsed.prompt === null) {\n    throw new Error(\n      `provider \"${target.providerName}\" (engine codex) supports headless runs only: ` +\n        `pass the task with -p '<prompt>'`\n    );\n  }\n  const env = buildCodexEnv(target.provider);\n  const wid = a.id ?? newWorkerId();\n  const cwd = a.cwd ?? process.cwd();\n  const term = (await resolveHostSession()).term;\n  const paneVerified = await requireVisiblePane(logDir, config, wid, term, noPane, false, ledgerPath(logDir));\n  const session = resolveSession(term);\n  const spawnerSession = resolveSpawnerSession(logDir, cwd);\n\n  let worktree: WorktreeInfo | null = null;\n  if (worktreeWanted(a.worktreeFlag, { cwd, allowedTools: parsed.allowedTools })) {\n    try {\n      worktree = addWorktree(logDir, wid, cwd);\n    } catch (e) {\n      const why = (e as Error).message;\n      process.stderr.write(`pai worker: no worktree (${why}) — running in place\\n`);\n      appendLedger(ledgerPath(logDir), \"WORKER-NOTE\", { id: wid, note: `no worktree: ${why}` });\n    }\n  }\n  env.PAI_WORKER_ID = wid;\n  // codex takes instructions through the prompt, not a system prompt flag\n  const prompt =\n    (a.limit ? deadlinePromptLine(a.limit) + \"\\n\\n\" : \"\") +\n    (worktree ? worktreeSystemPrompt(wid, worktree.branch, worktree.dir, worktree.snapshot) + \"\\n\\n\" : \"\") +\n    parsed.prompt;\n\n  let status: WorkerStatus = {\n    id: wid,\n    pid: process.pid,\n    ...parentInfo(),\n    label,\n    cwd,\n    term,\n    provider: target.providerName,\n    model,\n    state: \"running\",\n    started: nowStamp(),\n    updated: nowStamp(),\n    turns: 0,\n    tools: 0,\n    last: \"starting\",\n    rc: null,\n    secs: null,\n    origin: \"spawn\",\n    outputFormat: parsed.outputFormat,\n    // codex takes instructions through the prompt, not the AG2 contract flag\n    reportFormat: \"json\",\n    ...(session ? { session } : {}),\n    ...(spawnerSession ? { spawnerSession } : {}),\n    // codex has no init event of its own: the synthetic one below announces\n    // an explicitly configured window (never a guessed default)\n    ...(target.provider.contextWindow ? { contextWindow: target.provider.contextWindow } : {}),\n    ...(a.parent ? { parent: a.parent, stage: a.stage } : {}),\n    ...(worktree\n      ? {\n          worktreeDir: worktree.dir,\n          branch: worktree.branch,\n          worktreeBase: worktree.base,\n          worktreeSnapshot: worktree.snapshot,\n        }\n      : {}),\n    ...(a.specPath ? { spec: a.specPath } : {}),\n    ...(a.limit ? { deadlineAt: a.limit.deadlineAt, maxMinutes: a.limit.minutes } : {}),\n  };\n  saveStatus(logDir, status);\n  a.onWorkerStart?.(wid);\n  const ledger = ledgerPath(logDir);\n  appendLedger(ledger, \"WORKER-START\", {\n    id: wid,\n    provider: target.providerName,\n    mode: \"headless\",\n    engine: \"codex\",\n    model,\n    cwd,\n    label,\n    ...(a.capability ? { capability: a.capability } : {}),\n  });\n  const dropped = codexDroppedFlags(a.claudeArgs);\n  if (dropped.length) {\n    appendLedger(ledger, \"WORKER-NOTE\", {\n      id: wid,\n      note: `dropped for codex: ${dropped.join(\", \")}`,\n    });\n  }\n\n  if (paneVerified) status.pane = { opened: true };\n  else void announcePane(logDir, config, wid, term, noPane, ledger, status);\n\n  const t0 = Date.now();\n  const proc = spawn(\"codex\", buildCodexArgs(prompt, parsed.callerModel ? undefined : model), {\n    env,\n    cwd: worktree?.dir ?? cwd,\n    stdio: [\"ignore\", \"pipe\", \"inherit\"],\n  });\n\n  const fold = emptyCodexResult();\n  const eventsFd = openSync(eventsPath(logDir, wid), \"a\");\n  const writeEvent = (obj: Record<string, unknown>) => {\n    try {\n      writeSync(eventsFd, JSON.stringify({ ...obj, _ts: isoStamp() }) + \"\\n\");\n    } catch {\n      // best effort transcript\n    }\n  };\n  writeEvent({\n    type: \"system\",\n    subtype: \"init\",\n    model,\n    cwd,\n    ...(target.provider.contextWindow ? { context_window: target.provider.contextWindow } : {}),\n  });\n\n  let killed = false;\n  const stopDeadline = a.limit ? armDeadline(proc, a.limit, () => fireDeadline(logDir, status, ledger, a.limit!)) : null;\n  process.once(\"SIGTERM\", onCodexSignal(\"SIGTERM\"));\n  process.once(\"SIGINT\", onCodexSignal(\"SIGINT\"));\n  process.once(\"SIGHUP\", onCodexSignal(\"SIGHUP\"));\n  function onCodexSignal(sig: string) {\n    return () => {\n      killed = true;\n      status.state = \"killed\";\n      status.rc = 143;\n      status.secs = Math.floor((Date.now() - t0) / 1000);\n      const end = recordSignalEnd(logDir, status, sig);\n      status.last = `killed by signal ${sig} (${end.reason}${end.by ? ` by ${end.by}` : \"\"})`;\n      saveStatus(logDir, status);\n      appendLedger(ledger, \"WORKER-END\", {\n        id: wid,\n        provider: target.providerName,\n        mode: \"headless\",\n        model,\n        rc: 143,\n        secs: status.secs,\n        killed: 1,\n        ...endFields(end),\n        note: end.note,\n        label,\n      });\n      // same as the claude path: a killed run's worktree and branch go now\n      if (worktree) {\n        salvageOnExit(ledger, wid, label, worktree.dir, SIGNAL_SALVAGE_MS);\n        try {\n          recordWorktree(logDir, status, worktree, false);\n        } catch {\n          /* best effort: the status already says killed */\n        }\n      }\n      try {\n        proc.kill();\n      } catch {\n        /* already gone */\n      }\n      process.exit(143);\n    };\n  }\n\n  const rl = createInterface({ input: proc.stdout! });\n  rl.on(\"line\", (line) => {\n    if (parsed.outputFormat === \"stream-json\") process.stdout.write(line + \"\\n\");\n    const parsedLine = parseCodexLine(line);\n    if (parsedLine === null) return;\n    foldCodexLine(parsedLine, fold);\n    if (fold.threadId && !status.claudeSession) status.claudeSession = fold.threadId;\n    status.turns = fold.turns;\n    status.tools = fold.tools;\n    if (fold.last) status.last = shortText(fold.last, 90);\n    bumpContextTokens(status, fold.contextTokens);\n    saveStatus(logDir, status);\n    for (const ev of fold.events.splice(0)) writeEvent(ev);\n  });\n\n  const rc = await new Promise<number>((resolve, reject) => {\n    proc.on(\"error\", reject);\n    proc.on(\"close\", (code) => resolve(code ?? (killed ? 143 : 1)));\n  });\n  stopDeadline?.();\n  closeSync(eventsFd);\n\n  const secs = Math.floor((Date.now() - t0) / 1000);\n  const finalText = fold.finalText ?? \"\";\n  const report = parseWorkerReport(finalText);\n  const resultEvent: StreamEvent = {\n    type: \"result\",\n    result: finalText,\n    is_error: fold.isError || rc !== 0,\n    num_turns: fold.turns,\n    duration_ms: secs * 1000,\n  };\n  writeEvent(resultEvent);\n\n  const ok = rc === 0 && !fold.isError;\n  status.last = shortText(report?.notes ?? finalText, 90) || (ok ? \"done\" : \"failed\");\n  if (report?.changed?.length) {\n    const verify = verifyReportChanges(report.changed, worktree?.dir ?? cwd, t0);\n    if (!verify.ok) {\n      status.reportValid = false;\n      const note = verifyFailNote(verify);\n      status.last = shortText(note, 90);\n      report.notes = report.notes ? `${report.notes} — ${note}` : note;\n      appendLedger(ledger, \"REPORT-UNVERIFIED\", {\n        id: wid,\n        failures: verify.failures.length,\n        paths: verify.failures.map((f) => f.path).slice(0, 5).join(\",\"),\n      });\n    }\n  }\n  return finaliseRun({\n    logDir,\n    ledger,\n    status,\n    ok,\n    rc,\n    secs,\n    end: {\n      provider: target.providerName,\n      mode: \"headless\",\n      engine: \"codex\",\n      model,\n      turns: status.turns,\n      tools: status.tools,\n      label,\n    },\n    report,\n    fallbackText: finalText,\n    worktree,\n    timedOut: status.timedOut ? a.limit?.minutes : undefined,\n    beforeHandoff: (st, rep) => {\n      if (!a.quiet) printResult(parsed.outputFormat, resultEvent, rc, logDir, wid, rep, worktreeExtras(st));\n    },\n  });\n}\n\n// ---------------------------------------------------------------------------\n// result printing\n// ---------------------------------------------------------------------------\n\nexport function printResult(\n  fmt: \"text\" | \"json\" | \"stream-json\",\n  resultEvent: StreamEvent | null,\n  rc: number,\n  logDir: string,\n  wid: string,\n  report?: WorkerReport | null,\n  extras?: Record<string, unknown>\n): void {\n  if (fmt === \"stream-json\") return; // already mirrored live\n  if (fmt === \"json\") {\n    const payload = report\n      ? { ...(resultEvent ?? { is_error: true, result: \"no result event\", rc }), report, ...extras }\n      : { ...(resultEvent ?? { is_error: true, result: \"no result event\", rc }), ...extras };\n    console.log(JSON.stringify(payload));\n    return;\n  }\n  if (resultEvent) {\n    console.log(resultEvent.result ?? \"\");\n  } else {\n    process.stderr.write(\n      `pai worker: run produced no result (rc=${rc}); see ${eventsPath(logDir, wid)}\\n`\n    );\n  }\n}\n\n// ---------------------------------------------------------------------------\n// providers test: the 90-second pong probe\n// ---------------------------------------------------------------------------\n\nexport interface ProviderTestResult {\n  provider: string;\n  model: string;\n  latencyMs: number;\n  result: string;\n  ok: boolean;\n  /** Set when the probe could not run (e.g. \"codex not installed\"). */\n  skipped?: string;\n}\n\n/**\n * Run a one-word pong probe through the provider (headless, no pane) and\n * report provider, model, latency and the reply. ok is false unless the reply\n * was exactly \"pong\" (case-insensitive, whitespace-trimmed).\n */\nexport async function testProvider(\n  providerName: string,\n  provider: WorkerProvider,\n  logDir: string,\n  timeoutMs = 90_000,\n  caveman = false\n): Promise<ProviderTestResult> {\n  assertProviderRunnable(providerName, provider);\n  const model = provider.models.default;\n\n  if (provider.engine === \"codex\") {\n    const skipped = codexInstalled() ? undefined : \"codex not installed\";\n    return {\n      provider: providerName,\n      model,\n      latencyMs: 0,\n      result: skipped ?? \"codex engine: pong probe through codex exec not implemented\",\n      ok: false,\n      ...(skipped ? { skipped } : {}),\n    };\n  }\n\n  let proxyUrl: string | undefined;\n  if (provider.protocol === \"openai\") {\n    const base = await ensureProxyRunning(DEFAULT_PROXY_PORT, logDir);\n    proxyUrl = `${base}/${providerName}`;\n  }\n  const env = buildRunEnv(provider, true, proxyUrl, providerName);\n  const [bin, ...head] = claudeCommand(env, caveman);\n  const t0 = Date.now();\n  const proc = spawn(\n    bin,\n    [\n      ...head,\n      \"--model\", model,\n      \"--strict-mcp-config\", \"--mcp-config\", ensureNoMcpConfig(logDir),\n      \"-p\", \"Reply with exactly one word: pong\",\n      \"--output-format\", \"json\",\n    ],\n    { env, stdio: [\"ignore\", \"pipe\", \"inherit\"] }\n  );\n  const timer = setTimeout(() => {\n    try { proc.kill(\"SIGKILL\"); } catch { /* already gone */ }\n  }, timeoutMs);\n\n  let out = \"\";\n  proc.stdout.on(\"data\", (chunk: Buffer) => {\n    out += chunk.toString(\"utf8\");\n  });\n  const rc = await new Promise<number>((resolve) => {\n    proc.on(\"error\", () => resolve(1));\n    proc.on(\"close\", (code) => resolve(code ?? 1));\n  });\n  clearTimeout(timer);\n\n  return {\n    provider: providerName,\n    model,\n    latencyMs: Date.now() - t0,\n    result: resultFromOutput(out),\n    ok: rc === 0 && resultFromOutput(out).trim().toLowerCase() === \"pong\",\n  };\n}\n\n/**\n * Pull the reply out of claude's stdout. With --verbose, `--output-format\n * json` dumps a JSON array of stream events and the reply sits in the last\n * \"result\" event; without it, stdout is the single result object. Accept\n * either shape, plus a bare-text fallback for error output.\n */\nexport function resultFromOutput(out: string): string {\n  const trimmed = out.trim();\n  if (!trimmed) return \"\";\n  const parse = (s: string): unknown => {\n    try {\n      return JSON.parse(s);\n    } catch {\n      return undefined;\n    }\n  };\n  const fromValue = (v: unknown): string | null => {\n    if (Array.isArray(v)) {\n      for (let i = v.length - 1; i >= 0; i--) {\n        const r = fromValue(v[i]);\n        if (r !== null) return r;\n      }\n      return null;\n    }\n    if (typeof v === \"object\" && v !== null) {\n      const o = v as StreamEvent;\n      if (o.type === \"result\" && typeof o.result === \"string\") return o.result;\n    }\n    return null;\n  };\n  const direct = fromValue(parse(trimmed));\n  if (direct !== null) return direct;\n  const lines = trimmed.split(\"\\n\");\n  for (let i = lines.length - 1; i >= 0; i--) {\n    const r = fromValue(parse(lines[i]));\n    if (r !== null) return r;\n  }\n  return trimmed.slice(0, 200);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,oBAAoB,KAAK,SAAS,EAAE,aAAa,qBAAqB;;;;;AAMnF,SAAgB,OAAO,MAAsB;AAC3C,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,OAAO,KAAK,MAAM,KAAK,EAAE,CAAC;CAChC,MAAM,QAAQ,KAAK,MAAM,EAAE,CAAC,MAAM,IAAI;AACtC,KAAI,KAAK,WAAW,IAAI,IAAI,MAAM,WAAW,KAAK,MAAM,OAAO,MAAM,QAAQ,KAAK,EAAE,CAAC,CACnF,QAAO;AAET,QAAO;;;AAIT,SAAgB,cAAc,MAAyB,QAAQ,KAAa;AAC1E,QAAO,OAAO,IAAI,oBAAoB,GAAG;;;AAI3C,SAAgB,UAAU,MAAsB;AAC9C,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,KAAK,MAAM,IAAI,CAAC,KAAK,IAAI;;;;;;;;AAclC,SAAgB,eACd,MACA,eAAuB,mBACC;CACxB,MAAM,OAAO,UAAU,KAAK;AAC5B,KAAI,CAAC,KAAM,QAAO;CAClB,IAAI;AACJ,KAAI;AACF,MAAI,CAAC,WAAW,aAAa,CAAE,QAAO;AACtC,UAAQ,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;SAChD;AACN,SAAO;;CAET,MAAM,OAAO,MAAM;AACnB,KAAI,OAAO,SAAS,YAAY,CAAC,KAAM,QAAO;AAC9C,QAAO;EAAE,IAAI;EAAM;EAAM;;;;;;;AAQ3B,SAAgB,cAAc,QAAsB,MAAuB;AACzE,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,OAAO,UAAU,KAAK;AAC5B,KAAI,OAAO,SAAS,MAAM,KAAM,QAAO,OAAO,QAAQ,OAAO;AAC7D,QAAO,OAAO,OAAO,KAAK,KAAK,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK;;;;;;AAOlE,SAAgB,SAAS,MAAsB;CAC7C,MAAM,UAAU,eAAe,KAAK;AACpC,KAAI,QAAS,QAAO,QAAQ;AAC5B,QAAO,eAAe,IAAI,UAAU,KAAK;;;AAI3C,SAAgB,WAAW,QAAwD;AACjF,QAAO,OAAO,SAAS,OAAO,IAAI,OAAO,QAAQ,KAAK,KAAK;;;;;;;;;AAU7D,SAAgB,iBACd,GACA,eAAuB,mBACf;AACR,KAAI,EAAE,SAAS,KAAM,QAAO,EAAE,QAAQ;CACtC,MAAM,OAAO,EAAE,SAAS,MAAM,UAAU,EAAE,QAAQ,GAAG;AACrD,KAAI,MAAM;EACR,MAAM,WAAW,eAAe,MAAM,aAAa;AACnD,MAAI,UAAU,KAAM,QAAO,SAAS;;CAEtC,MAAM,MAAM,OAAO,EAAE,QAAQ,GAAG;AAChC,QAAO,MAAM,OAAO,QAAQ;;;AA4B9B,MAAa,yBAAyB,KAAK;;AAG3C,MAAM,uBAAuB,KAAK;AAElC,SAAgB,eAAe,QAAwB;AACrD,QAAO,KAAK,QAAQ,0BAA0B;;;;;;;AAuDhD,SAAgB,sBACd,QACA,KACA,MAAyB,QAAQ,KACjC,MAAc,KAAK,KAAK,EACT;CACf,MAAM,WAAW,IAAI;AACrB,KAAI,UAAU;EACZ,MAAM,YAAY,WAAW,QAAQ,SAAS,EAAE;AAChD,MAAI,UAAW,QAAO;;CAExB,MAAM,OAAO,eAAe,OAAO;AACnC,KAAI,CAAC,WAAW,KAAK,IAAI,CAAC,IAAK,QAAO;AACtC,KAAI;EACF,MAAM,QAAS,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC,CAAqC;AAC1F,MAAI,SAAS,MAAM,WAAW,MAAM,MAAM,KAAK,uBAAwB,QAAO,MAAM;SAC9E;AAGR,QAAO;;;;;;;;;AAUT,SAAgB,sBACd,QACA,eACA,MAAc,KAAK,KAAK,EACT;CACf,MAAM,OAAO,eAAe,OAAO;AACnC,KAAI,CAAC,WAAW,KAAK,IAAI,CAAC,cAAe,QAAO;CAChD,IAAI;AACJ,KAAI;AACF,QAAM,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;SACtC;AACN,SAAO;;CAET,IAAI,OAA+B;AACnC,MAAK,MAAM,KAAK,OAAO,OAAO,IAAI,EAAE;AAClC,MAAI,EAAE,YAAY,iBAAiB,CAAC,EAAE,KAAM;AAE5C,MAAI,MAAM,EAAE,MAAM,qBAAsB;AACxC,MAAI,CAAC,QAAQ,EAAE,KAAK,KAAK,GAAI,QAAO;;CAEtC,MAAM,OAAO,MAAM;AACnB,QAAO,OAAO,UAAU,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1NlC,MAAa,eAAe;;AAG5B,SAAgB,qBAA6B;AAC3C,QAAO,QAAQ,IAAI,sBAAsB,KACvC,SAAS,EACT,WACA,uBACA,UACA,mBACA,kBACD;;AAWH,MAAM,eAAe;;;;;;;;;;;;;AAgBrB,MAAM,WAAW;AAOjB,MAAM,YAAY;;;;;;;AAWlB,MAAa,sBAAsB,GAAG,aAAa;iBAClC,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA0CpB,UAAU;;;;;;;;AAWhB,MAAM,kBAAkB,GAAG,aAAa;;;;;;;;AAYxC,MAAa,uBAAuB,GAAG,aAAa;;;;;;;;;AAYpD,MAAa,eAAe,GAAG,aAAa;;;;;;;;;;;;;;;AAoB5C,SAAS,UAAU,QAAgB,MAA6D;AAC9F,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,OAAO,MAAM,aAAa;GAAC;GAAM;GAAc;GAAK,GAAG;GAAK,EAAE,EAAE,OAAO;GAAC;GAAQ;GAAQ;GAAO,EAAE,CAAC;EACxG,IAAI,MAAM;EACV,IAAI,MAAM;AACV,OAAK,OAAO,GAAG,SAAS,MAAe,OAAO,EAAE,SAAS,OAAO,CAAE;AAClE,OAAK,OAAO,GAAG,SAAS,MAAe,OAAO,EAAE,SAAS,OAAO,CAAE;AAClE,OAAK,GAAG,SAAS,OAAO;AACxB,OAAK,GAAG,eAAe,QAAQ;GAAE,QAAQ;GAAK,QAAQ;GAAK,CAAC,CAAC;AAC7D,OAAK,MAAM,MAAM,OAAO;AACxB,OAAK,MAAM,KAAK;GAChB;;AAGJ,SAAS,SAAS,QAAwB;AACxC,KAAI;AACF,SAAO,aAAa,MAAM,CAAC,QAAQ,OAAO,EAAE,EAAE,UAAU,QAAQ,CAAC;SAC3D;AACN,SAAO;;;;AAKX,SAAS,aAA0B;CACjC,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,QAAQ,SAAS,gBAAgB,CAAC,MAAM,KAAK,EAAE;EACxD,MAAM,QAAQ,KAAK,MAAM,CAAC,MAAM,OAAO,EAAE;AACzC,MAAI,MAAM,SAAS,KAAK,MAAM,OAAO,KAAM;AAC3C,MAAI,CAAC,0DAA0D,KAAK,MAAM,GAAG,CAAE;AAC/E,MAAI,MAAM,GAAG,WAAW,OAAO,CAAE,MAAK,IAAI,QAAQ,MAAM,KAAK;;AAE/D,QAAO;;;AAIT,SAAgB,eAAe,KAAsB;CACnD,MAAM,MAAM,IAAI,OAAO,iDAAiD,IAAI,QAAQ,uBAAuB,OAAO,CAAC,KAAK;AACxH,QAAO,SAAS,WAAW,CAAC,MAAM,KAAK,CAAC,MAAM,OAAO,IAAI,KAAK,GAAG,CAAC;;;AAkBpE,SAAS,QAAQ,GAAoB;CACnC,MAAM,MAAM;AAEZ,UADe,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,IAAI,QAAQ,SAAS,OAAO,KACvE,IAAI,WAAW,OAAO,EAAE,EAAE,MAAM;;;;;;;;;;AAWpD,SAAgB,eAAe,WAA8B;CAC3D,IAAI,YAAwB,EAAE;AAC9B,KAAI;EACF,MAAM,MAAM,aACV,UACA;GAAC;GAAY;GAAiB;GAAQ;GAAM;GAAK;GAAU,EAC3D;GAAE,UAAU;GAAQ,SAAS;GAAQ,CACtC;EACD,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,MAAI,MAAM,QAAQ,OAAO,CAAE,aAAY;UAChC,GAAG;AACV,SAAO;GAAE,WAAW,EAAE;GAAE,aAAa;GAAM,OAAO,6BAA6B,QAAQ,EAAE;GAAI;;CAE/F,IAAI,cAA6B;AACjC,KAAI;AAMF,gBALY,aACV,UACA;GAAC;GAAY;GAAyB;GAAO;GAAM;GAAK;GAAU,EAClE;GAAE,UAAU;GAAQ,SAAS;GAAQ,CACtC,CACiB,MAAM,IAAI;SACtB;AAGR,QAAO;EAAE;EAAW;EAAa,OAAO;EAAM;;;AAIhD,SAAgB,aAAwB;CACtC,IAAI,MAAqB;AACzB,KAAI;AACF,QAAM,YAAY,KAAK,QAAQ,EAAE,aAAa,CAAC;EAC/C,MAAM,QAAQ,KAAK,KAAK,eAAe;AACvC,eAAa,YAAY;GAAC;GAAU;GAAyB;GAAM,EAAE;GACnE,UAAU;GACV,SAAS;GACV,CAAC;AACF,SAAO,eAAe,MAAM;UACrB,GAAG;AACV,SAAO;GAAE,WAAW,EAAE;GAAE,aAAa;GAAM,OAAO,oBAAoB,QAAQ,EAAE;GAAI;WAC5E;AACR,MAAI,IACF,KAAI;AACF,UAAO,KAAK;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;UACvC;;;;AAQd,SAAS,aAAa,MAAiB,OAAgC;AACrE,QAAO,KAAK,UAAU,MAAM,MAAM,EAAE,SAAS,MAAM,IAAI;;;AAIzD,SAAS,oBAAoB,MAAkC;AAC7D,QAAO,KAAK,cACR,KAAK,UAAU,MAAM,MAAM,EAAE,SAAS,KAAK,YAAY,IAAI,OAC3D;;;AAIN,SAAgB,kBAAmC;AACjD,QAAO,oBAAoB,YAAY,CAAC;;;;;;;AAQ1C,SAAgB,SAAS,MAA0B,UAA0B;CAC3E,MAAM,OAAO,QAAQ,IAAI,YAAY,IAAI;AACzC,KAAI,MAAM,GAAG;EACX,MAAM,SAAS,KAAM,MAAM,GAAG,IAAI;AAClC,MAAI,CAAC,OAAO,MAAM,WAAW,KAAM,MAAM,MAAM,EAAE,CAAC,CAAC,CAAE,QAAO,GAAG,OAAO,GAAG;;AAE3E,QAAO,iBAAiB;;;AAI1B,SAAgB,oBACd,QACA,UACA,OAAwB,YAClB;CACN,MAAM,UAAmC;EACvC,MAAM;EACN,MAAM;EACN,eAAe,SAAS,SAAS,gBAAgB,SAAS;EAC1D,yBAAyB;EAC1B;AAGD,KAAI,QAAQ,QAAQ,aAAa,MAAM,EAAE,OAAO,KAAK,CACnD,SAAQ,iCAAiC,OAAO;CAElD,MAAM,OAAO,oBAAoB;CACjC,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CACzD,MAAM,MAAM,GAAG,KAAK;AACpB,eAAc,KAAK,KAAK,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO;AACnF,YAAW,KAAK,KAAK;;AAGvB,IAAI,cAAc;;;;;;;;;;AAWlB,eAAsB,cACpB,UACA,OAAwB,YACP;CACjB,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,oBAAoB,MAAM;CACzC,MAAM,OAAO,oBAAoB;CACjC,MAAM,UAAU,WAAW,KAAK;AAChC,KAAI,CAAC,QAAS,qBAAoB,QAAQ,gBAAgB,MAAM;AAChE,KAAI,CAAC,QAAQ;AACX,MAAI,CAAC,aAAa;AAChB,iBAAc;GACd,MAAM,SAAS,MAAM,SAAS;GAC9B,MAAM,OAAO,UACT,WAAW,KAAK,UAChB,SAAS,KAAK,sBAAsB,SAAS;AACjD,WAAQ,OAAO,MACb,yDAAyD,OAAO,MAAM,KAAK,IAC5E;;AAEH,MAAI,MAAM,MAAO,QAAO;;AAE1B,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;AAE3B,MAAI,aAAa,MAAM,EAAE,aAAa,CAAE,QAAO;AAC/C,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;;AAE9C,SAAQ,OAAO,MAAM,4BAA4B,aAAa,wCAAwC;AACtG,QAAO;;;;;;;;AAqBT,SAAS,UAAU,MAAc,OAA4B;AAC3D,KAAI;EACF,MAAM,IAAI,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAChD,UAAQ,MAAM,QAAQ,EAAE,GAAG,IAAI,CAAC,EAAE,EAC/B,QAAQ,MAAsB,CAAC,CAAC,KAAK,OAAQ,EAAgB,YAAY,YAAY,OAAQ,EAAgB,WAAW,SAAS,CACjI,KAAK,OAAO;GAAE,GAAG;GAAG;GAAO,EAAE;SAC1B;AACN,SAAO,EAAE;;;;AAKb,SAAgB,aAAa,QAAgB,OAA6B;CACxE,MAAM,OAAO,SAAS,OAAO;CAC7B,MAAM,MAAmB,EAAE;CAC3B,IAAI,QAAkB,EAAE;AACxB,KAAI;AACF,UAAQ,YAAY,KAAK;SACnB;AACN,SAAO;;AAET,MAAK,MAAM,QAAQ,MAAM,MAAM,EAAE;EAC/B,MAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,MAAI,KAAK,SAAS,QAAQ,EAAE;GAC1B,MAAM,KAAK,KAAK,MAAM,GAAG,GAAG;AAC5B,OAAI,CAAC,SAAS,OAAO,MAAO,KAAI,KAAK,GAAG,UAAU,MAAM,GAAG,CAAC;aACnD,CAAC,SAAS,SAAS,MAC5B,KAAI;AACF,QAAK,MAAM,KAAK,YAAY,KAAK,CAAE,KAAI,EAAE,SAAS,QAAQ,CAAE,KAAI,KAAK,GAAG,UAAU,KAAK,MAAM,EAAE,EAAE,KAAK,CAAC;UACjG;;AAKZ,QAAO,IAAI,MAAM,GAAG,MAAM,EAAE,OAAO,cAAc,EAAE,OAAO,CAAC;;AAG7D,SAAS,UAAU,QAAgB,GAAsB;AACvD,QAAO,KAAK,SAAS,OAAO,EAAE,EAAE,SAAS,IAAI,GAAG,EAAE,OAAO,OAAO;;AAGlE,SAAS,UAAU,QAAgB,GAAoB;CACrD,MAAM,OAAO,UAAU,QAAQ,EAAE;AACjC,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;CAC7C,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,IAAI;AACnC,eAAc,KAAK,KAAK,UAAU;EAAE,SAAS,EAAE;EAAS,QAAQ,EAAE;EAAQ,QAAQ,EAAE;EAAQ,EAAE,MAAM,EAAE,EAAE,OAAO;AAC/G,YAAW,KAAK,KAAK;;;AAIvB,SAAS,UAAU,QAAgB,GAAoB;CACrD,MAAM,SAAS,KAAK,SAAS,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO;AACxD,QAAO,UAAU,QAAQ,EAAE,EAAE,EAAE,OAAO,MAAM,CAAC;AAC7C,KAAI,WAAW,OAAO,EAAE;EACtB,MAAM,OAAO,UAAU,QAAQ,EAAE,SAAS,GAAG,CAAC,QAAQ,MAAM,EAAE,YAAY,EAAE,QAAQ;AACpF,gBAAc,QAAQ,KAAK,UAAU,KAAK,KAAK,EAAE,OAAO,IAAI,GAAG,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO;;;AAUhG,MAAM,uBAAuB;;;;;;;;;;;;;;;;;AA6B7B,eAAsB,kBACpB,MAAuE,WAC9C;AAEzB,SADU,MAAM,IAAI,sBAAsB,EAAE,CAAC,EACpC,OACN,MAAM,KAAK,CACX,OAAO,QAAQ,CACf,KAAK,MAAM;EACV,MAAM,CAAC,IAAI,KAAK,QAAQ,KAAK,GAAG,QAAQ,EAAE,MAAM,IAAK;AACrD,SAAO;GAAE;GAAI;GAAK,QAAQ,OAAO,OAAO,IAAI;GAAW,KAAK,OAAO,IAAI,IAAI;GAAW,MAAM,KAAK,KAAK,IAAK,IAAI;GAAW;GAC1H;;AAIN,MAAM,qBAAqB,GAAG,aAAa;;;;;;;;;AAW3C,MAAa,mBAAmB,GAAG,aAAa;;;;;;;;;;MAU1C,UAAU;;;;AAehB,MAAa,cAAuB;CAClC,MAAM,YAAY;AAChB,MAAI,QAAQ,aAAa,SAAU,QAAO,EAAE;EAC5C,MAAM,WAAW,MAAM,mBAAmB;EAG1C,MAAM,2BAAW,IAAI,KAAqB;AAC1C,OAAK,MAAM,QAAQ,SAAS,gBAAgB,CAAC,MAAM,KAAK,EAAE;GACxD,MAAM,CAAC,KAAK,GAAG,QAAQ,KAAK,MAAM,CAAC,MAAM,MAAM;AAC/C,OAAI,OAAO,QAAQ,KAAM,UAAS,IAAI,QAAQ,OAAO,KAAK,KAAK,IAAI,CAAC;;AAEtE,SAAO,SAAS,KAAK,OAAO;GAAE,IAAI,EAAE;GAAI,MAAM,GAAG,EAAE,QAAQ,GAAG,GAAG,SAAS,IAAI,EAAE,IAAI,IAAI,KAAK,MAAM;GAAE,EAAE;;CAEzG,MAAM,WAAW,IAAI;AAEnB,UADU,MAAM,UAAU,oBAAoB,CAAC,GAAG,CAAC,EAC1C,OAAO,MAAM,KAAK;;CAE7B,MAAM,eAAe,KAAK;AACxB,MAAI,IAAI,OAAQ,OAAM,UAAU,kBAAkB,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;;CAEpE,WAAW;AACT,MAAI;AAKF,UAJY,aAAa,QAAQ;IAAC;IAAc;IAAM;IAAM;IAAoC,EAAE;IAChG,UAAU;IACV,OAAO;KAAC;KAAU;KAAQ;KAAS;IACpC,CAAC,CACS,MAAM,KAAK,CAAC,OAAO,QAAQ,CAAC,KAAK,OAAO;IAAE,IAAI,EAAE,MAAM,IAAK,CAAC;IAAI,SAAS,EAAE,MAAM,IAAK,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI;IAAE,EAAE;UAClH;AACN,UAAO,EAAE;;;CAGb,UAAU,IAAI;AACZ,MAAI;AACF,gBAAa,QAAQ;IAAC;IAAa;IAAM;IAAG,EAAE,EAAE,OAAO,UAAU,CAAC;AAClE,UAAO;UACD;AACN,UAAO;;;CAGZ;AAED,MAAM,YAAY;;AAGlB,eAAsB,mBAAmB,QAAgB,KAAa,MAAe,aAA+B;CAClH,IAAI,SAAS;AACb,MAAK,MAAM,KAAK,aAAa,OAAO,CAAC,QAAQ,MAAM,EAAE,WAAW,IAAI,EAAE;AACpE,MAAI;AACF,OAAI,MAAM,IAAI,WAAW,EAAE,QAAQ,CAAE,UAAS;UACxC;AAGR,YAAU,QAAQ,EAAE;;AAEtB,QAAO;;;;;;;AAQT,eAAsB,aAAa,MAAyB,QAAQ,KAAK,MAAe,aAA+B;CACrH,MAAM,MAAM,UAAU,IAAI,oBAAoB,GAAG;AACjD,KAAI,CAAC,IAAK,QAAO;AACjB,QAAO,IAAI,WAAW,IAAI,CAAC,YAAY,MAAM;;;;;;;;AAc/C,eAAsB,gBACpB,QACA,OACA,QACA,MAAe,aACY;CAC3B,MAAM,MAAwB;EAAE,QAAQ,EAAE;EAAE,MAAM,EAAE;EAAE;CACtD,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,SAAS,OAAO,SAA2B,IAAY,QAAgB,UAAqC;EAChH,MAAM,MAAM,GAAG,QAAQ,GAAG;AAC1B,MAAI,KAAK,IAAI,IAAI,CAAE;AACnB,OAAK,IAAI,IAAI;AACb,MAAI,CAAC,MAAM,OAAO,EAAE;AAClB,OAAI,KAAK,KAAK,GAAG,OAAO,GAAG,IAAI,YAAY;AAC3C;;AAEF,MAAI,CAAC,QAAQ;GACX,MAAM,KAAK,YAAY,SAAS,IAAI,UAAU,GAAG,GAAG,MAAM,IAAI,WAAW,GAAG,CAAC,YAAY,MAAM;AAC/F,OAAI,MAAO,WAAU,QAAQ,MAAM;AACnC,OAAI,CAAC,MAAM,CAAC,MAAO;;AAErB,MAAI,OAAO,KAAK,GAAG,OAAO,GAAG,MAAM,SAAS,eAAe,KAAK;;CAGlE,MAAM,WAAW,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,CAAC;CACtD,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,GAAG,CAAC;AAC/C,MAAK,MAAM,KAAK,aAAa,OAAO,EAAE;AAEpC,MAAI,SAAS,UAAU,CAAC,KAAK,IAAI,EAAE,QAAQ,EAAE;AAC3C,OAAI,CAAC,OAAQ,WAAU,QAAQ,EAAE;AACjC;;AAEF,QAAM,OAAO,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE;;AAE/C,MAAK,MAAM,KAAK,UAAU;EACxB,MAAM,IAAI,UAAU,KAAK,EAAE,KAAK;AAChC,MAAI,EAAG,OAAM,OAAO,SAAS,EAAE,IAAI,EAAE,GAAG;;AAE1C,MAAK,MAAM,KAAK,IAAI,UAAU,EAAE;EAC9B,MAAM,IAAI,UAAU,KAAK,EAAE,QAAQ;AACnC,MAAI,EAAG,OAAM,OAAO,QAAQ,EAAE,IAAI,EAAE,GAAG;;AAGzC,KAAI,CAAC,UAAU,IAAI,OAAO,OACxB,OAAM,IAAI,iBAAiB,aAAa,OAAO,CAAC,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,YAAY,OAAU;AAE/F,QAAO;;;;;;;;;;;;AAiBT,SAAgB,cAAc,KAAqB;AACjD,QAAO,GAAG,QAAQ,SAAS,GAAG,iBAAiB,CAAC,iBAAiB;;;AAInE,SAAS,kBAA0B;CACjC,MAAM,QAAQ,QAAQ,KAAK,MAAM;AACjC,KAAI,MAAM,SAAS,MAAM,CAAE,QAAO;AAClC,QAAO;;AAiBT,MAAa,eAAyB;CACpC,KAAK,KAAK;AACR,MAAI;GAKF,MAAM,CAAC,MAAM,OAJD,aAAa,MAAM;IAAC;IAAM;IAAc;IAAM,OAAO,IAAI;IAAC,EAAE;IACtE,UAAU;IACV,KAAK;KAAE,GAAG,QAAQ;KAAK,QAAQ;KAAK;IACrC,CAAC,CAAC,MAAM,CACe,MAAM,MAAM;AACpC,UAAO,QAAQ,MAAM,CAAC,OAAO,KAAK,EAAE,IAAI,GAAG;UACrC;AACN,UAAO;;;CAGX,WAAW,MAAM;EACf,MAAM,oBAAI,IAAI,KAAqB;AACnC,MAAI,CAAC,KAAK,OAAQ,QAAO;EACzB,IAAI,MAAM;AACV,MAAI;AACF,SAAM,aAAa,MAAM;IAAC;IAAM;IAAiB;IAAM,KAAK,KAAK,IAAI;IAAC,EAAE;IACtE,UAAU;IACV,KAAK;KAAE,GAAG,QAAQ;KAAK,QAAQ;KAAK;IACrC,CAAC;WACK,GAAG;AACV,SAAO,EAA0B,UAAU;;AAE7C,OAAK,MAAM,KAAK,IAAI,MAAM,KAAK,EAAE;GAC/B,MAAM,IAAI,oBAAoB,KAAK,EAAE;AACrC,OAAI,EAAG,GAAE,IAAI,OAAO,EAAE,GAAG,EAAE,EAAE,GAAG;;AAElC,SAAO;;CAET,MAAM,eAAe;AACnB,MAAI,QAAQ,aAAa,SAAU,QAAO,EAAE;AAC5C,MAAI;AACF,UAAO,MAAM,mBAAmB;UAC1B;AACN,UAAO,EAAE;;;CAGd;;AAGD,SAAS,aAAa,MAAgB,OAAyB;CAC7D,MAAM,OAAiB,EAAE;AACzB,MAAK,IAAI,MAAM,OAAO,KAAK,SAAS,MAAM,MAAM,IAAK;AACnD,OAAK,KAAK,IAAI;EACd,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,MAAI,CAAC,IAAK;AACV,QAAM,IAAI;;AAEZ,QAAO;;;AAIT,SAAgB,YAAY,OAAiB,cAAc,QAAgB,QAAQ,KAAoB;CACrG,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,MAAM,GAAG,KAAK;EACtC,MAAM,MAAM,KAAK,KAAK,IAAI;AAC1B,MAAI,CAAC,IAAK,QAAO;EACjB,MAAM,CAAC,MAAM,OAAO;AACpB,MAAI,OAAO,QAAQ,QAAQ,QAAQ,IAAK,QAAO,IAAI,WAAW,QAAQ,GAAG,MAAM,QAAQ;AACvF,QAAM;;AAER,QAAO;;AAaT,MAAM,UAAU;;AAGhB,SAAS,WAAW,KAAiC;AACnD,QAAO,IAAI,OAAO,oBAAoB,QAAQ,IAAI,IAAI,CAAC,KAAK,IAAI,GAAG,MAAM,iDAAiD,KAAK,IAAI,GAAG;;;;;;;;;;;;;AAcxI,eAAsB,mBACpB,MAAyB,QAAQ,KACjC,OAAiB,cACjB,QAAgB,QAAQ,KACF;CACtB,MAAM,WAAW,MAAM,KAAK,cAAc;CAC1C,MAAM,UAAU,IAAI,oBAAoB;CACxC,MAAM,SAAS,UAAU,QAAQ;CACjC,MAAM,SAAS,GAAmB,SAA8B;EAAE,KAAK,EAAE;EAAI,MAAM,EAAE,OAAO,SAAS,UAAU,EAAE;EAAI;EAAK;CAC1H,MAAM,QAAkB,EAAE;CAE1B,MAAM,YAAY,aAAa,MAAM,MAAM;CAC3C,MAAM,UAAU,SAAS,SAAS,MAAO,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,EAAE,CAAE;CACrE,MAAM,OAAO,KAAK,WAAW,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC;CAEtE,MAAM,KAAK,IAAI,IAAI,UAAU;AAC7B,KAAI,OAAO,IAAI,WAAW,CAAE,IAAG,IAAI,OAAO,IAAI,WAAW,CAAC;CAC1D,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,OAAO,CAAC;AAChE,KAAI,MAAO,QAAO,MAAM,OAAO,oBAAoB;AACnD,OAAM,KAAK,+CAA+C,IAAI,cAAc,UAAU,iBAAiB;CAEvG,MAAM,MAAM,IAAI,0BAA0B,UAAU,KAAK,MAAM,WAAW,KAAK,IAAI,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,QAAQ;AAC3G,KAAI,KAAK;EACP,MAAM,QAAQ,IAAI,MAAM,GAAG,EAAE;EAC7B,MAAM,SAAS,IAAI,OAAO,iBAAiB,MAAM,GAAG,IAAI,OAAO,IAAI;EACnE,MAAM,OAAO,SAAS,QAAQ,MAAM,EAAE,UAAU,OAAO,KAAK,KAAK,IAAI,EAAE,OAAO,IAAI,GAAG,CAAC;AACtF,MAAI,KAAK,WAAW,EAAG,QAAO,MAAM,KAAK,IAAI,UAAU,QAAQ;EAC/D,MAAM,MAAM,KAAK,SAAS,cAAc,KAAK,OAAO,uBAAuB,MAAM,IAAI,KAAK,KAAK,MAAM,EAAE,GAAG,CAAC,KAAK,KAAK,KAAK,0BAA0B;AACpJ,MAAI,KAAK,OAAQ,QAAO;GAAE,KAAK;GAAI,MAAM;GAAI,KAAK;GAAQ,QAAQ,CAAC,GAAG,OAAO,WAAW,MAAM,CAAC,KAAK,KAAK;GAAE;AAC3G,QAAM,KAAK,WAAW,MAAM;OAE5B,OAAM,KAAK,mDAAmD;CAGhE,MAAM,MAAM,YAAY,MAAM,MAAM;CACpC,MAAM,QAAQ,MAAM,SAAS,MAAM,MAAM,EAAE,QAAQ,IAAI,GAAG;AAC1D,KAAI,MAAO,QAAO,MAAM,OAAO,MAAM;AACrC,OAAM,KAAK,MAAM,qBAAqB,IAAI,uBAAuB,uBAAuB;AAExF,KAAI,UAAU,SAAS,MAAM,MAAM,EAAE,OAAO,OAAO,CAAE,QAAO;EAAE,KAAK;EAAQ,MAAM;EAAS,KAAK;EAAO;AACtG,OAAM,KAAK,yBAAyB,UAAU,GAAG,QAAQ,gBAAgB,aAAa;AAGtF,QAAO;EAAE,KAAK;EAAI,MAAM;EAAI,KAAK;EAAQ,QAAQ,GADrC,SAAS,WAAW,IAAI,sCAAsC,iCAClB,IAAI,MAAM,KAAK,KAAK;EAAI;;;AAMlF,SAAgB,YACd,MAAyB,QAAQ,KACjC,WAAmB,QAAQ,UAC3B,WAAmB,IACN;AACb,KAAI,IAAI,KAAM,QAAO;AACrB,KAAI,aAAa,aAAa,IAAI,oBAAoB,UAAW,QAAO;AACxE,QAAO;;AAGT,MAAM,UAAU;AAEhB,SAAgB,cAAc,KAAsB;AAClD,QAAO,+CAA+C,MAAM,IAAI,QAAQ;;;AAI1E,SAAS,UAAU,KAAmB;CACpC,MAAM,SAAS,QAAQ,IAAI,YAAY,CAAC,MAAM,QAAQ,IAAI,UAAU,GAAG,EAAE;AACzE,KAAI;AACF,eAAa,QAAQ;GAAC;GAAgB;GAAM;GAAM;GAAM;GAAO,GAAG;GAAQ;GAAI,EAAE,EAAE,OAAO,QAAQ,CAAC;UAC3F,GAAG;EACV,MAAM,MAAM;AACZ,QAAM,IAAI,MAAM,+CAA+C,IAAI,QAAQ,UAAU,IAAI,IAAI,WAAW,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG;;;;AAKvI,SAAS,YAAY,QAAgB,KAAsB;AACzD,KAAI;EACF,MAAM,KAAK,KAAK,MAAM,aAAa,WAAW,QAAQ,IAAI,EAAE,OAAO,CAAC;AACpE,SAAO,CAAC,CAAC,GAAG,SAAS,GAAG,UAAU;SAC5B;AACN,SAAO;;;;AAKX,eAAe,kBAAkB,QAAgB,OAAe,MAAe,aAA4B;AACzG,MAAK,MAAM,KAAK,aAAa,QAAQ,MAAM,EAAE;AAC3C,MAAI,CAAC,YAAY,QAAQ,EAAE,OAAO,CAAE;AACpC,QAAM,IAAI,WAAW,EAAE,QAAQ,CAAC,YAAY,MAAM;AAClD,YAAU,QAAQ,EAAE;;;;AAWxB,eAAsB,kBACpB,QACA,QACA,KACA,MACyB;AACzB,KAAI,eAAe,IAAI,CAAE,QAAO;EAAE,SAAS,YAAY,IAAI;EAAgB,SAAS;EAAM;CAC1F,MAAM,UAAU,YAAY,QAAQ,KAAK,QAAQ,UAAU,KAAK;AAChE,KAAI,YAAY,OAAQ,QAAO;EAAE,SAAS,cAAc,IAAI;EAAE,SAAS;EAAM;AAC7E,KAAI,YAAY,QAAQ;AACtB,YAAU,cAAc,IAAI,CAAC;AAC7B,SAAO;GAAE,SAAS,mBAAmB;GAAO,SAAS;GAAM;;CAE7D,MAAM,MAAM,UAAU,KAAK;AAC3B,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,QAAQ;CAClC,MAAM,QAAQ,SAAS,KAAK;AAC5B,OAAM,kBAAkB,QAAQ,MAAM;CACtC,MAAM,MAAM,aAAa,QAAQ,MAAM;CACvC,MAAM,UAAU,MAAM,cAAc,OAAO,KAAK,SAAS;CACzD,MAAM,MAAM,cAAc,IAAI;CAG9B,MAAM,IAAI,MAAM,UAAU,qBAAqB;EAAC;EADlC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,EAAE,QAAQ,CAAC,KAAK,IAAI;EACJ;EAAK;EAAQ,CAAC;CAC1E,MAAM,MAAM,EAAE,OAAO,MAAM;AAC3B,KAAI,QAAQ,cAAc,QAAQ,aAChC,OAAM,IAAI,MACR,QAAQ,aACJ,sDACA,yCACL;CAEH,MAAM,MAAM,IAAI,QAAQ,IAAI;AAC5B,KAAI,MAAM,EACR,OAAM,IAAI,MACR,uCAAuC,EAAE,UAAU,KAAK,MAAM,GAAG,IAAI,GACtE;CAEH,MAAM,OAAO,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,QAAQ,CAAC;AAClE,MAAK,MAAM,KAAK,IAAK,KAAI,CAAC,KAAK,IAAI,EAAE,QAAQ,CAAE,WAAU,QAAQ,EAAE;AACnE,WAAU,QAAQ;EAChB,SAAS,IAAI,MAAM,MAAM,EAAE;EAC3B,QAAQ;EACR,yBAAQ,IAAI,MAAM,EAAC,aAAa,CAAC,QAAQ,KAAK,IAAI,CAAC,MAAM,GAAG,GAAG;EAC/D;EACD,CAAC;AACF,QAAO;EAAE,SAAS,mBAAmB;EAAO,SAAS,IAAI,MAAM,MAAM,EAAE;EAAE;;;;;;;AAQ3E,eAAe,iBAAiB,MAA+B;CAC7D,MAAM,MAAM,UAAU,KAAK;AAC3B,KAAI,CAAC,IAAK,QAAO;AACjB,KAAI;EACF,MAAM,IAAI,MAAM,UAAU,sBAAsB,CAAC,IAAI,CAAC;EACtD,MAAM,MAAM,EAAE,OAAO,MAAM;AAC3B,MAAI,OAAO,QAAQ,cAAc,QAAQ,aAAc,QAAO,kBAAkB;AAKhF,SAAO,mBAHL,QAAQ,aAAa,6BACnB,QAAQ,eAAe,wBACtB,EAAE,OAAO,MAAM,IAAI,aAAa,MAAM,GAAG,IAAI,CACpB;UACvB,GAAG;AACV,SAAO,8BAA8B,OAAQ,EAAY,WAAW,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC;;;;;;;;;AAUzF,eAAsB,mBAAmB,KAAa,UAAkB,MAA+B;CACrG,MAAM,QAAQ,CAAC,eAAe,IAAI,GAAG,YAAY,IAAI,SAAS,eAAe,MAAM;AACnF,KAAI,YAAY,QAAQ,KAAK,QAAQ,UAAU,KAAK,KAAK,QAAS,QAAO,MAAM,KAAK,KAAK;AACzF,OAAM,KAAK,MAAM,iBAAiB,KAAK,CAAC;CACxC,MAAM,OAAO,oBAAoB;AACjC,KAAI,WAAW,KAAK,EAAE;EACpB,IAAI,OAAO;AACX,MAAI;AAIF,UAHe,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC,CAGvC,WAAW,KAAK,kBAAkB;UAC1C;AAGR,QAAM,KAAK,iBAAiB,KAAK,WAAW;AAC5C,QAAM,KAAK,iBAAiB,OAAO;QAC9B;EACL,MAAM,SAAS,iBAAiB;AAChC,QAAM,KAAK,iBAAiB,KAAK,YAAY;AAC7C,QAAM,KAAK,+BAA+B,SAAS,SAAS,gBAAgB,SAAS,GAAG;;AAE1F,QAAO,MAAM,KAAK,KAAK;;;;;;;AAQzB,eAAsB,eACpB,QACA,SACA,MACA,WACiB;CAEjB,MAAM,UAAU,YAAY,QAAQ,KAAK,QAAQ,UAAU,KAAK;AAChE,KAAI,YAAY,OAAQ,QAAO,eAAe;AAC9C,KAAI,YAAY,QAAQ;AACtB,MAAI,UAAW,QAAO;AACtB,YAAU,GAAG,QAAQ,SAAS,GAAG,iBAAiB,CAAC,gBAAgB;AACnE,SAAO;;CAET,MAAM,MAAM,UAAU,KAAK;AAC3B,KAAI,CAAC,IAAK,OAAM,IAAI,MAAM,QAAQ;CAElC,MAAM,OADI,MAAM,UAAU,iBAAiB,CAAC,IAAI,CAAC,EACnC,OAAO,MAAM;AAC3B,KAAI,QAAQ,WAAY,OAAM,IAAI,MAAM,oDAAoD;AAC5F,KAAI,QAAQ,aAAc,OAAM,IAAI,MAAM,yCAAyC;AAGnF,KADgB,CAAC,GADD,IAAI,IAAI,IAAI,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,CAChD,CAAC,QAAQ,MAAM,YAAY,CAAC,IAAI,EAAE,CAAC,CACnD,SAAS,EAAG,QAAO;AAC/B,KAAI,UAAW,QAAO;CAGtB,MAAM,IAAI,MAAM,UAAU,cAAc,CAAC,KAD7B,GAAG,QAAQ,SAAS,GAAG,iBAAiB,CAAC,gBACH,CAAC;CACnD,MAAM,OAAO,EAAE,OAAO,MAAM;AAC5B,KAAI,SAAS,SAAU,QAAO;AAC9B,KAAI,SAAS,WAAY,OAAM,IAAI,MAAM,oDAAoD;AAC7F,OAAM,IAAI,MAAM,uCAAuC,EAAE,UAAU,MAAM,MAAM,GAAG,IAAI,GAAG;;;;;;;AAY3F,SAAgB,uBAA+B;AAC7C,QAAO,QAAQ,IAAI,wBAAwB,KAAK,SAAS,EAAE,WAAW,OAAO,cAAc;;;;;;;;;;AAW7F,MAAa,mBAAmB;AAEhC,SAAgB,gBAAgB,MAAyB,QAAQ,KAAK,OAAe,sBAAsB,EAAiB;AAC1H,KAAI,IAAI,eAAe,IAAK,QAAO;AACnC,KAAI,IAAI,sBAAsB,IAAK,QAAO,GAAG,iBAAiB;AAC9D,KAAI,WAAW,KAAK,CAAE,QAAO,0BAA0B;AACvD,QAAO;;AAMT,MAAa,qBAAqB,GAAG,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgClD,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;;AAGtB,SAAS,aAAa,KAAa,KAAsB;CACvD,MAAM,QAAQ,IAAI,QAAQ,YAAY,GAAG;AACzC,QAAO,SAAS,gBAAgB,CAC7B,MAAM,KAAK,CACX,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,GAAG,MAAM,GAAG,IAAI,IAAI,OAAO,iBAAiB,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC;;;;;;;;;AAUtG,eAAsB,WACpB,SACA,SACA,KACA,MAAuF,WACvF,UAAiD,cACjD,QAAQ,IACR,SAAS,KACQ;CACjB,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,OAAO,KAAK;EAC9B,MAAM,IAAI,MAAM,IAAI,oBAAoB,CAAC,SAAS,QAAQ,CAAC;EAC3D,IAAI;AACJ,MAAI;AACF,OAAI,KAAK,MAAM,EAAE,OAAO,MAAM,CAAC;UACzB;AACN,SAAM,IAAI,MAAM,6CAA6C,EAAE,UAAU,EAAE,UAAU,WAAW,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG;;AAEzH,MAAI,OAAO,EAAE,SAAS,SAAU,OAAM,IAAI,MAAM,6BAA6B,EAAE,OAAO;EACtF,MAAM,OAAO,OAAO,EAAE,KAAK;EAC3B,MAAM,OAAO,OAAO,EAAE,KAAK;EAC3B,MAAM,QAAQ,UAAU,EAAE,OAAO,IAAK,EAAE,OAAoB,KAAK,IAAI,CAAC,QAAQ,EAAE,IAAI,SAAS,KAAK,GAAG,KAAK,IAAI,EAAE;AAChH,MAAI,EAAE,iBAAiB,KAAM,OAAM,IAAI,MAAM,kDAAkD,MAAM,GAAG;AACxG,MAAI,EAAE,gBAAgB,KAAM,OAAM,IAAI,MAAM,8DAA8D,MAAM,GAAG;AACnH,MAAI,EAAE,WAAW,KAAM,OAAM,IAAI,MAAM,+CAA+C,MAAM,GAAG;AAC/F,MAAI,EAAE,aAAa,KAAM,OAAM,IAAI,MAAM,uDAAuD,MAAM,GAAG;AACzG,MAAI,OAAO,iBAAiB,OAAO,cACjC,OAAM,IAAI,MAAM,6CAA6C,KAAK,GAAG,KAAK,KAAK,cAAc,GAAG,cAAc,IAAI,MAAM,GAAG;AAE7H,MAAI,QAAQ,OAAO,EAAE,IAAI,EAAE,IAAI,CAAE,QAAO;AACxC,SAAO;AACP,QAAM,IAAI,SAAS,QAAQ,WAAW,KAAK,OAAO,CAAC;;AAErD,OAAM,IAAI,MAAM,6CAA6C,IAAI,iCAAiC,KAAK,GAAG;;;;;;;;AAS5G,eAAsB,iBACpB,QACA,QACA,KACA,MACA,MAAyB,QAAQ,KAChB;AACjB,KAAI,IAAI,MAAM;EACZ,MAAM,IAAI,MAAM,kBAAkB,QAAQ,QAAQ,KAAK,KAAK;AAC5D,MAAI,CAAC,EAAE,QAAQ,WAAW,cAAc,CAAE,OAAM,IAAI,MAAM,6BAA6B,EAAE,UAAU;AACnG,SAAO;;CAET,MAAM,SAAS,UAAU,IAAI,oBAAoB,GAAG;CACpD,MAAM,UAAU,UAAU,KAAK;AAC/B,KAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2GAA2G;AACxI,KAAI,WAAW,QACb,OAAM,IAAI,MAAM,0BAA0B,OAAO,sCAAsC,UAAU,iBAAiB,QAAQ,KAAK,KAAK;CAEtI,MAAM,IAAI,MAAM,kBAAkB,QAAQ,QAAQ,KAAK,KAAK;AAC5D,KAAI,CAAC,EAAE,QAAS,OAAM,IAAI,MAAM,6BAA6B,EAAE,UAAU;AACzE,KAAI;AACF,SAAO,GAAG,MAAM,WAAW,SAAS,EAAE,SAAS,IAAI,CAAC,kBAAkB,EAAE,QAAQ;UACzE,GAAG;AACV,QAAM,mBAAmB,QAAQ,IAAI,CAAC,YAAY,MAAM;AACxD,QAAM;;;;AAKV,SAAgB,sBAAsB,YAAqB,MAAyB,QAAQ,KAAW;CACrG,MAAM,OAAO,aAAa,cAAc,IAAI,wBAAwB,MAAM,0BAA0B;AACpG,KAAI,QAAQ,CAAC,gBAAgB,IAAI,CAC/B,OAAM,IAAI,MACR,GAAG,KAAK,kGAC2C,iBAAiB,wBAC7C,sBAAsB,CAAC,eAC/C;;;;;;;;;;;;;;;;;ACvsCL,SAAS,cAAc,GAAkC;AACvD,KAAI,CAAC,MAAM,QAAQ,EAAE,CAAE,QAAO;CAC9B,MAAM,MAAM,EAAE,QAAQ,MAAmB,OAAO,MAAM,SAAS;AAC/D,QAAO,IAAI,SAAS,MAAM;;;;;;;;AAS5B,eAAsB,oBACpB,KACA,UACqC;CACrC,MAAM,SAAS,QAAQ,IAAI;CAE3B,MAAM,OAAO,OADG,YAAa,MAAM,oBAAoB,EAC5B,6BAA6B,EAAE,iBAAiB,MAAM,CAAC;AAClF,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,OAAO,QAAQ,IAAI,UAAU;AACnC,MAAI,WAAW,QAAQ,CAAC,OAAO,WAAW,OAAO,IAAI,CAAE;AACvD,MAAI,CAAC,IAAI,eAAgB,QAAO;EAChC,IAAI;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,IAAI,eAAe;UACjC;AACN,UAAO;;EAET,MAAM,MAAM,cAAc,OAAO,IAAI;EACrC,MAAM,QAAQ,cAAc,OAAO,MAAM;AACzC,SAAO,OAAO,QAAQ;GAAE,MAAM,IAAI;GAAM;GAAK;GAAO,GAAG;;AAEzD,QAAO;;;;;;;AAQT,SAAgB,WAAW,QAAoC,YAA+B;AAC5F,KAAI,CAAC,OAAQ,QAAO,EAAE;CACtB,MAAM,QAAkB,EAAE;AAC1B,KAAI,OAAO,KAAK;EACd,MAAM,MAAM,OAAO,KAAK,eAAe,WAAW,CAAC;EACnD,MAAM,SAAS,IAAI,QAAQ,MAAM,OAAO,IAAK,SAAS,EAAE,CAAC;EACzD,MAAM,UAAU,IAAI,QAAQ,MAAM,CAAC,OAAO,IAAK,SAAS,EAAE,CAAC;AAC3D,QAAM,KACJ,kBAAkB,OAAO,KAAK,YAAY,OAAO,OAAO,MAAM,IAAI,OAAO,wBACxD,QAAQ,KAAK,KAAK,IAAI,SAAS,4CACjD;;AAEH,KAAI,OAAO,MACT,OAAM,KAAK,oBAAoB,OAAO,KAAK,IAAI,OAAO,MAAM,KAAK,IAAI,CAAC,kCAAkC;AAE1G,QAAO;;;;;;;;;;;;;;;;;;ACjCT,MAAa,gBAAgB,KAAK,SAAS,EAAE,WAAW,OAAO,qBAAqB;AACpF,MAAa,eAAe,KAAK,SAAS,EAAE,WAAW,OAAO,oBAAoB;;AAElF,MAAa,gBAAgB,KAAK,SAAS,EAAE,WAAW,OAAO,QAAQ,0BAA0B;;AAEjG,MAAa,wBAAwB,IAAI;;AAEzC,MAAa,cAAc,KAAK;AAQhC,SAAgB,aAAa,OAAe,eAA8C;AACxF,KAAI;EACF,MAAM,IAAI,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;EAChD,MAAM,MAAqC,EAAE;AAC7C,OAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAE,CACpC,KAAI,KAAK,MAAM,QAAQ,EAAE,QAAQ,IAAI,OAAO,EAAE,cAAc,SAAU,KAAI,KAAK;GAAE,SAAS,EAAE;GAAS,WAAW,EAAE;GAAW;AAE/H,SAAO;SACD;AACN,SAAO,EAAE;;;AAIb,SAAS,cAAc,MAAc,SAA8C;AACjF,KAAI;AACF,YAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,gBAAc,MAAM,KAAK,UAAU,SAAS,MAAM,EAAE,CAAC;SAC/C;;;;;;AASV,SAAgB,aACd,UACA,OACA,OACA,KACA,SAAiB,uBACW;AAC5B,KAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,UAAU,MAAM,MAAM,aAAa,OAAQ,QAAO;CAC/E,MAAM,MAAM,KAAK,OAAO,MAAM,MAAM,aAAa,IAAO;AACxD,QAAO;EAAE;EAAU,MAAM;EAAQ,SAAS,MAAM;EAAS,WAAW,MAAM;EAAW,OAAO,SAAS,IAAI,OAAO,MAAM;EAAI;;;AAI5H,IAAa,UAAb,MAAqB;CACnB,AAAQ,uBAAO,IAAI,KAAqB;CACxC,AAAQ,wBAAQ,IAAI,KAAqB;CAEzC,YACE,AAAiB,QACjB,AAAiB,QAAgB,aACjC;EAFiB;EACA;;;CAInB,SAAS,UAAkB,KAAqB;EAC9C,MAAM,IAAI,KAAK,KAAK,KAAK,KAAK,IAAI,SAAS,IAAI,KAAK,GAAG,KAAK,QAAQ,KAAK,OAAO;AAChF,OAAK,KAAK,IAAI,UAAU,EAAE;AAC1B,OAAK,MAAM,IAAI,UAAU,MAAM,IAAI,KAAK,OAAO;AAC/C,SAAO,IAAI,KAAK;;;CAIlB,UAAU,UAAkB,KAAqB;AAC/C,SAAO,KAAK,IAAI,IAAI,KAAK,MAAM,IAAI,SAAS,IAAI,KAAK,IAAI;;;CAI3D,MAAM,UAAwB;AAC5B,OAAK,KAAK,OAAO,SAAS;AAC1B,OAAK,MAAM,OAAO,SAAS;;;AAI/B,SAAS,KAAK,GAAY,MAA0D;AAClF,KAAI,MAAM,QAAQ,MAAM,UAAa,MAAM,GAAI,QAAO;AACtD,KAAI,SAAS,SAAU,SAAS,UAAa,OAAO,MAAM,YAAY,CAAC,gBAAgB,KAAK,EAAE,EAAG;EAC/F,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE,CAAC;AAC/B,SAAO,OAAO,SAAS,EAAE,GAAG,IAAI;;CAElC,MAAM,IAAI,OAAO,EAAE;AACnB,KAAI,CAAC,OAAO,SAAS,EAAE,CAAE,QAAO;AAChC,QAAO,SAAS,MAAM,IAAI,MAAO;;AAGnC,SAAS,GAAG,MAAc,OAAmC;CAC3D,MAAM,IAAI,UAAU,MAAM,CAAC,MAAM,KAAK,EAAE;EAAE;EAAO,UAAU;EAAQ,SAAS;EAAM,CAAC;AACnF,KAAI,EAAE,WAAW,EAAG,QAAO;CAC3B,MAAM,MAAM,EAAE,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC;AACxC,QAAO,QAAQ,UAAa,QAAQ,MAAM,QAAQ,SAAS,SAAY;;;AAIzE,SAAgB,gBAAgB,MAAc,KAAmC;CAC/E,MAAM,MAAqB,EAAE;AAC7B,MAAK,MAAM,KAAK,IAAI,SAAS;EAC3B,MAAM,MAAM,OAAO,GAAG,EAAE,SAAS,KAAK,CAAC;AACvC,MAAI,CAAC,OAAO,SAAS,IAAI,IAAI,GAAG,EAAE,SAAS,KAAK,KAAK,OAAW;EAChE,MAAM,QAAQ,EAAE,UAAU,KAAK,GAAG,EAAE,SAAS,KAAK,EAAE,EAAE,UAAU,GAAG;AACnE,MAAI,KAAK;GAAE,MAAM,EAAE;GAAM,SAAS,KAAK,MAAM,MAAM,GAAG,GAAG;GAAI,GAAI,QAAQ,EAAE,SAAS,OAAO,GAAG,EAAE;GAAG,CAAC;;AAEtG,QAAO;;;AAIT,SAAgB,oBAAoB,MAA6B;CAC/D,IAAI;AACJ,KAAI;AACF,MAAI,KAAK,MAAM,KAAK;SACd;AACN,SAAO,EAAE;;CAEX,MAAM,MAAqB,EAAE;AAC7B,MAAK,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,aAAa,KAAK,EAAE,CAAC,aAAa,KAAK,CAAC,EAAW;EAC7E,MAAM,IAAI,EAAE;AACZ,MAAI,CAAC,KAAK,OAAO,EAAE,gBAAgB,SAAU;EAC7C,MAAM,QAAQ,KAAK,EAAE,WAAW,MAAM;AACtC,MAAI,KAAK;GAAE;GAAM,SAAS,EAAE;GAAa,GAAI,QAAQ,EAAE,SAAS,OAAO,GAAG,EAAE;GAAG,CAAC;;AAElF,QAAO;;;AAIT,SAAgB,gBACd,MACA,UACA,MACA,KACe;CACf,MAAM,QAAgC;EAAE,MAAM,IAAI;EAAW,MAAM,IAAI;EAAY;CACnF,MAAM,MAAqB,EAAE;AAC7B,MAAK,MAAM,CAAC,MAAM,SAAS,OAAO,QAAQ,MAAM,EAAE;EAChD,MAAM,MAAM,KAAK;AACjB,MAAI,CAAC,IAAK;EACV,MAAM,QAAQ,KAAK,QAAQ,MAAM,EAAE,aAAa,YAAY,KAAK,MAAM,EAAE,GAAG,IAAI,MAAM,KAAK;EAC3F,MAAM,QAAQ,MAAM,QAAQ,GAAG,MAAM,KAAK,IAAI,GAAG,KAAK,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS;AAC7E,MAAI,KAAK;GACP;GACA,SAAS,KAAK,MAAO,MAAM,SAAS,MAAO,IAAK,GAAG;GACnD,GAAI,OAAO,SAAS,MAAM,GAAG,EAAE,SAAS,QAAQ,MAAM,GAAG,EAAE;GAC3D,WAAW;GACZ,CAAC;;AAEJ,QAAO;;AAGT,SAAgB,iBAAiB,OAAe,eAAuD;AACrG,KAAI;AACF,SAAO,WAAW,KAAK,GAAI,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC,GAA8C,EAAE;SAC3G;AACN,SAAO,EAAE;;;;AAKb,MAAa,sBAAsB;AAEnC,SAAgB,QAAQ,KAAU,SAAkD;AAClF,QAAO,IAAI,SAAS,SAAS,WAAW;EAEtC,MAAM,MADM,IAAI,aAAa,WAAW,QAAQ,MACjC,IAAI,KAAK;GAAE;GAAS,SAAS;GAAM,GAAG,QAAQ;GAC3D,IAAI,IAAI;AACR,OAAI,GAAG,SAAS,MAAO,KAAK,EAAG;AAC/B,OAAI,GAAG,aAAc,IAAI,eAAe,MAAM,QAAQ,EAAE,GAAG,uBAAO,IAAI,MAAM,QAAQ,IAAI,aAAa,CAAC,CAAE;IACxG;AACF,KAAG,GAAG,iBAAiB,GAAG,wBAAQ,IAAI,MAAM,UAAU,CAAC,CAAC;AACxD,KAAG,GAAG,SAAS,OAAO;GACtB;;AAGJ,SAAS,GAAG,KAAa,MAAiC;AACxD,QAAO,IAAI,SAAS,YAAY,SAAS,KAAK,MAAM,EAAE,SAAS,KAAM,GAAG,IAAI,QAAQ,QAAQ,OAAO,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;;AAGzH,eAAsB,iBAA8C;AAClE,MAAK,MAAM,QAAQ,CAAC,CAAC,MAAM,QAAQ,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC,EAAE;EACvD,MAAM,MAAM,MAAM,GAAG,YAAY;GAAC;GAAyB;GAAM;GAA2B,GAAG;GAAM;GAAK,CAAC;AAC3G,MAAI;GACF,MAAM,IAAK,KAAK,MAAM,IAAI,CAAkD,eAAe;AAC3F,OAAI,EAAG,QAAO;UACR;;;AAOZ,eAAe,gBAAgB,KAAa,aAAqB,UAA8D;CAC7H,MAAM,OAAO;EAAE,UAAU;EAAa,MAAM;EAAiB,WAAW;EAAK;AAC7E,KAAI;EACF,MAAM,QAAQ,MAAM,gBAAgB;AACpC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,6BAA6B;EAKzD,MAAM,UAAU,oBAJH,MAAM,QAAQ,IAAI,IAAI,oBAAoB,EAAE;GACvD,eAAe,UAAU;GACzB,kBAAkB;GACnB,CAAC,CACuC;AACzC,MAAI,QAAQ,OAAQ,QAAO;GAAE,GAAG;GAAM;GAAS;AAC/C,QAAM,IAAI,MAAM,+BAA+B;UACxC,GAAG;EACV,MAAM,QAAQ,aAAa,aAAa,UAAW,EAAY,SAAS,IAAI;AAC5E,MAAI,MAAO,QAAO;AAClB,MAAI;GACF,MAAM,IAAI,KAAK,MAAM,aAAa,aAAa,OAAO,CAAC;AACvD,OAAI,OAAO,EAAE,wBAAwB,YAAY,EAAE,QAAQ,MAAM,MAAO,EAAE,OAAO,IAAI,KACnF,QAAO;IAAE,GAAG;IAAM,SAAS,CAAC;KAAE,MAAM;KAAM,SAAS,EAAE;KAAqB,CAAC;IAAE,OAAO,GAAI,EAAY,QAAQ;IAA0B;UAElI;AAGR,SAAO;GAAE,GAAG;GAAM,SAAS,EAAE;GAAE,OAAQ,EAAY;GAAS;;;AAIhE,eAAe,mBAAmB,MAAc,GAAmB,KAAa,UAA8D;CAC5I,MAAM,OAAO;EAAE,UAAU;EAAM,MAAM;EAAiB,WAAW;EAAK;CACtE,MAAM,IAAI,EAAE;AACZ,KAAI;EACF,MAAM,MAAM,mBAAmB,EAAE;EACjC,MAAM,CAAC,IAAI,GAAG,QAAQ,EAAE,cAAc,yBAAyB,MAAM,OAAO;EAC5E,MAAM,SAAS,IAAI,KAAK,KAAK;EAC7B,MAAM,UAAkC,GAAG,GAAI,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,OAAO,IAAI,EAAE;EAE1G,MAAM,UAAU,gBADH,MAAM,QAAQ,IAAI,IAAI,EAAE,IAAI,EAAE,QAAQ,EACb,EAAE;AACxC,SAAO,QAAQ,SAAS;GAAE,GAAG;GAAM;GAAS,GAAG;GAAE,GAAG;GAAM;GAAS,OAAO;GAAqB;UACxF,GAAG;EACV,MAAM,MAAO,EAAY,QAAQ,QAAQ,iBAAiB,aAAa;AACvE,SAAO,aAAa,MAAM,UAAU,KAAK,IAAI,IAAI;GAAE,GAAG;GAAM,SAAS,EAAE;GAAE,OAAO;GAAK;;;AAIzF,SAAS,SAAS,MAAc,MAAgC;AAC9D,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,IAAI,IAAI,QAAQ;GAAE;GAAM;GAAM,SAAS;GAAK,CAAC;AACnD,IAAE,GAAG,kBAAkB,EAAE,SAAS,EAAE,QAAQ,KAAK,EAAE;AACnD,IAAE,GAAG,kBAAkB,EAAE,SAAS,EAAE,QAAQ,MAAM,EAAE;AACpD,IAAE,GAAG,eAAe,QAAQ,MAAM,CAAC;GACnC;;AAGJ,eAAe,YAAY,MAAc,GAAmB,MAAkB,KAAsC;CAClH,MAAM,IAAI,IAAI,IAAI,EAAE,QAAQ;CAC5B,MAAM,OAAO,OAAO,EAAE,KAAK,IAAI;CAC/B,MAAM,YAAY,MAAM,SAAS,MAAM,EAAE,SAAS;CAClD,MAAM,SAAS,YAAY,MAAM,aAAa,EAAE,GAAG;CACnD,MAAM,KAAK,aAAa,YAAY,QAAQ,OAAO,OAAO,EAAE,OAAO,CAAC,QAAQ,MAAmB,OAAO,MAAM,SAAS,CAAC;CACtH,IAAI;AACJ,KAAI,IAAI;EACN,MAAM,OAAO,MAAM,GAAG,QAAQ;GAAC;GAAO,OAAO;GAAQ;GAAe,CAAC,EAAE,MAAM,KAAK,CAAC;EACnF,MAAM,MAAM,MAAM,OAAO,MAAM,GAAG,MAAM;GAAC;GAAM;GAAQ;GAAM;GAAI,CAAC,CAAC,GAAG;AACtE,MAAI,OAAO,SAAS,IAAI,IAAI,MAAM,EAAG,SAAQ,KAAK,MAAM,MAAM,KAAK;;AAErE,QAAO;EAAE,UAAU;EAAM,MAAM;EAAS,SAAS,EAAE;EAAE;EAAI,GAAI,QAAQ,EAAE,OAAO,GAAG,EAAE;EAAG,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;EAAG,GAAG,aAAa,MAAM,MAAM,IAAI;EAAE,WAAW;EAAK;;;AAIzK,eAAe,aAAa,MAA0C;AACpE,KAAI;EAEF,MAAM,OADO,KAAK,MAAM,MAAM,QAAQ,IAAI,IAAI,cAAc,KAAK,EAAE,EAAE,CAAC,CAAC,CACrD,QAAQ,EAAE,EAAE,KAAK,MAAM,EAAE,GAAG,CAAC,QAAQ,MAAmB,OAAO,MAAM,SAAS;AAChG,SAAO,IAAI,SAAS,MAAM;SACpB;AACN;;;;AAKJ,SAAgB,YAAY,QAA8B,QAA2B;AACnF,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO,OAAO,MAAM,MAAM,OAAO,SAAS,EAAE,CAAC;;;AAI/C,SAAgB,aAAa,MAAkB,UAAkB,KAAwC;CACvG,IAAI,MAAM;CACV,IAAI,KAAK;AACT,MAAK,MAAM,KAAK,MAAM;AACpB,MAAI,EAAE,aAAa,YAAY,KAAK,MAAM,EAAE,GAAG,GAAG,MAAM,QAAa,EAAE,WAAW,OAAO,CAAC,EAAE,cAAe;AAC3G,SAAO,EAAE;AACT,QAAM,EAAE;;AAEV,QAAO,KAAK,EAAE,cAAc,KAAK,MAAO,OAAO,KAAK,OAAS,GAAG,GAAG,IAAI,GAAG,EAAE;;AAG9E,SAAS,WAAW,GAAoB;AACtC,KAAI;EACF,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;AACrB,SAAO,MAAM,eAAe,MAAM;SAC5B;AACN,SAAO;;;;;;;;;AAmBX,IAAa,cAAb,MAAyB;CACvB,AAAQ,QAA0B,EAAE;CACpC,AAAQ,KAAK;CACb,AAAQ;CACR,AAAQ;CAER,YAAY,AAAiB,MAAkB,AAAiB,QAAQ,KAAQ;EAAnD;EAAmC;AAC9D,OAAK,UAAU,IAAI,QAAQ,MAAM;;CAGnC,IAAI,MAAc,KAAK,KAAK,EAA6B;AACvD,MAAI,KAAK,MAAM,UAAU,MAAM,KAAK,KAAK,KAAK,MAAO,QAAO,QAAQ,QAAQ,KAAK,MAAM;AACvF,OAAK,aAAa,KAAK,KAAK,IAAI,CAAC,MAAM,MAAM;AAC3C,QAAK,QAAQ;AACb,QAAK,KAAK;AACV,QAAK,WAAW;AAChB,UAAO;IACP;AACF,SAAO,KAAK;;CAGd,MAAc,KAAK,KAAwC;EACzD,MAAM,OAAO,KAAK,KAAK,MAAM;EAC7B,IAAI,YAA4C,EAAE;AAClD,MAAI;AACF,eAAY,KAAK,KAAK,WAAW;UAC3B;EAGR,MAAM,OAAO,iBAAiB,KAAK,KAAK,aAAa;EACrD,MAAM,eAAe,KAAK,KAAK,gBAAgB;EAC/C,MAAM,WAAW,aAAa,aAAa;EAC3C,MAAM,WAAW,IAAI,IAAI,KAAK,MAAM,KAAK,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;EAChE,MAAM,OAAuC,EAAE;EAC/C,MAAM,UAAoB,EAAE;;EAE5B,MAAM,QAAQ,MAAc,OAAyE;AACnG,OAAI,KAAK,QAAQ,UAAU,MAAM,IAAI,GAAG,GAAG;AACzC,YAAQ,KAAK,KAAK;AAClB;;AAEF,QAAK,KAAK,GAAG,SAAS,MAAM,CAAC;;AAE/B,OAAK,cAAc,OAAO,gBAAgB,KAAK,KAAK,KAAK,eAAe,cAAc,GAAG,CAAC;AAC1F,OAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,UAAU,EAAE;AACjD,OAAI,EAAE,UAAU,SAAS,eAAe,EAAE,YAAY,SAAS,CAAC,EAAE,QAAS;AAC3E,OAAI,WAAW,EAAE,QAAQ,CAAE,MAAK,KAAK,YAAY,MAAM,GAAG,MAAM,IAAI,CAAC;YAC5D,EAAE,MAAO,MAAK,OAAO,OAAO,mBAAmB,MAAM,GAAG,KAAK,GAAG,CAAC;YACjE,KAAK,MACZ,MAAK,KAAK,QAAQ,QAAQ;IAAE,UAAU;IAAM,MAAM;IAAa,SAAS,gBAAgB,MAAM,MAAM,KAAK,OAAQ,IAAI;IAAE,WAAW;IAAK,CAAC,CAAC;;EAG7I,MAAM,UAAU,MAAM,QAAQ,IAAI,KAAK;EACvC,MAAM,QAAQ,EAAE,GAAG,UAAU;EAC7B,IAAI,QAAQ;AACZ,OAAK,MAAM,KAAK,QACd,KAAI,EAAE,OACJ;OAAI,EAAE,MAAM,SAAS,WAAW,CAAE,MAAK,QAAQ,SAAS,EAAE,UAAU,IAAI;SACnE;AACL,QAAK,QAAQ,MAAM,EAAE,SAAS;AAC9B,OAAI,EAAE,QAAQ,QAAQ;AACpB,UAAM,EAAE,YAAY;KAAE,SAAS,EAAE;KAAS,WAAW,EAAE;KAAW;AAClE,YAAQ;;;AAId,MAAI,MAAO,eAAc,cAAc,MAAM;AAE7C,OAAK,MAAM,QAAQ,SAAS;GAC1B,MAAM,OAAO,SAAS,IAAI,KAAK;GAC/B,MAAM,QAAQ,MAAM,OAAO,SAAS,WAAW,GAAG,aAAc,MAAM,SAAS;AAC/E,WAAQ,KACN,OACI;IAAE,GAAG;IAAM,OAAO,SAAS,KAAK,IAAI,GAAG,KAAK,OAAO,MAAM,KAAK,aAAa,IAAO,CAAC,CAAC,OAAO,MAAM;IAAI,GACrG;IAAE,UAAU;IAAM,MAAM;IAAQ,SAAS,EAAE;IAAE,OAAO;IAAO,WAAW;IAAK,CAChF;;AAEH,SAAO;;;;;;;;;;;;;;;;;;;;;AC3YX,MAAa,gBAAgB,IAAI;;;;;;AAOjC,SAAgB,iBAAiB,SAAwB,KAA4B;CACnF,IAAI,OAA2B;AAC/B,MAAK,MAAM,KAAK,QAAS,KAAI,CAAC,QAAQ,EAAE,UAAU,KAAK,QAAS,QAAO;AACvE,KAAI,CAAC,KAAM,QAAO;EAAE,SAAS;EAAM,SAAS;EAAM,WAAW;EAAK;AAClE,QAAO;EAAE,SAAS,KAAK;EAAS,SAAS,KAAK,WAAW;EAAM,WAAW;EAAK;;;;;;;;AASjF,eAAsB,mBAAmB,MAAc,GAA2C;CAChG,MAAM,MAAM,KAAK,KAAK;AACtB,KAAI;AACF,MAAI,EAAE,UAAU,SAAS,aAAa;GACpC,MAAM,QAAQ,MAAM,gBAAgB;AACpC,OAAI,CAAC,MAAO,OAAM,IAAI,MAAM,6BAA6B;AAKzD,UAAO,iBAAiB,oBAJX,MAAM,QAAQ,IAAI,IAAI,oBAAoB,EAAE;IACvD,eAAe,UAAU;IACzB,kBAAkB;IACnB,CAAC,CAC+C,EAAE,IAAI;;AAEzD,MAAI,CAAC,EAAE,MAAO,QAAO;GAAE,SAAS;GAAM,SAAS;GAAM,WAAW;GAAK;EACrE,MAAM,MAAM,mBAAmB,EAAE;EACjC,MAAM,CAAC,IAAI,GAAG,QAAQ,EAAE,MAAM,cAAc,yBAAyB,MAAM,OAAO;EAClF,MAAM,SAAS,IAAI,KAAK,KAAK;AAI7B,SAAO,iBAAiB,gBAHX,MAAM,QAAQ,IAAI,IAAI,EAAE,MAAM,IAAI,EAAE,GAC9C,GAAI,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,OAAO,IAAI,EAC/D,CAAC,EAC4C,EAAE,MAAM,EAAE,IAAI;SACtD;AACN,SAAO;GAAE,SAAS;GAAM,SAAS;GAAM,WAAW;GAAK;;;;;;;;AAS3D,IAAa,cAAb,MAAyB;CACvB,AAAQ,QAAmB,EAAE;CAC7B,AAAQ,KAAK;CACb,AAAQ;CAER,YACE,AAAiB,QAAgB,eACjC,AAAiB,QAAqE,oBACtF;EAFiB;EACA;;;CAInB,MAAM,KAAK,QAAuB,MAAc,KAAK,KAAK,EAAsB;AAC9E,MAAI,KAAK,MAAM,MAAM,KAAK,KAAK,KAAK,MAAO,QAAO,KAAK;AACvD,OAAK,aAAa,KAAK,KAAK,OAAO,CAAC,MAAM,MAAM;AAC9C,QAAK,QAAQ;AACb,QAAK,KAAK;AACV,QAAK,WAAW;AAChB,UAAO;IACP;AACF,SAAO,KAAK;;CAGd,MAAc,KAAK,QAA2C;EAC5D,MAAM,MAAiB,EAAE,GAAG,KAAK,OAAO;EACxC,MAAM,OAA6B,CAEjC,KAAK,MAAM,aAAa,wBAAwB,OAAO,aAAa,CAAC,CAAC,MAAM,MAAM;AAChF,OAAI,YAAY;IAChB,CACH;AACD,OAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,OAAO,UAAU,CACtD,MAAK,KACH,KAAK,MAAM,MAAM,EAAE,CAAC,MAAM,MAAM;AAC9B,OAAI,QAAQ;IACZ,CACH;AAEH,QAAM,QAAQ,IAAI,KAAK;AACvB,SAAO;;;CAIT,IAAI,MAAyC;AAC3C,SAAO,KAAK,MAAM;;CAGpB,WAAsB;AACpB,SAAO,EAAE,GAAG,KAAK,OAAO;;CAG1B,KAAK,KAAgB,KAAa,KAAK,KAAK,EAAQ;AAClD,OAAK,QAAQ,EAAE,GAAG,KAAK;AACvB,OAAK,KAAK;;CAGZ,QAAc;AACZ,OAAK,QAAQ,EAAE;AACf,OAAK,KAAK;AACV,OAAK,WAAW;;;;AAKpB,MAAa,cAAc,IAAI,aAAa;;AAG5C,SAAgB,WAAW,SAA+B,MAAsB;AAC9E,QAAO,QAAQ,YAAY,SAAS,qBAAqB,SAAS;;;AAIpE,SAAgB,eAAe,GAA8B,MAAuB;AAClF,QAAO,CAAC,CAAC,KAAK,EAAE,YAAY,QAAQ,EAAE,WAAW;;;AAInD,SAAgB,YAAY,GAAkB,KAAa,QAAgB,eAAwB;AACjG,QAAO,MAAM,EAAE,YAAY;;;AAI7B,SAAgB,iBAAiB,GAAkB,sBAAY,IAAI,MAAM,EAAU;CACjF,MAAM,IAAI,EAAE,WAAW,EAAE,UAAU,IAAI,SAAS,GAAG,EAAE,UAAU,IAAI,SAAS,GAAG,KAAK;CACpF,MAAM,IAAI,IAAI,KAAK,EAAE;AACrB,QAAO,GAAG,OAAO,EAAE,UAAU,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,OAAO,EAAE,YAAY,CAAC,CAAC,SAAS,GAAG,IAAI;;;;;;AAO5F,SAAgB,iBAAiB,MAAc,GAAkB,sBAAY,IAAI,MAAM,EAAU;AAC/F,QAAO,GAAG,KAAK,GAAG,EAAE,QAAQ,UAAU,iBAAiB,GAAG,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACrIhE,MAAM,qBAAqB;AAC3B,MAAM,mBAAmB;;AAEzB,MAAa,sBAAsB;AAOnC,SAAgB,iBAAiB,QAA8B;CAC7D,MAAM,OAAO,iBAAiB,OAAO;AACrC,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE,WAAW,EAAE,EAAE;AAC/C,KAAI;AAEF,SAAO,EAAE,WADM,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC,CAC1B,aAAa,EAAE,EAAE;SACtC;AACN,SAAO,EAAE,WAAW,EAAE,EAAE;;;AAI5B,SAAgB,kBAAkB,QAAgB,OAA2B;CAC3E,MAAM,OAAO,iBAAiB,OAAO;CACrC,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CACzD,MAAM,MAAM,GAAG,KAAK;AACpB,eAAc,KAAK,KAAK,UAAU,OAAO,MAAM,EAAE,GAAG,MAAM,OAAO;AACjE,YAAW,KAAK,KAAK;;AAGvB,SAAgB,kBACd,OACA,UACA,sBAAY,IAAI,MAAM,EACd;CACR,MAAM,MAAM,MAAM,UAAU;AAC5B,KAAI,CAAC,IAAK,QAAO;CACjB,MAAM,KAAK,KAAK,MAAM,IAAI,GAAG,IAAI,SAAS;AAC1C,QAAO,KAAK,IAAI,KAAK;;AAGvB,SAAgB,YACd,QACA,UACA,SACA,sBAAY,IAAI,MAAM,EAChB;CACN,MAAM,QAAQ,iBAAiB,OAAO;AACtC,OAAM,UAAU,YAAY,IAAI,KAAK,IAAI,SAAS,GAAG,UAAU,IAAO,CAAC,aAAa;AACpF,mBAAkB,QAAQ,MAAM;;AAGlC,SAAgB,cAAc,QAAgB,UAAwB;CACpE,MAAM,QAAQ,iBAAiB,OAAO;AACtC,KAAI,EAAE,YAAY,MAAM,WAAY;AACpC,QAAO,MAAM,UAAU;AACvB,mBAAkB,QAAQ,MAAM;;;;;;;AAQlC,SAAgB,UAAU,QAAuB,KAAsC;AACrF,KAAI,KAAK,OAAO,OAAQ,QAAO,IAAI;AACnC,KAAI,OAAO,QAAQ,WAAW,QAAS,QAAO,OAAO,QAAQ;AAC7D,KAAI,OAAO,QAAQ,SAAS,OAAQ,QAAO,OAAO,QAAQ;AAC1D,KAAI,OAAO,QAAQ,MAAM,OAAQ,QAAO,OAAO,QAAQ;AACvD,QAAO,CAAC,GAAG,wBAAwB;;;;;;;AAQrC,SAAgB,WAAW,UAAyC;AAClE,KAAI,CAAC,SAAS,WAAY,QAAO;AACjC,KAAI;EAMF,MAAM,IALM,aAAa,WAAW,CAAC,MAAM,SAAS,WAAW,EAAE;GAC/D,SAAS;GACT,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC,CACY,MAAM,kBAAkB;AACtC,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;SACzD;AACN,SAAO;;;AAIX,SAAgB,mBAAmB,UAAkC;AACnE,QAAO,SAAS,eAAe;;;AAIjC,SAAgB,cAAc,UAAmC;CAC/D,MAAM,OAAO,WAAW,SAAS;AACjC,QAAO,SAAS,QAAQ,QAAQ,mBAAmB,SAAS;;;;;;;;;AAU9D,SAAgB,sBAAsB,QAAuB,QAAgB,sBAAY,IAAI,MAAM,EAAQ;AACzG,KAAI,OAAO,QAAQ,WAAW,QAAS;CACvC,MAAM,QAAQ,iBAAiB,OAAO;CACtC,IAAI,QAAQ;AACZ,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,YAAY,UAAU,CAAC,EAAoC;AAChG,MAAI,EAAE,YAAY,QAAQ,CAAC,YAAY,GAAG,IAAI,SAAS,CAAC,CAAE;AAC1D,MAAI,CAAC,eAAe,GAAG,WAAW,OAAO,SAAS,KAAK,CAAC,CAAE;EAC1D,MAAM,QAAQ,KAAK,IAAI,IAAI,SAAS,GAAG,sBAAsB,KAAQ,EAAE,WAAW,EAAE;EACpF,MAAM,OAAO,MAAM,UAAU;AAC7B,MAAI,QAAQ,KAAK,MAAM,KAAK,IAAI,MAAO;AACvC,QAAM,UAAU,QAAQ,IAAI,KAAK,MAAM,CAAC,aAAa;AACrD,UAAQ;;AAEV,KAAI,MAAO,mBAAkB,QAAQ,MAAM;;AAmB7C,IAAa,kBAAb,cAAqC,mBAAmB;AAExD,SAAS,UAAU,QAAuB,MAA8B;CACtE,MAAM,IAAI,oBAAoB,QAAQ,KAAK;AAC3C,KAAI,CAAC,EACH,OAAM,IAAI,gBACR,6BAA6B,KAAK,iBAC7B,OAAO,KAAK,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,SAAS,qGAE3D;AAEH,QAAO;;AAGT,SAAS,eAAe,MAAc,GAAmC;AACvE,KAAI,CAAC,EAAE,QACL,OAAM,IAAI,gBACR,aAAa,KAAK,6DAA6D,OAChF;AAEH,QAAO;;;AAIT,SAAS,gBACP,QACA,OACA,MACA,KACA,SACe;CACf,MAAM,IAAI,oBAAoB,QAAQ,KAAK;AAC3C,KAAI,CAAC,EAAG,QAAO;AACf,KAAI,CAAC,EAAE,QAAS,QAAO;AACvB,KAAI,kBAAkB,OAAO,KAAK,GAAG,EAAG,QAAO;AAC/C,KAAI,cAAc,EAAE,CAAE,QAAO;AAC7B,KAAI,SAAS;EACX,MAAM,IAAI,QAAQ;AAGlB,MAAI,KAAK,EAAE,YAAY,QAAQ,eAAe,GAAG,WAAW,OAAO,SAAS,KAAK,CAAC,CAChF,QAAO,UAAU,EAAE,QAAQ,WAAW,WAAW,OAAO,SAAS,KAAK;;CAG1E,MAAM,OAAO,iBAAiB,EAAE;AAChC,KAAI,KAAK,gBAAgB,UAAa,OAAO,IAAI,YAC/C,QAAO,aAAa,KAAK,SAAS,IAAI;AAExC,KAAI,KAAK,aAAa,QAAQ;EAC5B,MAAM,OAAO,EAAE,QAAQ,EAAE;EACzB,MAAM,UAAU,IAAI,YAAY,QAAQ,MAAM,CAAC,KAAK,SAAS,EAAiB,CAAC;AAC/E,MAAI,QAAQ,OAAQ,QAAO,iBAAiB,QAAQ,KAAK,KAAK;;AAEhE,QAAO;;;;;;;;AAST,SAAgB,cACd,QACA,QACA,OAAsD,EAAE,EACxC;AAChB,KAAI,KAAK,aACP,QAAO;EACL,cAAc,KAAK;EACnB,UAAU,eAAe,KAAK,cAAc,UAAU,QAAQ,KAAK,aAAa,CAAC;EACjF,YAAY;EACZ,UAAU;EACV,KAAK;EACN;CAIH,MAAM,YAAY,KAAK,YAAY,OAAO,QAAQ,KAAK,aAAa;CACpE,MAAM,MACJ,OAAO,cAAc,YAAY,cAAc,OAAO,YAAY;AAEpE,KAAI,KAAK,aAAa,cAAc,UAAa,OAAO,cAAc,UAAU;EAC9E,MAAM,CAAC,MAAM,SAAS,UAAU,MAAM,IAAI;AAC1C,SAAO;GACL,cAAc;GACd,UAAU,eAAe,MAAM,UAAU,QAAQ,KAAK,CAAC;GACvD,YAAY,SAAS;GACrB,UAAU;GACV,KAAK;GACN;;AAEH,KAAI,KAAK,aAAa,KAAK,UAAU;EACnC,MAAM,WAAW,eAAe,IAAI,UAAU,UAAU,QAAQ,IAAI,SAAS,CAAC;AAC9E,SAAO;GACL,cAAc,IAAI;GAClB;GACA,YAAY;GACZ,UAAU,IAAI,OAAO;GACrB,KAAK;GACN;;AAEH,KAAI,KAAK,aAAa,cAAc,QAGlC;MAAI,CAAE,eAAqC,SAAS,KAAK,UAAU,CACjE,OAAM,IAAI,gBACR,mBAAmB,KAAK,UAAU,uBAAuB,eAAe,KAAK,KAAK,CAAC,YACrE,OAAO,KAAK,OAAO,QAAQ,CAAC,KAAK,KAAK,IAAI,SAAS,0CACrB,KAAK,UAAU,qBAC5D;;AAIL,KAAI,OAAO,WAAW,QAAQ;EAE5B,MAAM,OAAO,OAAO,UAAU;AAC9B,SAAO;GACL,cAAc;GACd,UAAU,eAAe,MAAM,UAAU,QAAQ,KAAK,CAAC;GACvD,YAAY;GACZ,UAAU;GACV,KAAK;GACN;;CAOH,MAAM,QAAQ,iBAAiB,OAAO;CACtC,MAAM,YAAY,OAAO,QAAQ,WAAW;CAC5C,MAAM,QAAQ,UAAU,QAAQ,IAAI;CACpC,MAAM,UAAiC,YAAY,YAAY,UAAU,GAAG;CAC5E,MAAM,aAAa,YACf,MACG,KAAK,MAAM,SAAS;EAAE;EAAM;EAAK,SAAS,UAAU,OAAO;EAAS,EAAE,CACtE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,MAAM,EAAE,WAAW,GAAG,CACpE,KAAK,MAAM,EAAE,KAAK,GACrB;CACJ,MAAM,WAAqB,EAAE;CAC7B,IAAI,gBAA+B;AACnC,MAAK,MAAM,QAAQ,YAAY;EAC7B,MAAM,IAAI,oBAAoB,QAAQ,KAAK;EAC3C,MAAM,MAAM,gBAAgB,QAAQ,OAAO,MAAM,KAAK,QAAQ;AAC9D,MAAI,KAAK;AACP,YAAS,KAAK,GAAG,KAAK,IAAI,MAAM;AAChC,OAAI,aAAa,kBAAkB,QAAQ,IAAI,WAAW,SAAS,IAAI,EACrE,iBAAgB,iBAAiB,MAAM,QAAS,MAAM;AAExD;;AAIF,SAAO;GACL,cAAc;GACd,UAAU;GACV,YAAY;GACZ,UAAU,KAAK,OAAO;GACtB,KAAK;GACL,GAAI,gBAAgB,EAAE,cAAc,GAAG,KAAK,IAAI,iBAAiB,GAAG,EAAE;GACvE;;AAEH,OAAM,IAAI,gBACR,wCAAwC,KAAK,YAAY,eAAe,KAAK,UAAU,KAAK,GAAG,YACjF,MAAM,KAAK,KAAK,CAAC,KAC1B,SAAS,SAAS,eAAe,SAAS,KAAK,KAAK,CAAC,KAAK,+GACgD,KAAK,aAAa,UAAU,WAC5I;;;;;;;;AASH,SAAgB,iBACd,QACA,QACA,MACA,sBAAY,IAAI,MAAM,EACP;CACf,MAAM,QAAQ,iBAAiB,OAAO;CACtC,MAAM,YAAY,OAAO,QAAQ,WAAW;CAC5C,MAAM,QAAQ,UAAU,OAAO;CAC/B,MAAM,UAAiC,YAAY,YAAY,UAAU,GAAG;CAC5E,MAAM,QAAQ,MAAM,QAAQ,KAAK;AACjC,MAAK,IAAI,IAAI,QAAQ,GAAG,IAAI,MAAM,QAAQ,KAAK;EAC7C,MAAM,OAAO,MAAM;EACnB,MAAM,IAAI,oBAAoB,QAAQ,KAAK;AAC3C,MAAI,CAAC,KAAK,CAAC,EAAE,QAAS;EACtB,MAAM,MAAM,MAAM,UAAU;AAC5B,MAAI,OAAO,KAAK,MAAM,IAAI,GAAG,IAAI,SAAS,CAAE;AAC5C,MAAI,cAAc,EAAE,CAAE;AACtB,MAAI,SAAS;GACX,MAAM,IAAI,QAAQ;AAClB,OAAI,KAAK,eAAe,GAAG,WAAW,OAAO,SAAS,KAAK,CAAC,CAAE;;AAEhE,SAAO;;AAET,QAAO;;;;;;AAOT,SAAgB,eAAe,YAA6B;AAC1D,QAAO,wCAAwC,KAAK,WAAW;;;;;;;;;AAmBjE,SAAgB,iBACd,QACA,MACe;AACf,KAAI,KAAK,eAAgB,QAAO,KAAK;AACrC,KAAI,CAAC,KAAK,UAAW,QAAO;CAC5B,MAAM,aAAa,qBAAqB,KAAK,UAAU;AACvD,KAAI,eAAe,UAAW,QAAO;CACrC,MAAM,YAAY,OAAO,QAAQ,KAAK;AAEtC,SADsB,OAAO,cAAc,WAAW,OAAO,QAAQ,WAAW,SAAS,IAClE,OAAO;;;;;;;;;;;AAYhC,SAAgB,qBAAqB,QAAuB,YAA6C;CACvG,MAAM,IAAI,kBAAkB,QAAQ,WAAW;AAC/C,KAAI,EAAE,SACJ,OAAM,IAAI,gBACR,6BAA6B,WAAW,2DACb,WAAW,iDAChB,WAAW,+BAClC;AAOH,KAAI,eAAe,WAAW,EAAE,WAAW,QACzC,OAAM,IAAI,gBACR,aAAa,EAAE,SAAS,uCAAuC,EAAE,MAAM,kBAClE,EAAE,OAAO,yMAGf;CAEH,MAAM,WAAW,oBAAoB,QAAQ,EAAE,SAAS;AACxD,KAAI,CAAC,SACH,OAAM,IAAI,gBAAgB,eAAe,WAAW,kCAAkC,EAAE,SAAS,GAAG;AAEtG,QAAO;EACL,cAAc,EAAE;EAChB;EACA,OAAO,EAAE;EACT,QAAQ,EAAE;EACV,UAAU,EAAE;EACZ;EACD;;;;;;;;;;;;;;;;;;AClcH,MAAa,qBAAqB;AAClC,MAAM,OAAO;AAOb,SAAgB,aAAa,QAAwB;AACnD,QAAO,KAAK,QAAQ,YAAY;;;AA8KlC,SAAgB,eAAe,OAAO,oBAAoB,YAAY,KAAuB;AAC3F,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,OAAO,QAAQ;GAAE;GAAM,MAAM;GAAM,CAAC;EAC1C,MAAM,QAAQ,OAAgB;AAC5B,QAAK,oBAAoB;AACzB,QAAK,SAAS;AACd,WAAQ,GAAG;;AAEb,OAAK,WAAW,iBAAiB,KAAK,MAAM,CAAC;AAC7C,OAAK,KAAK,iBAAiB,KAAK,KAAK,CAAC;AACtC,OAAK,KAAK,eAAe,KAAK,MAAM,CAAC;GACrC;;;;;;AAOJ,SAAgB,sBAAqC;CACnD,IAAI,MAAM,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AACjD,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,OAAK,MAAM,QAAQ;GACjB,KAAK,KAAK,mBAAmB;GAC7B,KAAK,KAAK,SAAS,mBAAmB;GACtC,KAAK,KAAK,QAAQ,SAAS,mBAAmB;GAC/C,CACC,KAAI,WAAW,KAAK,CAAE,QAAO;EAE/B,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IAAK;AACpB,QAAM;;AAER,QAAO;;;;;;AAOT,eAAsB,mBAAmB,MAAc,QAAiC;AACtF,KAAI,MAAM,eAAe,KAAK,CAAE,QAAO,UAAU,KAAK,GAAG;CACzD,MAAM,SAAS,qBAAqB;AACpC,KAAI,CAAC,OACH,OAAM,IAAI,MACR,uIACuE,KAAK,GAC7E;AAEH,KAAI,CAAC,WAAW,OAAO,CAAE,WAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;CAC/D,MAAM,QAAQ,MAAM,QAAQ,UAAU;EAAC;EAAQ;EAAU,OAAO,KAAK;EAAC,EAAE;EACtE,UAAU;EACV,OAAO;EACR,CAAC;AACF,OAAM,OAAO;AACb,eAAc,aAAa,OAAO,EAAE,GAAG,MAAM,IAAI,KAAK,OAAO;AAC7D,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,MAAI,MAAM,eAAe,KAAK,CAAE,QAAO,UAAU,KAAK,GAAG;AACzD,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;;AAE9C,OAAM,IAAI,MAAM,gDAAgD,KAAK,QAAQ,aAAa,OAAO,CAAC,GAAG;;;AAIvG,SAAgB,UAAU,QAAwB;CAChD,MAAM,OAAO,aAAa,OAAO;AACjC,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO;CAC9B,MAAM,MAAM,SAAS,aAAa,MAAM,OAAO,CAAC,MAAM,EAAE,GAAG;AAC3D,YAAW,KAAK;AAChB,KAAI,CAAC,OAAO,SAAS,IAAI,CAAE,QAAO;AAClC,KAAI;AACF,UAAQ,KAAK,KAAK,UAAU;SACtB;AACN,SAAO,aAAa,IAAI;;AAE1B,QAAO,aAAa,IAAI;;;;;;;;;;;;;;;;;AClQ1B,MAAa,mBAAmB;AAChC,MAAa,kBAAkB;AAC/B,MAAa,mBAAmB;AAChC,MAAa,yBAAyB,KAAK;AAC3C,MAAa,wBAAwB,MAAS;AAC9C,MAAa,YAAY,KAAK;AAC9B,MAAM,gBAAgB;AACtB,MAAM,UAAU;AAChB,MAAM,YAAY;AAClB,MAAM,aAAa;AAYnB,MAAM,SAAS,QAAoC,IAAI,MAAM,8CAA8C,GAAG,IAAI,QAAQ,SAAS,GAAG;AACtI,MAAM,aAAa,MAA2C,EAAE,OAAO,MAAM,IAAI,CAAC;;;;;;AAOlF,MAAM,QAA6E;CACjF;EAAE,OAAO,MAAM,cAAc,KAAK,EAAE,IAAI,IAAI,gBAAgB,KAAK,EAAE,KAAK;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAAwB,QAAQ;GAAU;EAAE;CAC/I;EACE,OAAO,MAAM;GACX,MAAM,MAAM,MAAM,EAAE,IAAI,EAAE,IAAI,UAAU,EAAE;AAC1C,UAAO,EAAE,SAAS,UAAU,uBAAuB,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,MAAM,OAAO,EAAE,WAAW,MAAM,IAAI;;EAEzH,GAAG;GAAE,KAAK;GAAS,MAAM;GAAgD,QAAQ;GAAU;EAC5F;CACD;EAAE,OAAO,MAAM,gDAAgD,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAAgC,QAAQ;GAAG;EAAE;CAClJ;EAAE,OAAO,MAAM,qBAAqB,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAAuC,QAAQ;GAAG;EAAE;CAC9H;EAAE,OAAO,MAAM,8CAA8C,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAA+B,QAAQ;GAAG;EAAE;CAC/I;EAAE,OAAO,MAAM,mBAAmB,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAAwB,QAAQ;GAAG;EAAE;CAC7G;EAAE,OAAO,MAAM,0BAA0B,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAA+B,QAAQ;GAAG;EAAE;CAC3H;EACE,OAAO,MAAM;AACX,OAAI,CAAC,cAAc,KAAK,EAAE,IAAI,CAAE,QAAO;GACvC,MAAM,MAAM,MAAM,EAAE,IAAI;AAExB,WADc,EAAE,IAAI,MAAM,iCAAiC,EAAE,KAAK,MAAM,EAAE,QAAQ,aAAa,GAAG,CAAC,IAAI,EAAE,EAC5F,MAAM,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,EAAE,WAAW,MAAM,IAAI,MAAM,WAAW,EAAE,CAAC;;EAE9F,GAAG;GAAE,KAAK;GAAW,MAAM;GAAyD;EACrF;CACD;EAAE,OAAO,MAAM,mBAAmB,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAS,MAAM;GAAgH;EAAE;CAC1L;EAAE,OAAO,MAAM,oCAAoC,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAW,MAAM;GAAsB;EAAE;CACnH;EAAE,OAAO,MAAM,iDAAiD,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAW,MAAM;GAAkB;EAAE;CAC5H;EAAE,OAAO,MAAM,0BAA0B,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAW,MAAM;GAA8B;EAAE;CACjH;EAAE,OAAO,MAAM,YAAY,KAAK,EAAE,IAAI,IAAI,yBAAyB,KAAK,EAAE,IAAI;EAAE,GAAG;GAAE,KAAK;GAAW,MAAM;GAAgC;EAAE;CAC9I;AAED,SAAgB,kBAAkB,GAAoC;AACpE,QAAO,MAAM,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK;EAAE,KAAK;EAAW,MAAM;EAAgB;;AAGpF,SAAgB,eAAe,QAAwB;AACrD,QAAO,KAAK,QAAQ,iBAAiB;;;AAIvC,SAAgB,eAAe,MAAc,MAAsB;AASjE,QAAO,GAAG,KAAK,GARF,KACV,QAAQ,QAAQ,IAAI,CACpB,MAAM,CACN,QAAQ,qBAAqB,MAAM,CACnC,QAAQ,kCAAkC,QAAQ,CAClD,QAAQ,oCAAoC,OAAO,CACnD,QAAQ,wBAAwB,OAAO,CACvC,QAAQ,YAAY,MAAM,CACN,MAAM,GAAG,cAAc;;;AAIhD,SAAgB,mBAAmB,OAAoC;AACrE,KAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;CAChD,MAAM,IAAI;CACV,MAAM,OAAO,MAAe,OAAO,EAAE,OAAO,WAAY,EAAE,KAAgB;CAC1E,MAAM,IAAI;EAAC,IAAI,YAAY,IAAI,IAAI,OAAO;EAAE,IAAI,UAAU;EAAE,IAAI,UAAU;EAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;AACrG,QAAO,IAAI,EAAE,QAAQ,QAAQ,IAAI,CAAC,MAAM,GAAG,UAAU,GAAG;;AAG1D,SAAgB,gBACd,QACA,KACA,MAAM,KAAK,KAAK,EACV;AACN,KAAI;AACF,MAAI,CAAC,WAAW,OAAO,CAAE,WAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;EAC/D,MAAM,OAAwB;GAC5B,IAAI;GACJ,QAAQ,IAAI;GACZ,UAAU,IAAI;GACd,OAAO,IAAI;GACX,MAAM,IAAI;GACV,WAAW,eAAe,IAAI,MAAM,IAAI,KAAK;GAC7C,KAAK,IAAI,KAAK,QAAQ,QAAQ,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ;GAC5D;EACD,MAAM,QAAQ,mBAAmB,IAAI,MAAM;AAC3C,MAAI,MAAO,MAAK,QAAQ;AACxB,iBAAe,eAAe,OAAO,EAAE,KAAK,UAAU,KAAK,GAAG,MAAM,OAAO;SACrE;;;AAMV,SAAgB,eAAe,QAAgB,UAAU,GAAsB;CAC7E,MAAM,OAAO,eAAe,OAAO;AACnC,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE;CAChC,IAAI;CACJ,MAAM,OAAO,SAAS,KAAK,CAAC;AAC5B,KAAI,OAAO,YAAY;EACrB,MAAM,KAAK,SAAS,MAAM,IAAI;EAC9B,MAAM,MAAM,OAAO,MAAM,WAAW;AACpC,WAAS,IAAI,KAAK,GAAG,YAAY,OAAO,WAAW;AACnD,YAAU,GAAG;AACb,SAAO,IAAI,SAAS,OAAO,CAAC,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,KAAK;OAE3D,QAAO,aAAa,MAAM,OAAO;CAEnC,MAAM,MAAyB,EAAE;AACjC,MAAK,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE;AAChC,MAAI,CAAC,EAAG;AACR,MAAI;GACF,MAAM,IAAI,KAAK,MAAM,EAAE;AACvB,OAAI,EAAE,MAAM,WAAW,OAAO,EAAE,cAAc,SAAU,KAAI,KAAK,EAAE;UAC7D;;AAIV,QAAO;;AAkBT,MAAM,QAAQ,MAAuB;CACnC,MAAM,EAAE,KAAK,SAAS,kBAAkB,EAAE;AAC1C,QAAO;EAAE;EAAK;EAAM;;AAGtB,SAAgB,gBAAgB,SAA8C;CAC5E,MAAM,oBAAI,IAAI,KAA6B;AAC3C,MAAK,MAAM,KAAK,SAAS;EACvB,IAAI,IAAI,EAAE,IAAI,EAAE,UAAU;AAC1B,MAAI,CAAC,GAAG;AACN,OAAI;IAAE,WAAW,EAAE;IAAW,OAAO;IAAG,SAAS,EAAE;IAAE,WAAW,EAAE;IAAE,OAAO,EAAE;IAAI,MAAM,EAAE;IAAI,QAAQ,EAAE;IAAK,aAAa,EAAE;IAAO,YAAY,EAAE;IAAQ,GAAG,KAAK,EAAE;IAAE;AACpK,KAAE,IAAI,EAAE,WAAW,EAAE;;AAEvB,IAAE,SAAS;AACX,MAAI,CAAC,EAAE,QAAQ,SAAS,EAAE,OAAO,CAAE,GAAE,QAAQ,KAAK,EAAE,OAAO;AAC3D,MAAI,CAAC,EAAE,UAAU,SAAS,EAAE,SAAS,CAAE,GAAE,UAAU,KAAK,EAAE,SAAS;AACnE,IAAE,QAAQ,KAAK,IAAI,EAAE,OAAO,EAAE,GAAG;AACjC,MAAI,EAAE,MAAM,EAAE,MAAM;AAClB,KAAE,OAAO,EAAE;AACX,KAAE,aAAa,EAAE;AACjB,KAAE,SAAS,EAAE;AACb,KAAE,cAAc,EAAE;;;AAGtB,QAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK;;;;;;;;AASxD,SAAgB,iBACd,SACA,KACA,MACkB;CAClB,MAAM,MAAwB,EAAE;CAChC,MAAM,wBAAQ,IAAI,KAAgC;AAClD,MAAK,MAAM,KAAK,QAAS,OAAM,IAAI,EAAE,WAAW,CAAC,GAAI,MAAM,IAAI,EAAE,UAAU,IAAI,EAAE,EAAG,EAAE,CAAC;AACvF,MAAK,MAAM,CAAC,KAAK,QAAQ,OAAO;AAC9B,MAAI,OAAO,KAAK,QAAQ,KAAK,UAAW;EAExC,MAAM,OAAO,IAAI,QAAQ,MAAM,EAAE,MAAM,KAAK,QAAQ,MAAM,kBAAkB,EAAE,CAAC,WAAW,SAAS;AACnG,MAAI,CAAC,KAAK,OAAQ;EAClB,MAAM,SAAS,kBAAkB,KAAK,GAAG,CAAC;EAC1C,MAAM,4BAAY,IAAI,KAAqB;AAC3C,OAAK,MAAM,KAAK,KAAM,KAAI,MAAM,EAAE,MAAM,sBAAuB,WAAU,IAAI,EAAE,SAAS,UAAU,IAAI,EAAE,OAAO,IAAI,KAAK,EAAE;AAC1H,MAAI,WAAW,QAAW;AAGxB,OADU,KAAK,IAAI,GAAG,GAAG,UAAU,QAAQ,CAAC,GACpC,OAAQ;AAChB,QAAK,OAAO;AACZ,OAAI,KAAK,gBAAgB,KAAK,CAAC,GAAG;AAClC;;EAEF,MAAM,IAAI,CAAC,GAAG,UAAU,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK,gBAAgB;EACnE,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,EAAE,MAAM,uBAAuB;EACvE,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ;EACvD,MAAM,IAAI,KAAK,GAAG,IAAI,SAAS,aAAa,IAAI,CAAC,GAAG,UAAU,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK,EAAE;AAC3F,MAAI,EAAE,KAAK,KAAK,GAAI;AACpB,OAAK,OAAO;AAEZ,MAAI,KAAK,gBAAgB,KAAK,IAAI,OAAO,OAAO,CAAC,GAAG;;AAEtD,QAAO;;AAGT,SAAgB,cAAc,GAA2B;CACvD,MAAM,OAAO,GAAG,EAAE,UAAU,IAAI,EAAE,MAAM,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC,aAAa,EAAE,SAAS,EAAE,cAAc,YAAY,EAAE,YAAY,KAAK;CACzI,MAAM,OAAO;AACb,KAAI,EAAE,QAAQ,UAAW,QAAO,aAAa,EAAE,KAAK,IAAI,KAAK,uBAAuB;AACpF,KAAI,EAAE,QAAQ,QAAS,QAAO,WAAW,EAAE,MAAM,IAAI,EAAE,KAAK,IAAI,KAAK,yDAAyD;AAC9H,QAAO,aAAa,KAAK,KAAK;;;;;;AAuBhC,SAAgB,gBAAgB,WAA2B;CACzD,IAAI,IAAI;AACR,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IAAK,MAAM,IAAI,KAAM,UAAU,WAAW,EAAE,MAAM;AACxF,QAAO,EAAE,SAAS,GAAG;;;;;;;;;;;;;;;AAgBvB,SAAgB,0BACd,SACA,SACA,MACA,KACoB;CACpB,MAAM,WAAW,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAU,CAAC;CACxE,MAAM,2BAAW,IAAI,KAA6C;AAClE,MAAK,MAAM,KAAK,SAAS;AACvB,MAAI,CAAC,SAAS,IAAI,EAAE,OAAO,CAAE;EAC7B,IAAI,QAAQ,SAAS,IAAI,EAAE,OAAO;AAClC,MAAI,CAAC,MAAO,UAAS,IAAI,EAAE,QAAS,wBAAQ,IAAI,KAAK,CAAE;AACvD,QAAM,IAAI,EAAE,WAAW,CAAC,GAAI,MAAM,IAAI,EAAE,UAAU,IAAI,EAAE,EAAG,EAAE,CAAC;;CAEhE,MAAM,MAA0B,EAAE;AAClC,MAAK,MAAM,CAAC,QAAQ,UAAU,UAAU;EACtC,MAAM,SAAU,KAAK,YAAY,EAAE;AACnC,OAAK,MAAM,CAAC,KAAK,QAAQ,OAAO;AAC9B,OAAI,OAAO,SAAS,OAAW;AAC/B,UAAO,OAAO;AACd,OAAI,KAAK;IAAE;IAAQ,SAAS,SAAS,IAAI,OAAO;IAAG,WAAW;IAAK,OAAO,gBAAgB,IAAI,CAAC;IAAI,CAAC;;;AAGxG,QAAO;;;AAIT,SAAgB,WAAW,GAA0B;CACnD,MAAM,IAAI,EAAE,MAAM,iBAAiB;AACnC,QAAO,IAAI,OAAO,EAAE,GAAG,GAAG;EAAE,GAAG;EAAQ,GAAG;EAAW,GAAG;EAAY,CAAC,EAAE,MAAyB;;AAGlG,SAAS,KAAK,IAAoB;CAChC,MAAM,IAAI,IAAI,KAAK,GAAG;CACtB,MAAM,KAAK,MAAc,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI;AACnD,QAAO,GAAG,EAAE,EAAE,UAAU,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC;;AAGzF,SAAgB,YAAY,QAAkC;AAC5D,KAAI,CAAC,OAAO,OAAQ,QAAO;CAC3B,MAAM,OAAO,CAAC;EAAC;EAAa;EAAS;EAAW;EAAa;EAAS;EAAO,CAAC;AAC9E,MAAK,MAAM,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CAC3D,MAAK,KAAK;EAAC,EAAE;EAAW,OAAO,EAAE,MAAM;EAAE,OAAO,EAAE,QAAQ,OAAO;EAAE,EAAE,UAAU,KAAK,IAAI;EAAE,KAAK,EAAE,MAAM;EAAE,KAAK,EAAE,KAAK;EAAC,CAAC;CAEzH,MAAM,IAAI,KAAK,GAAG,KAAK,GAAG,MAAM,KAAK,IAAI,GAAG,KAAK,KAAK,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC;AAC1E,QAAO,KAAK,KAAK,MAAM,EAAE,KAAK,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,KAAK;;;;;;;;;;;;;;;;;AC/T/E,IAAa,cAAb,cAAiC,mBAAmB;;;;AAgBpD,SAAgB,wBAAwB,SAAqD;AAC3F,QAAO,CACL,kBACA,GAAG,OAAO,QAAQ,QAAQ,UAAU,CACjC,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAC5B,KAAK,CAAC,UAAU,KAAK,CACzB;;AAGH,SAAS,iBAAiB,QAA+B;AACvD,QAAO,UAAU;;;;;;;;AASnB,SAAgB,gBACd,SACa;CACb,MAAM,OAAoB,EAAE;CAC5B,IAAI,IAAI;CACR,MAAM,aAAa,iBAAiB,QAAQ,OAAO;CACnD,MAAM,SAAS,wBAAwB,QAAQ,aAAa;AAC5D,MAAK,MAAM,OAAO,oBAAoB;EACpC,MAAM,KAAK,OAAO,OAAO;AACzB,MAAI,CAAC,GAAI;AACT,OAAK,KAAK;GACR,OAAO;GACP,UAAU;GACV,OAAO;GACP,YAAY;GACZ,QAAQ;GACR,cAAc,eAAe;GAC9B,CAAC;;AAEJ,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,QAAQ,UAAU,EAAE;AACzD,MAAI,CAAC,EAAE,QAAS;AAChB,OAAK,MAAM,OAAO,oBAAoB;GACpC,MAAM,KAAK,EAAE,OAAO;AACpB,OAAI,CAAC,GAAI;AACT,QAAK,KAAK;IACR,OAAO;IACP,UAAU;IACV,OAAO;IACP,YAAY;IACZ,QAAQ;IACR,cAAc,eAAe;IAC9B,CAAC;;;AAGN,QAAO;;;;;;;;;;AAWT,SAAgB,0BACd,SACA,MAAyB,QAAQ,KACzB;CACR,MAAM,OAAO,IAAI;AACjB,KAAI,CAAC,KAAM,QAAO;AAClB,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,QAAQ,UAAU,CACvD,KAAI,EAAE,aAAa,eAAe,EAAE,WAAW,EAAE,YAAY,KAAM,QAAO;AAE5E,MAAK,MAAM,CAAC,MAAM,MAAM,OAAO,QAAQ,QAAQ,UAAU,CACvD,KAAI,EAAE,aAAa,YAAY,KAAK,SAAS,IAAI,OAAO,CAAE,QAAO;AAEnE,QAAO;;;;;;;;;;;;AAaT,SAAgB,mBAAmB,MAAyB,QAAQ,KAAoB;AACtF,QAAO,IAAI,kCAAkC;;;AAI/C,SAAgB,sBACd,SACA,MACgB;CAChB,MAAM,QAAQ,wBAAwB,QAAQ;CAC9C,MAAM,IAAI,oBAAoB,SAAS,KAAK;AAC5C,KAAI,CAAC,EACH,OAAM,IAAI,YAAY,sBAAsB,KAAK,iBAAiB,MAAM,KAAK,KAAK,GAAG;AAEvF,KAAI,CAAC,EAAE,QACL,OAAM,IAAI,YAAY,aAAa,KAAK,6BAA6B,MAAM,KAAK,KAAK,GAAG;AAE1F,QAAO;;;;AAKT,SAAgB,mBAAmB,UAA0B,WAA4B;AACvF,KAAI,CAAC,UAAW,QAAO,SAAS,OAAO;CACvC,MAAM,QAAQ,OAAO,OAAO,SAAS,OAAO;AAC5C,KAAI,CAAC,MAAM,SAAS,UAAU,CAC5B,OAAM,IAAI,YAAY,UAAU,UAAU,qDAAqD,MAAM,KAAK,KAAK,GAAG;AAEpH,QAAO;;;;;;;;AAkBT,eAAsB,gBACpB,SACA,cACA,WACA,WACA,QACqB;CACrB,MAAM,WAAW,sBAAsB,SAAS,aAAa;AAC7D,KAAI,SAAS,WAAW,QACtB,OAAM,IAAI,YACR,aAAa,aAAa,+FAC3B;CAEH,MAAM,QAAQ,mBAAmB,UAAU,UAAU;CAErD,IAAI;AACJ,KAAI,SAAS,aAAa,SAExB,YAAW,GADE,MAAM,mBAAmB,oBAAoB,OAAO,CAC9C,GAAG;CAExB,MAAM,MAAM,YAAY,UAAU,OAAO,SAAS;AAUlD,QAAO,IAAI;AACX,QAAO,IAAI;AAKX,QAAO;EAAE;EAAc;EAAU;EAAO,MAD3B;GAAC,GADF,mBAAmB,IAAI;GACb;GAAW;GAAO,GAAG;GAAU;EACP;EAAK;;;;;AAMrD,SAAgB,cAAc,KAAgD;CAC5E,MAAM,SAAiC,EAAE;AACzC,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,IAAI,EAAE;AACxC,MAAI,MAAM,OAAW;AACrB,SAAO,KAAK,MAAM,0BAA0B,MAAM,UAAU,QAAQ,EAAE,GAAG;;AAE3E,QAAO;;;;;;;;;;;ACvMT,MAAa,kBAAkB,QAAgB,OAAuB,KAAK,QAAQ,GAAG,GAAG,OAAO;;AAGhG,SAAgB,kBAAkB,MAAyB,QAAQ,KAAa;CAC9E,MAAM,OACJ,IAAI,qBAAqB,IAAI,oBAAoB,IAAI,yBAAyB,IAAI;AACpF,KAAI,KAAM,QAAO;AACjB,KAAI,IAAI,uBAAwB,QAAO,kBAAkB,IAAI;AAC7D,QAAO,IAAI,aAAa,gBAAgB;;;AAI1C,SAAgB,YAAY,QAAgB,QAAgB,IAAY,KAAa,mBAAmB,EAAQ;CAC9G,MAAM,MAAmB;EACvB,qBAAI,IAAI,MAAM,EAAC,aAAa;EAC5B;EACA,KAAK,QAAQ;EACb,MAAM,QAAQ;EACd,KAAK,QAAQ,KAAK;EACnB;AACD,eAAc,eAAe,QAAQ,GAAG,EAAE,KAAK,UAAU,IAAI,CAAC;AAC9D,cAAa,QAAQ,eAAe;EAAE;EAAI;EAAI,KAAK,IAAI;EAAK,MAAM,IAAI;EAAM,KAAK,IAAI;EAAK,CAAC;;AAG7F,SAAS,WAAW,QAAgB,IAAgC;CAClE,MAAM,IAAI,eAAe,QAAQ,GAAG;AACpC,KAAI,CAAC,WAAW,EAAE,CAAE,QAAO;AAC3B,KAAI;AACF,SAAO,KAAK,MAAM,aAAa,GAAG,OAAO,CAAC;SACpC;AACN,SAAO;GAAE,IAAI;GAAI,IAAI;GAAW,KAAK;GAAG,MAAM;GAAG,KAAK;GAAI;WAClD;AACR,MAAI;AACF,cAAW,EAAE;UACP;;;AAMZ,SAAgB,SAAS,KAAsB;AAC7C,KAAI;AACF,UAAQ,KAAK,KAAK,EAAE;AACpB,SAAO;UACA,GAAG;AACV,SAAQ,EAA4B,SAAS;;;;AAKjD,SAAgB,OAAO,KAAiC;AACtD,KAAI;AACF,SAAO,aAAa,MAAM;GAAC;GAAM;GAAS;GAAM,OAAO,IAAI;GAAC,EAAE,EAAE,UAAU,QAAQ,CAAC,CAAC,MAAM,IAAI;SACxF;AACN;;;;AAKJ,SAAgB,aAAqF;CACnG,MAAM,YAAY,QAAQ;AAC1B,QAAO;EACL;EACA,YAAY,OAAO,UAAU;EAE7B,kBAAkB,QAAQ,IAAI,aAAa,OAAO;EACnD;;;;;;;AAQH,SAAgB,gBAAgB,GASpB;AACV,KAAI,EAAE,SAAU,QAAO,EAAE,QAAQ,YAAY;AAC7C,KAAI,CAAC,EAAE,OAAQ,QAAO,EAAE,QAAQ,QAAQ;CACxC,MAAM,SAAS,WAAW,EAAE,QAAQ,EAAE,GAAG;AACzC,KAAI,OAAQ,QAAO;EAAE,QAAQ;EAAmB,IAAI,OAAO;EAAI,MAAM,GAAG,EAAE,OAAO,oBAAoB,OAAO;EAAO;CACnH,MAAM,SAAS,EAAE,WAAW,UAAU,EAAE,aAAa,QAAQ,KAAK;CAClE,MAAM,SAAS,UAAU,EAAE,aAAa,MAAM,EAAE,aAAa,KAAK,EAAE,WAAW,KAAK;AACpF,KAAI,CAAC,MAAO,QAAO;EAAE,QAAQ;EAAe,MAAM,GAAG,EAAE,OAAO,IAAI,OAAO;EAAa;AACtF,QAAO;EACL,QAAQ;EACR,MACE,GAAG,EAAE,OAAO,sJACkD,OAAO,WACpE,EAAE,mBAAmB,yCAAyC;EAClE;;;AAIH,SAAgB,UAAU,GAAgD;AACxE,QAAO;EAAE,QAAQ,EAAE;EAAQ,GAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE;EAAG;;;AAI5D,SAAgB,SAAS,GAA0D;AACjF,QAAO,EAAE,YAAY,GAAG,EAAE,YAAY,EAAE,QAAQ,OAAO,EAAE,UAAU,OAAO;;;;;;;;;;;;;;;;;;;;;ACjH5E,MAAM,cAAc;CAClB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;AAGD,MAAa,eAAe;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAEZ,MAAM,gBAAgB;CAAC;CAAsB;CAAwB;CAAoB;;AAKzF,SAAgB,aACd,MACA,UAA6B,EAAE,EACN;CACzB,MAAM,MAA+B,EAAE;AACvC,MAAK,MAAM,KAAK;EAAC;EAAO;EAAe;EAAQ,CAAE,KAAI,KAAK,OAAO,OAAW,KAAI,KAAK,KAAK;AAG1F,KAAI,IAAI,OAAO,OAAO,IAAI,QAAQ,UAAU;EAC1C,MAAM,IAAI,EAAE,GAAI,IAAI,KAAiC;AACrD,OAAK,MAAM,KAAK,cAAe,KAAI,QAAQ,GAAI,QAAO,EAAE;AACxD,MAAI,MAAM;;CAEZ,MAAM,QAAqC,EAAE;AAC7C,MAAK,MAAM,CAAC,OAAO,WAAW,OAAO,QAAS,KAAK,SAAS,EAAE,CAAiC,EAAE;EAC/F,MAAM,OAAO,OACV,KAAK,OAAO;GAAE,GAAG;GAAG,QAAQ,EAAE,SAAS,EAAE,EAAE,QAAQ,MAAM,YAAY,MAAM,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC,CAAC;GAAE,EAAE,CAC7G,QAAQ,MAAM,EAAE,MAAM,SAAS,EAAE;AACpC,MAAI,KAAK,OAAQ,OAAM,SAAS;;AAElC,KAAI,QAAQ;AAEZ,KAAI,cAAc;EAAE,QAAQ;EAAI,IAAI;EAAI;AACxC,QAAO;;;;;;AAOT,SAAgB,gBACd,QACA,KACA,MACA,MAAyB,QAAQ,KACjC,mBAAmB,KAAK,SAAS,EAAE,WAAW,gBAAgB,EACpD;AACV,KAAI,IAAI,4BAA4B,IAAK,QAAO,EAAE;AAClD,KAAI,KAAK,MAAM,MAAM,MAAM,gBAAgB,EAAE,WAAW,cAAc,IAAI,EAAE,WAAW,oBAAoB,CAAC,CAAE,QAAO,EAAE;AACvH,KAAI,CAAC,WAAW,iBAAiB,CAAE,QAAO,EAAE;CAC5C,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,aAAa,kBAAkB,OAAO,CAAC;SACnD;AACN,SAAO,EAAE;;AAEX,WAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;CACtC,MAAM,OAAO,KAAK,QAAQ,GAAG,IAAI,gBAAgB;AACjD,eAAc,MAAM,KAAK,UAAU,aAAa,MAAM,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,OAAO;AACpF,QAAO;EAAC;EAAqB;EAAiB;EAAc;EAAK;;;;;;;;;;;;;;;;;;ACtFnE,SAAgB,mBAAmB,IAAY,KAAmB,KAAwB;AACxF,KAAI,QAAQ,KAAM,QAAO,CAAC,YAAY,KAAK;CAC3C,MAAM,OAAO;EAAC;EAAY;EAAO;EAAW;EAAG;AAC/C,KAAI,QAAQ,OAAW,MAAK,KAAK,SAAS,OAAO,IAAI,CAAC;AACtD,QAAO;;;AAIT,SAAgB,yBAAyB,IAAsB;AAC7D,QAAO;EAAC;EAAY;EAAU;EAAW;EAAG;;;AAI9C,SAAgB,iBAAiB,QAAgB,IAAkB;CACjE,MAAM,SAAS,WAAW,QAAQ,GAAG;AACrC,KAAI,CAAC,OAAQ;AACb,QAAO,eAAe;AACtB,YAAW,QAAQ,OAAO;;;;;;;;AAS5B,eAAsB,qBAAqB,QAAgB,IAAuC;AAChG,KAAI,CAAC,GAAI;AAET,KAAI,CADW,WAAW,QAAQ,GAAG,EACxB,aAAc;AAC3B,OAAM,IAAI,SAAe,YAAY;EACnC,IAAI,OAAO;EACX,MAAM,eAAe;AACnB,OAAI,KAAM;AACV,UAAO;AACP,YAAS;;AAEX,MAAI;GACF,MAAM,OAAO,MAAM,UAAU,yBAAyB,GAAG,EAAE,EAAE,OAAO,UAAU,CAAC;AAC/E,QAAK,GAAG,SAAS,OAAO;AACxB,QAAK,GAAG,SAAS,OAAO;UAClB;AACN,WAAQ;;AAEV,aAAW,QAAQ,IAAK,CAAC,OAAO;GAChC;;;;;;;;;;;;;;;;;;;ACpCJ,MAAa,eAAe;CAAC;CAAc;CAAc;CAAgB;CAAgB;CAAO;AAiBhG,SAAgB,WAAW,QAAwB;AACjD,QAAO,KAAK,QAAQ,sBAAsB;;;AAI5C,SAAgB,WAAW,QAAgB,sBAAY,IAAI,MAAM,EAAsB;CACrF,MAAM,OAAO,WAAW,OAAO;AAC/B,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO;AAC9B,KAAI;EACF,MAAM,IAAI,KAAK,MAAM,aAAa,MAAM,OAAO,CAAC;AAChD,MAAI,CAAE,aAAmC,SAAS,EAAE,KAAK,CAAE,QAAO;AAClE,MAAI,EAAE,SAAS,KAAK,MAAM,EAAE,MAAM,IAAI,IAAI,SAAS,CAAE,QAAO;AAC5D,SAAO;SACD;AACN,SAAO;;;AAIX,SAAgB,YAAY,QAAgB,MAAkB,OAAgB,sBAAY,IAAI,MAAM,EAAe;CACjH,MAAM,QAAqB;EACzB;EACA,GAAI,QAAQ,EAAE,OAAO,IAAI,KAAK,IAAI,SAAS,GAAG,MAAM,CAAC,aAAa,EAAE,GAAG,EAAE;EACzE,KAAK,IAAI,aAAa;EACvB;CACD,MAAM,MAAM,GAAG,WAAW,OAAO,CAAC;AAClC,eAAc,KAAK,KAAK,UAAU,OAAO,MAAM,EAAE,GAAG,MAAM,OAAO;AACjE,YAAW,KAAK,WAAW,OAAO,CAAC;AACnC,QAAO;;AAGT,SAAgB,YAAY,QAAsB;AAChD,KAAI,WAAW,WAAW,OAAO,CAAC,CAAE,YAAW,WAAW,OAAO,CAAC;;;AAIpE,SAAgB,cAAc,GAAmB;CAC/C,MAAM,IAAI,iBAAiB,KAAK,EAAE,MAAM,CAAC;AACzC,KAAI,CAAC,EAAG,OAAM,IAAI,mBAAmB,uDAAuD,EAAE,GAAG;AACjG,QAAO,OAAO,EAAE,GAAG,GAAG;EAAE,GAAG;EAAQ,GAAG;EAAW,GAAG;EAAY,CAAC,EAAE;;AAGrE,SAAgB,YAAqB;AACnC,KAAI;AACF,SAAO,gBAAgB,KAAK,aAAa,SAAS,CAAC,MAAM,OAAO,EAAE;GAAE,UAAU;GAAQ,SAAS;GAAM,CAAC,CAAC;SACjG;AACN,SAAO;;;AAwBX,SAAgB,YAAY,GAA6B;CACvD,MAAM,SAAS,YAAiC;EAAE,OAAO;EAAM;EAAQ;CACvE,MAAM,YAAY,YAAiC;EAAE,OAAO,EAAE;EAAQ;EAAQ;AAC9E,KAAI,EAAE,SAAS,aAAc,QAAO,MAAM,oBAAoB;AAC9D,KAAI,EAAE,SAAS,cAAc;AAC3B,MAAI,CAAC,EAAE,MACL,OAAM,IAAI,mBAAmB,sBAAsB,EAAE,SAAS,kEAAkE;AAElI,SAAO,SAAS,oBAAoB;;AAEtC,KAAI,EAAE,SAAS,gBAAgB;AAC7B,MAAI,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,MAAO,QAAO,SAAS,aAAa;AACnE,MAAI,EAAE,oBAAoB,EAAE,MAAO,QAAO,SAAS,oBAAoB;AACvE,SAAO,MAAM,sBAAsB;;AAGrC,KAAI,EAAE,SAAS,UAAU,EAAE,QAAS,QAAO,MAAM,UAAU;AAC3D,KAAI,EAAE,OAAQ,QAAO,MAAM,SAAS;AACpC,KAAI,EAAE,MAAO,QAAO,MAAM,UAAU;AACpC,KAAI,CAAC,EAAE,MAAO,QAAO,MAAM,EAAE,SAAS;AACtC,QAAO,SAAS,UAAU,EAAE,OAAO;;;AAIrC,SAAgB,gBAAgB,SAA0B;AACxD,KAAI;EACF,MAAM,IAAI,IAAI,IAAI,QAAQ,CAAC;AAC3B,SAAO,MAAM,eAAe,MAAM,eAAe,MAAM,SAAS,MAAM;SAChE;AACN,SAAO;;;;AAKX,eAAsB,UAAU,SAAiB,YAAY,KAAwB;AACnF,KAAI;AAEF,UADU,MAAM,MAAM,GAAG,QAAQ,QAAQ,QAAQ,GAAG,CAAC,aAAa,EAAE,QAAQ,YAAY,QAAQ,UAAU,EAAE,CAAC,EACpG,SAAS;SACZ;AACN,SAAO;;;;AAKX,eAAsB,UACpB,QACA,QACA,OAA0C,WACK;CAC/C,MAAM,QAAQ,iBAAiB,OAAO;CACtC,MAAM,aAAa,OAAO,QAAQ,OAAO,UAAU,CAAC,QACjD,CAAC,MAAM,OAAO,EAAE,WAAW,EAAE,WAAW,WAAW,gBAAgB,EAAE,QAAQ,IAAI,kBAAkB,OAAO,KAAK,KAAK,EACtH;AACD,KAAI,CAAC,WAAW,OAAQ,QAAO;EAAE,MAAM;EAAM,KAAK;EAAqB;AACvE,MAAK,MAAM,CAAC,MAAM,MAAM,WAAY,KAAI,MAAM,KAAK,EAAE,QAAQ,CAAE,QAAO;EAAE;EAAM,KAAK;EAAI;AACvF,QAAO;EAAE,MAAM;EAAM,KAAK;EAAc;;;;;;;AAc1C,eAAsB,SACpB,QACA,QACA,OACmE;CACnE,MAAM,SAAS,WAAW,OAAO;AACjC,KAAI,CAAC,OAAQ,QAAO;CACpB,MAAM,OAAO,OAAO;CAEpB,MAAM,QADY,SAAS,eACD,MAAM,UAAU,QAAQ,OAAO,GAAG;EAAE,MAAM;EAAM,KAAK;EAAI;CACnF,MAAM,SAAS,oBAAoB,QAAQ,iBAAiB;CAC5D,MAAM,mBACJ,SAAS,mBACR,kBAAkB,iBAAiB,OAAO,EAAE,iBAAiB,GAAG,MAAM,SAAS,cAAc,OAAO,GAAG;CAC1G,MAAM,SAAS,YAAY;EACzB;EACA,QAAQ,MAAM,UAAU;EACxB,SAAS,MAAM,WAAW;EAC1B,OAAO,MAAM,SAAS;EACtB,SAAS,SAAS,SAAS,WAAW,GAAG;EACzC,OAAO,MAAM;EACb,UAAU,MAAM;EAChB;EACD,CAAC;AACF,QAAO;EAAE,cAAc,OAAO;EAAO,OAAO;GAAE,QAAQ;GAAM,QAAQ,OAAO;GAAQ,OAAO,OAAO,UAAU;GAAM;EAAE;;;;;;;;;AAqBrH,SAAgB,kBACd,OACA,QACA,QACe;AACf,KAAI,CAAC,OAAO,SAAS,MAAM,WAAW,aAAc,QAAO;AAC3D,KAAI,OAAO,SAAU,QAAO;AAC5B,KAAI,OAAO,OAAO,IAAK,QAAO;AAC9B,KAAI,OAAO,gBAAgB,MAAO,QAAO;AACzC,KAAI,QAAQ,WAAW,OAAO,QAAQ,WAAW,IAAK,QAAO,KAAK,OAAO;AACzE,KAAI,CAAC,UAAU,OAAO,UAAU,SAAU,QAAO;AACjD,QAAO;;;;;;;;;;;;;;ACzMT,MAAM,eAAe;AACrB,MAAa,2BAA2B;;;;;;AAWxC,eAAsB,mBAAmB,MAAgD;AACvF,KAAI,CAAC,KAAK,SAAS,QACjB,OAAM,IAAI,mBACR,aAAa,KAAK,aAAa,uFACE,KAAK,aAAa,mBACpD;CAEH,MAAM,QAAQ,mBAAmB,KAAK,SAAS;CAC/C,MAAM,MAAM,GAAG,KAAK,SAAS,QAAQ,QAAQ,QAAQ,GAAG,CAAC;CACzD,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,QAAQ,iBAAiB,WAAW,OAAO,EAAE,UAAU;CAC7D,MAAM,KAAK,KAAK,KAAK;CACrB,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,MAAM,KAAK;GACrB,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,GAAI,QAAQ,EAAE,eAAe,UAAU,SAAS,GAAG,EAAE;IACtD;GACD,MAAM,KAAK,UAAU;IACnB,OAAO,KAAK;IACZ,QAAQ,KAAK;IACb,MAAM,KAAK,QAAQ;IACnB,GAAG;IACH,iBAAiB;IAClB,CAAC;GACF,QAAQ,WAAW;GACpB,CAAC;UACK,GAAG;AACV,MAAI,aAAa,SAAS,EAAE,SAAS,aACnC,OAAM,IAAI,mBACR,aAAa,KAAK,aAAa,mCAAmC,UAAU,IAC7E;AAEH,QAAM,IAAI,mBACR,aAAa,KAAK,aAAa,2BAA2B,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,GACrG;WACO;AACR,eAAa,MAAM;;CAErB,MAAM,aAAa,KAAK,KAAK,GAAG;CAChC,MAAM,OAAO,MAAM,IAAI,MAAM;AAC7B,KAAI,CAAC,IAAI,GACP,OAAM,IAAI,mBACR,aAAa,KAAK,aAAa,2BAA2B,IAAI,OAAO,KAAK,KAAK,MAAM,GAAG,IAAI,GAC7F;CAEH,IAAI;AACJ,KAAI;AACF,WAAS,KAAK,MAAM,KAAK;SACnB;AACN,QAAM,IAAI,mBAAmB,aAAa,KAAK,aAAa,gCAAgC;;CAE9F,MAAM,MAAM,OAAO,OAAO,IAAI;AAC9B,KAAI,CAAC,IACH,OAAM,IAAI,mBAAmB,aAAa,KAAK,aAAa,2CAA2C;CAEzG,MAAM,QAAQ,OAAO,KAAK,KAAK,SAAS;AACxC,WAAU,QAAQ,KAAK,QAAQ,EAAE,EAAE,WAAW,MAAM,CAAC;AACrD,eAAc,KAAK,SAAS,MAAM;AAClC,QAAO;EACL,IAAI;EACJ,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,UAAU,KAAK;EACf;EACA,OAAO,MAAM;EACd;;;;;;AC9FH,MAAa,gBAAgB;;AAG7B,SAAgB,cAAc,MAAyB,QAAQ,KAAoB;CACjF,MAAM,KAAK,IAAI;AACf,QAAO,OAAO,OAAO,YAAY,GAAG,MAAM,GAAG,GAAG,MAAM,GAAG;;;;;;;;;;AAW3D,SAAgB,YACd,UACA,IACA,MACQ;CACR,MAAM,OAAO,IAAI,IAAI,SAAS,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;CACpD,IAAI,QAAQ;CACZ,IAAI,MAAM,KAAK,IAAI,GAAG;CACtB,MAAM,OAAO,IAAI,IAAY,CAAC,GAAG,CAAC;AAClC,QAAO,KAAK,UAAU,CAAC,KAAK,IAAI,IAAI,OAAO,EAAE;AAC3C,OAAK,IAAI,IAAI,OAAO;EACpB,MAAM,OAAO,KAAK,IAAI,IAAI,OAAO;AACjC,MAAI,CAAC,KAAM;AACX,MAAI,EAAE,MAAM,cAAc,KAAK,WAAW,QAAS,UAAS;AAC5D,QAAM;;AAER,QAAO;;;AAIT,SAAgB,gBAAgB,UAA0B,QAAgC;AACxF,QAAO,SAAS,QAAQ,MAAM,EAAE,WAAW,UAAU,OAAO,EAAE,CAAC;;;;;;;AAQjE,SAAgB,mBACd,QACA,QACA,MACA,WAA2B,aAAa,OAAO,EACzC;AACN,KAAI,CAAC,SAAS,MAAM,MAAM,EAAE,OAAO,OAAO,CAAE;CAC5C,MAAM,QAAQ,YAAY,UAAU,OAAO;AAC3C,KAAI,QAAQ,IAAI,KAAK,SACnB,OAAM,IAAI,MACR,gBAAgB,OAAO,iBAAiB,MAAM,gCAChB,KAAK,SAAS,gIAE7C;CAEH,MAAM,OAAO,gBAAgB,UAAU,OAAO;AAC9C,KAAI,KAAK,UAAU,KAAK,YACtB,OAAM,IAAI,MACR,gBAAgB,OAAO,eAAe,KAAK,OAAO,wBAC5C,KAAK,KAAK,MAAM,EAAE,GAAG,CAAC,KAAK,KAAK,CAAC,oCAAoC,KAAK,YAAY,mEAE7F;;;;;;AAQL,SAAgB,aAAa,UAA8B,MAAyB,QAAQ,KAAoB;AAC9G,QAAO,YAAY,cAAc,IAAI;;;AAIvC,SAAgB,WAAW,QAAgB,IAAqB;AAC9D,QAAO,WAAW,QAAQ,GAAG,KAAK;;;;;;;;;;;;;;;;;AAkBpC,SAAgB,sBAAsB,QAAgB,QAA+B;CACnF,MAAM,WAAW,aAAa,OAAO;CACrC,MAAM,OAAO,gBAAgB,UAAU,OAAO;CAC9C,MAAM,YAAY,SAAS,QACxB,MAAM,EAAE,WAAW,UAAU,EAAE,UAAU,aAAa,CAAC,OAAO,EAAE,CAClE;AACD,MAAK,MAAM,SAAS,UAClB,YAAW,QAAQ;EAAE,GAAG;EAAO,OAAO;EAAU,MAAM,UAAU,OAAO;EAAU,CAAC;CAEpF,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,UAAU,CAAC,KAAK,MAAM,EAAE,GAAG;AACpD,KAAI,CAAC,IAAI,OAAQ,QAAO;AACxB,QACE,eAAe,IAAI,OAAO,aAAa,IAAI,WAAW,IAAI,KAAK,IAAI,kBAC/D,IAAI,KAAK,KAAK,CAAC;;;;;;;;;;;;;;;;ACrHvB,SAAgB,mBAAmB,QAAgB,IAAoB;AACrE,QAAO,KAAK,QAAQ,GAAG,GAAG,OAAO;;;;;;;;;AAUnC,SAAgB,qBACd,QACA,IACA,QAC2B;CAC3B,MAAM,OAAO,mBAAmB,QAAQ,GAAG;AAC3C,KAAI;AACF,MAAI,WAAW,KAAK,CAAE,YAAW,KAAK;SAChC;CAGR,MAAM,SAAS,cAAc,WAAmB;EAC9C,IAAI,MAAM;AACV,SAAO,GAAG,eAAe,GAAG;AAC5B,SAAO,GAAG,QAAQ,OAAO,UAAkB;AACzC,UAAO,MAAM,SAAS,OAAO;GAC7B,IAAI;AACJ,WAAQ,KAAK,IAAI,QAAQ,KAAK,KAAK,GAAG;IACpC,MAAM,OAAO,IAAI,MAAM,GAAG,GAAG,CAAC,QAAQ,OAAO,GAAG;AAChD,UAAM,IAAI,MAAM,KAAK,EAAE;IACvB,IAAI;AACJ,QAAI,KAAK,MAAM,CACb,KAAI;AACF,eAAU,MAAM,OAAO,KAAK;aACrB,GAAG;AACV,eAAW,EAAY;;AAG3B,QAAI,CAAC,OAAO,UAAW,QAAO,MAAM,OAAO,YAAY,YAAY,UAAU,OAAO,QAAQ,QAAQ,OAAO,IAAI,CAAC,MAAM,OAAO;;IAE/H;GACF;AACF,QAAO,OAAO,KAAK;AACnB,QAAO,GAAG,eAAe;AACvB,MAAI;AACF,OAAI,WAAW,KAAK,CAAE,YAAW,KAAK;UAChC;GAGR;AACF,QAAO;;;;;;AAOT,SAAgB,YAAY,QAAgB,IAAY,MAAc,YAAY,KAAuB;CACvG,MAAM,SAAS,WAAW,QAAQ,GAAG;AACrC,KAAI,CAAC,OACH,QAAO,QAAQ,uBAAO,IAAI,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAE7D,KAAI,CAAC,OAAO,OAAO,CACjB,QAAO,QAAQ,uBACb,IAAI,MACF,UAAU,GAAG,0BAA0B,OAAO,MAAM,kDACH,GAAG,WACrD,CACF;CAEH,MAAM,OAAO,mBAAmB,QAAQ,GAAG;AAC3C,KAAI,CAAC,WAAW,KAAK,CACnB,QAAO,QAAQ,uBACb,IAAI,MAAM,UAAU,GAAG,2BAA2B,KAAK,qCAAqC,CAC7F;AAEH,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,OAAO,QAAQ,KAAK;EAC1B,MAAM,QAAQ,MAAa;AACzB,QAAK,SAAS;AACd,0BAAO,IAAI,MAAM,yBAAyB,GAAG,IAAI,EAAE,UAAU,CAAC;;AAEhE,OAAK,WAAW,iBAAiB,qBAAK,IAAI,MAAM,UAAU,CAAC,CAAC;AAC5D,OAAK,KAAK,UAAU,MAAa,KAAK,EAAE,CAAC;AACzC,OAAK,KAAK,iBAAiB;AACzB,QAAK,MAAM,KAAK,QAAQ,OAAO,IAAI,GAAG,KAAK;IAC3C;AACF,OAAK,KAAK,SAAS,MAAc;AAC/B,QAAK,KAAK;GACV,MAAM,QAAQ,EAAE,SAAS,OAAO,CAAC,MAAM;AACvC,OAAI,UAAU,KAAM,SAAQ,KAAK;OAC5B,wBAAO,IAAI,MAAM,UAAU,GAAG,6BAA6B,MAAM,QAAQ,SAAS,GAAG,IAAI,uBAAuB,CAAC;IACtH;AACF,OAAK,KAAK,eAAe,qBAAK,IAAI,MAAM,mDAAmD,CAAC,CAAC;GAC7F;;;;;;;;;;;;;;;;;AC9FJ,MAAa,gBAAgB;CAAC;CAAY;CAAU;CAAY;CAAU;;AAkB1E,SAAgB,UAAU,QAAgB,IAAoB;AAC5D,QAAO,KAAK,QAAQ,GAAG,GAAG,cAAc;;AAG1C,SAAgB,cAAc,GAA8B;AAC1D,QAAO,OAAO,MAAM,YAAa,cAAoC,SAAS,EAAE;;;;;;;AAQlF,SAAgB,aACd,GACA,SAAgD,EAAE,EACzC;AACT,KAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,QAAQ,EAAE,CACzD,OAAM,IAAI,MAAM,wCAAwC;CAE1D,MAAM,IAAI;CACV,MAAM,OAAO,OAAO,EAAE,SAAS,YAAY,EAAE,OAAO,EAAE,OAAO,OAAO;CACpE,MAAM,KAAK,OAAO,EAAE,OAAO,YAAY,EAAE,KAAK,EAAE,KAAK,OAAO;AAC5D,KAAI,CAAC,KAAM,OAAM,IAAI,MAAM,+CAA+C;AAC1E,KAAI,CAAC,GAAI,OAAM,IAAI,MAAM,4CAA4C;AACrE,KAAI,CAAC,cAAc,EAAE,KAAK,CACxB,OAAM,IAAI,MAAM,kCAAkC,cAAc,KAAK,KAAK,GAAG;CAE/E,MAAM,OAAO,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AACnD,KAAI,CAAC,KAAK,MAAM,CAAE,OAAM,IAAI,MAAM,mCAAmC;CACrE,MAAM,OACJ,OAAO,EAAE,SAAS,YAAY,EAAE,SAAS,QAAQ,CAAC,MAAM,QAAQ,EAAE,KAAK,GAClE,EAAE,OACH;AACN,QAAO;EAAE;EAAM;EAAI,MAAM,EAAE;EAAM;EAAM,GAAI,OAAO,EAAE,MAAM,GAAG,EAAE;EAAG;;;AAIpE,SAAgB,cAAc,QAAgB,GAAY,sBAAY,IAAI,MAAM,EAAQ;CACtF,MAAM,OAAO,UAAU,QAAQ,EAAE,GAAG;CACpC,MAAM,MAAM,QAAQ,KAAK;AACzB,KAAI,CAAC,WAAW,IAAI,CAAE,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACzD,gBAAe,MAAM,KAAK,UAAU;EAAE,GAAG;EAAG,KAAK,IAAI,aAAa;EAAE,CAAC,GAAG,MAAM,OAAO;;;AAIvF,SAAgB,UAAU,QAAgB,IAAuB;CAC/D,MAAM,OAAO,UAAU,QAAQ,GAAG;AAClC,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE;CAChC,MAAM,MAAiB,EAAE;AACzB,MAAK,MAAM,QAAQ,aAAa,MAAM,OAAO,CAAC,MAAM,KAAK,EAAE;AACzD,MAAI,CAAC,KAAK,MAAM,CAAE;AAClB,MAAI;AACF,OAAI,KAAK,KAAK,MAAM,KAAK,CAAY;UAC/B;;AAIV,QAAO;;;AAIT,SAAgB,eAAe,GAAoD;AACjF,QAAO,iBAAiB,EAAE,KAAK,KAAK,EAAE,KAAK,IAAI,EAAE,KAAK,QAAQ,QAAQ,IAAI,CAAC,MAAM;;;AAInF,SAAgB,iBAAiB,MAAuB;AACtD,QAAO,IAAI,OAAO,gCAAgC,cAAc,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,KAAK;;;;;;;;;AAe9F,eAAsB,eACpB,QACA,GACA,OAAoB,EAAE,EACtB,sBAAY,IAAI,MAAM,EACJ;AAClB,eAAc,QAAQ,GAAG,IAAI;CAC7B,MAAM,MAAM,KAAK,SAAS,IAAY,SAAiB,YAAY,QAAQ,IAAI,KAAK;CACpF,MAAM,SAAS,WAAW,QAAQ,EAAE,GAAG;AACvC,KAAI,UAAU,OAAO,OAAO,CAC1B,KAAI;AACF,QAAM,IAAI,EAAE,IAAI,eAAe,EAAE,CAAC;SAC5B;AAIV,QAAO;;;;;;;;AAST,eAAsB,kBACpB,QACA,KACA,SACA,OAAoB,EAAE,EACJ;CAClB,MAAM,OAAO,IAAI;AACjB,KAAI,CAAC,KACH,OAAM,IAAI,MACR,0IAED;CAEH,MAAM,KAAK,WAAW,QAAQ,KAAK;AACnC,KAAI,CAAC,GAAI,OAAM,IAAI,MAAM,8BAA8B,KAAK,4BAA4B;AACxF,KAAI,CAAC,GAAG,UAAU,CAAC,WAAW,QAAQ,GAAG,OAAO,CAC9C,OAAM,IAAI,MACR,UAAU,KAAK,gDACD,GAAG,UAAU,SAAS,yCACrC;AAGH,QAAO,eAAe,QADZ,aAAa,SAAS;EAAE,MAAM;EAAM,IAAI,GAAG;EAAQ,CAAC,EAC7B,KAAK;;;;;;;;;;;;;ACnIxC,MAAM,UAAU;AAEhB,SAAgB,SAAS,KAA8B;AACrD,KAAI;EACF,MAAM,IAAI,QAAQ,KAChB,aAAa,MAAM;GAAC;GAAM;GAAW;GAAM;GAAY;GAAM,OAAO,IAAI;GAAC,EAAE;GAAE,UAAU;GAAQ,KAAK;IAAE,GAAG,QAAQ;IAAK,QAAQ;IAAK;GAAE,CAAC,CAAC,MAAM,KAAK,CAAC,MAAM,GAC1J;AACD,MAAI,CAAC,EAAG,QAAO;EACf,MAAM,IAAI,KAAK,MAAM,EAAE,GAAG;AAC1B,SAAO;GAAE,KAAK,EAAE;GAAI,WAAW,OAAO,SAAS,EAAE,GAAG,IAAI;GAAM;SACxD;AACN,SAAO;;;AAIX,SAAgB,YAAY,MAAsB;AAChD,KAAI,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAE,QAAO;AACvC,KAAI;EACF,MAAM,QAAQ,aAAa,MAAM,OAAO,CAAC,MAAM,KAAK;AACpD,OAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,IAAK,KAAI,MAAM,GAAG,MAAM,CAAE,QAAO,MAAM,GAAG,MAAM;SACjF;AAGR,QAAO;;AAGT,MAAa,eAAyB;CAAE,MAAM;CAAU,SAAS;CAAa;;AAG9E,SAAgB,WAAW,MAAsB;CAC/C,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,CAAC;AACvC,QAAO,GAAG,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;;;;;;;AAQlE,SAAgB,SAAS,KAAa,MAAM,IAAY;CACtD,IAAI,IAAI,IAAI,MAAM;AAClB,MAAK,IAAI,OAAO,IAAI,SAAS,IAAK;AAChC,SAAO;AACP,MAAI,EAAE,QAAQ,aAAa,GAAG,CAAC,QAAQ,2CAA2C,GAAG;AACrF,MAAI,EAAE,QAAQ,uBAAuB,KAAK,CAAC,MAAM;;AAEnD,KAAI,EAAE,QAAQ,6BAA6B,GAAG,CAAC,QAAQ,qCAAqC,GAAG;AAC/F,QAAO,UAAU,GAAG,IAAI;;;;;;;AAQ1B,SAAgB,YAAY,GAAc,MAAM,KAAK,KAAK,EAAE,IAAc,cAAc,QAAQ,IAAY;CAC1G,MAAM,OAAO,eAAe,SAAS,EAAE,KAAK,MAAM;CAClD,MAAM,IAAI,EAAE,KAAK,EAAE,IAAI;CACvB,MAAM,SAAS,KAAK,EAAE,cAAc,QAAQ,EAAE,cAAc,QAAQ,KAAK,IAAI,EAAE,YAAY,EAAE,UAAU,GAAG;AAC1G,KAAI,CAAC,KAAK,OAAQ,QAAO,GAAG,KAAK;CACjC,MAAM,QAAQ,CAAC,KAAK;CACpB,MAAM,QAAQ,EAAE,aAAa,EAAE;AAC/B,KAAI,UAAU,KAAM,OAAM,KAAK,WAAW,YAAY,MAAM,SAAS,IAAK,GAAG;CAC7E,MAAM,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,GAAG;AACxC,KAAI,KAAM,OAAM,KAAK,QAAQ,UAAU,MAAM,GAAG,GAAG;AACnD,QAAO,MAAM,KAAK,MAAM;;;AAI1B,SAAgB,YACd,SACA,WAA4C,MAAM;CAChD,MAAM,IAAI,OAAO,WAAW,EAAE,CAAC,MAAM,CAAC;AACtC,QAAO,OAAO,UAAU,EAAE,IAAI,IAAI,IAAI,IAAI;GAEO;CACnD,MAAM,IAAI,sEAAsE,KAAK,QAAQ;AAC7F,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,MAAM,iBAAiB,KAAK,QAAQ,GAAG,MAAM;AACnD,QAAO;EAAE,KAAK,EAAE,KAAK,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,GAAG;EAAE;EAAK;;AAG1D,SAAS,WAAW,GAAmB;AACrC,KAAI;AACF,SAAO,aAAa,GAAG,OAAO;SACxB;AACN,SAAO;;;;AAKX,SAAgB,eAAe,SAAiB,MAAM,KAAK,KAAK,EAAE,IAAc,cAAc,SAAuD;CACnJ,MAAM,IAAI,YAAY,SAAS,QAAQ;AACvC,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,IAAI,EAAE,QAAQ,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG;AAC3C,KAAI,EAAE,QAAQ,QAAQ,CAAC,EAAG,QAAO,eAAe,EAAE,QAAQ,OAAO,qBAAqB,OAAO,EAAE,MAAM;AACrG,QAAO,YAAY;EAAE,KAAK,EAAE;EAAK,KAAK,EAAE;EAAK,WAAW,EAAE;EAAW,KAAK,EAAE;EAAK,EAAE,KAAK,EAAE;;;AAI5F,SAAgB,iBAAiB,MAA6B;CAC5D,MAAM,OAAO,4DAA4D,KAAK,KAAK;AACnF,KAAI,KAAM,QAAO,OAAO,KAAK,OAAO,SAAY,UAAU,KAAK,GAAG,KAAK,KAAK,KAAK,KAAK,UAAU,KAAK,OAAO;CAC5G,MAAM,QAAQ,2CAA2C,KAAK,KAAK;AACnE,QAAO,QAAQ,uBAAuB,MAAM,GAAG,QAAQ,MAAM,GAAG,oBAAoB;;;AAItF,SAAgB,aAAa,SAAgC;CAC3D,MAAM,IAAI,wEAAwE,KAAK,QAAQ;AAC/F,QAAO,IAAI,8BAA8B,SAAS,EAAE,GAAG,KAAK;;;;;;;;;;;;;ACnI9D,MAAa,oBAAoB;AACjC,MAAa,gBAAgB;AAC7B,MAAM,UAAU;AAChB,MAAM,eAAe;;AAErB,MAAM,iBAAiB;;AAwBvB,SAAgB,aAAa,QAAgB,IAA2C;AACtF,SAAQ,MAAM;EACZ,MAAM,IAAI,WAAW,QAAQ,GAAG;AAChC,MAAI,CAAC,EAAG;AACR,IAAE,YAAY;AACd,aAAW,QAAQ,EAAE;;;AAIzB,MAAa,WAAuB;CAClC,MAAM,KAAK;AACT,MAAI;AACF,WAAQ,KAAK,KAAK,EAAE;AACpB,UAAO;WACA,GAAG;AACV,UAAQ,EAA4B,SAAS,UAAU,YAAY;;;CAGvE,UAAU,KAAK;AACb,MAAI;GACF,MAAM,IAAI,KAAK,MAAM,aAAa,MAAM;IAAC;IAAM;IAAW;IAAM,OAAO,IAAI;IAAC,EAAE;IAAE,UAAU;IAAQ,KAAK;KAAE,GAAG,QAAQ;KAAK,QAAQ;KAAK;IAAE,CAAC,CAAC,MAAM,CAAC;AACjJ,UAAO,OAAO,SAAS,EAAE,GAAG,IAAI;UAC1B;AACN,UAAO;;;CAGX,WAAW,KAAK,KAAK;CACrB,QAAQ,OAAO,IAAI,SAAS,MAAM,WAAW,GAAG,GAAG,CAAC;CACpD,MAAM,MAAM,QAAQ,IAAI,EAAE;CAC1B,MAAM;CACP;AAED,MAAM,aAAa,SAAuC;AACxD,KAAI,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAE,QAAO,EAAE;CACzC,MAAM,IAAI,aAAa,MAAM,OAAO;AACpC,QAAO,MAAM,KAAK,EAAE,GAAG,EAAE,QAAQ,OAAO,GAAG,CAAC,MAAM,KAAK;;AAGzD,SAAS,KAAK,KAAa,KAA6B;AACtD,MAAK,MAAM,KAAK;EAAC,OAAO,GAAG,IAAI;EAAM,OAAO,IAAI,QAAQ,aAAa,MAAM;EAAE,QAAQ,IAAI;EAAK,CAC5F,KAAI,KAAK,WAAW,EAAE,CAAE,QAAO,aAAa,GAAG,OAAO,CAAC,MAAM;AAE/D,QAAO;;AAGT,eAAsB,OAAO,GAAe,IAAgB,UAA2B;CACrF,MAAM,QAAQ,KAAK,IAAI,EAAE,WAAW,mBAAmB,cAAc,GAAG;CACxE,MAAM,OAAO,EAAE,QAAQ;CACvB,MAAM,KAAK,EAAE,KAAK;CAClB,MAAM,YAAY,UAAU,EAAE,IAAI,CAAC;CAEnC,IAAI,WAA0B;CAC9B,MAAM,eAAuB;EAC3B,MAAM,KAAK,KAAK,EAAE,KAAK,EAAE,IAAI;EAC7B,MAAM,QAAQ,aAAa,OAAO,UAAU,YAAY,EAAE,KAAK,GAAG,YAAY,IAAK,KAAK;AACxF,IAAE,IAAI,OAAO,EAAE,IAAI,OAAO,QAAQ,OAAO,OAAO,eAAe,OAAO,KAAK;AAC3E,MAAI,OAAO,EAAG,MAAK,MAAM,KAAK,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAE,GAAE,IAAI,EAAE;AACrE,SAAO;;AAGT,KAAI,CAAC,OAAO,UAAU,EAAE,IAAI,IAAI,EAAE,OAAO,GAAG;AAC1C,IAAE,IAAI,wBAAwB,EAAE,MAAM;AACtC,SAAO;;CAET,MAAM,QAAQ,EAAE,MAAM,EAAE,IAAI;AAC5B,KAAI,UAAU,WAAW;AACvB,IAAE,IAAI,gBAAgB,EAAE,IAAI,4BAA4B;AACxD,SAAO;;AAET,KAAI,UAAU,QAAQ;AAEpB,MAAI,KAAK,EAAE,KAAK,EAAE,IAAI,KAAK,QAAS,EAAE,OAAO,WAAW,EAAE,IAAI,CAAG,QAAO,QAAQ;AAChF,IAAE,IAAI,gBAAgB,EAAE,IAAI,iBAAiB;AAC7C,SAAO;;CAGT,MAAM,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI;AAC7B,YAAW,GAAG,aAAa;AAC3B,GAAE,OAAO;EAAE,KAAK,EAAE;EAAK,KAAK,GAAG,OAAO;EAAI,WAAW,GAAG,aAAa;EAAM,KAAK,EAAE,OAAO;EAAM,CAAC;AAChG,KAAI;EACF,IAAI,WAAW,KAAK;AACpB,SAAO,EAAE,KAAK,GAAG,KAAK,OAAO;AAC3B,SAAM,EAAE,MAAM,KAAK,IAAI,SAAS,KAAK,IAAI,GAAG,SAAS,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;GACrE,MAAM,KAAK,EAAE,UAAU,EAAE,IAAI;AAC7B,OAAI,EAAE,MAAM,EAAE,IAAI,KAAK,WAAY,OAAO,QAAQ,KAAK,KAAK,eAAiB,QAAO,QAAQ;AAC5F,OAAI,EAAE,KAAK,IAAI,UAAU;AACvB,MAAE,IAAI,WAAW,KAAK,OAAO,EAAE,KAAK,GAAG,MAAM,IAAK,CAAC,UAAU,UAAU,EAAE,IAAI,CAAC,SAAS,UAAU,QAAQ;AACzG,gBAAY;;;AAGhB,IAAE,IAAI,uBAAuB,KAAK,OAAO,EAAE,KAAK,GAAG,MAAM,IAAK,CAAC,SAAS,EAAE,IAAI,uBAAuB;AACrG,MAAI,OAAO,EAAG,MAAK,MAAM,KAAK,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAE,GAAE,IAAI,EAAE;AACrE,SAAO;WACC;AACR,IAAE,OAAO,KAAK;;;;AAKlB,MAAa,cACX;;;;;;;;;;;;;;ACjIF,MAAa,oBACX;;AAGF,MAAa,0BAA0B;AAQvC,IAAI,aAAmC;;;;;;AAOvC,SAAgB,UAAyB;AACvC,KAAI,WAAY,QAAO;AACvB,KAAI;EAMF,MAAM,QALM,aAAa,YAAY,CAAC,YAAY,OAAO,EAAE;GACzD,SAAS;GACT,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,UAAU;GACX,CAAC,CACgB,MAAM,KAAK,CAAC,QAAQ,MAAM,EAAE,MAAM,CAAC;AACrD,MAAI,MAAM,IAAI,WAAW,OAAO,EAAE;AAChC,gBAAa;IAAE,MAAM,MAAM;IAAI,YAAY,MAAM,MAAM;IAAyB,QAAQ;IAAY;AACpG,UAAO;;SAEH;AAGR,cAAa;EAAE,MAAM;EAAmB,YAAY;EAAyB,QAAQ;EAAW;AAChG,QAAO;;;;;;AAYT,SAAgB,UAAU,MAAsB;CAC9C,IAAI,KAAK,QAAQ,IAAI,MAAM;CAC3B,MAAM,SAAS,EAAE,MAAM,gCAAgC;AACvD,KAAI,OAAQ,KAAI,OAAO,GAAG,MAAM;AAChC,QAAO,EAAE,QAAQ,0CAA0C,GAAG,CAAC,MAAM;;;;;;;;;AAgBvE,SAAgB,eAAe,MAAgC;CAC7D,MAAM,QAAQ,IAAI,IAAI;EAAC;EAAK;EAAK;EAAK;EAAK;EAAK;EAAI,CAAC;CACrD,MAAM,QAAQ,UAAU,KAAK,CAC1B,MAAM,KAAK,CACX,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,QAAQ,MAAM,KAAK,CAAC,EAAE,WAAW,MAAM,CAAC;AAC3C,KAAI,CAAC,MAAM,OAAQ,QAAO;CAC1B,MAAM,OAAO,MAAM,GAAG;AACtB,KAAI,CAAC,MAAM,IAAI,KAAK,CAAE,QAAO;CAE7B,MAAM,SAAiC,EAAE;CACzC,MAAM,UAAkC,EAAE;AAC1C,MAAK,MAAM,QAAQ,MAAM,MAAM,EAAE,EAAE;EACjC,MAAM,KAAK,KAAK,QAAQ,IAAI;AAC5B,MAAI,KAAK,EAAG;EACZ,MAAM,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC,MAAM;EACpC,MAAM,QAAQ,KAAK,MAAM,KAAK,EAAE,CAAC,MAAM;AACvC,MAAI,IAAI,WAAW,IAAI,CACrB,SAAQ,IAAI,MAAM,EAAE,IAAI;MAExB,QAAO,OAAO;;AAGlB,QAAO;EAAE;EAAM;EAAQ;EAAS;;;AAIlC,SAAS,OAAO,OAAwB;AACtC,QAAO,MAAM,SAAS,IAAI;;;;;;AAO5B,SAAgB,kBAAkB,QAAiC;CACjE,MAAM,IAAI,OAAO;CACjB,MAAM,MAAoB,EAAE,QAAQ,OAAO;AAE3C,KAAI,EAAE,EACJ,KAAI,UAAU,EAAE,EACb,MAAM,IAAI,CACV,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,OAAO,QAAQ,CACf,KAAK,UAAU;EACd,MAAM,QAAQ,MAAM,QAAQ,IAAI;EAChC,MAAM,QAAQ,MAAM,QAAQ,IAAI;EAChC,MAAM,MAAM,SAAS,MAAM,QAAQ,KAAK,QAAQ,SAAS,QAAQ;AACjE,MAAI,MAAM,EAAG,QAAO;GAAE,MAAM;GAAO,SAAS;GAAI;AAChD,SAAO;GAAE,MAAM,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM;GAAE,SAAS,MAAM,MAAM,MAAM,EAAE,CAAC,MAAM;GAAE;GACjF;AAGN,KAAI,EAAE,GAAG;EACP,MAAM,SAAS,EAAE,EAAE,MAAM,MAAM,CAAC,OAAO,QAAQ;AAE/C,MAAI,SADc,OAAO,SAAS,KAAK,OAAO,OAAO,QAAQ,gBAAgB,KAAK,IAAI,CAAC,GAEnF,OAAO,KAAK,WAAW;GACrB,MAAM,MAAM,MAAM,GAAG,GAAG;GACxB,IAAI,OAAO,MAAM;GACjB,QAAQ,MAAM,MAAM,GAAG;GACxB,EAAE,GACH,CAAC;GAAE,MAAM;GAAS,QAAQ,EAAE;GAAG,CAAC;;AAGtC,KAAI,EAAE,EAAG,KAAI,WAAW,EAAE,EAAE,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ;CAE3E,MAAM,OAAiB,EAAE;AACzB,KAAI,EAAE,EAAG,MAAK,KAAK,EAAE,EAAE;AACvB,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,EAAE,CACpC,MAAK,EAAE,SAAS,IAAI,IAAI,EAAE,SAAS,IAAI,KAAK,MAAM,OAAO,MAAM,MAAO,MAAK,KAAK,GAAG,EAAE,IAAI,IAAI;AAE/F,KAAI,KAAK,OAAQ,KAAI,OAAO;AAE5B,KAAI,EAAE,EAAG,KAAI,QAAQ,EAAE;CACvB,MAAM,MAAM,EAAE,KAAK,EAAE;AACrB,KAAI,QAAQ,OAAO,QAAQ,OAAO,QAAQ,OAAO,QAAQ,OAAO,QAAQ,IAAK,KAAI,SAAS;AAC1F,KAAI,EAAE,EAAG,KAAI,MAAM,EAAE;AAErB,QAAO;;;;;;;;AAoBT,SAAgB,YAAY,MAAmC;AAC7D,KAAI;EACF,MAAM,MAAM,aAAa,YAAY;GAAC;GAAY;GAAS;GAAK;GAAS,EAAE;GACzE,OAAO,UAAU,KAAK;GACtB,SAAS;GACT,OAAO;IAAC;IAAQ;IAAQ;IAAS;GACjC,UAAU;GACX,CAAC;EACF,MAAM,SAAS,KAAK,MAAM,IAAI;EAC9B,MAAM,UAAU,OAAO,UAAU,EAAE,EAAE,KAAK,MAAM,EAAE,WAAW,EAAE,QAAQ,gBAAgB;AACvF,SAAO;GAAE,IAAI,OAAO,OAAO;GAAO;GAAQ,WAAW;GAAY;UAC1D,GAAG;EAEV,MAAM,MAAO,EAAmC;AAChD,MAAI,IACF,KAAI;GACF,MAAM,SAAS,KAAK,MAAM,IAAI,UAAU,CAAC;GACzC,MAAM,UAAU,OAAO,UAAU,EAAE,EAAE,KAAK,OAAO,GAAG,WAAW,GAAG,QAAQ,gBAAgB;AAC1F,UAAO;IAAE,IAAI,OAAO,OAAO;IAAO;IAAQ,WAAW;IAAY;UAC3D;AAIV,SAAO;GAAE,IAAI;GAAM,QAAQ,EAAE;GAAE,WAAW;GAAQ;;;;;;;;;;;;;;;;ACxLtD,MAAa,gBAAgB;;AAG7B,MAAM,yBAAyB,IAAI,IAAI,CAAC,aAAa,SAAS,CAAC;;;;;;;;AAS/D,SAAS,iBAA2B;CAClC,MAAM,OAAO,SAAS;AACtB,QAAO;EACL,KAAK;EACL,KAAK;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;AAIH,SAAS,gBAAgB,MAAwB;AAC/C,QAAO,CACL,MACA,6KACD;;;AAIH,SAAS,qBAA+B;AACtC,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;;;AAIH,SAAS,0BAAoC;AAC3C,QAAO;EACL;EACA;EACA;EACD;;;AAIH,SAAS,oBAAoB,QAAgC;CAC3D,MAAM,QAAQ;EACZ;EACA;EACA;EACA,GAAG,yBAAyB;EAC7B;AACD,OAAM,KACJ,GAAI,WAAW,QACX,gBAAgB,GAChB,gBACE,sGACD,CACN;AACD,QAAO,MAAM,KAAK,KAAK;;;AAIzB,SAAS,mBAAmB,QAAgC;CAC1D,MAAM,QAAQ;EACZ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,uLAAuL,YAAY;EACnM;EACA,GAAG,oBAAoB;EACvB;EACA,gDAAgD,cAAc;EAC9D;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD;AACD,OAAM,KACJ,GAAI,WAAW,QACX,gBAAgB,GAChB,gBACE,sGACD,CACN;AACD,KAAI,WAAW,OACb,OAAM,KACJ,sGACA,6GACA,wDACD;AAEH,QAAO,MAAM,KAAK,KAAK;;;;;;;;;AAUzB,SAAgB,qBAAqB,KAAyB,QAAgC;AAC5F,QAAO,uBAAuB,IAAI,OAAO,GAAG,GAAG,oBAAoB,OAAO,GAAG,mBAAmB,OAAO;;;;;;;;;;AAWzG,SAAgB,cAAc,QAAgC;AAC5D,QAAO,WAAW,QACd,mFACA;;;AAIN,MAAa,iBACX;;AAMF,MAAa,yBAAyB,mBAAmB,MAAM;;;;;;;;;;AA6B/D,SAAgB,kBAAkB,MAAmC;CACnE,MAAM,WAAW,QAAQ,IAAI,MAAM;AACnC,KAAI,CAAC,QAAS,QAAO;CAErB,MAAM,MAAM,eAAe,QAAQ;AACnC,KAAI,OAAO,IAAI,SAAS,IAAK,QAAO,kBAAkB,IAAI;CAE1D,MAAM,aAAuB,EAAE;CAC/B,MAAM,QAAQ,QAAQ,MAAM,sCAAsC;AAClE,KAAI,MAAO,YAAW,KAAK,MAAM,GAAG;AACpC,KAAI,QAAQ,WAAW,IAAI,CAEzB,YAAW,KAAK,QAAQ;CAG1B,MAAM,QAAQ,QAAQ,QAAQ,IAAI;CAClC,MAAM,OAAO,QAAQ,YAAY,IAAI;AACrC,KAAI,SAAS,KAAK,OAAO,MAAO,YAAW,KAAK,QAAQ,MAAM,OAAO,OAAO,EAAE,CAAC;AAC/E,MAAK,MAAM,QAAQ,WACjB,KAAI;EACF,MAAM,IAAI,KAAK,MAAM,KAAK;AAC1B,MAAI,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,EAAE,IAAI,gBAAgB,EAAE,CAChF,QAAO;GAAE,GAAI;GAAoB,QAAQ;GAAQ;SAE7C;AAIV,QAAO;;;AAIT,SAAS,gBAAgB,GAAoB;CAC3C,MAAM,IAAI;AACV,QAAO,MAAM,QAAQ,EAAE,QAAQ,IAAI,MAAM,QAAQ,EAAE,OAAO,IAAI,OAAO,EAAE,UAAU;;;;;;AAOnF,SAAgB,aAAa,GAAU,QAAgB,GAAiB,MAAM,IAAc;CAC1F,MAAM,MAAgB,EAAE;CACxB,MAAM,OAAO,QAA+B,MAAM,EAAE,EAAE,SAAS;AAC/D,KAAI,IAAI,EAAE,QAAQ,EAAE;AAClB,MAAI,KAAK,GAAG,SAAS,EAAE,QAAQ,UAAU,GAAG;AAC5C,OAAK,MAAM,MAAM,EAAE,QACjB,KAAI,KAAK,GAAG,OAAO,IAAI,SAAS,GAAG,QAAQ,KAAK,IAAI,CAAC,KAAK,UAAU,GAAG,WAAW,IAAI,GAAG,GAAG;;AAGhG,KAAI,IAAI,EAAE,OAAO,EAAE;AACjB,MAAI,KAAK,GAAG,SAAS,EAAE,QAAQ,SAAS,GAAG;AAC3C,OAAK,MAAM,MAAM,EAAE,QAAS;GAC1B,MAAM,OAAO,GAAG,OAAO,QAAQ,EAAE,OAAO,IAAI,GAAG,GAAG,OAAO,OAAO,EAAE,SAAS,IAAI,GAAG,EAAE,OAAO,IAAI;GAC/F,MAAM,SAAS,GAAG,SAAS,EAAE,OAAO,MAAM,UAAU,GAAG,QAAQ,GAAG,GAAG,GAAG;AACxE,OAAI,KAAK,GAAG,OAAO,IAAI,KAAK,GAAG,GAAG,QAAQ,MAAM,SAAS;;;AAG7D,KAAI,IAAI,EAAE,SAAS,EAAE;AACnB,MAAI,KAAK,GAAG,SAAS,EAAE,QAAQ,WAAW,GAAG;AAC7C,OAAK,MAAM,OAAO,EAAE,SAAW,KAAI,KAAK,GAAG,OAAO,IAAI,EAAE,OAAO,UAAU,KAAK,IAAI,CAAC,GAAG;;AAExF,KAAI,IAAI,EAAE,KAAK,EAAE;AACf,MAAI,KAAK,GAAG,SAAS,EAAE,QAAQ,OAAO,GAAG;AACzC,OAAK,MAAM,KAAK,EAAE,KAAO,KAAI,KAAK,GAAG,OAAO,IAAI,EAAE,UAAU,UAAU,GAAG,IAAI,CAAC,GAAG;;AAEnF,KAAI,EAAE,MAAO,KAAI,KAAK,GAAG,SAAS,EAAE,QAAQ,QAAQ,CAAC,IAAI,UAAU,EAAE,OAAO,IAAI,GAAG;AACnF,QAAO;;;AAcT,MAAM,aAAa;;;;;;;;;;AAWnB,SAAgB,oBACd,SACA,KACA,SACoB;CACpB,MAAM,WAAkC,EAAE;AAC1C,MAAK,MAAM,MAAM,WAAW,EAAE,EAAE;EAC9B,MAAM,OAAO,GAAG,QAAQ,IAAI,MAAM;AAClC,MAAI,CAAC,IAAK;EACV,MAAM,MAAM,WAAW,IAAI,GAAG,MAAM,QAAQ,KAAK,IAAI;EACrD,IAAI;AACJ,MAAI;AACF,aAAU,SAAS,IAAI,CAAC;UAClB;AACN,aAAU;;AAGZ,MADgB,WAAW,KAAK,GAAG,WAAW,GAAG,EAE/C;OAAI,YAAY,KAAM,UAAS,KAAK;IAAE,MAAM;IAAK,QAAQ;IAAe,CAAC;aAChE,YAAY,KACrB,UAAS,KAAK;GAAE,MAAM;GAAK,QAAQ;GAAW,CAAC;WACtC,UAAU,QACnB,UAAS,KAAK;GAAE,MAAM;GAAK,QAAQ;GAAS,CAAC;;AAGjD,QAAO;EAAE,IAAI,SAAS,WAAW;EAAG;EAAU;;;AAIhD,SAAgB,eAAe,GAA+B;CAC5D,MAAM,QAAQ,EAAE,SAAS,KAAK,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,KAAK;AAClE,QAAO,WAAW,EAAE,SAAS,OAAO,iBAAiB,EAAE,SAAS,WAAW,IAAI,KAAK,IAAI,kBAAkB;;;AAI5G,SAAS,SAAS,GAAW,KAAqB;AAChD,KAAI,CAAC,IAAK,QAAO;CACjB,MAAM,IAAI,SAAS,KAAK,EAAE;AAC1B,QAAO,KAAK,CAAC,EAAE,WAAW,KAAK,GAAG,IAAI;;AASxC,MAAM,WAAW,MAAsB,EAAE,QAAQ,QAAQ,IAAI,CAAC,MAAM;;;;;AAMpE,SAAS,iBAAiB,KAAqB;CAC7C,IAAI,IAAI,QAAQ,IAAI,CAAC,QAAQ,sCAAsC,OAAO;AAC1E,MAAK,IAAI,OAAO,IAAI,SAAS,IAAK;AAChC,SAAO;AACP,MAAI,EAAE,QAAQ,yBAAyB,GAAG,CAAC,QAAQ,wBAAwB,GAAG;;AAEhF,MAAK,IAAI,OAAO,IAAI,SAAS,IAAK;AAChC,SAAO;AACP,MAAI,EAAE,QAAQ,uCAAuC,GAAG,CAAC,QAAQ,sEAAsE,GAAG,CAAC,QAAQ,yBAAyB,GAAG;;AAEjL,QAAO,EAAE,MAAM;;;;;;;;;;;AAYjB,SAAgB,WACd,MACA,MACA,YAAsB,EAAE,EAC8B;AAEtD,QAAO,KAAK,QAAQ,gEAAgE,GAAG;CACvF,MAAM,SAAS,eAAe,KAAK;CACnC,IAAI,MAAM,KAAK,SAAS;CACxB,IAAI,YAAY;CAChB,IAAI;AACJ,KAAI,QAAQ,SAAS,OAAO,OAAO,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG;EACtE,MAAM,UAAU,OAAO,OAAO;EAE9B,MAAM,QAAQ,QAAQ,MAAM,OAAO,CAAC,IAAI,iBAAiB,CAAC,OAAO,QAAQ;EACzE,MAAM,KAAK,KAAK,QAAQ,MAAM,CAAC,EAAE,OAAO,CAAC,KAAK,OAAO;GAAE,KAAK,QAAQ,EAAE,QAAQ;GAAE,MAAM,iBAAiB,EAAE,QAAQ;GAAE,EAAE;AAErH,MAAI,EADQ,GAAG,MAAM,MAAM,EAAE,IAAI,SAAS,QAAQ,QAAQ,CAAC,CAAC,IAAK,MAAM,SAAS,KAAK,MAAM,OAAO,MAAM,GAAG,MAAM,MAAM,EAAE,KAAK,SAAS,EAAE,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC,CAAC,GACtJ;AACR,eAAY;AACZ,WAAQ,OAAO,OAAO;AACtB,SAAM,IACH,MAAM,KAAK,CACX,QAAQ,MAAM,CAAC,SAAS,KAAK,EAAE,CAAC,CAChC,KAAK,MAAO,eAAe,KAAK,EAAE,GAAG,QAAQ,EAAG,CAChD,OAAO,sCAAsC,OAAO,OAAO,IAAI,CAC/D,KAAK,KAAK;;;AAGjB,KAAI,UAAU,UAAU,OAAQ,QAAO,OAAO,UAAU,KAAK,IAAI;AACjE,QAAO;EAAE,MAAM;EAAK;EAAW;EAAO;;;AAIxC,SAAgB,aAAa,KAAa,QAAuC;AAC/E,QAAO,eAAe,IAAI,CAAC,QAAQ,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;;AAG1D,SAAgB,eAAe,KAAuB;AACpD,KAAI;AACF,SAAO,aAAa,OAAO;GAAC;GAAU;GAAe;GAAQ,EAAE;GAAE;GAAK,UAAU;GAAQ,OAAO;IAAC;IAAU;IAAQ;IAAS;GAAE,CAAC,CAC3H,MAAM,KAAK,CACX,QAAQ,MAAM,EAAE,WAAW,MAAM,CAAC,CAClC,KAAK,MAAM,EAAE,MAAM,EAAE,CAAC;SACnB;AACN,SAAO,EAAE;;;;;;;AC1Zb,MAAa,cAAc;AAC3B,MAAM,mBAAmB;;;;;;AAOzB,SAAgB,gBACd,MACA,eAAe,KACwC;CACvD,MAAM,MAAM,KAAK,MAAM,kGAAkG;AACzH,KAAI,IAAK,QAAO;EAAE,MAAM,IAAI;EAAI,MAAM,IAAI,MAAM;EAAQ,QAAQ,IAAI,GAAG,MAAM;EAAE;CAC/E,MAAM,QAAQ,KAAK,MAAM,oDAAoD;AAC7E,KAAI,MAAO,QAAO;EAAE,MAAM;EAAc,MAAM,MAAM;EAAI,QAAQ,KAAK,MAAM;EAAE;AAC7E,KAAI,kGAAkG,KAAK,KAAK,CAC9G,QAAO;EAAE,MAAM;EAAc,MAAM;EAAc,QAAQ,KAAK,MAAM;EAAE;AAExE,QAAO;;;AAIT,SAAgB,YAAY,OAAwB;AAClD,KAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;CACxD,MAAM,IAAI;CACV,MAAM,MAAM;EAAC;EAAW;EAAa;EAAQ;EAAW;EAAM,CAAC,MAAM,MAAM,OAAO,EAAE,OAAO,SAAS;AACpG,SAAQ,MAAM,OAAO,EAAE,KAAK,GAAG,KAAK,UAAU,EAAE,EAAE,QAAQ,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI;;AAGtF,MAAM,mBAAmB,MAAsB,EAAE,aAAa,CAAC,QAAQ,QAAQ,IAAI,CAAC,QAAQ,QAAQ,IAAI,CAAC,MAAM;;;;;;;;;AAU/G,SAAgB,kBAAkB,SAAmB,MAA2B;CAC9E,MAAM,OAAO,MAAsB,GAAG,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI,gBAAgB,EAAE,OAAO;CACrF,MAAM,uBAAO,IAAI,KAAqB;AACtC,MAAK,MAAM,KAAK,QAAS,MAAK,IAAI,IAAI,EAAE,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC,IAAI,KAAK,EAAE;AACtE,MAAK,MAAM,KAAK,QACd,GAAE,gBAAgB,KAAK,IAAI,IAAI,EAAE,CAAC,IAAI,MAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,GAAG;;AAIzF,SAAS,gBAAgB,OAAe,MAAuB;AAC7D,MAAK,IAAI,IAAI,GAAG,IAAI,oBAAoB,MAAM,QAAQ,IACpD,KAAI,KAAK,SAAS,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,CAAE,QAAO;AAElE,QAAO;;;;;;AAaT,SAAgB,aACd,QACA,OACA,MACA,MACA,MACA,IACS;AACT,KAAI,MAAM,aAAa,KAAM,QAAO;CACpC,MAAM,MAAM,MAAM,cAAc,KAAK,IAAI,MAAM,YAAY,GAAG;CAC9D,MAAM,IAAI,gBAAgB,MAAM,KAAK,QAAQ,IAAI;AACjD,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,OAAQ,OAAO,YAAY,EAAE;AACnC,QAAO,eAAe,OAAO,eAAe,KAAK;AACjD,KAAI,KAAK,SAAS,YAChB,MAAK,KAAK;EACR;EACA,MAAM,KAAK,QAAQ,EAAE;EACrB,MAAM,EAAE;EACR,OAAO,YAAY,KAAK,MAAM;EAC9B,QAAQ,EAAE,OAAO,MAAM,GAAG,IAAI;EAC9B,cAAc;EACf,CAAC;AAEJ,mBAAkB,MAAM,KAAK;AAC7B,QAAO;;;AAIT,SAAgB,WAAW,GAAyB;CAClD,MAAM,IAAI,EAAE,eAAe,EAAE,SAAS,UAAU;AAChD,KAAI,KAAK,EAAG,QAAO;AACnB,QAAO,UAAU,IAAI,EAAE,SAAS,MAAM,MAAM,EAAE,aAAa,GAAG,WAAW;;;AAI3E,SAAgB,YAAY,GAA2B;CACrD,MAAM,OAAO,EAAE,WAAW,EAAE;AAC5B,KAAI,CAAC,KAAK,OAAQ,QAAO,EAAE;CAC3B,MAAM,QAAQ,EAAE,eAAe,KAAK,UAAU,KAAK;AACnD,QAAO;EACL,KAAK,WAAW,EAAE,CAAC;EACnB,GAAG,KAAK,KAAK,MAAM,KAAK,EAAE,eAAe,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,KAAK,EAAE,SAAS;EAC1H,GAAI,OAAO,IAAI,CAAC,SAAS,KAAK,OAAO,GAAG,EAAE;EAC3C;;;;;;;;;;;ACnHH,SAAgB,qBAAqB,UAAkB,UAA4C;CACjG,MAAM,OAAO,YAAY,YAAY,aAAa,GAAG,SAAS,KAAK;AACnE,KAAI,CAAC,WAAW,KAAK,CAAE,QAAO;CAC9B,MAAM,OAAO,aAAa,MAAM,OAAO,CAAC,MAAM;AAC9C,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO;EAAE;EAAM;EAAM,KAAK,WAAW,SAAS,CAAC,OAAO,KAAK,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,EAAE;EAAE;;;;;;ACJzF,SAAgB,cAAc,MAAc,sBAAY,IAAI,MAAM,EAAU;CAC1E,MAAM,IAAI,sBAAsB,KAAK,KAAK,MAAM,CAAC;AACjD,KAAI,CAAC,KAAK,OAAO,EAAE,GAAG,GAAG,MAAM,OAAO,EAAE,GAAG,GAAG,GAC5C,OAAM,IAAI,MAAM,8DAA8D,KAAK,GAAG;CAExF,MAAM,KAAK,IAAI,KAAK,IAAI;AACxB,IAAG,SAAS,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE;AAC7C,KAAI,GAAG,SAAS,IAAI,IAAI,SAAS,CAAE,IAAG,QAAQ,GAAG,SAAS,GAAG,EAAE;AAC/D,QAAO,GAAG,SAAS;;;AAIrB,SAAgB,aACd,OACA,gBACA,sBAAY,IAAI,MAAM,EACL;AACjB,KAAI,MAAM,eAAe,UAAa,MAAM,aAAa,OACvD,OAAM,IAAI,MAAM,sDAAsD;AAExE,KAAI,MAAM,aAAa,QAAW;EAChC,MAAM,aAAa,cAAc,MAAM,UAAU,IAAI;AACrD,SAAO;GAAE;GAAY,SAAS,KAAK,IAAI,GAAG,KAAK,OAAO,aAAa,IAAI,SAAS,IAAI,IAAO,CAAC;GAAE;;CAEhG,MAAM,UAAU,MAAM,cAAc;AACpC,KAAI,CAAC,OAAO,SAAS,QAAQ,IAAI,UAAU,EAAG,OAAM,IAAI,MAAM,sDAAsD,UAAU;AAC9H,QAAO,YAAY,IAAI,OAAO;EAAE,YAAY,IAAI,SAAS,GAAG,UAAU;EAAQ;EAAS;;AAGzF,MAAM,OAAO,MAAsB,OAAO,EAAE,CAAC,SAAS,GAAG,IAAI;;AAG7D,SAAgB,mBAAmB,OAAyB;CAC1D,MAAM,IAAI,IAAI,KAAK,MAAM,WAAW;AACpC,QACE,iBAAiB,IAAI,EAAE,UAAU,CAAC,CAAC,GAAG,IAAI,EAAE,YAAY,CAAC,CAAC,IAAI,MAAM,QAAQ;;;AAMhF,SAAgB,cACd,GACA,MAAc,KAAK,KAAK,EACT;AACf,KAAI,EAAE,SAAU,QAAO;AACvB,KAAI,EAAE,UAAU,aAAa,CAAC,EAAE,WAAY,QAAO;CACnD,MAAM,OAAO,KAAK,IAAI,EAAE,aAAa,IAAI,GAAG;CAC5C,IAAI;AACJ,KAAI,OAAO,GACT,SAAQ,GAAG,KAAK,MAAM,KAAK,CAAC;UACnB,OAAO,KAChB,SAAQ,GAAG,KAAK,MAAM,OAAO,GAAG,CAAC;MAC5B;EACL,MAAM,QAAQ,KAAK,MAAM,OAAO,KAAK;EACrC,MAAM,OAAO,KAAK,MAAO,OAAO,OAAQ,GAAG;AAC3C,UAAQ,GAAG,MAAM,GAAG,OAAO,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;;AAEpD,QAAO,EAAE,cAAc,MAAM,GAAG,MAAM,SAAS,WAAW;;;AAI5D,MAAa,oBAAoB;;;;;AAMjC,SAAgB,YACd,MACA,OACA,QACA,UAAkB,mBACN;CACZ,IAAI,OAA8B;CAClC,MAAM,IAAI,iBAAiB;AACzB,UAAQ;AACR,MAAI;AACF,QAAK,KAAK,UAAU;UACd;AAGR,SAAO,iBAAiB;AACtB,OAAI,KAAK,aAAa,QAAQ,KAAK,eAAe,KAChD,KAAI;AACF,SAAK,KAAK,UAAU;WACd;KAIT,QAAQ;AACX,OAAK,OAAO;IACX,KAAK,IAAI,GAAG,MAAM,aAAa,KAAK,KAAK,CAAC,CAAC;AAC9C,cAAa;AACX,eAAa,EAAE;AACf,MAAI,KAAM,cAAa,KAAK;;;;;;;;;;;;;ACxGhC,MAAM,iBAAiB;;AAGvB,SAAgB,cAAc,MAAuB;CACnD,MAAM,SAAS,oBAAoB,KAAK,KAAK,GAAG,MAAM;AACtD,QAAO,eAAe,KAAK,OAAO,KAAK,KAAK,WAAW,QAAQ,IAAI,CAAC,KAAK,SAAS,KAAK;;AAGzF,MAAM,kBAAkB,SACtB,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;;AAG5E,SAAgB,iBAAiB,MAA0B;CACzD,MAAM,MAAgB,EAAE;AACxB,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,IAAI,KAAK;AACf,MAAI,MAAM,WAAY;AACtB,MAAI,MAAM,oBAAoB,IAAI,IAAI,KAAK,UAAU,CAAC,KAAK,IAAI,GAAG,WAAW,IAAI,EAAE;GACjF,MAAM,OAAO,eAAe,KAAK,EAAE,GAAG;AACtC,OAAI,KAAK,OAAQ,KAAI,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC;aACnC,EAAE,WAAW,kBAAkB,EAAE;GAC1C,MAAM,OAAO,eAAe,EAAE,MAAM,GAAyB,CAAC;AAC9D,OAAI,KAAK,OAAQ,KAAI,KAAK,kBAAkB,KAAK,KAAK,IAAI,GAAG;QACxD,KAAI,KAAK,EAAE;;AAEpB,QAAO;;;AAIT,SAAgB,kBAAkB,OAA2B;AAC3D,QAAO,MAAM,KAAK,MAAM,eAAe,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,QAAQ;;;;;;;;;;;;;;;;;;;;;ACjBtE,SAAgB,eAAe,QAAgB,OAAqC;AAClF,QAAO;EACL;EACA;EACA;EACA,GAAI,QAAQ,CAAC,MAAM,MAAM,GAAG,EAAE;EAC9B;EACA;EACD;;;AAIH,SAAgB,cAAc,UAA6C;CACzE,MAAM,MAAyB,EAAE,GAAG,QAAQ,KAAK;AACjD,QAAO,IAAI;AACX,QAAO,IAAI;AACX,QAAO,IAAI;AACX,QAAO,IAAI;AACX,QAAO,IAAI;AACX,QAAO,IAAI;CAKX,MAAM,UAAU,gBAAgB,SAAS;AACzC,KAAI,SAAS,OAAQ,WAAW,WAAW,QAAQ,CACjD,KAAI,iBAAiB,mBAAmB,SAAS,IAAI;AAEvD,KAAI,SAAS,YAAa,KAAI,kBAAkB,SAAS;AACzD,MAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,SAAS,IAAI,CAAE,KAAI,KAAK;AAC5D,KAAI,aAAa;AACjB,QAAO;;;AAIT,SAAgB,kBAAkB,MAA0B;CAC1D,MAAM,UAAoB,EAAE;AAC5B,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;EACpC,MAAM,IAAI,KAAK;AACf,MAAI,MAAM,oBAAoB,MAAM,uBAAuB,MAAM,kBAAkB,MAAM,SAAS;AAChG,WAAQ,KAAK,EAAE;AACf,OAAI,IAAI,IAAI,KAAK,UAAU,CAAC,KAAK,IAAI,GAAG,WAAW,IAAI,CAAE;aAChD,EAAE,WAAW,kBAAkB,IAAI,EAAE,WAAW,gBAAgB,CACzE,SAAQ,KAAK,EAAE,MAAM,IAAI,CAAC,GAAG;;AAGjC,QAAO;;AAmBT,MAAa,0BAA4C;CACvD,QAAQ,EAAE;CACV,OAAO;CACP,OAAO;CACP,MAAM;CACN,SAAS;CACT,eAAe;CACf,WAAW;CACX,UAAU;CACX;;;;;AAMD,SAAgB,cAAc,MAAe,GAA2B;AACtE,KAAI,OAAO,SAAS,YAAY,SAAS,KAAM;CAC/C,MAAM,IAAI;CACV,MAAM,OAAO,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAEnD,KAAI,SAAS,oBAAoB,OAAO,EAAE,cAAc,UAAU;AAChE,IAAE,WAAW,EAAE;AACf;;AAEF,KAAI,SAAS,oBAAoB,OAAO,EAAE,SAAS,YAAY,EAAE,SAAS,MAAM;EAC9E,MAAM,OAAO,EAAE;EACf,MAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACzD,MAAI,SAAS,mBAAmB,OAAO,KAAK,SAAS,UAAU;AAC7D,KAAE,SAAS;AACX,KAAE,OAAO,SAAS,KAAK,KAAK,MAAM,GAAG,GAAG;AACxC,KAAE,YAAY,KAAK;AACnB,KAAE,OAAO,KAAK;IACZ,MAAM;IACN,SAAS,EAAE,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM,KAAK;KAAM,CAAC,EAAE;IAC1D,CAAC;aACO,SAAS,qBAAqB;AACvC,KAAE,SAAS;GACX,MAAM,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU;GAC9D,MAAM,KAAK,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;AACjE,KAAE,OAAO,SAAS,IAAI,MAAM,GAAG,GAAG;AAClC,KAAE,OAAO,KAAK;IACZ,MAAM;IACN,SAAS,EAAE,SAAS,CAAC;KAAE,MAAM;KAAY,MAAM;KAAQ,OAAO,EAAE,SAAS,KAAK;KAAE,CAAC,EAAE;IACpF,CAAC;AACF,KAAE,OAAO,KAAK;IACZ,MAAM;IACN,SAAS,EACP,SAAS,CACP;KACE,MAAM;KACN,aAAa;KACb,UAAU,OAAO;KACjB,SAAS,KAAK,qBAAqB;KACpC,CACF,EACF;IACF,CAAC;aACO,SAAS,eAAe;AACjC,KAAE,SAAS;GAEX,MAAM,SADU,MAAM,QAAQ,KAAK,QAAQ,GAAG,KAAK,UAAU,EAAE,EAE5D,KAAK,MAAO,OAAO,MAAM,YAAY,MAAM,QAAQ,OAAQ,EAAyB,SAAS,WAAY,EAAuB,OAAO,IAAK,CAC5I,KAAK,KAAK;AACb,KAAE,OAAO,UAAU,MAAM,MAAM,GAAG,GAAG;AACrC,KAAE,OAAO,KAAK;IACZ,MAAM;IACN,SAAS,EAAE,SAAS,CAAC;KAAE,MAAM;KAAY,MAAM;KAAS,OAAO,EAAE,WAAW,OAAO;KAAE,CAAC,EAAE;IACzF,CAAC;aACO,SAAS,iBAAiB;AACnC,KAAE,SAAS;AACX,KAAE,OAAO,QAAQ,OAAO,KAAK,QAAQ,IAAI;;AAE3C;;AAEF,KAAI,SAAS,kBAAkB;EAC7B,MAAM,QAAS,OAAO,EAAE,UAAU,YAAY,EAAE,UAAU,OAAO,EAAE,QAAQ,EAAE;EAK7E,MAAM,UACH,MAAM,gBAAgB,MAAM,MAAM,uBAAuB,MAAM,MAAM,iBAAiB;AACzF,MAAI,SAAS,EAAG,GAAE,gBAAgB;AAClC;;AAEF,KAAI,SAAS,iBAAiB,SAAS,SAAS;AAC9C,IAAE,UAAU;EACZ,MAAM,MAAM,OAAO,EAAE,UAAU,YAAY,EAAE,UAAU,OAAO,EAAE,QAAQ,EAAE;AAC1E,IAAE,OAAO,OAAQ,IAA8B,WAAW,EAAE,WAAW,oBAAoB;;;;AAK/F,SAAgB,eAAe,MAA8B;CAC3D,MAAM,IAAI,KAAK,MAAM;AACrB,KAAI,CAAC,EAAE,WAAW,IAAI,CAAE,QAAO;AAC/B,KAAI;AACF,SAAO,KAAK,MAAM,EAAE;SACd;AACN,SAAO;;;;AASX,SAAgB,iBAA0B;AACxC,KAAI;AACF,eAAa,SAAS,CAAC,YAAY,EAAE;GAAE,SAAS;GAAM,OAAO;GAAU,CAAC;AACxE,SAAO;SACD;AACN,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6BX,SAAgB,oBACd,MACA,MAAyB,QAAQ,KACjB;AAChB,KAAI,KAAM,QAAO;AACjB,QAAO,IAAI,sBAAsB,SAAS,SAAS;;;AAOrD,SAAgB,iBAAiB,MAAsB;AACrD,QAAO,KAAK,UAAU;EAAE,MAAM;EAAQ,SAAS;GAAE,MAAM;GAAQ,SAAS;GAAM;EAAE,CAAC;;;AAInF,SAAgB,iBAAiB,MAAsB;AACrD,QAAO,GAAG,cAAc,GAAG;;;;;;;;AAS7B,SAAgB,SAAS,oBAAI,IAAI,MAAM,EAAE,SAAS,CAAC,EAAE,mBAAmB,EAAU;CAChF,MAAM,IAAI,IAAI,KAAK,EAAE,SAAS,GAAG,SAAS,IAAO;CACjD,MAAM,OAAO,SAAS,IAAI,MAAM;CAChC,MAAM,MAAM,KAAK,IAAI,OAAO;CAC5B,MAAM,KAAK,OAAO,KAAK,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI;CACxD,MAAM,KAAK,OAAO,MAAM,GAAG,CAAC,SAAS,GAAG,IAAI;AAC5C,QAAO,GAAG,EAAE,aAAa,CAAC,MAAM,GAAG,GAAG,GAAG,OAAO,GAAG,GAAG;;;AA6CxD,SAAgB,mBAAmB,GAA0C;AAC3E,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,KACH,EAAE,gBAAgB,MAClB,EAAE,2BAA2B,MAC7B,EAAE,+BAA+B,MACjC,EAAE,iBAAiB;AACtB,QAAO,IAAI,IAAI,IAAI;;;AAIrB,SAAgB,iBAAiB,GAA0C;AACzE,KAAI,CAAC,EAAG,QAAO;CACf,MAAM,KAAK,EAAE,gBAAgB,MAAM,EAAE,2BAA2B,MAAM,EAAE,+BAA+B;AACvG,QAAO,IAAI,IAAI,IAAI;;;;;;;;AASrB,SAAgB,kBAAkB,QAA6C,QAA6B;AAC1G,KAAI,WAAW,QAAQ,UAAU,EAAG;AACpC,QAAO,gBAAgB,KAAK,IAAI,OAAO,iBAAiB,GAAG,OAAO;;;;;;;;;AAUpE,SAAgB,4BAA4B,QAA6C,GAAsB;AAC7G,QAAO,gBAAgB,mBAAmB,EAAE,MAAM,IAAI;;;AAIxD,SAAgB,kBAAkB,GAAyB;AACzD,QAAO,EAAE,SAAS,aAAa,EAAE,YAAY,sBAAsB,EAAE,YAAY;;;AAInF,SAAS,YAAY,OAAwB;AAC3C,KAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAQ,MAAgC,YAAY,SACrG,QAAQ,MAA8B;AAExC,QAAO;;AAGT,SAAgB,eAAe,SAA0B;AACvD,KAAI,OAAO,YAAY,SAAU,QAAO;AACxC,KAAI,MAAM,QAAQ,QAAQ,CACxB,QAAO,QACJ,KAAK,MACJ,OAAO,MAAM,YAAY,MAAM,QAAQ,OAAQ,EAAyB,SAAS,WAC5E,EAAuB,OACxB,GACL,CACA,KAAK,KAAK;AAEf,QAAO;;;;;;;;AAST,SAAgB,iBAAiB,OAA2D;AAC1F,KAAI,MAAM,aAAa,KAAM,QAAO;CACpC,MAAM,IAAI,eAAe,MAAM,QAAQ,CAAC,MAAM,kBAAkB;AAChE,QAAO,MAAM,QAAQ,OAAO,EAAE,GAAG,KAAK;;;;;AAMxC,MAAM,uBAAuB,IAAI,IAAI,OAAO,OAAO,wBAAwB,CAAC;;;;;;;;;;;AAY5E,SAAgB,eAAe,QAAqC,GAAsB;AACxF,KAAI,OAAO,SAAS,CAAC,qBAAqB,IAAI,OAAO,MAAM,CAAE;CAC7D,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM;AAChC,KAAI,EAAG,QAAO,QAAQ;;;;;;;;;;AAWxB,SAAgB,gBACd,QACA,WACA,WACQ;AACR,KAAI,UAAW,QAAO;CACtB,MAAM,QAAQ,OAAO;CACrB,MAAM,aACJ,SAAS,kBAAkB,MAAM,GAAG,QAAQ,qBAAqB,UAAU;AAC7E,QAAO,uBAAuB,OAAO,UAAU,WAAW;;;;;;;AAQ5D,SAAgB,UAAU,OAAe,cAAiC;AACxE,QAAO,CAAC,gBAAgB,QAAQ,CAAC,WAAW,MAAM,GAAG,EAAE;;;;;;;;AASzD,SAAgB,iBAAiB,IAAoB;AACnD,QAAO,GAAG,MAAM,CAAC,QAAQ,eAAe,GAAG;;;;;;;;;;;;;;;;;AAyB7C,SAAgB,0BAA0B,MASxB;AAChB,KAAI,KAAK,aAAa,KAAK,aAAc,QAAO;EAAE,OAAO,KAAK;EAAU,QAAQ;EAAM;CACtF,MAAM,eAAe,mBAAmB,KAAK,OAAO,QAAQ,IAAI;AAChE,KAAI,CAAC,aAAc,QAAO;EAAE,OAAO,KAAK;EAAU,QAAQ;EAAM;AAChE,KAAI,iBAAiB,KAAK,SAAS,KAAK,iBAAiB,aAAa,CACpE,QAAO;EAAE,OAAO,KAAK;EAAU,QAAQ;EAAM;CAE/C,MAAM,OAAO,KAAK,SAAS,OAAO;AAClC,KAAI,CAAC,QAAQ,iBAAiB,KAAK,KAAK,iBAAiB,KAAK,SAAS,CACrE,QAAO;EACL,OAAO,KAAK;EACZ,QACE,uDAAuD,KAAK,aAAa,wCAClD,KAAK,SAAS;EACxC;AAEH,QAAO;EACL,OAAO;EACP,QACE,6DAA6D,KAAK;EAErE;;;;;;;;;;;;;;AAeH,SAAgB,cAAc,UAAmB,OAAe,cAAiC;AAC/F,QAAO,WAAW,UAAU,OAAO,aAAa,GAAG,EAAE;;;;;;;;;;AAWvD,SAAgB,mBACd,QACA,UACA,oBACA,QAC2C;CAC3C,MAAM,UAAU,YAAY,WAAW,QAAQ,CAAC;AAChD,QAAO;EAAE,MAAM,UAAU,SAAU,cAAc,OAAO,GAAG;EAAQ;EAAS;;;;;;;;;AAU9E,SAAgB,kBACd,YACA,eACA,YACS;AACT,QAAO,CAAC,WAAW,MAAM,WAAW,cAAc,cAAc,CAAC,iBAAiB;;;;;;;;;;AAWpF,SAAgB,gBAAgB,YAAgC,OAAwB;AACtF,QAAO,UAAU,MAAM,cAAc,IAAI,MAAM,KAAK;;;;;;;;;AAUtD,SAAgB,aACd,UACA,IACA,aACS;AACT,KAAI,OAAO,EAAG,QAAO;AACrB,KAAI,CAAC,SAAU,QAAO;AACtB,QAAO,gBAAgB,QAAQ,CAAC,YAAY;;AAG9C,SAAgB,kBAAkB,GAA+B;AAC/D,KAAI,OAAO,EAAE,mBAAmB,YAAY,EAAE,iBAAiB,EAAG,QAAO,EAAE;AAC3E,KAAI,EAAE,cAAc,OAAO,EAAE,WAAW,mBAAmB,YAAY,EAAE,WAAW,iBAAiB,EACnG,QAAO,EAAE,WAAW;AAGtB,KAAI,UAAU,MAAM,EAAE,SAAS,IAAI,MAAM,CAAC,CAAE,QAAO;AACnD,QAAO;;;;;;AAOT,eAAsB,UAAU,MAAmC;CACjE,MAAM,EAAE,KAAK,MAAM,SAAS,WAAW,oBAAoB;AAE3D,KAAI,CAAC,OAAO,QACV,OAAM,IAAI,MACR,mIAED;CAEH,MAAM,SAAS,cAAc,OAAO;AACpC,WAAU,QAAQ,EAAE,WAAW,MAAM,CAAC;AACtC,KAAI;EACF,MAAM,KAAK,qBAAqB,OAAO,EAAE,QAAQ,MAAM,EAAE,QAAQ;AACjE,MAAI,IAAI,OAAQ,SAAQ,OAAO,MAAM,wBAAwB,GAAG,OAAO,yCAAyC;SAC1G;AAKR,KAAI,KAAK,cAAc,UAAU,CAAC,KAAK,UAAU;EAC/C,MAAM,EAAE,eAAe,MAAM,OAAO;AACpC,SAAO,WAAW,KAAK;;CAMzB,MAAM,SAAS,aAAa,KAAK,OAAO;AACxC,KAAI,OAAQ,oBAAmB,QAAQ,QAAQ,OAAO,KAAK;CAK3D,MAAM,aAAa,KAAK,eACpB,OACA,iBAAiB,QAAQ;EAAE,gBAAgB,KAAK;EAAgB,WAAW,KAAK;EAAW,CAAC;CAChG,MAAM,uBAAuD,aACzD,qBAAqB,QAAQ,WAAW,GACxC;CAGJ,IAAI,QAAQ,KAAK;CACjB,IAAI,eAAe,KAAK;AACxB,KAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,cAAc,KAAK,cAAc,UAAU,gBAAgB,KAAK,WAAW,CAAC,UAAU;EACpH,MAAM,SAAS,MAAM,SAAS,QAAQ,QAAQ,KAAK;AACnD,MAAI,QAAQ;AACV,WAAQ,OAAO;AACf,kBAAe,OAAO,gBAAgB;;;AAQ1C,KAAI,CAAC,gBAAgB,CAAC,wBAAwB,OAAO,QAAQ,WAAW,QACtE,KAAI;AACF,QAAM,YAAY,KAAK,OAAO;AAC9B,wBAAsB,QAAQ,OAAO;SAC/B;CAKV,MAAM,SAAS,uBACX;EACE,cAAc,qBAAqB;EACnC,UAAU,qBAAqB;EAC/B,YAAY;EACZ,UAAU;EACV,KAAK;EACN,GACD,cAAc,QAAQ,QAAQ;EAC5B,cAAc;EACd,WAAW,KAAK;EACjB,CAAC;AACN,wBAAuB,OAAO,cAAc,OAAO,SAAS;CAE5D,MAAM,YAAY,KAAK,YAAY,SAAY,OAAO,qBAAqB,CAAC,KAAK;CACjF,MAAM,SAAS,gBAAgB,YAAY,iBAAiB,KAAK,WAAW,GAAG,KAAK,WAAW;CAG/F,MAAM,QAAQ,aAAa,MAAM,OAAO,WAAW,OAAO,oBAAoB,EAAE;CAChF,MAAM,QACJ,KAAK,SACL,UAAU,OAAO,UAAU,WAAW,GAAG;CAE3C,IAAI,QAAQ,uBACR,qBAAqB,QACrB,gBAAgB,QAAQ,KAAK,WAAW,KAAK,UAAU;AAC3D,KAAI,CAAC,sBAAsB;EAGzB,MAAM,OAAO,0BAA0B;GACrC,UAAU;GACV,cAAc,OAAO;GACrB,UAAU,OAAO;GACjB,WAAW,KAAK;GAChB,cAAc,OAAO;GACtB,CAAC;AACF,UAAQ,KAAK;AACb,MAAI,KAAK,UAAU,CAAC,KAAK,MAAO,SAAQ,OAAO,MAAM,GAAG,KAAK,OAAO,IAAI;;CAE1E,MAAM,eAAe,oBAAoB,KAAK,iBAAiB;CAK/D,IAAI,WAA+B,KAAK;CACxC,MAAM,cAAc,QAAgB;AAClC,aAAW;AACX,OAAK,gBAAgB,IAAI;;AAG3B,KAAI;EACF,IAAI;AACJ,MAAI,OAAO,SAAS,WAAW,QAC7B,MAAK,MAAM,gBAAgB;GACzB;GACA;GACA;GACA;GACA;GACA;GACA,YAAY,cAAc;GAC1B,SAAS,KAAK;GACd,MAAM,KAAK;GACX,WAAW,KAAK;GAChB,KAAK,KAAK;GACV,UAAU,KAAK;GACf,QAAQ,UAAU;GAClB,OAAO,KAAK;GACZ,OAAO,KAAK;GACZ,eAAe;GACf,IAAI,KAAK;GACV,CAAC;WACO,OAAO,SAAS,WAAW,QACpC,MAAK,MAAM,gBAAgB;GACzB;GACA;GACA;GACA;GACA;GACA;GACA,YAAY,KAAK;GACjB,QAAQ,KAAK,UAAU;GACvB,KAAK,KAAK;GACV,UAAU,KAAK;GACf,QAAQ,UAAU;GAClB,OAAO,KAAK;GACZ,OAAO,KAAK;GACZ,eAAe;GACf,IAAI,KAAK;GACT,cAAc,KAAK;GACnB,WAAW,KAAK;GAChB;GACA,YAAY,cAAc;GAC1B;GACA;GACD,CAAC;OACG;GACL,MAAM,WAAwB;IAC5B;IACA;IACA;IACA;IACA;IACA;IACA,YAAY,KAAK;IACjB,QAAQ,KAAK,UAAU;IACvB,SAAS,KAAK;IACd,KAAK,KAAK;IACV,UAAU,KAAK;IACf,QAAQ,UAAU;IAClB,OAAO,KAAK;IACZ,OAAO,KAAK;IACZ,eAAe;IACf,IAAI,KAAK;IACT,YAAY,cAAc;IAC1B,cAAc,KAAK;IACnB,UAAU,KAAK;IACf,WAAW,KAAK;IAChB,UAAU,KAAK,aAAa;IAC5B,cAAc,KAAK,iBAAiB;IACpC;IACA,eAAe,KAAK;IACpB,UAAU,KAAK;IACf;IACA,eAAe,KAAK,iBAAiB;IACrC;IACA;IACA;IACD;GAGD,MAAM,WAAW,MAAM,WAAW,SAAS;AAC3C,OAAI,aAAa,MAAM;AACrB,UAAM,qBAAqB,QAAQ,SAAS;AAC5C,WAAO;;AAET,QAAK,MAAM,WAAW,SAAS;;AAEjC,QAAM,qBAAqB,QAAQ,SAAS;AAC5C,SAAO;UACA,GAAG;AACV,QAAM,qBAAqB,QAAQ,SAAS;AAC5C,MAAI,aAAa,SAAS,EAAE,QAAQ,WAAW,WAAW,CACxD,OAAM,IAAI,MACR,aAAa,OAAO,aAAa,KAAK,EAAE,8EAEzC;AAEH,QAAM;;;;;;;;;;;AAYV,MAAa,uBAAuB;;AAGpC,SAAgB,mBAAmB,cAAkC;AACnE,QAAO,aAAa,SAAS,EAAE,GAAG,CAAC,kBAAkB,qBAAqB;;;;;;;;;;;;;;;;;;;AAoB5E,SAAgB,kBAAkB,cAAkC;CAClE,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,SAAS,aAClB,MAAK,MAAM,OAAO,MAAM,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,MAAM,CAAC,CAAC,OAAO,QAAQ,EAAE;AACvE,MAAI,IAAI,WAAW,QAAQ,CAAE;EAC7B,MAAM,YAAY,IAAI,MAAM,sBAAsB;EAClD,MAAM,OAAO,YAAY,UAAU,KAAK;AACxC,MAAI,CAAC,MAAM,SAAS,KAAK,CAAE,OAAM,KAAK,KAAK;;AAG/C,QAAO,MAAM,SAAS,CAAC,WAAW,MAAM,KAAK,IAAI,CAAC,GAAG,EAAE;;;;;;;;;;;AAYzD,SAAgB,iBAAiB,KAAyB;CACxD,MAAM,UAAU,IAAI,WAAW,MAAM,MAAM,UAAU;AACrD,KAAI,WAAW,KAAK,UAAU,IAAI,IAAI,QAAQ;EAC5C,MAAM,QAAQ,IAAI,UAAU;AAC5B,MAAI,SAAS,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,aAAa,EAAE;GACrD,MAAM,MAAM,CAAC,GAAG,IAAI;AACpB,OAAI,UAAU,KAAK,GAAG,MAAM;AAC5B,WAAQ,OAAO,MAAM,kEAAkE;AACvF,UAAO;;AAET,SAAO;;CAET,MAAM,QAAQ,IAAI,WAAW,MAAM,EAAE,WAAW,WAAW,CAAC;AAC5D,KAAI,SAAS,GAAG;EACd,MAAM,QAAQ,IAAI,OAAO,MAAM,EAAkB;AACjD,MAAI,SAAS,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,aAAa,EAAE;GACrD,MAAM,MAAM,CAAC,GAAG,IAAI;AACpB,OAAI,SAAS,WAAW,MAAM;AAC9B,WAAQ,OAAO,MAAM,kEAAkE;AACvF,UAAO;;;AAGX,QAAO;;;;;;;;;;;;AAaT,SAAgB,gBAAgB,QAAkB,OAAiB,EAAE,EAAY;AAC/E,KAAI,KAAK,SAAS,WAAW,CAAE,QAAO,EAAE;AACxC,QAAO,aAAa,OAAO,GAAG,CAAC,WAAW,GAAG,EAAE;;;;;;;;AASjD,SAAgB,oBACd,aACA,aACA,eACyC;AACzC,QAAO;EACL,UAAU,YAAY,SAAS,cAAe,eAAe,OAAO,EAAE;EACtE,OAAO,CAAC,eAAe,eAAe,OAAO,SAAS,cAAc,QAAQ,EAAE;EAC/E;;;;;;;;;;AA0DH,MAAM,oBAAoB;;AAG1B,SAAgB,aAAa,QAAgB,QAAsB,QAAgB,OAAuB;AACxG,QAAO,WAAW;AAClB,QAAO,OAAO,0BAA0B,MAAM,QAAQ;AACtD,YAAW,QAAQ,OAAO;AAC1B,cAAa,QAAQ,mBAAmB;EAAE,IAAI,OAAO;EAAI,SAAS,MAAM;EAAS,CAAC;;;AAIpF,SAAgB,gBAAgB,QAAgB,QAAsB,QAAyB;CAC7F,MAAM,MAAM,gBAAgB;EAC1B;EACA,IAAI,OAAO;EACX;EACA,UAAU,OAAO;EACjB,WAAW,OAAO;EAClB,YAAY,OAAO;EACnB,kBAAkB,OAAO;EAC1B,CAAC;AACF,QAAO,YAAY,IAAI;AACvB,QAAO,QAAQ,IAAI;AACnB,QAAO,UAAU,IAAI;AACrB,QAAO;;AAGT,eAAsB,YAAY,GAAkC;CAClE,MAAM,EAAE,QAAQ,QAAQ,IAAI,OAAO;CACnC,IAAI,EAAE,QAAQ,WAAW;CACzB,MAAM,MAAM,OAAO;CAGnB,MAAM,WAAW,EAAE,WACf,cAAc,QAAQ,KAAK,OAAO,SAAS,WAAW,EAAE,SAAS,IAAI,GACrE,EAAE;AACN,KAAI,SAAS,QAAQ;EACnB,MAAM,MAAM,gDAAgD,SAAS,KAAK,KAAK,CAAC;AAChF,WAAS;GAAE,GAAI,UAAU,EAAE;GAAG,OAAO,CAAC,QAAQ,SAAS,EAAE,cAAc,IAAI,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK;GAAE,QAAQ,QAAQ,UAAU;GAAK;;AAEzI,KAAI,EAAE,UAAU;EACd,MAAM,SAAS,0BAA0B,EAAE,SAAS;AACpD,SAAO,WAAW;AAClB,SAAO,OAAO;AAEd,WAAS;GAAE,GAAI,UAAU,EAAE;GAAG,QAAQ;GAAK,KAAK;GAAQ,OAAO,CAAC,QAAQ,QAAQ,SAAS,EAAE,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK;GAAE;;CAEvI,MAAM,eAAe,sBAAsB,QAAQ,IAAI;CACvD,MAAM,YAAY,MAAM,CAAC,gBAAgB,CAAC,EAAE;AAC5C,QAAO,QAAQ,YAAY,SAAS;AACpC,QAAO,KAAK;AACZ,QAAO,OAAO,EAAE;AAChB,KAAI,cAAc;AAChB,SAAO,OAAO,UAAU,cAAc,GAAG;AACzC,WAAS;GAAE,GAAI,UAAU,EAAE;GAAG,OAAO;GAAc,QAAQ;GAAK;AAChE,eAAa,QAAQ,0BAA0B;GAAE,IAAI;GAAK,QAAQ;GAAc,CAAC;;CAEnF,MAAM,MAAM,gBAAgB;EAAE;EAAQ,IAAI;EAAK,UAAU,EAAE,aAAa;EAAW,CAAC;AACpF,QAAO,YAAY,IAAI;AAEvB,QAAO,iBAAiB,QAAQ,WAAW;AAC3C,YAAW,QAAQ,OAAO;AAC1B,cAAa,QAAQ,cAAc;EAAE,IAAI;EAAK,GAAG,EAAE;EAAK;EAAI,MAAM,EAAE;EAAM,GAAG,UAAU,IAAI;EAAE,CAAC;AAE9F,KAAI,EAAE,SAAU,UAAS,eAAe,QAAQ,QAAQ,EAAE,UAAU,GAAG;CAEvE,MAAM,QAAQ,MAAM,EAAE,gBAAgB,QAAQ,OAAO;AACrD,KAAI,OAAO,UAAU,SAAU,QAAO;CAEtC,MAAM,UAAU,OAAO,IAAI,KAAK,YAAY,IAAI;AAChD,KAAI,OAAO,UAAU,WAAW,QAAQ,OAAO,OAAO,CACpD,KAAI;AACF,QAAM,eAAe,QAAQ;GAC3B,MAAM;GACN,IAAI,OAAO;GACX,MAAM;GACN,MAAM,UAAU,QAAQ,SAAS,EAAE,iBAAiB,YAAY,SAAS,WAAW,IAAI;GACxF,MAAM;IACJ,IAAI;IACJ,IAAI;IACJ,GAAI,OAAO,SAAS;KAAE,QAAQ,OAAO;KAAQ,SAAS,OAAO,WAAW;KAAG,GAAG,EAAE;IAChF,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;IAC7B;GACF,CAAC;SACI;AAIV,QAAO;;AAGT,eAAe,gBAAgB,GAAqG;CAClI,MAAM,EAAE,QAAQ,QAAQ,OAAO,OAAO,WAAW;AACjD,KAAI,CAAC,OAAO,YAAY,OAAO,WAAW,KACxC,OAAM,IAAI,MACR,aAAa,OAAO,aAAa,gFAElC;CAEH,MAAM,MAAM,EAAE,MAAM,aAAa;CACjC,MAAM,MAAM,EAAE,OAAO,QAAQ,KAAK;CAClC,MAAM,OAAO,QAAQ,IAAI,oBAAoB;CAC7C,MAAM,UAAU,EAAE,WAAW,KAAK,QAAQ,GAAG,IAAI,MAAM;CAEvD,MAAM,SAAuB;EAC3B,IAAI;EACJ,KAAK,QAAQ;EACb;EACA;EACA;EACA,UAAU,OAAO;EACjB;EACA,OAAO;EACP,SAAS,UAAU;EACnB,SAAS,UAAU;EACnB,OAAO;EACP,OAAO;EACP,MAAM;EACN,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,cAAc,OAAO;EACrB,cAAc;EACd,GAAI,EAAE,SAAS;GAAE,QAAQ,EAAE;GAAQ,OAAO,EAAE;GAAO,GAAG,EAAE;EACxD,GAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE;EAC3C;AACD,YAAW,QAAQ,OAAO;AAC1B,GAAE,gBAAgB,IAAI;CACtB,MAAM,SAAS,WAAW,OAAO;AACjC,cAAa,QAAQ,gBAAgB;EACnC,IAAI;EACJ,UAAU,OAAO;EACjB,MAAM;EACN,QAAQ;EACR;EACA;EACA;EACA,YAAY,EAAE;EACd,GAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE;EAC3C,CAAC;CAEF,MAAM,WAAW;EACf,UAAU,OAAO;EACjB,MAAM;EACN,QAAQ;EACR;EACA;EACA,YAAY,EAAE;EACf;CACD,MAAM,KAAK,KAAK,KAAK;AACrB,KAAI;EACF,MAAM,SAAS,MAAM,mBAAmB;GACtC,cAAc,OAAO;GACrB,UAAU,OAAO;GACjB;GACA,QAAQ,OAAO;GACf;GACA,MAAM,EAAE;GACR,WAAW,EAAE;GACd,CAAC;EACF,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,GAAG,MAAM,IAAK;AACjD,SAAO,OAAO,SAAS,OAAO;AAC9B,SAAO,MAAM,YAAY;GACvB;GACA;GACA;GACA,IAAI;GACJ,IAAI;GACJ;GACA,KAAK;GACL,QAAQ;GACR,qBAAqB;AACnB,QAAI,CAAC,EAAE,MAAO,SAAQ,OAAO,MAAM,KAAK,UAAU,OAAO,GAAG,KAAK;;GAEpE,CAAC;UACK,GAAG;EACV,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,GAAG,MAAM,IAAK;AACjD,SAAO,OAAO,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;AACxD,QAAM,YAAY;GAAE;GAAQ;GAAQ;GAAQ,IAAI;GAAO,IAAI;GAAG;GAAM,KAAK;GAAU,QAAQ;GAAM,CAAC;AAClG,QAAM;;;;;;;;;;;;;;;;AAiBV,eAAsB,mBACpB,QACA,QACA,KACA,MACA,QACA,QACA,QACkB;AAClB,KAAI,OAAQ,QAAO;CACnB,MAAM,QAAQ,iBAAiB;AAC/B,KAAI,OAAO;AACT,UAAQ,OAAO,MAAM,uCAAuC,IAAI,IAAI,MAAM,IAAI;AAC9E,SAAO;;CAET,MAAM,MAAM,mCAAmC,iBAAiB,QAAQ,sBAAsB;AAC9F,KAAI,QAAQ;AACV,eAAa,QAAQ,kBAAkB;GAAE,IAAI;GAAK,QAAQ,oCAAoC,IAAI,GAAG,MAAM,GAAG,IAAI;GAAE,CAAC;AACrH,QAAM,IAAI,MAAM,uFAAuF,IAAI,GAAG;;AAEhH,KAAI;EACF,MAAM,QAAQ,MAAM,iBAAiB,QAAQ,QAAQ,KAAK,KAAK;AAC/D,UAAQ,OAAO,MAAM,kCAAkC,IAAI,IAAI,MAAM,IAAI;AACzE,SAAO;UACA,GAAG;EACV,MAAM,SAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;AACzD,eAAa,QAAQ,kBAAkB;GAAE,IAAI;GAAK,QAAQ,GAAG,OAAO,IAAI,IAAI,GAAG,MAAM,GAAG,IAAI;GAAE,CAAC;AAC/F,QAAM,IAAI,MAAM,wCAAwC,OAAO,IAAI,IAAI,GAAG;;;;;;;;;AAU9E,eAAsB,aACpB,QACA,QACA,KACA,MACA,QACA,QACA,QACe;CACf,MAAM,WAAW,MAA+C;AAC9D,MAAI,OAAQ,QAAO,OAAO;;CAE5B,MAAM,OAAO,CAAC,SACV,CAAC,OAAO,KAAK,UACX,kCACA,QAAQ,IAAI,wBAAwB,MAClC,0BACA,YAAY,QAAQ,KAAK,QAAQ,UAAU,KAAK,KAAK,SACnD,sDACA,OACN;AACJ,KAAI,MAAM;AACR,UAAQ,OAAO,MAAM,4BAA4B,IAAI,IAAI,KAAK,IAAI;AAClE,UAAQ;GAAE,QAAQ;GAAO,QAAQ;GAAM,CAAC;AACxC;;AAEF,KAAI;EACF,MAAM,IAAI,MAAM,kBAAkB,QAAQ,QAAQ,KAAK,KAAK;EAC5D,MAAM,SAAS,EAAE,UAAU,mBAAmB,EAAE,QAAQ,KAAK;AAC7D,UAAQ,OAAO,MAAM,gCAAgC,MAAM,OAAO,IAAI;AACtE,UAAQ;GAAE,QAAQ;GAAM,GAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,EAAE;GAAG,CAAC;UAChE,GAAG;EACV,MAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;AAC1D,UAAQ,OAAO,MAAM,4BAA4B,IAAI,IAAI,QAAQ,MAAM,GAAG,IAAI,CAAC,IAAI;AACnF,eAAa,QAAQ,aAAa;GAAE,IAAI;GAAK,OAAO,QAAQ,MAAM,GAAG,IAAI;GAAE,CAAC;AAC5E,UAAQ;GAAE,QAAQ;GAAO,QAAQ,QAAQ,MAAM,GAAG,IAAI;GAAE,CAAC;;;;;;;;;;;;AAkD7D,eAAsB,WAAW,GAAwC;CACvE,MAAM,WAAW,aAAa,EAAE,OAAO;AACvC,KAAI,CAAC,SAAS,QAAS,QAAO;CAC9B,MAAM,SAAS,qBAAqB;EAClC,UAAU,EAAE,OAAO;EACnB,QAAQ,EAAE,OAAO;EACjB,cAAc,EAAE,OAAO;EACvB,UAAU,EAAE,OAAO;EACnB,cAAc,EAAE;EAChB,UAAU,EAAE;EACZ,UAAU,EAAE;EACZ,QAAQ,EAAE;EACV,SAAS,EAAE;EACX,WAAW,EAAE;EACb,cAAc,EAAE,OAAO;EACvB,KAAK,EAAE;EACP,SAAS,YAAY,EAAE,OAAO;EAC/B,CAAC;AACF,KAAI,QAAQ;AACV,MAAI,CAAC,EAAE,MAAO,SAAQ,OAAO,MAAM,kCAAkC,OAAO,sBAAsB;AAClG,SAAO;;CAET,MAAM,MAAM,EAAE,MAAM,aAAa;CACjC,MAAM,eAAe,EAAE,SAAS,EAAE,MAAM,UAAU,IAAI,EAAE,MAAM,UAAU;CAExE,MAAM,YAAY,eAAe,eAAe,IAAI,MAAS,OAAU;CACvE,IAAI;AACJ,KAAI;AACF,SAAO,MAAM,eACX,SAAS,MACT;GACE,IAAI;GACJ,OAAO,EAAE;GACT,QAAQ,EAAE,OAAO,UAAU;GAC3B,UAAU,EAAE,OAAO;GACnB,OAAO,EAAE;GACT,WAAW,EAAE;GACb,YAAY,EAAE;GACd,UAAU,EAAE;GACZ,QAAQ,EAAE;GACV,OAAO,EAAE;GACT,cAAc,EAAE;GAChB,cAAc,EAAE,OAAO,iBAAiB,SAAS,SAAS;GAC1D,YAAY;GACZ,SAAS,cAAc,EAAE,aAAa;GACvC,EACD,UACD;SACK;AACN,SAAO;;AAET,KAAI,SAAS,MAAM;AACjB,UAAQ,OAAO,MAAM,+CAA+C,SAAS,KAAK,qBAAqB;AACvG,SAAO;;AAET,KAAI,CAAC,KAAK,QAAQ;AAChB,MAAI,CAAC,EAAE,MAAO,SAAQ,OAAO,MAAM,kCAAkC,KAAK,UAAU,UAAU,sBAAsB;AACpH,SAAO;;AAET,GAAE,gBAAgB,IAAI;CACtB,MAAM,KAAK,KAAK,MAAM;AACtB,KAAI,CAAC,EAAE,MACL,aAAY,EAAE,OAAO,cAAc,KAAK,eAAe,MAAM,IAAI,EAAE,QAAQ,KAAK,KAAK,OAAO;AAE9F,QAAO;;AAGT,eAAe,WAAW,GAAiC;CACzD,MAAM,EAAE,QAAQ,QAAQ,QAAQ,OAAO,OAAO,QAAQ,QAAQ,OAAO,cAAc;CACnF,MAAM,WAAW,OAAO;CAGxB,IAAI;AACJ,KAAI,OAAO,SAAS,aAAa,SAE/B,YAAW,GADE,MAAM,mBAAmB,oBAAoB,OAAO,CAC9C,GAAG,OAAO;CAE/B,MAAM,MAAM,YAAY,OAAO,UAAU,UAAU,UAAU,OAAO,aAAa;CAEjF,MAAM,MAAM,EAAE,MAAM,aAAa;CAEjC,MAAM,UAAU,EAAE,WAAW,UAAU,QAAQ,EAAE,SAAS,GAAG;CAC7D,MAAM,MAAM,SAAS,OAAO,EAAE,OAAO,QAAQ,KAAK;CAClD,MAAM,QAAQ,MAAM,oBAAoB,EAAE;CAC1C,MAAM,eAAe,YAAa,MAAM,mBAAmB,QAAQ,QAAQ,KAAK,MAAM,QAAQ,EAAE,aAAa,MAAM,WAAW,OAAO,CAAC;CACtI,MAAM,UAAU,eAAe,KAAK;CAGpC,MAAM,iBAAiB,sBAAsB,QAAQ,IAAI;CAMzD,IAAI,WAAgC;AACpC,KACE,CAAC,WACD,YACA,CAAC,EAAE,YACH,eAAe,EAAE,cAAc;EAAE;EAAK,cAAc,OAAO;EAAc,CAAC,CAE1E,KAAI;AACF,aAAW,YAAY,QAAQ,KAAK,IAAI;UACjC,GAAG;EACV,MAAM,MAAO,EAAY;AACzB,UAAQ,OAAO,MAAM,4BAA4B,IAAI,wBAAwB;AAC7E,eAAa,WAAW,OAAO,EAAE,eAAe;GAAE,IAAI;GAAK,MAAM,gBAAgB;GAAO,CAAC;;CAI7F,IAAI,WAA4B;AAChC,KAAI,UAAU;EACZ,MAAM,SAAS,aAAa,IAAI;AAChC,MAAI,aAAa,QAAQ;AACvB,WAAQ,OAAO,MAAM,iCAAiC,OAAO,QAAQ,IAAI;AACzE,gBAAa,WAAW,OAAO,EAAE,eAAe;IAAE,IAAI;IAAK,MAAM,qBAAqB,OAAO;IAAW,CAAC;AACzG,UAAO;;AAET,aAAW,OAAO;AAClB,MAAI,CAAC,EAAE,MAAO,SAAQ,OAAO,MAAM,aAAa,SAAS,GAAG,KAAK;;CAGnE,MAAM,WACJ,YAAY,CAAC,OAAO,qBAAqB,qBAAqB,OAAO,cAAc,OAAO,SAAS,SAAS,GAAG;AACjH,KAAI,YAAY,CAAC,EAAE,MAAO,SAAQ,OAAO,MAAM,sBAAsB,SAAS,KAAK,OAAO,SAAS,IAAI,IAAI;AAE3G,KAAI,gBAAgB;AACpB,KAAI,UAAW,KAAI,wBAAwB;CAE3C,MAAM,UAAU,YAAY,kBAAkB,EAAE,UAAU,CAAC,EAAE,QAAQ,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE;CACnF,MAAM,WAAW,aAAa,OAAO,WAAW,kBAAkB,OAAO,SAAS,GAAG,OAAO;AAC5F,KAAI,UAAW,QAAO,MAAM,kBAAkB,OAAO,IAAI;CACzD,MAAM,aAAa,YACf,EAAE,GACF,gBACE;EAAC,GAAI,UAAU,CAAC,QAAQ,GAAG,EAAE;EAAG,GAAG,OAAO;EAAK,GAAI,YAAY,EAAE;EAAG,GAAG,OAAO;EAAa,EAC3F,OAAO,KACR;CAML,IAAI,UAAoB,EAAE;CAE1B,IAAI,YAAsB,WAAW,kBAAkB,OAAO,aAAa,GAAG,EAAE;AAChF,KAAI,YAAY,CAAC,OAAO,aAAa,UAAU,CAAC,OAAO,YACrD,aAAY,CAAC,WAAW,qBAAqB;AAE/C,KAAI,YAAY,CAAC,OAAO,iBAAiB;EACvC,MAAM,SAAS;GACb,GAAI,UAAU,CAAC,QAAQ,GAAG,EAAE;GAC5B,GAAG,OAAO;GACV,GAAI,YAAY,EAAE;GAClB,GAAG,yBAAyB,OAAO,aAAa;GACjD;AACD,MAAI,OAAO,OAET,WAAU;GAAC;GAAuB;GAAgB,eAAe,QAAQ,KAD3D,eAAe,QAAQ,OAAO,CACwC;GAAC;MAErF,WAAU;GAAC;GAAuB;GAAgB,kBAAkB,OAAO;GAAC;YAErE,CAAC,YAAY,CAAC,OAAO,iBAAiB;EAC/C,MAAM,cAAc,CAAC,GAAI,UAAU,CAAC,QAAQ,GAAG,EAAE,EAAG,GAAG,OAAO,IAAI;EAClE,MAAM,gBAAgB,MAAM,oBAAoB,IAAI;EACpD,MAAM,SAAS,oBAAoB,aAAa,OAAO,aAAa,cAAc;EAElF,MAAM,UAAU,iBAAiB;GAC/B,GAAG;GACH,KAAK,YAAY,SAAS,SAAY,cAAc;GACpD,OAAO,OAAO,MAAM,SAAS,cAAc,QAAQ;GACpD;AACD,OAAK,MAAM,QAAQ,WAAW,QAAQ,CAAE,SAAQ,MAAM,KAAK;AAC3D,MAAI,OAAO,SAAS,OAElB,WAAU;GAAC;GAAuB;GAAgB,eAAe,QAAQ,KAD3D,eAAe,OAAO,UAAU,OAAO,CAC+B;GAAC;AAEvF,MAAI,OAAO,MAAM,OAAQ,aAAY,CAAC,WAAW,OAAO,MAAM,KAAK,IAAI,CAAC;;CAK1E,MAAM,WAAW,WAAW,kBAAkB,OAAO,KAAK,GAAG,OAAO;CACpE,MAAM,WAAW,WAAW,mBAAmB,OAAO,aAAa,GAAG,EAAE;CACxE,MAAM,EAAE,MAAM,YAAY,SAAS,mBAAmB,mBACpD,OAAO,QACP,UACA,OAAO,oBACP,EAAE,aACH;CACD,MAAM,YAAY,cAAc,KAAK,OAAO,QAAQ;CACpD,IAAI,MAAgB,CAAC,GAAG,UAAU;AAClC,KAAI,KAAK,GAAG,cAAc,UAAU,OAAO,QAAQ,OAAO,YAAY,CAAC,CAAC;CACxE,MAAM,WAAW,WAAW,gBAAgB,QAAQ,KAAK,OAAO,MAAM,IAAI,GAAG,EAAE;AAC/E,KAAI,KAAK,GAAG,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS;AACxF,KAAI,SACF,KAAI,KAAK,mBAAmB,eAAe,aAAa,kBAAkB,cAAc;AAG1F,KAAI,KACF,GAAG,uBAAuB;EACxB,GAAI,YAAY,CAAC,OAAO,qBAAqB,CAAC,qBAAqB,EAAE,WAAW,EAAE,aAAa,CAAC,GAAG,EAAE;EACrG,GAAI,WAAW,CAAC,SAAS,KAAK,GAAG,EAAE;EACnC,GAAI,SAAS,SAAS,CAAC,aAAa,GAAG,EAAE;EACzC,GAAI,WAAW,CAAC,cAAc,SAAS,CAAC,GAAG,EAAE;EAC7C,GAAI,WACA,CAAC,WAAW,qBAAqB,KAAK,SAAS,QAAQ,SAAS,KAAK,SAAS,SAAS,GAAG,qBAAqB,CAAC,GAChH,EAAE;EACN,GAAI,YAAY,QAAQ,CAAC,mBAAmB,MAAM,CAAC,GAAG,EAAE;EACxD,GAAG,OAAO;EACX,CAAC,CACH;AACD,OAAM,iBAAiB,IAAI;AAG3B,KAAI,EAAE,UAAU;AACd,UAAQ,IACN,KAAK,UAAU;GACb;GACA,KAAK,UAAU,OAAO;GACtB,SAAS,YAAY,QAAQ;GAC7B,GAAI,QAAQ;IAAE,UAAU,IAAI,KAAK,MAAM,WAAW,CAAC,aAAa;IAAE,YAAY,MAAM;IAAS,GAAG,EAAE,OAAO,QAAQ;GAClH,CAAC,CACH;AACD,SAAO;;CAGT,IAAI,SAAuB;EACzB,IAAI;EACJ,KAAK,QAAQ;EACb,GAAG,YAAY;EACf;EACA;EACA;EACA,UAAU,OAAO;EACjB;EACA,OAAO;EACP,SAAS,UAAU;EACnB,SAAS,UAAU;EACnB,OAAO;EACP,OAAO;EACP,MAAM,WAAW,aAAa;EAC9B,IAAI;EACJ,MAAM;EAEN,QAAQ,WAAW,UAAU;EAC7B,cAAc,OAAO;EACrB,GAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE;EACrC,GAAI,OAAO,QAAQ,UAAU,OAAO,eAAe,EAAE,cAAc,OAAO,cAAc,GAAG,EAAE;EAC7F,GAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,GAAG,EAAE;EAC7D,GAAI,WAAW,EAAE,cAAc,EAAE,cAAc,GAAG,EAAE;EACpD,GAAI,iBAAiB,EAAE,eAAe,MAAM,GAAG,EAAE;EACjD,GAAI,WAAW,EAAE,UAAU;GAAE,MAAM,SAAS;GAAM,MAAM,SAAS;GAAM,EAAE,GAAG,EAAE;EAC9E,GAAI,WAAW,EAAE,kBAAkB;GAAE,MAAM,SAAS;GAAM,KAAK,SAAS;GAAK,EAAE,GAAG,EAAE;EACpF,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC9B,GAAI,iBAAiB,EAAE,gBAAgB,GAAG,EAAE;EAG5C,GAAI,EAAE,SAAS;GAAE,QAAQ,EAAE;GAAQ,OAAO,EAAE;GAAO,GAAG,EAAE;EACxD,GAAI,WACA;GACE,aAAa,SAAS;GACtB,QAAQ,SAAS;GACjB,cAAc,SAAS;GACvB,kBAAkB,SAAS;GAC5B,GACD,EAAE;EACN,GAAI,UAAU,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE;EAC9C,GAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE;EAC1C,GAAI,QAAQ;GAAE,YAAY,MAAM;GAAY,YAAY,MAAM;GAAS,GAAG,EAAE;EAC7E;AACD,YAAW,QAAQ,OAAO;AAC1B,KAAI,WAAW,EAAE,SAAU,aAAY,QAAQ,EAAE,UAAU,IAAI;AAC/D,GAAE,gBAAgB,IAAI;CACtB,MAAM,SAAS,WAAW,OAAO;AACjC,cAAa,QAAQ,gBAAgB;EACnC,IAAI;EACJ,UAAU,OAAO;EACjB,MAAM,WAAW,aAAa;EAC9B;EACA;EACA;EACA,GAAI,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,GAAG,EAAE;EACpD,GAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE;EAC1C,GAAI,OAAO,eAAe,EAAE,QAAQ,OAAO,cAAc,GAAG,EAAE;EAC/D,CAAC;AAIF,KAAI,aAAc,QAAO,OAAO,EAAE,QAAQ,MAAM;UACvC,SAAU,CAAK,aAAa,QAAQ,QAAQ,KAAK,MAAM,QAAQ,QAAQ,OAAO;CAEvF,MAAM,KAAK,KAAK,KAAK;CACrB,MAAM,OAAO,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,EAAE;EACvC;EACA,KAAK,UAAU,OAAO;EACtB,OAAO,WAAW;GAAC;GAAQ;GAAQ;GAAU,GAAG;EACjD,CAAC;CAGF,IAAI,mBAAmB;CACvB,IAAI,aAAoC;CACxC,MAAM,sBAAsB;AAC1B,MAAI,WAAY,cAAa,WAAW;AACxC,eAAa,iBAAiB;AAC5B,OAAI,qBAAqB,EACvB,KAAI;AACF,SAAK,OAAO,KAAK;WACX;KAIT,IAAM;;CAGX,IAAI,WAA0B;CAC9B,MAAM,cAAc,QAAuC;AACzD,MAAI,aAAa,KAAM;AACvB,MAAI;AACF,aAAU,UAAU,KAAK,UAAU;IAAE,GAAG;IAAK,KAAK,UAAU;IAAE,CAAC,GAAG,KAAK;UACjE;;CAKV,MAAM,iBAAiB,WACnB,qBAAqB,QAAQ,KAAK,OAAO,SAAS;EAChD,MAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,SAAS,CAAC,MAAM,YAAY,MAAM,cACrC,QAAO;AAET,sBAAoB;AACpB,MAAI,YAAY;AACd,gBAAa,WAAW;AACxB,gBAAa;;AAIf,aAAW;GAAE,MAAM;GAAY;GAAM,SAAS,iBAAiB,KAAK;GAAE,CAAC;AACvE,SAAO,IAAI,SAAwB,YAAY;AAC7C,OAAI;AACF,UAAM,MAAM,iBAAiB,iBAAiB,KAAK,CAAC,GAAG,OAAO,MAC5D,QAAQ,IAAI,+BAA+B,EAAE,YAAY,OAAU,CACpE;YACM,GAAG;AACV,YAAQ,+BAAgC,EAAY,UAAU;;IAEhE;GACF,GACF;CAEJ,IAAI,SAAS;CACb,MAAM,eAAe,QAAQ,YAAY,MAAM,aAAa,aAAa,QAAQ,QAAQ,QAAQ,MAAM,CAAC,GAAG;CAC3G,MAAM,gBAAgB;AACpB,kBAAgB;AAChB,MAAI,WAAY,cAAa,WAAW;AACxC,kBAAgB,OAAO;;CAEzB,MAAM,YAAY,QAAgB;AAChC,WAAS;AACT,SAAO,QAAQ;AACf,SAAO,KAAK;AACZ,SAAO,OAAO,KAAK,OAAO,KAAK,KAAK,GAAG,MAAM,IAAK;EAClD,MAAM,MAAM,gBAAgB,QAAQ,QAAQ,IAAI;AAChD,SAAO,OAAO,oBAAoB,IAAI,IAAI,IAAI,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO,GAAG;AACrF,aAAW,QAAQ,OAAO;AAC1B,eAAa,QAAQ,cAAc;GACjC,IAAI;GACJ,UAAU,OAAO;GACjB,MAAM;GACN;GACA,IAAI;GACJ,MAAM,OAAO;GACb,QAAQ;GACR,GAAG,UAAU,IAAI;GACjB,MAAM,IAAI;GACV;GACD,CAAC;AAGF,MAAI,UAAU;AACZ,iBAAc,QAAQ,KAAK,OAAO,SAAS,KAAK,kBAAkB;AAClE,OAAI;AACF,mBAAe,QAAQ,QAAQ,UAAU,MAAM;WACzC;;AAIV,WAAS;AACT,MAAI;AACF,QAAK,MAAM;UACL;AAGR,UAAQ,KAAK,IAAI;;AAEnB,SAAQ,KAAK,iBAAiB,SAAS,UAAU,CAAC;AAClD,SAAQ,KAAK,gBAAgB,SAAS,SAAS,CAAC;AAChD,SAAQ,KAAK,gBAAgB,SAAS,SAAS,CAAC;CAIhD,MAAM,MAGF;EACF,aAAa;EACb,cAAc;EACf;CAGD,MAAM,WAAsB,EAAE;CAC9B,MAAM,mBAAmB,IAAI,IAAI,WAAW,eAAe,UAAU,OAAO,IAAI,GAAG,EAAE,CAAC;AAEtF,KAAI,UAAU;AAEZ,MAAI,eAAe,KACjB,KAAI;AACF,QAAK,MAAO,MAAM,iBAAiB,WAAW,GAAG,KAAK;UAChD;AAIV,aAAW,SAAS,WAAW,QAAQ,IAAI,EAAE,IAAI;AACjD,aAAW;GAAE,MAAM;GAAY,MAAM,aAAa,SAAS;GAAE,GAAI,WAAW,EAAE,MAAM,SAAS,MAAM,GAAG,EAAE;GAAG,CAAC;AAC5G,MAAI,SAAU,YAAW;GAAE,MAAM;GAAqB,MAAM,GAAG,SAAS,KAAK,OAAO,SAAS;GAAO,KAAK,SAAS;GAAK,CAAC;EACxH,MAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK,QAAS,CAAC;EACnD,MAAM,2BAAW,IAAI,KAA+C;AACpE,KAAG,GAAG,SAAS,SAAS;AACtB,OAAI,OAAO,iBAAiB,cAC1B,SAAQ,OAAO,MAAM,OAAO,KAAK;AAEnC,OAAI,CAAC,KAAK,WAAW,IAAI,CAAE;GAC3B,IAAI;AACJ,OAAI;AACF,QAAI,KAAK,MAAM,KAAK;WACd;AACN;;AAEF,cAAW,EAA6B;AACxC,OAAI,EAAE,SAAS,YAAY,EAAE,YAAY,QAAQ;AAC/C,QAAI,EAAE,WAAY,QAAO,gBAAgB,EAAE;AAC3C,mBAAe,QAAQ,EAAE;IACzB,MAAM,KACJ,kBAAkB,EAAE,IAAI,OAAO,SAAS,iBAAiB,qBAAqB,OAAO,MAAM;AAC7F,QAAI,GAAI,QAAO,gBAAgB;AAC/B,eAAW,QAAQ,OAAO;cACjB,kBAAkB,EAAE,EAAE;AAC/B,gCAA4B,QAAQ,EAAE;AACtC,eAAW,QAAQ,OAAO;cACjB,EAAE,SAAS,aAAa;AACjC,WAAO,SAAS;AAChB,sBAAkB,QAAQ,mBAAmB,EAAE,SAAS,MAAM,CAAC;AAC/D,WAAO,cAAc,iBAAiB,EAAE,SAAS,MAAM,IAAI,OAAO;AAClE,SAAK,MAAM,SAAS,EAAE,SAAS,WAAW,EAAE,CAC1C,KAAI,MAAM,SAAS,YAAY;AAC7B,YAAO,SAAS;AAChB,SAAI,MAAM,GAAI,UAAS,IAAI,MAAM,IAAI;MAAE,MAAM,MAAM,QAAQ;MAAK,OAAO,MAAM;MAAO,CAAC;AACrF,YAAO,OAAO,aAAa,MAAM,QAAQ,KAAK,MAAM,MAAM;AAG1D,SAAI,MAAM,SAAS,OAAQ,QAAO,WAAW,eAAe,YAAY,MAAM,MAAM,CAAC;eAC5E,MAAM,SAAS,WAAW,MAAM,QAAQ,IAAI,MAAM,CAC3D,QAAO,OAAO,WAAW,UAAU,MAAM,MAAM,GAAG;AAGtD,eAAW,QAAQ,OAAO;cACjB,EAAE,SAAS,QAAQ;IAK5B,MAAM,SAAS,EAAE,SAAS;AAC1B,QAAI,MAAM,QAAQ,OAAO,EAAE;KACzB,IAAI,YAAY;AAChB,UAAK,MAAM,SAAS,QAAQ;AAC1B,UAAI,MAAM,SAAS,cAAe;AAClC,kBAAY;AACZ,aAAO,cAAc,iBAAiB,MAAM,IAAI,OAAO,eAAe,KAAK,IAAI;AAC/E,aAAO,WAAW;AAClB,mBAAa,QAAQ,OAAO,eAAe,MAAM,QAAQ,EAAE,UAAU,OAAO,yBAAQ,IAAI,MAAM,EAAC,aAAa,CAAC;AAC7G,UAAI,MAAM,aAAa,KACrB,iBAAgB,QAAQ;OACtB,QAAQ;OACR,UAAU,OAAO;OACjB;OACA,MAAM,SAAS,IAAI,MAAM,eAAe,GAAG,EAAE,QAAQ;OACrD,MAAM,eAAe,MAAM,QAAQ;OACnC,OAAO,SAAS,IAAI,MAAM,eAAe,GAAG,EAAE;OAC/C,CAAC;MAEJ,MAAM,MAAM,MAAM,cAAc,SAAS,IAAI,MAAM,YAAY,GAAG;AAClE,UAAI,KAAK,SAAS,OAAQ,UAAS,KAAK;OAAE,SAAS,YAAY,IAAI,MAAM;OAAE,QAAQ,iBAAiB,MAAM;OAAE,CAAC;;AAE/G,SAAI,UAAW,YAAW,QAAQ,OAAO;;cAElC,EAAE,SAAS,UAAU;IAC9B,MAAM,SAAS,mBAAmB,EAAE,MAAM;AAE1C,QAAI,EAAE,WAAY,QAAO,gBAAgB,UAAU,OAAO;QACrD,mBAAkB,QAAQ,OAAO;IACtC,MAAM,SAAS,kBAAkB,EAAE,UAAU,GAAG;AAChD,QAAI,QAAQ,MAAO,QAAO,OAAO,UAAU,OAAO,OAAO,GAAG;aACnD,EAAE,OAAQ,QAAO,OAAO,UAAU,EAAE,QAAQ,GAAG;AACxD,eAAW,QAAQ,OAAO;AAC1B,uBAAmB;AACnB,mBAAe;AACf,QAAI,cAAc;AAClB,QAAI,eAAe;;IAErB;;CAGJ,MAAM,KAAK,MAAM,IAAI,SAAiB,SAAS,WAAW;AACxD,OAAK,GAAG,SAAS,OAAO;AACxB,OAAK,GAAG,UAAU,SAAS,QAAQ,SAAS,SAAS,MAAM,GAAG,CAAC;GAC/D;AACF,KAAI,aAAa,KAAM,WAAU,SAAS;AAC1C,UAAS;CAET,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,GAAG,MAAM,IAAK;CACjD,MAAM,cAAc,IAAI;CACxB,MAAM,QAAQ,YAAY,OAAO,KAAK,gBAAgB,QAAQ,gBAAgB,YAAY,QAAQ,OAAO,MAAM;CAC/G,MAAM,KAAK,CAAC,SAAS,aAAa,UAAU,IAAI,YAAY;AAC5D,KAAI,YAAa,QAAO,OAAO,UAAU,YAAY,UAAU,IAAI,GAAG;AACtE,KAAI,IAAI,cAAc,MAAO,QAAO,OAAO,UAAU,IAAI,aAAa,OAAO,GAAG;AAChF,KAAI,OAAO;AACT,SAAO,OAAO;AACd,kBAAgB,QAAQ;GACtB,QAAQ;GACR,UAAU,OAAO;GACjB;GACA,MAAM;GACN,MAAM;GACP,CAAC;;AAEJ,KAAI,YAAY,EAAE,iBAAiB,SAAS,aAAa,QAAQ;EAC/D,MAAM,IAAI,YAAY,YAAY,OAAO;AACzC,SAAO,cAAc,EAAE;AACvB,SAAO,eAAe,EAAE;AAExB,MAAI,kBAAkB,GAAG,EAAE,iBAAiB,OAAO,QAAQ,OAAO,cAAc,CAAC,EAAE;AACjF,UAAO,gBAAgB;AACvB,cAAW,QAAQ,OAAO;AAC1B,gBAAa,QAAQ,uBAAuB;IAAE,IAAI;IAAK,QAAQ,EAAE,OAAO,KAAK,KAAK,IAAI;IAAuB,CAAC;AAC9G,OAAI;IACF,MAAM,YAAY,MAAM,eAAe;KACrC;KACA,KAAK,UAAU,OAAO;KACtB;KACA,mBAAmB,QAAQ,OAAO,YAAY;KAC9C;KACA;KACA;KACA;KACA;KACA,WAAW,OAAO;KAClB;KACD,CAAC;IACF,MAAM,KAAK,YAAY,UAAU;AACjC,WAAO,cAAc,GAAG;AACxB,WAAO,eAAe,GAAG;AACzB,QAAI,GAAG,IAAI;AACT,iBAAY,SAAS;AACrB,SAAI,eAAe,kBAAkB,UAAU;AAC/C,SAAI,IAAI,cAAc,MAAO,QAAO,OAAO,UAAU,IAAI,aAAa,OAAO,GAAG;;WAE5E;;;AAKZ,KAAI,YAAY,EAAE,iBAAiB,SAAS,aAAa,QAAQ;EAC/D,MAAM,QAAQ,aAAa,UAAU,OAAO,KAAK,iBAAiB;EAClE,MAAM,MAAM,WAAW,YAAY,QAAQ,UAAU,MAAM;AAC3D,MAAI,IAAI,aAAa,MAAM,QAAQ;AACjC,eAAY,SAAS,IAAI;AACzB,OAAI,eAAe,kBAAkB,IAAI,KAAK;AAC9C,OAAI,IAAI,aAAc,KAAI,aAAa,YAAY,MAAM,SAAS,QAAQ;;AAE5E,MAAI,IAAI,WAAW;AACjB,UAAO,cAAc;AACrB,UAAO,OAAO,UAAU,oCAAoC,IAAI,SAAS,GAAG;AAC5E,gBAAa,QAAQ,2BAA2B;IAAE,IAAI;IAAK,OAAO,IAAI;IAAO,CAAC;;AAEhF,MAAI,MAAM,OAAQ,SAAQ,OAAO,MAAM,wBAAwB,MAAM,OAAO,yCAAyC,MAAM,KAAK,KAAK,CAAC,IAAI;;AAE5I,KAAI,IAAI,gBAAgB,OAAO,SAAS,OAAQ,KAAI,aAAa,UAAU,OAAO;AAClF,KAAI,YAAY,IAAI,cAAc,SAAS,QAAQ;EACjD,MAAM,SAAS,oBAAoB,IAAI,aAAa,SAAS,UAAU,OAAO,KAAK,GAAG;AACtF,MAAI,CAAC,OAAO,IAAI;AACd,UAAO,cAAc;GACrB,MAAM,OAAO,eAAe,OAAO;AACnC,UAAO,OAAO,UAAU,MAAM,GAAG;AACjC,OAAI,aAAa,QAAQ,IAAI,aAAa,QAAQ,GAAG,IAAI,aAAa,MAAM,KAAK,SAAS;AAC1F,gBAAa,QAAQ,qBAAqB;IACxC,IAAI;IACJ,UAAU,OAAO,SAAS;IAC1B,OAAO,OAAO,SAAS,KAAK,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;IAChE,CAAC;;;AAGN,QAAO,YAAY;EACjB;EACA;EACA;EACA;EACA;EACA;EACA,KAAK;GACH,UAAU,OAAO;GACjB,MAAM,WAAW,aAAa;GAC9B;GACA,OAAO,OAAO;GACd,OAAO,OAAO;GACd;GACD;EACD,QAAQ,IAAI;EACZ,cAAc,aAAa;EAC3B;EACA,UAAU,OAAO,WAAW,OAAO,UAAU;EAC7C,eAAe,OAAO,IAAI,QAAQ;GAEhC,MAAM,MAAM,WAAW,kBAAkB,EAAE,OAAO,IAAI,IAAI,GAAG;AAC7D,OAAI,OAAO,EAAE,OAAO;AAClB,iBAAa,QAAQ,sBAAsB;KAAE,IAAI;KAAK,QAAQ;KAAK,CAAC;AACpE,YAAQ,OAAO,MAAM,eAAe,IAAI,MAAM,OAAO,aAAa,gBAAgB,IAAI,+BAA+B;AACrH,WAAO,UAAU;KACf,cAAc;KACd,OAAO,GAAG,MAAM;KAChB,QAAQ,EAAE;KACV,SAAS,EAAE;KACX,KAAK,EAAE;KACP,UAAU,EAAE;KACZ,QAAQ,EAAE;KACV,OAAO,EAAE;KACT,YAAY,EAAE;KACd,OAAO,EAAE;KACT,cAAc,EAAE;KAChB,WAAW,EAAE;KACb,QAAQ;MAAE,QAAQ,EAAE,MAAM;MAAQ,QAAQ,iBAAiB,IAAI;MAAI,OAAO;MAAO;KACjF,gBAAgB;KAChB,gBAAgB,QAAQ;AACtB,SAAG,gBAAgB;AACnB,iBAAW,QAAQ,GAAG;AACtB,QAAE,gBAAgB,IAAI;;KAEzB,CAAC;;AAEJ,OAAI,YAAY,CAAC,EAAE,MACjB,aAAY,OAAO,cAAc,aAAa,IAAI,QAAQ,KAAK,KAAK,eAAe,GAAG,CAAC;AAI3F,OAAI,SAAS,CAAC,EAAE,cAAc;AAC5B,iBAAa,QAAQ,sBAAsB;KAAE,IAAI;KAAK,UAAU,OAAO;KAAc,CAAC;AACtF,WAAO,UACL,gBAAgB,EAAE,MAAM;KACtB,cAAc,OAAO;KACrB,eAAe,EAAE;KACjB,eAAe;KAChB,CAAC,CACH;;GAIH,MAAM,aAAa,aAAa,UAAU;AAC1C,OACE,CAAC,MACD,YACA,EAAE,OAAO,QAAQ,UACjB,OAAO,QAAQ,gBACf,OAAO,SAAS,KAChB,OAAO,UAAU,KACjB,eAAe,WAAW,EAC1B;AACA,gBAAY,QAAQ,OAAO,cAAc,OAAO,QAAQ,gBAAgB;IACxE,MAAM,OAAO,iBAAiB,QAAQ,QAAQ,OAAO,aAAa;AAClE,QAAI,QAAQ,EAAE,WAAW,UAAU,OAAO,CAAC,QAAQ;AACjD,kBAAa,QAAQ,kBAAkB;MACrC,MAAM,OAAO;MACb,IAAI;MACJ,QAAQ;MACT,CAAC;AACF,YAAO,UACL,gBAAgB,EAAE,MAAM;MACtB,cAAc;MACd,WAAW;MACX,eAAe,EAAE;MACjB,WAAW,EAAE,WAAW;MACzB,CAAC,CACH;;;;EAIN,CAAC;;;;;;;;AASJ,SAAgB,gBAAgB,MAAkB,SAA0C;AAC1F,QAAO;EAAE,GAAG;EAAM,IAAI;EAAW,GAAG;EAAS;;;AAI/C,SAAgB,eAAe,GAAsD;AACnF,QAAO,EAAE,SAAS;EAAE,QAAQ,EAAE;EAAQ,SAAS,EAAE,WAAW;EAAG,GAAG;;;;;;;;;AA2BpE,eAAsB,eAAe,GAAkC;CACrE,IAAI,MAAgB,CAAC,GAAI,EAAE,aAAa,CAAC,SAAS,CAAE;AACpD,KAAI,KAAK,GAAG,cAAc,MAAM,EAAE,OAAO,EAAE,kBAAkB,CAAC;AAC9D,KAAI,KAAK,GAAG,EAAE,YAAY,GAAG,EAAE,SAAS,GAAG,EAAE,UAAU,GAAG,EAAE,WAAW,GAAI,EAAE,YAAY,EAAE,CAAE;AAC7F,KAAI,KAAK,YAAY,EAAE,WAAW,MAAM,gBAAgB,mBAAmB,OAAO;AAClF,OAAM,iBAAiB,IAAI;CAC3B,MAAM,OAAO,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,EAAE;EAAE,KAAK,EAAE;EAAK,KAAK,EAAE;EAAK,OAAO;GAAC;GAAU;GAAQ;GAAU;EAAE,CAAC;CAC1G,MAAM,QAAQ,iBAAiB;AAC7B,MAAI;AACF,QAAK,KAAK,UAAU;UACd;IAGP,EAAE,aAAa,IAAO;CACzB,IAAI,MAAM;AACV,MAAK,OAAO,GAAG,SAAS,UAAkB;AACxC,SAAO,MAAM,SAAS,OAAO;GAC7B;AACF,OAAM,IAAI,SAAe,YAAY;AACnC,OAAK,GAAG,eAAe,SAAS,CAAC;AACjC,OAAK,GAAG,eAAe,SAAS,CAAC;GACjC;AACF,cAAa,MAAM;AACnB,QAAO,iBAAiB,IAAI;;AAS9B,eAAe,gBAAgB,GAA+B;CAC5D,MAAM,EAAE,QAAQ,QAAQ,QAAQ,OAAO,OAAO,QAAQ,WAAW;AACjE,KAAI,CAAC,OAAO,YAAY,OAAO,WAAW,KACxC,OAAM,IAAI,MACR,aAAa,OAAO,aAAa,gFAElC;CAEH,MAAM,MAAM,cAAc,OAAO,SAAS;CAC1C,MAAM,MAAM,EAAE,MAAM,aAAa;CACjC,MAAM,MAAM,EAAE,OAAO,QAAQ,KAAK;CAClC,MAAM,QAAQ,MAAM,oBAAoB,EAAE;CAC1C,MAAM,eAAe,MAAM,mBAAmB,QAAQ,QAAQ,KAAK,MAAM,QAAQ,OAAO,WAAW,OAAO,CAAC;CAC3G,MAAM,UAAU,eAAe,KAAK;CACpC,MAAM,iBAAiB,sBAAsB,QAAQ,IAAI;CAEzD,IAAI,WAAgC;AACpC,KAAI,eAAe,EAAE,cAAc;EAAE;EAAK,cAAc,OAAO;EAAc,CAAC,CAC5E,KAAI;AACF,aAAW,YAAY,QAAQ,KAAK,IAAI;UACjC,GAAG;EACV,MAAM,MAAO,EAAY;AACzB,UAAQ,OAAO,MAAM,4BAA4B,IAAI,wBAAwB;AAC7E,eAAa,WAAW,OAAO,EAAE,eAAe;GAAE,IAAI;GAAK,MAAM,gBAAgB;GAAO,CAAC;;AAG7F,KAAI,gBAAgB;CAEpB,MAAM,UACH,EAAE,QAAQ,mBAAmB,EAAE,MAAM,GAAG,SAAS,OACjD,WAAW,qBAAqB,KAAK,SAAS,QAAQ,SAAS,KAAK,SAAS,SAAS,GAAG,SAAS,MACnG,OAAO;CAET,IAAI,SAAuB;EACzB,IAAI;EACJ,KAAK,QAAQ;EACb,GAAG,YAAY;EACf;EACA;EACA;EACA,UAAU,OAAO;EACjB;EACA,OAAO;EACP,SAAS,UAAU;EACnB,SAAS,UAAU;EACnB,OAAO;EACP,OAAO;EACP,MAAM;EACN,IAAI;EACJ,MAAM;EACN,QAAQ;EACR,cAAc,OAAO;EAErB,cAAc;EACd,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC9B,GAAI,iBAAiB,EAAE,gBAAgB,GAAG,EAAE;EAG5C,GAAI,OAAO,SAAS,gBAAgB,EAAE,eAAe,OAAO,SAAS,eAAe,GAAG,EAAE;EACzF,GAAI,EAAE,SAAS;GAAE,QAAQ,EAAE;GAAQ,OAAO,EAAE;GAAO,GAAG,EAAE;EACxD,GAAI,WACA;GACE,aAAa,SAAS;GACtB,QAAQ,SAAS;GACjB,cAAc,SAAS;GACvB,kBAAkB,SAAS;GAC5B,GACD,EAAE;EACN,GAAI,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,EAAE;EAC1C,GAAI,EAAE,QAAQ;GAAE,YAAY,EAAE,MAAM;GAAY,YAAY,EAAE,MAAM;GAAS,GAAG,EAAE;EACnF;AACD,YAAW,QAAQ,OAAO;AAC1B,GAAE,gBAAgB,IAAI;CACtB,MAAM,SAAS,WAAW,OAAO;AACjC,cAAa,QAAQ,gBAAgB;EACnC,IAAI;EACJ,UAAU,OAAO;EACjB,MAAM;EACN,QAAQ;EACR;EACA;EACA;EACA,GAAI,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,GAAG,EAAE;EACrD,CAAC;CACF,MAAM,UAAU,kBAAkB,EAAE,WAAW;AAC/C,KAAI,QAAQ,OACV,cAAa,QAAQ,eAAe;EAClC,IAAI;EACJ,MAAM,sBAAsB,QAAQ,KAAK,KAAK;EAC/C,CAAC;AAGJ,KAAI,aAAc,QAAO,OAAO,EAAE,QAAQ,MAAM;KAC3C,CAAK,aAAa,QAAQ,QAAQ,KAAK,MAAM,QAAQ,QAAQ,OAAO;CAEzE,MAAM,KAAK,KAAK,KAAK;CACrB,MAAM,OAAO,MAAM,SAAS,eAAe,QAAQ,OAAO,cAAc,SAAY,MAAM,EAAE;EAC1F;EACA,KAAK,UAAU,OAAO;EACtB,OAAO;GAAC;GAAU;GAAQ;GAAU;EACrC,CAAC;CAEF,MAAM,OAAO,kBAAkB;CAC/B,MAAM,WAAW,SAAS,WAAW,QAAQ,IAAI,EAAE,IAAI;CACvD,MAAM,cAAc,QAAiC;AACnD,MAAI;AACF,aAAU,UAAU,KAAK,UAAU;IAAE,GAAG;IAAK,KAAK,UAAU;IAAE,CAAC,GAAG,KAAK;UACjE;;AAIV,YAAW;EACT,MAAM;EACN,SAAS;EACT;EACA;EACA,GAAI,OAAO,SAAS,gBAAgB,EAAE,gBAAgB,OAAO,SAAS,eAAe,GAAG,EAAE;EAC3F,CAAC;CAEF,IAAI,SAAS;CACb,MAAM,eAAe,EAAE,QAAQ,YAAY,MAAM,EAAE,aAAa,aAAa,QAAQ,QAAQ,QAAQ,EAAE,MAAO,CAAC,GAAG;AAClH,SAAQ,KAAK,WAAW,cAAc,UAAU,CAAC;AACjD,SAAQ,KAAK,UAAU,cAAc,SAAS,CAAC;AAC/C,SAAQ,KAAK,UAAU,cAAc,SAAS,CAAC;CAC/C,SAAS,cAAc,KAAa;AAClC,eAAa;AACX,YAAS;AACT,UAAO,QAAQ;AACf,UAAO,KAAK;AACZ,UAAO,OAAO,KAAK,OAAO,KAAK,KAAK,GAAG,MAAM,IAAK;GAClD,MAAM,MAAM,gBAAgB,QAAQ,QAAQ,IAAI;AAChD,UAAO,OAAO,oBAAoB,IAAI,IAAI,IAAI,SAAS,IAAI,KAAK,OAAO,IAAI,OAAO,GAAG;AACrF,cAAW,QAAQ,OAAO;AAC1B,gBAAa,QAAQ,cAAc;IACjC,IAAI;IACJ,UAAU,OAAO;IACjB,MAAM;IACN;IACA,IAAI;IACJ,MAAM,OAAO;IACb,QAAQ;IACR,GAAG,UAAU,IAAI;IACjB,MAAM,IAAI;IACV;IACD,CAAC;AAEF,OAAI,UAAU;AACZ,kBAAc,QAAQ,KAAK,OAAO,SAAS,KAAK,kBAAkB;AAClE,QAAI;AACF,oBAAe,QAAQ,QAAQ,UAAU,MAAM;YACzC;;AAIV,OAAI;AACF,SAAK,MAAM;WACL;AAGR,WAAQ,KAAK,IAAI;;;AAKrB,CADW,gBAAgB,EAAE,OAAO,KAAK,QAAS,CAAC,CAChD,GAAG,SAAS,SAAS;AACtB,MAAI,OAAO,iBAAiB,cAAe,SAAQ,OAAO,MAAM,OAAO,KAAK;EAC5E,MAAM,aAAa,eAAe,KAAK;AACvC,MAAI,eAAe,KAAM;AACzB,gBAAc,YAAY,KAAK;AAC/B,MAAI,KAAK,YAAY,CAAC,OAAO,cAAe,QAAO,gBAAgB,KAAK;AACxE,SAAO,QAAQ,KAAK;AACpB,SAAO,QAAQ,KAAK;AACpB,MAAI,KAAK,KAAM,QAAO,OAAO,UAAU,KAAK,MAAM,GAAG;AACrD,oBAAkB,QAAQ,KAAK,cAAc;AAC7C,aAAW,QAAQ,OAAO;AAC1B,OAAK,MAAM,MAAM,KAAK,OAAO,OAAO,EAAE,CAAE,YAAW,GAAG;GACtD;CAEF,MAAM,KAAK,MAAM,IAAI,SAAiB,SAAS,WAAW;AACxD,OAAK,GAAG,SAAS,OAAO;AACxB,OAAK,GAAG,UAAU,SAAS,QAAQ,SAAS,SAAS,MAAM,GAAG,CAAC;GAC/D;AACF,iBAAgB;AAChB,WAAU,SAAS;CAEnB,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,GAAG,MAAM,IAAK;CACjD,MAAM,YAAY,KAAK,aAAa;CACpC,MAAM,SAAS,kBAAkB,UAAU;CAC3C,MAAM,cAA2B;EAC/B,MAAM;EACN,QAAQ;EACR,UAAU,KAAK,WAAW,OAAO;EACjC,WAAW,KAAK;EAChB,aAAa,OAAO;EACrB;AACD,YAAW,YAAY;CAEvB,MAAM,KAAK,OAAO,KAAK,CAAC,KAAK;AAC7B,QAAO,OAAO,UAAU,QAAQ,SAAS,WAAW,GAAG,KAAK,KAAK,SAAS;AAC1E,KAAI,QAAQ,SAAS,QAAQ;EAC3B,MAAM,SAAS,oBAAoB,OAAO,SAAS,UAAU,OAAO,KAAK,GAAG;AAC5E,MAAI,CAAC,OAAO,IAAI;AACd,UAAO,cAAc;GACrB,MAAM,OAAO,eAAe,OAAO;AACnC,UAAO,OAAO,UAAU,MAAM,GAAG;AACjC,UAAO,QAAQ,OAAO,QAAQ,GAAG,OAAO,MAAM,KAAK,SAAS;AAC5D,gBAAa,QAAQ,qBAAqB;IACxC,IAAI;IACJ,UAAU,OAAO,SAAS;IAC1B,OAAO,OAAO,SAAS,KAAK,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;IAChE,CAAC;;;AAGN,QAAO,YAAY;EACjB;EACA;EACA;EACA;EACA;EACA;EACA,KAAK;GACH,UAAU,OAAO;GACjB,MAAM;GACN,QAAQ;GACR;GACA,OAAO,OAAO;GACd,OAAO,OAAO;GACd;GACD;EACD;EACA,cAAc;EACd;EACA,UAAU,OAAO,WAAW,EAAE,OAAO,UAAU;EAC/C,gBAAgB,IAAI,QAAQ;AAC1B,OAAI,CAAC,EAAE,MAAO,aAAY,OAAO,cAAc,aAAa,IAAI,QAAQ,KAAK,KAAK,eAAe,GAAG,CAAC;;EAExG,CAAC;;AAOJ,SAAgB,YACd,KACA,aACA,IACA,QACA,KACA,QACA,QACM;AACN,KAAI,QAAQ,cAAe;AAC3B,KAAI,QAAQ,QAAQ;EAClB,MAAM,UAAU,SACZ;GAAE,GAAI,eAAe;IAAE,UAAU;IAAM,QAAQ;IAAmB;IAAI;GAAG;GAAQ,GAAG;GAAQ,GAC5F;GAAE,GAAI,eAAe;IAAE,UAAU;IAAM,QAAQ;IAAmB;IAAI;GAAG,GAAG;GAAQ;AACxF,UAAQ,IAAI,KAAK,UAAU,QAAQ,CAAC;AACpC;;AAEF,KAAI,YACF,SAAQ,IAAI,YAAY,UAAU,GAAG;KAErC,SAAQ,OAAO,MACb,0CAA0C,GAAG,SAAS,WAAW,QAAQ,IAAI,CAAC,IAC/E;;;;;;;AAuBL,eAAsB,aACpB,cACA,UACA,QACA,YAAY,KACZ,UAAU,OACmB;AAC7B,wBAAuB,cAAc,SAAS;CAC9C,MAAM,QAAQ,SAAS,OAAO;AAE9B,KAAI,SAAS,WAAW,SAAS;EAC/B,MAAM,UAAU,gBAAgB,GAAG,SAAY;AAC/C,SAAO;GACL,UAAU;GACV;GACA,WAAW;GACX,QAAQ,WAAW;GACnB,IAAI;GACJ,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;GAC/B;;CAGH,IAAI;AACJ,KAAI,SAAS,aAAa,SAExB,YAAW,GADE,MAAM,mBAAmB,oBAAoB,OAAO,CAC9C,GAAG;CAExB,MAAM,MAAM,YAAY,UAAU,MAAM,UAAU,aAAa;CAC/D,MAAM,CAAC,KAAK,GAAG,QAAQ,cAAc,KAAK,QAAQ;CAClD,MAAM,KAAK,KAAK,KAAK;CACrB,MAAM,OAAO,MACX,KACA;EACE,GAAG;EACH;EAAW;EACX;EAAuB;EAAgB,kBAAkB,OAAO;EAChE;EAAM;EACN;EAAmB;EACpB,EACD;EAAE;EAAK,OAAO;GAAC;GAAU;GAAQ;GAAU;EAAE,CAC9C;CACD,MAAM,QAAQ,iBAAiB;AAC7B,MAAI;AAAE,QAAK,KAAK,UAAU;UAAU;IACnC,UAAU;CAEb,IAAI,MAAM;AACV,MAAK,OAAO,GAAG,SAAS,UAAkB;AACxC,SAAO,MAAM,SAAS,OAAO;GAC7B;CACF,MAAM,KAAK,MAAM,IAAI,SAAiB,YAAY;AAChD,OAAK,GAAG,eAAe,QAAQ,EAAE,CAAC;AAClC,OAAK,GAAG,UAAU,SAAS,QAAQ,QAAQ,EAAE,CAAC;GAC9C;AACF,cAAa,MAAM;AAEnB,QAAO;EACL,UAAU;EACV;EACA,WAAW,KAAK,KAAK,GAAG;EACxB,QAAQ,iBAAiB,IAAI;EAC7B,IAAI,OAAO,KAAK,iBAAiB,IAAI,CAAC,MAAM,CAAC,aAAa,KAAK;EAChE;;;;;;;;AASH,SAAgB,iBAAiB,KAAqB;CACpD,MAAM,UAAU,IAAI,MAAM;AAC1B,KAAI,CAAC,QAAS,QAAO;CACrB,MAAM,SAAS,MAAuB;AACpC,MAAI;AACF,UAAO,KAAK,MAAM,EAAE;UACd;AACN;;;CAGJ,MAAM,aAAa,MAA8B;AAC/C,MAAI,MAAM,QAAQ,EAAE,EAAE;AACpB,QAAK,IAAI,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,KAAK;IACtC,MAAM,IAAI,UAAU,EAAE,GAAG;AACzB,QAAI,MAAM,KAAM,QAAO;;AAEzB,UAAO;;AAET,MAAI,OAAO,MAAM,YAAY,MAAM,MAAM;GACvC,MAAM,IAAI;AACV,OAAI,EAAE,SAAS,YAAY,OAAO,EAAE,WAAW,SAAU,QAAO,EAAE;;AAEpE,SAAO;;CAET,MAAM,SAAS,UAAU,MAAM,QAAQ,CAAC;AACxC,KAAI,WAAW,KAAM,QAAO;CAC5B,MAAM,QAAQ,QAAQ,MAAM,KAAK;AACjC,MAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;EAC1C,MAAM,IAAI,UAAU,MAAM,MAAM,GAAG,CAAC;AACpC,MAAI,MAAM,KAAM,QAAO;;AAEzB,QAAO,QAAQ,MAAM,GAAG,IAAI"}