{
  "version": 3,
  "sources": ["../../../src/workers/args.ts", "../../../src/config/pai-home.ts", "../../../src/workers/paths.ts", "../../../node_modules/yaml/dist/nodes/identity.js", "../../../node_modules/yaml/dist/visit.js", "../../../node_modules/yaml/dist/doc/directives.js", "../../../node_modules/yaml/dist/doc/anchors.js", "../../../node_modules/yaml/dist/doc/applyReviver.js", "../../../node_modules/yaml/dist/nodes/toJS.js", "../../../node_modules/yaml/dist/nodes/Node.js", "../../../node_modules/yaml/dist/nodes/Alias.js", "../../../node_modules/yaml/dist/nodes/Scalar.js", "../../../node_modules/yaml/dist/doc/createNode.js", "../../../node_modules/yaml/dist/nodes/Collection.js", "../../../node_modules/yaml/dist/stringify/stringifyComment.js", "../../../node_modules/yaml/dist/stringify/foldFlowLines.js", "../../../node_modules/yaml/dist/stringify/stringifyString.js", "../../../node_modules/yaml/dist/stringify/stringify.js", "../../../node_modules/yaml/dist/stringify/stringifyPair.js", "../../../node_modules/yaml/dist/log.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/merge.js", "../../../node_modules/yaml/dist/nodes/addPairToJSMap.js", "../../../node_modules/yaml/dist/nodes/Pair.js", "../../../node_modules/yaml/dist/stringify/stringifyCollection.js", "../../../node_modules/yaml/dist/nodes/YAMLMap.js", "../../../node_modules/yaml/dist/schema/common/map.js", "../../../node_modules/yaml/dist/nodes/YAMLSeq.js", "../../../node_modules/yaml/dist/schema/common/seq.js", "../../../node_modules/yaml/dist/schema/common/string.js", "../../../node_modules/yaml/dist/schema/common/null.js", "../../../node_modules/yaml/dist/schema/core/bool.js", "../../../node_modules/yaml/dist/stringify/stringifyNumber.js", "../../../node_modules/yaml/dist/schema/core/float.js", "../../../node_modules/yaml/dist/schema/core/int.js", "../../../node_modules/yaml/dist/schema/core/schema.js", "../../../node_modules/yaml/dist/schema/json/schema.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/binary.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/pairs.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/omap.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/bool.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/float.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/int.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/set.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/timestamp.js", "../../../node_modules/yaml/dist/schema/yaml-1.1/schema.js", "../../../node_modules/yaml/dist/schema/tags.js", "../../../node_modules/yaml/dist/schema/Schema.js", "../../../node_modules/yaml/dist/stringify/stringifyDocument.js", "../../../node_modules/yaml/dist/doc/Document.js", "../../../node_modules/yaml/dist/errors.js", "../../../node_modules/yaml/dist/compose/resolve-props.js", "../../../node_modules/yaml/dist/compose/util-contains-newline.js", "../../../node_modules/yaml/dist/compose/util-flow-indent-check.js", "../../../node_modules/yaml/dist/compose/util-map-includes.js", "../../../node_modules/yaml/dist/compose/resolve-block-map.js", "../../../node_modules/yaml/dist/compose/resolve-block-seq.js", "../../../node_modules/yaml/dist/compose/resolve-end.js", "../../../node_modules/yaml/dist/compose/resolve-flow-collection.js", "../../../node_modules/yaml/dist/compose/compose-collection.js", "../../../node_modules/yaml/dist/compose/resolve-block-scalar.js", "../../../node_modules/yaml/dist/compose/resolve-flow-scalar.js", "../../../node_modules/yaml/dist/compose/compose-scalar.js", "../../../node_modules/yaml/dist/compose/util-empty-scalar-position.js", "../../../node_modules/yaml/dist/compose/compose-node.js", "../../../node_modules/yaml/dist/compose/compose-doc.js", "../../../node_modules/yaml/dist/compose/composer.js", "../../../node_modules/yaml/dist/parse/cst-scalar.js", "../../../node_modules/yaml/dist/parse/cst-stringify.js", "../../../node_modules/yaml/dist/parse/cst-visit.js", "../../../node_modules/yaml/dist/parse/cst.js", "../../../node_modules/yaml/dist/parse/lexer.js", "../../../node_modules/yaml/dist/parse/line-counter.js", "../../../node_modules/yaml/dist/parse/parser.js", "../../../node_modules/yaml/dist/public-api.js", "../../../node_modules/yaml/dist/index.js", "../../../src/workers/preamble.ts", "../../../src/notifications/types.ts", "../../../src/tasks/types.ts", "../../../src/runtime-paths.ts", "../../../src/config/test-isolation.ts", "../../../src/config/json-store.ts", "../../../src/config/yaml-store.ts", "../../../src/config/main-config.ts", "../../../src/daemon/config.ts", "../../../src/session/forge-sink.ts", "../../../src/workers/status.ts", "../../../src/workers/stagger.ts", "../../../src/workers/capacity.ts", "../../../src/builds/config.ts", "../../../src/builds/queue.ts", "../../../src/workers/ledger.ts", "../../../src/workers/run-lock.ts", "../../../src/workers/plan-status.ts", "../../../src/workers/plan.ts", "../../../src/workers/plan-runner.ts", "../../../src/workers/run-events.ts", "../../../src/workers/local-status.ts", "../../../src/workers/local-idle.ts", "../../../src/utils/model-window.ts", "../../../src/workers/workers-config.ts", "../../../src/workers/config.ts", "../../../src/workers/worker-id.ts", "../../../src/workers/scope.ts", "../../../src/hooks/ts/lib/supervision-banner.ts", "../../../src/workers/pane.ts", "../../../src/workers/ps-table.ts", "../../../src/hooks/ts/lib/worker-session.ts", "../../../src/hooks/ts/session-start/supervision-banner.ts"],
  "sourcesContent": ["/**\n * args.ts \u2014 parse the claude-args tail that `pai worker run` receives.\n *\n * The runner needs a few things out of the caller's argument vector: the\n * prompt (for the default label), the requested --output-format (so the final\n * print matches what plain `claude -p` would have produced), whether the\n * caller already chose a --model or an --mcp-config (both suppress the\n * defaults the runner would otherwise force), any --mcp allowlist names, any\n * --allowedTools grants (their mcp__server__\u2026 entries decide which servers\n * load), and whether they appended their own system prompt (the worker\n * contract is then added alongside, not instead). Everything is passed\n * through untouched \u2014 the runner never rewrites the caller's task.\n */\n\nexport interface ParsedRunnerArgs {\n  /** Prompt string after -p/--print, when the run is headless. */\n  prompt: string | null;\n  /** Caller's --output-format: text (default), json, or stream-json. */\n  outputFormat: \"text\" | \"json\" | \"stream-json\";\n  /** Args to hand to claude (minus --output-format/--verbose, which we add). */\n  rest: string[];\n  /** Headless: a -p/--print flag is present. */\n  headless: boolean;\n  /** Caller passed --model (or --model=\u2026): do not force the provider model. */\n  callerModel: boolean;\n  /** Caller passed --mcp-config (or --mcp-config=\u2026): keep their MCP setup. */\n  callerMcpConfig: boolean;\n  /** Caller passed --append-system-prompt: the contract is added alongside. */\n  callerSystemPrompt: boolean;\n  /** The caller's --append-system-prompt texts, pulled out of `rest` (claude keeps only the last flag). */\n  callerSystemPrompts: string[];\n  /** Caller passed --tools (or --tools=\u2026): a project's `pai project tools` pin does not apply. */\n  callerTools: boolean;\n  /** --mcp values (repeatable, comma-separated inside one flag). */\n  mcp: string[];\n  /** --allowedTools values (repeatable, comma-separated inside one flag). */\n  allowedTools: string[];\n}\n\n/**\n * True when `v` is a real command-line option token (`--flag`, `--flag=x`,\n * `-c`), not task text. The value after -p is the prompt unless it is one of\n * these, so a spec starting `--- shared ---` or `-x` still counts as the prompt.\n */\nexport function isFlagToken(v: string): boolean {\n  return /^--[A-Za-z][\\w-]*(=.*)?$/s.test(v) || /^-[cdhprvV]$/.test(v);\n}\n\nexport function parseRunnerArgs(argv: string[]): ParsedRunnerArgs {\n  let prompt: string | null = null;\n  let outputFormat: ParsedRunnerArgs[\"outputFormat\"] = \"text\";\n  const rest: string[] = [];\n  let headless = false;\n  let callerModel = false;\n  let callerMcpConfig = false;\n  let callerSystemPrompt = false;\n  const callerSystemPrompts: string[] = [];\n  let callerTools = false;\n  const mcp: string[] = [];\n  const allowedTools: string[] = [];\n\n  let i = 0;\n  while (i < argv.length) {\n    const a = argv[i];\n    if (a === \"-p\" || a === \"--print\") {\n      headless = true;\n      rest.push(a);\n      if (i + 1 < argv.length && !isFlagToken(argv[i + 1])) {\n        prompt = argv[i + 1];\n        rest.push(prompt);\n        i += 1;\n      }\n    } else if (a === \"--output-format\") {\n      const v = argv[i + 1];\n      if (v === \"json\" || v === \"stream-json\") outputFormat = v;\n      i += 1; // dropped; the runner prints the result in this format itself\n    } else if (a.startsWith(\"--output-format=\")) {\n      const v = a.slice(\"--output-format=\".length);\n      if (v === \"json\" || v === \"stream-json\") outputFormat = v;\n    } else if (a === \"--verbose\") {\n      // dropped; re-added by the runner\n    } else if (a === \"--append-system-prompt\") {\n      callerSystemPrompt = true;\n      if (i + 1 < argv.length) callerSystemPrompts.push(argv[++i]);\n    } else if (a.startsWith(\"--append-system-prompt=\")) {\n      callerSystemPrompt = true;\n      callerSystemPrompts.push(a.slice(\"--append-system-prompt=\".length));\n    } else if (a === \"--mcp\") {\n      const v = argv[i + 1];\n      if (v !== undefined && !v.startsWith(\"-\")) {\n        mcp.push(v);\n        i += 1;\n      }\n    } else if (a.startsWith(\"--mcp=\")) {\n      mcp.push(a.slice(\"--mcp=\".length));\n    } else if (a === \"--allowedTools\") {\n      // captured for the MCP derivation, but still claude's flag: it passes through\n      rest.push(a);\n      const v = argv[i + 1];\n      if (v !== undefined && !v.startsWith(\"-\")) {\n        allowedTools.push(v);\n        rest.push(v);\n        i += 1;\n      }\n    } else if (a.startsWith(\"--allowedTools=\")) {\n      allowedTools.push(a.slice(\"--allowedTools=\".length));\n      rest.push(a);\n    } else {\n      if (a === \"--model\") callerModel = true;\n      if (a.startsWith(\"--model=\")) callerModel = true;\n      if (a === \"--mcp-config\") callerMcpConfig = true;\n      if (a.startsWith(\"--mcp-config=\")) callerMcpConfig = true;\n      if (a === \"--tools\") callerTools = true;\n      if (a.startsWith(\"--tools=\")) callerTools = true;\n      if (\n        prompt === null && !isFlagToken(a) && rest.length > 0 &&\n        (rest[rest.length - 1] === \"-p\" || rest[rest.length - 1] === \"--print\")\n      ) {\n        prompt = a;\n      }\n      rest.push(a);\n    }\n    i += 1;\n  }\n\n  return {\n    prompt,\n    outputFormat,\n    rest,\n    headless,\n    callerModel,\n    callerMcpConfig,\n    callerSystemPrompt,\n    callerSystemPrompts,\n    callerTools,\n    mcp,\n    allowedTools,\n  };\n}\n\n/**\n * The one --append-system-prompt flag for a spawn: claude keeps only the LAST\n * occurrence, so every piece is joined (blank line between) into a single flag.\n * Empty parts are dropped; no parts, no flag.\n */\nexport function appendSystemPromptArgs(parts: string[]): string[] {\n  const text = parts.filter((p) => p.trim()).join(\"\\n\\n\");\n  return text ? [\"--append-system-prompt\", text] : [];\n}\n\n/**\n * Turn `-p \"<prompt>\"` into bare `-p` (same for --print): for stream-json\n * stdin runs the prompt moves to the first user message on stdin, so the\n * value must not stay on the command line. Only prompt values directly\n * following the flag are touched; everything else passes through.\n */\nexport function stripPromptValues(argv: string[]): string[] {\n  const out: string[] = [];\n  for (let i = 0; i < argv.length; i++) {\n    const a = argv[i];\n    const isPrint = a === \"-p\" || a === \"--print\";\n    out.push(a);\n    if (isPrint && i + 1 < argv.length && !isFlagToken(argv[i + 1])) {\n      i += 1; // drop the prompt value; the flag itself stays\n    }\n  }\n  return out;\n}\n\n/**\n * A one-line stderr hint (never blocking) for an inline -p prompt long or\n * multi-line enough to be a shell-quoting risk \u2014 write it to a file and pass\n * --spec instead. null when the prompt is short/simple enough to be fine.\n */\nexport function longInlinePromptHint(prompt: string | null): string | null {\n  if (!prompt) return null;\n  const newlines = (prompt.match(/\\n/g) ?? []).length;\n  if (prompt.length > 600 || newlines > 3) {\n    return \"hint: long inline prompts break on shell quoting \u2014 write the spec to a file and use --spec <file>\";\n  }\n  return null;\n}\n\n/** Collapse whitespace and cut to n chars with an ellipsis (label rendering). */\nexport function shortText(s: unknown, n: number): string {\n  const t = String(s ?? \"\").split(/\\s+/).filter(Boolean).join(\" \");\n  return t.length <= n ? t : t.slice(0, n - 1) + \"\u2026\";\n}\n", "/**\n * pai-home.ts \u2014 the PAI_HOME namespace directory and generic per-user file\n * migration into it.\n *\n * Every PAI-owned per-user file (workers.yaml, config.json, whisper-rules.md,\n * advisor-mode.json, ...) lives under PAI_HOME (~/.pai by default) so\n * nothing PAI writes can ever collide with a file Claude Code itself\n * introduces under ~/.claude. Decided 2026-09-19 \u2014 see docs/workers-config.md.\n */\n\nimport { existsSync, mkdirSync, readFileSync, copyFileSync, renameSync, readdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport { execFileSync } from \"node:child_process\";\n\n/**\n * Byte-identical check that works for files of any size. `cmp` compares by\n * streaming rather than loading either file into memory, unlike a\n * readFileSync + Buffer.compare \u2014 which throws (\"File size is greater than\n * 2 GiB\") on anything past Node's 2GiB single-read ceiling, a real limit hit\n * migrating a multi-gigabyte legacy federation database file.\n */\nfunction filesByteIdentical(a: string, b: string): boolean {\n  try {\n    execFileSync(\"cmp\", [\"-s\", a, b], { stdio: \"pipe\" });\n    return true;\n  } catch (e) {\n    if (e && typeof e === \"object\" && \"status\" in e && (e as { status: number }).status === 1) return false;\n    // cmp missing or errored for an unrelated reason \u2014 fall back to an\n    // in-memory compare (fine for the small files this path normally sees).\n    return Buffer.compare(readFileSync(a), readFileSync(b)) === 0;\n  }\n}\n\n/** The pre-2026-10-19 PAI home, inside the Claude Code directory. */\nexport function legacyPaiHomeDir(): string {\n  return join(homedir(), \".claude\", \"pai\");\n}\n\n/** The default PAI home: independent of Claude Code. */\nexport function defaultPaiHomeDir(): string {\n  return join(homedir(), \".pai\");\n}\n\n/**\n * THE one PAI_HOME resolver. PAI_HOME env overrides; else ~/.pai; except that\n * until `pai home migrate` has run (~/.pai has no config.yaml but the legacy\n * ~/.claude/pai does) the legacy dir keeps being used so nothing breaks.\n * Shell mirrors (statusline, hooks) must apply the same rule.\n */\nexport function paiHomeDir(): string {\n  if (process.env.PAI_HOME) return process.env.PAI_HOME;\n  const fresh = defaultPaiHomeDir();\n  const legacy = legacyPaiHomeDir();\n  if (!existsSync(join(fresh, \"config.yaml\")) && existsSync(join(legacy, \"config.yaml\"))) return legacy;\n  return fresh;\n}\n\n/** A path under PAI_HOME, e.g. `paiHomePath(\"config.json\")`. */\nexport function paiHomePath(...segments: string[]): string {\n  return join(paiHomeDir(), ...segments);\n}\n\nconst noticesPrinted = new Set<string>();\n\n/**\n * Resolve a per-user PAI file: the new PAI_HOME path if it exists, else the\n * first existing entry in `oldCandidates` (checked in order \u2014 most recent\n * old location first), else the new path (the target a first write\n * creates). Prints one stderr notice per process per new-path when a\n * fallback location is actually used.\n */\nexport function resolvePaiFile(newPath: string, oldCandidates: string[], migrateHint: string): string {\n  if (existsSync(newPath)) return newPath;\n  for (const old of oldCandidates) {\n    if (old !== newPath && existsSync(old)) {\n      if (!noticesPrinted.has(newPath)) {\n        noticesPrinted.add(newPath);\n        process.stderr.write(\n          `pai: ${old} is at an old location \u2014 run \\`${migrateHint}\\` to move it to ${newPath}\\n`\n        );\n      }\n      return old;\n    }\n  }\n  return newPath;\n}\n\nexport class PaiFileMigrationError extends Error {}\n\nexport interface MigrateFileResult {\n  fromPath: string | null;\n  toPath: string;\n  dryRun: boolean;\n  note?: string;\n}\n\n/**\n * Move a per-user PAI file to its new PAI_HOME location: copy, verify\n * byte-identical, then rename the source to `<name>.migrated-<YYYYMMDD>`\n * (never deleted). Idempotent: if the new path already holds bytes\n * identical to the found source, the source is just renamed aside; if it\n * differs, this refuses rather than overwrite silently.\n */\nexport function migratePaiFile(\n  newPath: string,\n  oldCandidates: string[],\n  opts: { dryRun?: boolean } = {}\n): MigrateFileResult {\n  const fromPath = oldCandidates.find((p) => p !== newPath && existsSync(p)) ?? null;\n  if (!fromPath) {\n    const note = existsSync(newPath) ? \"already at new location\" : \"nothing to migrate \u2014 file does not exist yet\";\n    return { fromPath: null, toPath: newPath, dryRun: !!opts.dryRun, note };\n  }\n  if (opts.dryRun) return { fromPath, toPath: newPath, dryRun: true };\n\n  const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n\n  if (existsSync(newPath)) {\n    if (filesByteIdentical(fromPath, newPath)) {\n      renameSync(fromPath, `${fromPath}.migrated-${stamp}`);\n      return { fromPath, toPath: newPath, dryRun: false, note: \"identical \u2014 old file renamed aside\" };\n    }\n    throw new PaiFileMigrationError(\n      `${newPath} already exists and differs from ${fromPath} \u2014 resolve manually, nothing changed`\n    );\n  }\n\n  const dir = dirname(newPath);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  copyFileSync(fromPath, newPath);\n\n  if (!filesByteIdentical(fromPath, newPath)) {\n    throw new PaiFileMigrationError(\n      `${newPath}: copy did not match ${fromPath} byte-for-byte \u2014 aborting, old file left in place`\n    );\n  }\n\n  renameSync(fromPath, `${fromPath}.migrated-${stamp}`);\n  return { fromPath, toPath: newPath, dryRun: false };\n}\n\nexport interface MigrateDirResult {\n  fromDir: string | null;\n  toDir: string;\n  dryRun: boolean;\n  movedCount?: number;\n  note?: string;\n}\n\n/**\n * Move a per-user PAI directory (queries/, session-state/, ...) into\n * PAI_HOME: move every entry from the first found old candidate into the\n * new dir (never overwriting an existing entry there), then rename the now-\n * empty old dir aside to `<name>.migrated-<YYYYMMDD>` (never deleted).\n * Idempotent \u2014 an already-migrated dir has nothing left to find.\n */\nexport function migratePaiDir(\n  newDir: string,\n  oldCandidates: string[],\n  opts: { dryRun?: boolean } = {}\n): MigrateDirResult {\n  const fromDir = oldCandidates.find((p) => p !== newDir && existsSync(p) && statSync(p).isDirectory()) ?? null;\n  if (!fromDir) {\n    const note = existsSync(newDir) ? \"already at new location\" : \"nothing to migrate \u2014 directory does not exist yet\";\n    return { fromDir: null, toDir: newDir, dryRun: !!opts.dryRun, note };\n  }\n\n  const entries = readdirSync(fromDir);\n  if (opts.dryRun) return { fromDir, toDir: newDir, dryRun: true, movedCount: entries.length };\n\n  if (!existsSync(newDir)) mkdirSync(newDir, { recursive: true });\n\n  let moved = 0;\n  const collided: string[] = [];\n  for (const entry of entries) {\n    const src = join(fromDir, entry);\n    const dst = join(newDir, entry);\n    if (existsSync(dst)) {\n      collided.push(entry);\n      continue;\n    }\n    renameSync(src, dst);\n    moved++;\n  }\n\n  const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, \"\");\n  const remaining = readdirSync(fromDir);\n  const note = collided.length\n    ? `${collided.length} entrie(s) left in ${fromDir} \u2014 name already existed in ${newDir}`\n    : undefined;\n\n  if (remaining.length === 0) {\n    renameSync(fromDir, `${fromDir}.migrated-${stamp}`);\n  }\n\n  return { fromDir, toDir: newDir, dryRun: false, movedCount: moved, note };\n}\n", "/**\n * paths.ts \u2014 where worker run artefacts live.\n *\n * Everything a run writes (event mirror, status file, pane registry, routing\n * state, the empty MCP config) sits under one logDir so the whole tree is\n * disposable and configurable: `workers.logDir`, default ~/.claude/logs/workers.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { expandHome, DEFAULT_LOG_DIR, type WorkersConfig } from \"./config.js\";\nimport { paiHomePath, resolvePaiFile } from \"../config/pai-home.js\";\n\n/**\n * Absolute logDir for the given config. An explicit `logDir` in workers.yaml\n * is honored verbatim (the user chose it). Otherwise this resolves like every\n * other PAI_HOME file: PAI_HOME/logs/workers if it exists, else the old\n * ~/.claude/logs/workers (with a one-time notice), else the new path.\n */\nexport function workersLogDir(config: Pick<WorkersConfig, \"logDir\">): string {\n  const expanded = expandHome(config.logDir);\n  if (config.logDir !== DEFAULT_LOG_DIR) return expanded;\n  return resolvePaiFile(paiHomePath(\"logs\", \"workers\"), [expanded], \"pai config migrate --logs\");\n}\n\n/** The strict empty MCP config for headless runs, written on demand. */\nexport function ensureNoMcpConfig(logDir: string): string {\n  const path = noMcpConfigPath(logDir);\n  if (!existsSync(path)) {\n    mkdirSync(logDir, { recursive: true });\n    writeFileSync(path, '{ \"mcpServers\": {} }\\n', \"utf8\");\n  }\n  return path;\n}\n\nexport function statusPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.status`);\n}\n\nexport function eventsPath(logDir: string, id: string): string {\n  return join(logDir, `${id}.jsonl`);\n}\n\nexport function ledgerPath(logDir: string): string {\n  return join(logDir, \"ledger.log\");\n}\n\nexport function routingStatePath(logDir: string): string {\n  return join(logDir, \"routing-state.json\");\n}\n\nexport function panesDir(logDir: string): string {\n  return join(logDir, \"panes\");\n}\n\n/**\n * The strict empty MCP config handed to headless workers. Written into the\n * logDir on demand (never into the user's vendor config directory \u2014 this is\n * PAI state, not vendor state).\n */\nexport function noMcpConfigPath(logDir: string): string {\n  return join(logDir, \"no-mcp.json\");\n}\n\n/** Job id a pooled slot is serving right now (one line); absent while the slot is idle. */\nexport function slotCurrentPath(logDir: string, slot: string): string {\n  return join(logDir, \"slots\", `${slot}.current`);\n}\n", "'use strict';\n\nconst ALIAS = Symbol.for('yaml.alias');\nconst DOC = Symbol.for('yaml.document');\nconst MAP = Symbol.for('yaml.map');\nconst PAIR = Symbol.for('yaml.pair');\nconst SCALAR = Symbol.for('yaml.scalar');\nconst SEQ = Symbol.for('yaml.seq');\nconst NODE_TYPE = Symbol.for('yaml.node.type');\nconst isAlias = (node) => !!node && typeof node === 'object' && node[NODE_TYPE] === ALIAS;\nconst isDocument = (node) => !!node && typeof node === 'object' && node[NODE_TYPE] === DOC;\nconst isMap = (node) => !!node && typeof node === 'object' && node[NODE_TYPE] === MAP;\nconst isPair = (node) => !!node && typeof node === 'object' && node[NODE_TYPE] === PAIR;\nconst isScalar = (node) => !!node && typeof node === 'object' && node[NODE_TYPE] === SCALAR;\nconst isSeq = (node) => !!node && typeof node === 'object' && node[NODE_TYPE] === SEQ;\nfunction isCollection(node) {\n    if (node && typeof node === 'object')\n        switch (node[NODE_TYPE]) {\n            case MAP:\n            case SEQ:\n                return true;\n        }\n    return false;\n}\nfunction isNode(node) {\n    if (node && typeof node === 'object')\n        switch (node[NODE_TYPE]) {\n            case ALIAS:\n            case MAP:\n            case SCALAR:\n            case SEQ:\n                return true;\n        }\n    return false;\n}\nconst hasAnchor = (node) => (isScalar(node) || isCollection(node)) && !!node.anchor;\n\nexports.ALIAS = ALIAS;\nexports.DOC = DOC;\nexports.MAP = MAP;\nexports.NODE_TYPE = NODE_TYPE;\nexports.PAIR = PAIR;\nexports.SCALAR = SCALAR;\nexports.SEQ = SEQ;\nexports.hasAnchor = hasAnchor;\nexports.isAlias = isAlias;\nexports.isCollection = isCollection;\nexports.isDocument = isDocument;\nexports.isMap = isMap;\nexports.isNode = isNode;\nexports.isPair = isPair;\nexports.isScalar = isScalar;\nexports.isSeq = isSeq;\n", "'use strict';\n\nvar identity = require('./nodes/identity.js');\n\nconst BREAK = Symbol('break visit');\nconst SKIP = Symbol('skip children');\nconst REMOVE = Symbol('remove node');\n/**\n * Apply a visitor to an AST node or document.\n *\n * Walks through the tree (depth-first) starting from `node`, calling a\n * `visitor` function with three arguments:\n *   - `key`: For sequence values and map `Pair`, the node's index in the\n *     collection. Within a `Pair`, `'key'` or `'value'`, correspondingly.\n *     `null` for the root node.\n *   - `node`: The current node.\n *   - `path`: The ancestry of the current node.\n *\n * The return value of the visitor may be used to control the traversal:\n *   - `undefined` (default): Do nothing and continue\n *   - `visit.SKIP`: Do not visit the children of this node, continue with next\n *     sibling\n *   - `visit.BREAK`: Terminate traversal completely\n *   - `visit.REMOVE`: Remove the current node, then continue with the next one\n *   - `Node`: Replace the current node, then continue by visiting it\n *   - `number`: While iterating the items of a sequence or map, set the index\n *     of the next step. This is useful especially if the index of the current\n *     node has changed.\n *\n * If `visitor` is a single function, it will be called with all values\n * encountered in the tree, including e.g. `null` values. Alternatively,\n * separate visitor functions may be defined for each `Map`, `Pair`, `Seq`,\n * `Alias` and `Scalar` node. To define the same visitor function for more than\n * one node type, use the `Collection` (map and seq), `Value` (map, seq & scalar)\n * and `Node` (alias, map, seq & scalar) targets. Of all these, only the most\n * specific defined one will be used for each node.\n */\nfunction visit(node, visitor) {\n    const visitor_ = initVisitor(visitor);\n    if (identity.isDocument(node)) {\n        const cd = visit_(null, node.contents, visitor_, Object.freeze([node]));\n        if (cd === REMOVE)\n            node.contents = null;\n    }\n    else\n        visit_(null, node, visitor_, Object.freeze([]));\n}\n// Without the `as symbol` casts, TS declares these in the `visit`\n// namespace using `var`, but then complains about that because\n// `unique symbol` must be `const`.\n/** Terminate visit traversal completely */\nvisit.BREAK = BREAK;\n/** Do not visit the children of the current node */\nvisit.SKIP = SKIP;\n/** Remove the current node */\nvisit.REMOVE = REMOVE;\nfunction visit_(key, node, visitor, path) {\n    const ctrl = callVisitor(key, node, visitor, path);\n    if (identity.isNode(ctrl) || identity.isPair(ctrl)) {\n        replaceNode(key, path, ctrl);\n        return visit_(key, ctrl, visitor, path);\n    }\n    if (typeof ctrl !== 'symbol') {\n        if (identity.isCollection(node)) {\n            path = Object.freeze(path.concat(node));\n            for (let i = 0; i < node.items.length; ++i) {\n                const ci = visit_(i, node.items[i], visitor, path);\n                if (typeof ci === 'number')\n                    i = ci - 1;\n                else if (ci === BREAK)\n                    return BREAK;\n                else if (ci === REMOVE) {\n                    node.items.splice(i, 1);\n                    i -= 1;\n                }\n            }\n        }\n        else if (identity.isPair(node)) {\n            path = Object.freeze(path.concat(node));\n            const ck = visit_('key', node.key, visitor, path);\n            if (ck === BREAK)\n                return BREAK;\n            else if (ck === REMOVE)\n                node.key = null;\n            const cv = visit_('value', node.value, visitor, path);\n            if (cv === BREAK)\n                return BREAK;\n            else if (cv === REMOVE)\n                node.value = null;\n        }\n    }\n    return ctrl;\n}\n/**\n * Apply an async visitor to an AST node or document.\n *\n * Walks through the tree (depth-first) starting from `node`, calling a\n * `visitor` function with three arguments:\n *   - `key`: For sequence values and map `Pair`, the node's index in the\n *     collection. Within a `Pair`, `'key'` or `'value'`, correspondingly.\n *     `null` for the root node.\n *   - `node`: The current node.\n *   - `path`: The ancestry of the current node.\n *\n * The return value of the visitor may be used to control the traversal:\n *   - `Promise`: Must resolve to one of the following values\n *   - `undefined` (default): Do nothing and continue\n *   - `visit.SKIP`: Do not visit the children of this node, continue with next\n *     sibling\n *   - `visit.BREAK`: Terminate traversal completely\n *   - `visit.REMOVE`: Remove the current node, then continue with the next one\n *   - `Node`: Replace the current node, then continue by visiting it\n *   - `number`: While iterating the items of a sequence or map, set the index\n *     of the next step. This is useful especially if the index of the current\n *     node has changed.\n *\n * If `visitor` is a single function, it will be called with all values\n * encountered in the tree, including e.g. `null` values. Alternatively,\n * separate visitor functions may be defined for each `Map`, `Pair`, `Seq`,\n * `Alias` and `Scalar` node. To define the same visitor function for more than\n * one node type, use the `Collection` (map and seq), `Value` (map, seq & scalar)\n * and `Node` (alias, map, seq & scalar) targets. Of all these, only the most\n * specific defined one will be used for each node.\n */\nasync function visitAsync(node, visitor) {\n    const visitor_ = initVisitor(visitor);\n    if (identity.isDocument(node)) {\n        const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node]));\n        if (cd === REMOVE)\n            node.contents = null;\n    }\n    else\n        await visitAsync_(null, node, visitor_, Object.freeze([]));\n}\n// Without the `as symbol` casts, TS declares these in the `visit`\n// namespace using `var`, but then complains about that because\n// `unique symbol` must be `const`.\n/** Terminate visit traversal completely */\nvisitAsync.BREAK = BREAK;\n/** Do not visit the children of the current node */\nvisitAsync.SKIP = SKIP;\n/** Remove the current node */\nvisitAsync.REMOVE = REMOVE;\nasync function visitAsync_(key, node, visitor, path) {\n    const ctrl = await callVisitor(key, node, visitor, path);\n    if (identity.isNode(ctrl) || identity.isPair(ctrl)) {\n        replaceNode(key, path, ctrl);\n        return visitAsync_(key, ctrl, visitor, path);\n    }\n    if (typeof ctrl !== 'symbol') {\n        if (identity.isCollection(node)) {\n            path = Object.freeze(path.concat(node));\n            for (let i = 0; i < node.items.length; ++i) {\n                const ci = await visitAsync_(i, node.items[i], visitor, path);\n                if (typeof ci === 'number')\n                    i = ci - 1;\n                else if (ci === BREAK)\n                    return BREAK;\n                else if (ci === REMOVE) {\n                    node.items.splice(i, 1);\n                    i -= 1;\n                }\n            }\n        }\n        else if (identity.isPair(node)) {\n            path = Object.freeze(path.concat(node));\n            const ck = await visitAsync_('key', node.key, visitor, path);\n            if (ck === BREAK)\n                return BREAK;\n            else if (ck === REMOVE)\n                node.key = null;\n            const cv = await visitAsync_('value', node.value, visitor, path);\n            if (cv === BREAK)\n                return BREAK;\n            else if (cv === REMOVE)\n                node.value = null;\n        }\n    }\n    return ctrl;\n}\nfunction initVisitor(visitor) {\n    if (typeof visitor === 'object' &&\n        (visitor.Collection || visitor.Node || visitor.Value)) {\n        return Object.assign({\n            Alias: visitor.Node,\n            Map: visitor.Node,\n            Scalar: visitor.Node,\n            Seq: visitor.Node\n        }, visitor.Value && {\n            Map: visitor.Value,\n            Scalar: visitor.Value,\n            Seq: visitor.Value\n        }, visitor.Collection && {\n            Map: visitor.Collection,\n            Seq: visitor.Collection\n        }, visitor);\n    }\n    return visitor;\n}\nfunction callVisitor(key, node, visitor, path) {\n    if (typeof visitor === 'function')\n        return visitor(key, node, path);\n    if (identity.isMap(node))\n        return visitor.Map?.(key, node, path);\n    if (identity.isSeq(node))\n        return visitor.Seq?.(key, node, path);\n    if (identity.isPair(node))\n        return visitor.Pair?.(key, node, path);\n    if (identity.isScalar(node))\n        return visitor.Scalar?.(key, node, path);\n    if (identity.isAlias(node))\n        return visitor.Alias?.(key, node, path);\n    return undefined;\n}\nfunction replaceNode(key, path, node) {\n    const parent = path[path.length - 1];\n    if (identity.isCollection(parent)) {\n        parent.items[key] = node;\n    }\n    else if (identity.isPair(parent)) {\n        if (key === 'key')\n            parent.key = node;\n        else\n            parent.value = node;\n    }\n    else if (identity.isDocument(parent)) {\n        parent.contents = node;\n    }\n    else {\n        const pt = identity.isAlias(parent) ? 'alias' : 'scalar';\n        throw new Error(`Cannot replace node with ${pt} parent`);\n    }\n}\n\nexports.visit = visit;\nexports.visitAsync = visitAsync;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar visit = require('../visit.js');\n\nconst escapeChars = {\n    '!': '%21',\n    ',': '%2C',\n    '[': '%5B',\n    ']': '%5D',\n    '{': '%7B',\n    '}': '%7D'\n};\nconst escapeTagName = (tn) => tn.replace(/[!,[\\]{}]/g, ch => escapeChars[ch]);\nclass Directives {\n    constructor(yaml, tags) {\n        /**\n         * The directives-end/doc-start marker `---`. If `null`, a marker may still be\n         * included in the document's stringified representation.\n         */\n        this.docStart = null;\n        /** The doc-end marker `...`.  */\n        this.docEnd = false;\n        this.yaml = Object.assign({}, Directives.defaultYaml, yaml);\n        this.tags = Object.assign({}, Directives.defaultTags, tags);\n    }\n    clone() {\n        const copy = new Directives(this.yaml, this.tags);\n        copy.docStart = this.docStart;\n        return copy;\n    }\n    /**\n     * During parsing, get a Directives instance for the current document and\n     * update the stream state according to the current version's spec.\n     */\n    atDocument() {\n        const res = new Directives(this.yaml, this.tags);\n        switch (this.yaml.version) {\n            case '1.1':\n                this.atNextDocument = true;\n                break;\n            case '1.2':\n                this.atNextDocument = false;\n                this.yaml = {\n                    explicit: Directives.defaultYaml.explicit,\n                    version: '1.2'\n                };\n                this.tags = Object.assign({}, Directives.defaultTags);\n                break;\n        }\n        return res;\n    }\n    /**\n     * @param onError - May be called even if the action was successful\n     * @returns `true` on success\n     */\n    add(line, onError) {\n        if (this.atNextDocument) {\n            this.yaml = { explicit: Directives.defaultYaml.explicit, version: '1.1' };\n            this.tags = Object.assign({}, Directives.defaultTags);\n            this.atNextDocument = false;\n        }\n        const parts = line.trim().split(/[ \\t]+/);\n        const name = parts.shift();\n        switch (name) {\n            case '%TAG': {\n                if (parts.length !== 2) {\n                    onError(0, '%TAG directive should contain exactly two parts');\n                    if (parts.length < 2)\n                        return false;\n                }\n                const [handle, prefix] = parts;\n                this.tags[handle] = prefix;\n                return true;\n            }\n            case '%YAML': {\n                this.yaml.explicit = true;\n                if (parts.length !== 1) {\n                    onError(0, '%YAML directive should contain exactly one part');\n                    return false;\n                }\n                const [version] = parts;\n                if (version === '1.1' || version === '1.2') {\n                    this.yaml.version = version;\n                    return true;\n                }\n                else {\n                    const isValid = /^\\d+\\.\\d+$/.test(version);\n                    onError(6, `Unsupported YAML version ${version}`, isValid);\n                    return false;\n                }\n            }\n            default:\n                onError(0, `Unknown directive ${name}`, true);\n                return false;\n        }\n    }\n    /**\n     * Resolves a tag, matching handles to those defined in %TAG directives.\n     *\n     * @returns Resolved tag, which may also be the non-specific tag `'!'` or a\n     *   `'!local'` tag, or `null` if unresolvable.\n     */\n    tagName(source, onError) {\n        if (source === '!')\n            return '!'; // non-specific tag\n        if (source[0] !== '!') {\n            onError(`Not a valid tag: ${source}`);\n            return null;\n        }\n        if (source[1] === '<') {\n            const verbatim = source.slice(2, -1);\n            if (verbatim === '!' || verbatim === '!!') {\n                onError(`Verbatim tags aren't resolved, so ${source} is invalid.`);\n                return null;\n            }\n            if (source[source.length - 1] !== '>')\n                onError('Verbatim tags must end with a >');\n            return verbatim;\n        }\n        const [, handle, suffix] = source.match(/^(.*!)([^!]*)$/s);\n        if (!suffix)\n            onError(`The ${source} tag has no suffix`);\n        const prefix = this.tags[handle];\n        if (prefix) {\n            try {\n                return prefix + decodeURIComponent(suffix);\n            }\n            catch (error) {\n                onError(String(error));\n                return null;\n            }\n        }\n        if (handle === '!')\n            return source; // local tag\n        onError(`Could not resolve tag: ${source}`);\n        return null;\n    }\n    /**\n     * Given a fully resolved tag, returns its printable string form,\n     * taking into account current tag prefixes and defaults.\n     */\n    tagString(tag) {\n        for (const [handle, prefix] of Object.entries(this.tags)) {\n            if (tag.startsWith(prefix))\n                return handle + escapeTagName(tag.substring(prefix.length));\n        }\n        return tag[0] === '!' ? tag : `!<${tag}>`;\n    }\n    toString(doc) {\n        const lines = this.yaml.explicit\n            ? [`%YAML ${this.yaml.version || '1.2'}`]\n            : [];\n        const tagEntries = Object.entries(this.tags);\n        let tagNames;\n        if (doc && tagEntries.length > 0 && identity.isNode(doc.contents)) {\n            const tags = {};\n            visit.visit(doc.contents, (_key, node) => {\n                if (identity.isNode(node) && node.tag)\n                    tags[node.tag] = true;\n            });\n            tagNames = Object.keys(tags);\n        }\n        else\n            tagNames = [];\n        for (const [handle, prefix] of tagEntries) {\n            if (handle === '!!' && prefix === 'tag:yaml.org,2002:')\n                continue;\n            if (!doc || tagNames.some(tn => tn.startsWith(prefix)))\n                lines.push(`%TAG ${handle} ${prefix}`);\n        }\n        return lines.join('\\n');\n    }\n}\nDirectives.defaultYaml = { explicit: false, version: '1.2' };\nDirectives.defaultTags = { '!!': 'tag:yaml.org,2002:' };\n\nexports.Directives = Directives;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar visit = require('../visit.js');\n\n/**\n * Verify that the input string is a valid anchor.\n *\n * Will throw on errors.\n */\nfunction anchorIsValid(anchor) {\n    if (/[\\x00-\\x19\\s,[\\]{}]/.test(anchor)) {\n        const sa = JSON.stringify(anchor);\n        const msg = `Anchor must not contain whitespace or control characters: ${sa}`;\n        throw new Error(msg);\n    }\n    return true;\n}\nfunction anchorNames(root) {\n    const anchors = new Set();\n    visit.visit(root, {\n        Value(_key, node) {\n            if (node.anchor)\n                anchors.add(node.anchor);\n        }\n    });\n    return anchors;\n}\n/** Find a new anchor name with the given `prefix` and a one-indexed suffix. */\nfunction findNewAnchor(prefix, exclude) {\n    for (let i = 1; true; ++i) {\n        const name = `${prefix}${i}`;\n        if (!exclude.has(name))\n            return name;\n    }\n}\nfunction createNodeAnchors(doc, prefix) {\n    const aliasObjects = [];\n    const sourceObjects = new Map();\n    let prevAnchors = null;\n    return {\n        onAnchor: (source) => {\n            aliasObjects.push(source);\n            prevAnchors ?? (prevAnchors = anchorNames(doc));\n            const anchor = findNewAnchor(prefix, prevAnchors);\n            prevAnchors.add(anchor);\n            return anchor;\n        },\n        /**\n         * With circular references, the source node is only resolved after all\n         * of its child nodes are. This is why anchors are set only after all of\n         * the nodes have been created.\n         */\n        setAnchors: () => {\n            for (const source of aliasObjects) {\n                const ref = sourceObjects.get(source);\n                if (typeof ref === 'object' &&\n                    ref.anchor &&\n                    (identity.isScalar(ref.node) || identity.isCollection(ref.node))) {\n                    ref.node.anchor = ref.anchor;\n                }\n                else {\n                    const error = new Error('Failed to resolve repeated object (this should not happen)');\n                    error.source = source;\n                    throw error;\n                }\n            }\n        },\n        sourceObjects\n    };\n}\n\nexports.anchorIsValid = anchorIsValid;\nexports.anchorNames = anchorNames;\nexports.createNodeAnchors = createNodeAnchors;\nexports.findNewAnchor = findNewAnchor;\n", "'use strict';\n\n/**\n * Applies the JSON.parse reviver algorithm as defined in the ECMA-262 spec,\n * in section 24.5.1.1 \"Runtime Semantics: InternalizeJSONProperty\" of the\n * 2021 edition: https://tc39.es/ecma262/#sec-json.parse\n *\n * Includes extensions for handling Map and Set objects.\n */\nfunction applyReviver(reviver, obj, key, val) {\n    if (val && typeof val === 'object') {\n        if (Array.isArray(val)) {\n            for (let i = 0, len = val.length; i < len; ++i) {\n                const v0 = val[i];\n                const v1 = applyReviver(reviver, val, String(i), v0);\n                // eslint-disable-next-line @typescript-eslint/no-array-delete\n                if (v1 === undefined)\n                    delete val[i];\n                else if (v1 !== v0)\n                    val[i] = v1;\n            }\n        }\n        else if (val instanceof Map) {\n            for (const k of Array.from(val.keys())) {\n                const v0 = val.get(k);\n                const v1 = applyReviver(reviver, val, k, v0);\n                if (v1 === undefined)\n                    val.delete(k);\n                else if (v1 !== v0)\n                    val.set(k, v1);\n            }\n        }\n        else if (val instanceof Set) {\n            for (const v0 of Array.from(val)) {\n                const v1 = applyReviver(reviver, val, v0, v0);\n                if (v1 === undefined)\n                    val.delete(v0);\n                else if (v1 !== v0) {\n                    val.delete(v0);\n                    val.add(v1);\n                }\n            }\n        }\n        else {\n            for (const [k, v0] of Object.entries(val)) {\n                const v1 = applyReviver(reviver, val, k, v0);\n                if (v1 === undefined)\n                    delete val[k];\n                else if (v1 !== v0)\n                    val[k] = v1;\n            }\n        }\n    }\n    return reviver.call(obj, key, val);\n}\n\nexports.applyReviver = applyReviver;\n", "'use strict';\n\nvar identity = require('./identity.js');\n\n/**\n * Recursively convert any node or its contents to native JavaScript\n *\n * @param value - The input value\n * @param arg - If `value` defines a `toJSON()` method, use this\n *   as its first argument\n * @param ctx - Conversion context, originally set in Document#toJS(). If\n *   `{ keep: true }` is not set, output should be suitable for JSON\n *   stringification.\n */\nfunction toJS(value, arg, ctx) {\n    // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n    if (Array.isArray(value))\n        return value.map((v, i) => toJS(v, String(i), ctx));\n    if (value && typeof value.toJSON === 'function') {\n        // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n        if (!ctx || !identity.hasAnchor(value))\n            return value.toJSON(arg, ctx);\n        const data = { aliasCount: 0, count: 1, res: undefined };\n        ctx.anchors.set(value, data);\n        ctx.onCreate = res => {\n            data.res = res;\n            delete ctx.onCreate;\n        };\n        const res = value.toJSON(arg, ctx);\n        if (ctx.onCreate)\n            ctx.onCreate(res);\n        return res;\n    }\n    if (typeof value === 'bigint' && !ctx?.keep)\n        return Number(value);\n    return value;\n}\n\nexports.toJS = toJS;\n", "'use strict';\n\nvar applyReviver = require('../doc/applyReviver.js');\nvar identity = require('./identity.js');\nvar toJS = require('./toJS.js');\n\nclass NodeBase {\n    constructor(type) {\n        Object.defineProperty(this, identity.NODE_TYPE, { value: type });\n    }\n    /** Create a copy of this node.  */\n    clone() {\n        const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));\n        if (this.range)\n            copy.range = this.range.slice();\n        return copy;\n    }\n    /** A plain JavaScript representation of this node. */\n    toJS(doc, { mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {\n        if (!identity.isDocument(doc))\n            throw new TypeError('A document argument is required');\n        const ctx = {\n            anchors: new Map(),\n            doc,\n            keep: true,\n            mapAsMap: mapAsMap === true,\n            mapKeyWarned: false,\n            maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100\n        };\n        const res = toJS.toJS(this, '', ctx);\n        if (typeof onAnchor === 'function')\n            for (const { count, res } of ctx.anchors.values())\n                onAnchor(res, count);\n        return typeof reviver === 'function'\n            ? applyReviver.applyReviver(reviver, { '': res }, '', res)\n            : res;\n    }\n}\n\nexports.NodeBase = NodeBase;\n", "'use strict';\n\nvar anchors = require('../doc/anchors.js');\nvar visit = require('../visit.js');\nvar identity = require('./identity.js');\nvar Node = require('./Node.js');\nvar toJS = require('./toJS.js');\n\nclass Alias extends Node.NodeBase {\n    constructor(source) {\n        super(identity.ALIAS);\n        this.source = source;\n        Object.defineProperty(this, 'tag', {\n            set() {\n                throw new Error('Alias nodes cannot have tags');\n            }\n        });\n    }\n    /**\n     * Resolve the value of this alias within `doc`, finding the last\n     * instance of the `source` anchor before this node.\n     */\n    resolve(doc, ctx) {\n        if (ctx?.maxAliasCount === 0)\n            throw new ReferenceError('Alias resolution is disabled');\n        let nodes;\n        if (ctx?.aliasResolveCache) {\n            nodes = ctx.aliasResolveCache;\n        }\n        else {\n            nodes = [];\n            visit.visit(doc, {\n                Node: (_key, node) => {\n                    if (identity.isAlias(node) || identity.hasAnchor(node))\n                        nodes.push(node);\n                }\n            });\n            if (ctx)\n                ctx.aliasResolveCache = nodes;\n        }\n        let found = undefined;\n        for (const node of nodes) {\n            if (node === this)\n                break;\n            if (node.anchor === this.source)\n                found = node;\n        }\n        if (found && ctx) {\n            const { anchors, doc, maxAliasCount } = ctx;\n            let data = anchors.get(found);\n            if (!data) {\n                // Resolve anchors for Node.prototype.toJS()\n                toJS.toJS(found, null, ctx);\n                data = anchors.get(found);\n            }\n            /* istanbul ignore if */\n            if (data?.res === undefined) {\n                const msg = 'This should not happen: Alias anchor was not resolved?';\n                throw new ReferenceError(msg);\n            }\n            if (maxAliasCount >= 0) {\n                data.count += 1;\n                if (data.aliasCount === 0)\n                    data.aliasCount = getAliasCount(doc, found, anchors);\n                if (data.count * data.aliasCount > maxAliasCount) {\n                    const msg = 'Excessive alias count indicates a resource exhaustion attack';\n                    throw new ReferenceError(msg);\n                }\n            }\n        }\n        return found;\n    }\n    toJSON(_arg, ctx) {\n        if (!ctx)\n            return { source: this.source };\n        const source = this.resolve(ctx.doc, ctx);\n        if (!source) {\n            const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;\n            throw new ReferenceError(msg);\n        }\n        return ctx.anchors.get(source).res;\n    }\n    toString(ctx, _onComment, _onChompKeep) {\n        const src = `*${this.source}`;\n        if (ctx) {\n            anchors.anchorIsValid(this.source);\n            if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) {\n                const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`;\n                throw new Error(msg);\n            }\n            if (ctx.implicitKey)\n                return `${src} `;\n        }\n        return src;\n    }\n}\nfunction getAliasCount(doc, node, anchors) {\n    if (identity.isAlias(node)) {\n        const source = node.resolve(doc);\n        const anchor = anchors && source && anchors.get(source);\n        return anchor ? anchor.count * anchor.aliasCount : 0;\n    }\n    else if (identity.isCollection(node)) {\n        let count = 0;\n        for (const item of node.items) {\n            const c = getAliasCount(doc, item, anchors);\n            if (c > count)\n                count = c;\n        }\n        return count;\n    }\n    else if (identity.isPair(node)) {\n        const kc = getAliasCount(doc, node.key, anchors);\n        const vc = getAliasCount(doc, node.value, anchors);\n        return Math.max(kc, vc);\n    }\n    return 1;\n}\n\nexports.Alias = Alias;\n", "'use strict';\n\nvar identity = require('./identity.js');\nvar Node = require('./Node.js');\nvar toJS = require('./toJS.js');\n\nconst isScalarValue = (value) => !value || (typeof value !== 'function' && typeof value !== 'object');\nclass Scalar extends Node.NodeBase {\n    constructor(value) {\n        super(identity.SCALAR);\n        this.value = value;\n    }\n    toJSON(arg, ctx) {\n        return ctx?.keep ? this.value : toJS.toJS(this.value, arg, ctx);\n    }\n    toString() {\n        return String(this.value);\n    }\n}\nScalar.BLOCK_FOLDED = 'BLOCK_FOLDED';\nScalar.BLOCK_LITERAL = 'BLOCK_LITERAL';\nScalar.PLAIN = 'PLAIN';\nScalar.QUOTE_DOUBLE = 'QUOTE_DOUBLE';\nScalar.QUOTE_SINGLE = 'QUOTE_SINGLE';\n\nexports.Scalar = Scalar;\nexports.isScalarValue = isScalarValue;\n", "'use strict';\n\nvar Alias = require('../nodes/Alias.js');\nvar identity = require('../nodes/identity.js');\nvar Scalar = require('../nodes/Scalar.js');\n\nconst defaultTagPrefix = 'tag:yaml.org,2002:';\nfunction findTagObject(value, tagName, tags) {\n    if (tagName) {\n        const match = tags.filter(t => t.tag === tagName);\n        const tagObj = match.find(t => !t.format) ?? match[0];\n        if (!tagObj)\n            throw new Error(`Tag ${tagName} not found`);\n        return tagObj;\n    }\n    return tags.find(t => t.identify?.(value) && !t.format);\n}\nfunction createNode(value, tagName, ctx) {\n    if (identity.isDocument(value))\n        value = value.contents;\n    if (identity.isNode(value))\n        return value;\n    if (identity.isPair(value)) {\n        const map = ctx.schema[identity.MAP].createNode?.(ctx.schema, null, ctx);\n        map.items.push(value);\n        return map;\n    }\n    if (value instanceof String ||\n        value instanceof Number ||\n        value instanceof Boolean ||\n        (typeof BigInt !== 'undefined' && value instanceof BigInt) // not supported everywhere\n    ) {\n        // https://tc39.es/ecma262/#sec-serializejsonproperty\n        value = value.valueOf();\n    }\n    const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx;\n    // Detect duplicate references to the same object & use Alias nodes for all\n    // after first. The `ref` wrapper allows for circular references to resolve.\n    let ref = undefined;\n    if (aliasDuplicateObjects && value && typeof value === 'object') {\n        ref = sourceObjects.get(value);\n        if (ref) {\n            ref.anchor ?? (ref.anchor = onAnchor(value));\n            return new Alias.Alias(ref.anchor);\n        }\n        else {\n            ref = { anchor: null, node: null };\n            sourceObjects.set(value, ref);\n        }\n    }\n    if (tagName?.startsWith('!!'))\n        tagName = defaultTagPrefix + tagName.slice(2);\n    let tagObj = findTagObject(value, tagName, schema.tags);\n    if (!tagObj) {\n        if (value && typeof value.toJSON === 'function') {\n            // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n            value = value.toJSON();\n        }\n        if (!value || typeof value !== 'object') {\n            const node = new Scalar.Scalar(value);\n            if (ref)\n                ref.node = node;\n            return node;\n        }\n        tagObj =\n            value instanceof Map\n                ? schema[identity.MAP]\n                : Symbol.iterator in Object(value)\n                    ? schema[identity.SEQ]\n                    : schema[identity.MAP];\n    }\n    if (onTagObj) {\n        onTagObj(tagObj);\n        delete ctx.onTagObj;\n    }\n    const node = tagObj?.createNode\n        ? tagObj.createNode(ctx.schema, value, ctx)\n        : typeof tagObj?.nodeClass?.from === 'function'\n            ? tagObj.nodeClass.from(ctx.schema, value, ctx)\n            : new Scalar.Scalar(value);\n    if (tagName)\n        node.tag = tagName;\n    else if (!tagObj.default)\n        node.tag = tagObj.tag;\n    if (ref)\n        ref.node = node;\n    return node;\n}\n\nexports.createNode = createNode;\n", "'use strict';\n\nvar createNode = require('../doc/createNode.js');\nvar identity = require('./identity.js');\nvar Node = require('./Node.js');\n\nfunction collectionFromPath(schema, path, value) {\n    let v = value;\n    for (let i = path.length - 1; i >= 0; --i) {\n        const k = path[i];\n        if (typeof k === 'number' && Number.isInteger(k) && k >= 0) {\n            const a = [];\n            a[k] = v;\n            v = a;\n        }\n        else {\n            v = new Map([[k, v]]);\n        }\n    }\n    return createNode.createNode(v, undefined, {\n        aliasDuplicateObjects: false,\n        keepUndefined: false,\n        onAnchor: () => {\n            throw new Error('This should not happen, please report a bug.');\n        },\n        schema,\n        sourceObjects: new Map()\n    });\n}\n// Type guard is intentionally a little wrong so as to be more useful,\n// as it does not cover untypable empty non-string iterables (e.g. []).\nconst isEmptyPath = (path) => path == null ||\n    (typeof path === 'object' && !!path[Symbol.iterator]().next().done);\nclass Collection extends Node.NodeBase {\n    constructor(type, schema) {\n        super(type);\n        Object.defineProperty(this, 'schema', {\n            value: schema,\n            configurable: true,\n            enumerable: false,\n            writable: true\n        });\n    }\n    /**\n     * Create a copy of this collection.\n     *\n     * @param schema - If defined, overwrites the original's schema\n     */\n    clone(schema) {\n        const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));\n        if (schema)\n            copy.schema = schema;\n        copy.items = copy.items.map(it => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it);\n        if (this.range)\n            copy.range = this.range.slice();\n        return copy;\n    }\n    /**\n     * Adds a value to the collection. For `!!map` and `!!omap` the value must\n     * be a Pair instance or a `{ key, value }` object, which may not have a key\n     * that already exists in the map.\n     */\n    addIn(path, value) {\n        if (isEmptyPath(path))\n            this.add(value);\n        else {\n            const [key, ...rest] = path;\n            const node = this.get(key, true);\n            if (identity.isCollection(node))\n                node.addIn(rest, value);\n            else if (node === undefined && this.schema)\n                this.set(key, collectionFromPath(this.schema, rest, value));\n            else\n                throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);\n        }\n    }\n    /**\n     * Removes a value from the collection.\n     * @returns `true` if the item was found and removed.\n     */\n    deleteIn(path) {\n        const [key, ...rest] = path;\n        if (rest.length === 0)\n            return this.delete(key);\n        const node = this.get(key, true);\n        if (identity.isCollection(node))\n            return node.deleteIn(rest);\n        else\n            throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);\n    }\n    /**\n     * Returns item at `key`, or `undefined` if not found. By default unwraps\n     * scalar values from their surrounding node; to disable set `keepScalar` to\n     * `true` (collections are always returned intact).\n     */\n    getIn(path, keepScalar) {\n        const [key, ...rest] = path;\n        const node = this.get(key, true);\n        if (rest.length === 0)\n            return !keepScalar && identity.isScalar(node) ? node.value : node;\n        else\n            return identity.isCollection(node) ? node.getIn(rest, keepScalar) : undefined;\n    }\n    hasAllNullValues(allowScalar) {\n        return this.items.every(node => {\n            if (!identity.isPair(node))\n                return false;\n            const n = node.value;\n            return (n == null ||\n                (allowScalar &&\n                    identity.isScalar(n) &&\n                    n.value == null &&\n                    !n.commentBefore &&\n                    !n.comment &&\n                    !n.tag));\n        });\n    }\n    /**\n     * Checks if the collection includes a value with the key `key`.\n     */\n    hasIn(path) {\n        const [key, ...rest] = path;\n        if (rest.length === 0)\n            return this.has(key);\n        const node = this.get(key, true);\n        return identity.isCollection(node) ? node.hasIn(rest) : false;\n    }\n    /**\n     * Sets a value in this collection. For `!!set`, `value` needs to be a\n     * boolean to add/remove the item from the set.\n     */\n    setIn(path, value) {\n        const [key, ...rest] = path;\n        if (rest.length === 0) {\n            this.set(key, value);\n        }\n        else {\n            const node = this.get(key, true);\n            if (identity.isCollection(node))\n                node.setIn(rest, value);\n            else if (node === undefined && this.schema)\n                this.set(key, collectionFromPath(this.schema, rest, value));\n            else\n                throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);\n        }\n    }\n}\n\nexports.Collection = Collection;\nexports.collectionFromPath = collectionFromPath;\nexports.isEmptyPath = isEmptyPath;\n", "'use strict';\n\n/**\n * Stringifies a comment.\n *\n * Empty comment lines are left empty,\n * lines consisting of a single space are replaced by `#`,\n * and all other lines are prefixed with a `#`.\n */\nconst stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, '#');\nfunction indentComment(comment, indent) {\n    if (/^\\n+$/.test(comment))\n        return comment.substring(1);\n    return indent ? comment.replace(/^(?! *$)/gm, indent) : comment;\n}\nconst lineComment = (str, indent, comment) => str.endsWith('\\n')\n    ? indentComment(comment, indent)\n    : comment.includes('\\n')\n        ? '\\n' + indentComment(comment, indent)\n        : (str.endsWith(' ') ? '' : ' ') + comment;\n\nexports.indentComment = indentComment;\nexports.lineComment = lineComment;\nexports.stringifyComment = stringifyComment;\n", "'use strict';\n\nconst FOLD_FLOW = 'flow';\nconst FOLD_BLOCK = 'block';\nconst FOLD_QUOTED = 'quoted';\n/**\n * Tries to keep input at up to `lineWidth` characters, splitting only on spaces\n * not followed by newlines or spaces unless `mode` is `'quoted'`. Lines are\n * terminated with `\\n` and started with `indent`.\n */\nfunction foldFlowLines(text, indent, mode = 'flow', { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {\n    if (!lineWidth || lineWidth < 0)\n        return text;\n    if (lineWidth < minContentWidth)\n        minContentWidth = 0;\n    const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);\n    if (text.length <= endStep)\n        return text;\n    const folds = [];\n    const escapedFolds = {};\n    let end = lineWidth - indent.length;\n    if (typeof indentAtStart === 'number') {\n        if (indentAtStart > lineWidth - Math.max(2, minContentWidth))\n            folds.push(0);\n        else\n            end = lineWidth - indentAtStart;\n    }\n    let split = undefined;\n    let prev = undefined;\n    let overflow = false;\n    let i = -1;\n    let escStart = -1;\n    let escEnd = -1;\n    if (mode === FOLD_BLOCK) {\n        i = consumeMoreIndentedLines(text, i, indent.length);\n        if (i !== -1)\n            end = i + endStep;\n    }\n    for (let ch; (ch = text[(i += 1)]);) {\n        if (mode === FOLD_QUOTED && ch === '\\\\') {\n            escStart = i;\n            switch (text[i + 1]) {\n                case 'x':\n                    i += 3;\n                    break;\n                case 'u':\n                    i += 5;\n                    break;\n                case 'U':\n                    i += 9;\n                    break;\n                default:\n                    i += 1;\n            }\n            escEnd = i;\n        }\n        if (ch === '\\n') {\n            if (mode === FOLD_BLOCK)\n                i = consumeMoreIndentedLines(text, i, indent.length);\n            end = i + indent.length + endStep;\n            split = undefined;\n        }\n        else {\n            if (ch === ' ' &&\n                prev &&\n                prev !== ' ' &&\n                prev !== '\\n' &&\n                prev !== '\\t') {\n                // space surrounded by non-space can be replaced with newline + indent\n                const next = text[i + 1];\n                if (next && next !== ' ' && next !== '\\n' && next !== '\\t')\n                    split = i;\n            }\n            if (i >= end) {\n                if (split) {\n                    folds.push(split);\n                    end = split + endStep;\n                    split = undefined;\n                }\n                else if (mode === FOLD_QUOTED) {\n                    // white-space collected at end may stretch past lineWidth\n                    while (prev === ' ' || prev === '\\t') {\n                        prev = ch;\n                        ch = text[(i += 1)];\n                        overflow = true;\n                    }\n                    // Account for newline escape, but don't break preceding escape\n                    const j = i > escEnd + 1 ? i - 2 : escStart - 1;\n                    // Bail out if lineWidth & minContentWidth are shorter than an escape string\n                    if (escapedFolds[j])\n                        return text;\n                    folds.push(j);\n                    escapedFolds[j] = true;\n                    end = j + endStep;\n                    split = undefined;\n                }\n                else {\n                    overflow = true;\n                }\n            }\n        }\n        prev = ch;\n    }\n    if (overflow && onOverflow)\n        onOverflow();\n    if (folds.length === 0)\n        return text;\n    if (onFold)\n        onFold();\n    let res = text.slice(0, folds[0]);\n    for (let i = 0; i < folds.length; ++i) {\n        const fold = folds[i];\n        const end = folds[i + 1] || text.length;\n        if (fold === 0)\n            res = `\\n${indent}${text.slice(0, end)}`;\n        else {\n            if (mode === FOLD_QUOTED && escapedFolds[fold])\n                res += `${text[fold]}\\\\`;\n            res += `\\n${indent}${text.slice(fold + 1, end)}`;\n        }\n    }\n    return res;\n}\n/**\n * Presumes `i + 1` is at the start of a line\n * @returns index of last newline in more-indented block\n */\nfunction consumeMoreIndentedLines(text, i, indent) {\n    let end = i;\n    let start = i + 1;\n    let ch = text[start];\n    while (ch === ' ' || ch === '\\t') {\n        if (i < start + indent) {\n            ch = text[++i];\n        }\n        else {\n            do {\n                ch = text[++i];\n            } while (ch && ch !== '\\n');\n            end = i;\n            start = i + 1;\n            ch = text[start];\n        }\n    }\n    return end;\n}\n\nexports.FOLD_BLOCK = FOLD_BLOCK;\nexports.FOLD_FLOW = FOLD_FLOW;\nexports.FOLD_QUOTED = FOLD_QUOTED;\nexports.foldFlowLines = foldFlowLines;\n", "'use strict';\n\nvar Scalar = require('../nodes/Scalar.js');\nvar foldFlowLines = require('./foldFlowLines.js');\n\nconst getFoldOptions = (ctx, isBlock) => ({\n    indentAtStart: isBlock ? ctx.indent.length : ctx.indentAtStart,\n    lineWidth: ctx.options.lineWidth,\n    minContentWidth: ctx.options.minContentWidth\n});\n// Also checks for lines starting with %, as parsing the output as YAML 1.1 will\n// presume that's starting a new document.\nconst containsDocumentMarker = (str) => /^(%|---|\\.\\.\\.)/m.test(str);\nfunction lineLengthOverLimit(str, lineWidth, indentLength) {\n    if (!lineWidth || lineWidth < 0)\n        return false;\n    const limit = lineWidth - indentLength;\n    const strLen = str.length;\n    if (strLen <= limit)\n        return false;\n    for (let i = 0, start = 0; i < strLen; ++i) {\n        if (str[i] === '\\n') {\n            if (i - start > limit)\n                return true;\n            start = i + 1;\n            if (strLen - start <= limit)\n                return false;\n        }\n    }\n    return true;\n}\nfunction doubleQuotedString(value, ctx) {\n    const json = JSON.stringify(value);\n    if (ctx.options.doubleQuotedAsJSON)\n        return json;\n    const { implicitKey } = ctx;\n    const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength;\n    const indent = ctx.indent || (containsDocumentMarker(value) ? '  ' : '');\n    let str = '';\n    let start = 0;\n    for (let i = 0, ch = json[i]; ch; ch = json[++i]) {\n        if (ch === ' ' && json[i + 1] === '\\\\' && json[i + 2] === 'n') {\n            // space before newline needs to be escaped to not be folded\n            str += json.slice(start, i) + '\\\\ ';\n            i += 1;\n            start = i;\n            ch = '\\\\';\n        }\n        if (ch === '\\\\')\n            switch (json[i + 1]) {\n                case 'u':\n                    {\n                        str += json.slice(start, i);\n                        const code = json.substr(i + 2, 4);\n                        switch (code) {\n                            case '0000':\n                                str += '\\\\0';\n                                break;\n                            case '0007':\n                                str += '\\\\a';\n                                break;\n                            case '000b':\n                                str += '\\\\v';\n                                break;\n                            case '001b':\n                                str += '\\\\e';\n                                break;\n                            case '0085':\n                                str += '\\\\N';\n                                break;\n                            case '00a0':\n                                str += '\\\\_';\n                                break;\n                            case '2028':\n                                str += '\\\\L';\n                                break;\n                            case '2029':\n                                str += '\\\\P';\n                                break;\n                            default:\n                                if (code.substr(0, 2) === '00')\n                                    str += '\\\\x' + code.substr(2);\n                                else\n                                    str += json.substr(i, 6);\n                        }\n                        i += 5;\n                        start = i + 1;\n                    }\n                    break;\n                case 'n':\n                    if (implicitKey ||\n                        json[i + 2] === '\"' ||\n                        json.length < minMultiLineLength) {\n                        i += 1;\n                    }\n                    else {\n                        // folding will eat first newline\n                        str += json.slice(start, i) + '\\n\\n';\n                        while (json[i + 2] === '\\\\' &&\n                            json[i + 3] === 'n' &&\n                            json[i + 4] !== '\"') {\n                            str += '\\n';\n                            i += 2;\n                        }\n                        str += indent;\n                        // space after newline needs to be escaped to not be folded\n                        if (json[i + 2] === ' ')\n                            str += '\\\\';\n                        i += 1;\n                        start = i + 1;\n                    }\n                    break;\n                default:\n                    i += 1;\n            }\n    }\n    str = start ? str + json.slice(start) : json;\n    return implicitKey\n        ? str\n        : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false));\n}\nfunction singleQuotedString(value, ctx) {\n    if (ctx.options.singleQuote === false ||\n        (ctx.implicitKey && value.includes('\\n')) ||\n        /[ \\t]\\n|\\n[ \\t]/.test(value) // single quoted string can't have leading or trailing whitespace around newline\n    )\n        return doubleQuotedString(value, ctx);\n    const indent = ctx.indent || (containsDocumentMarker(value) ? '  ' : '');\n    const res = \"'\" + value.replace(/'/g, \"''\").replace(/\\n+/g, `$&\\n${indent}`) + \"'\";\n    return ctx.implicitKey\n        ? res\n        : foldFlowLines.foldFlowLines(res, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false));\n}\nfunction quotedString(value, ctx) {\n    const { singleQuote } = ctx.options;\n    let qs;\n    if (singleQuote === false)\n        qs = doubleQuotedString;\n    else {\n        const hasDouble = value.includes('\"');\n        const hasSingle = value.includes(\"'\");\n        if (hasDouble && !hasSingle)\n            qs = singleQuotedString;\n        else if (hasSingle && !hasDouble)\n            qs = doubleQuotedString;\n        else\n            qs = singleQuote ? singleQuotedString : doubleQuotedString;\n    }\n    return qs(value, ctx);\n}\n// The negative lookbehind avoids a polynomial search,\n// but isn't supported yet on Safari: https://caniuse.com/js-regexp-lookbehind\nlet blockEndNewlines;\ntry {\n    blockEndNewlines = new RegExp('(^|(?<!\\n))\\n+(?!\\n|$)', 'g');\n}\ncatch {\n    blockEndNewlines = /\\n+(?!\\n|$)/g;\n}\nfunction blockString({ comment, type, value }, ctx, onComment, onChompKeep) {\n    const { blockQuote, commentString, lineWidth } = ctx.options;\n    // 1. Block can't end in whitespace unless the last line is non-empty.\n    // 2. Strings consisting of only whitespace are best rendered explicitly.\n    if (!blockQuote || /\\n[\\t ]+$/.test(value)) {\n        return quotedString(value, ctx);\n    }\n    const indent = ctx.indent ||\n        (ctx.forceBlockIndent || containsDocumentMarker(value) ? '  ' : '');\n    const literal = blockQuote === 'literal'\n        ? true\n        : blockQuote === 'folded' || type === Scalar.Scalar.BLOCK_FOLDED\n            ? false\n            : type === Scalar.Scalar.BLOCK_LITERAL\n                ? true\n                : !lineLengthOverLimit(value, lineWidth, indent.length);\n    if (!value)\n        return literal ? '|\\n' : '>\\n';\n    // determine chomping from whitespace at value end\n    let chomp;\n    let endStart;\n    for (endStart = value.length; endStart > 0; --endStart) {\n        const ch = value[endStart - 1];\n        if (ch !== '\\n' && ch !== '\\t' && ch !== ' ')\n            break;\n    }\n    let end = value.substring(endStart);\n    const endNlPos = end.indexOf('\\n');\n    if (endNlPos === -1) {\n        chomp = '-'; // strip\n    }\n    else if (value === end || endNlPos !== end.length - 1) {\n        chomp = '+'; // keep\n        if (onChompKeep)\n            onChompKeep();\n    }\n    else {\n        chomp = ''; // clip\n    }\n    if (end) {\n        value = value.slice(0, -end.length);\n        if (end[end.length - 1] === '\\n')\n            end = end.slice(0, -1);\n        end = end.replace(blockEndNewlines, `$&${indent}`);\n    }\n    // determine indent indicator from whitespace at value start\n    let startWithSpace = false;\n    let startEnd;\n    let startNlPos = -1;\n    for (startEnd = 0; startEnd < value.length; ++startEnd) {\n        const ch = value[startEnd];\n        if (ch === ' ')\n            startWithSpace = true;\n        else if (ch === '\\n')\n            startNlPos = startEnd;\n        else\n            break;\n    }\n    let start = value.substring(0, startNlPos < startEnd ? startNlPos + 1 : startEnd);\n    if (start) {\n        value = value.substring(start.length);\n        start = start.replace(/\\n+/g, `$&${indent}`);\n    }\n    const indentSize = indent ? '2' : '1'; // root is at -1\n    // Leading | or > is added later\n    let header = (startWithSpace ? indentSize : '') + chomp;\n    if (comment) {\n        header += ' ' + commentString(comment.replace(/ ?[\\r\\n]+/g, ' '));\n        if (onComment)\n            onComment();\n    }\n    if (!literal) {\n        const foldedValue = value\n            .replace(/\\n+/g, '\\n$&')\n            .replace(/(?:^|\\n)([\\t ].*)(?:([\\n\\t ]*)\\n(?![\\n\\t ]))?/g, '$1$2') // more-indented lines aren't folded\n            //                ^ more-ind. ^ empty     ^ capture next empty lines only at end of indent\n            .replace(/\\n+/g, `$&${indent}`);\n        let literalFallback = false;\n        const foldOptions = getFoldOptions(ctx, true);\n        if (blockQuote !== 'folded' && type !== Scalar.Scalar.BLOCK_FOLDED) {\n            foldOptions.onOverflow = () => {\n                literalFallback = true;\n            };\n        }\n        const body = foldFlowLines.foldFlowLines(`${start}${foldedValue}${end}`, indent, foldFlowLines.FOLD_BLOCK, foldOptions);\n        if (!literalFallback)\n            return `>${header}\\n${indent}${body}`;\n    }\n    value = value.replace(/\\n+/g, `$&${indent}`);\n    return `|${header}\\n${indent}${start}${value}${end}`;\n}\nfunction plainString(item, ctx, onComment, onChompKeep) {\n    const { type, value } = item;\n    const { actualString, implicitKey, indent, indentStep, inFlow } = ctx;\n    if ((implicitKey && value.includes('\\n')) ||\n        (inFlow && /[[\\]{},]/.test(value))) {\n        return quotedString(value, ctx);\n    }\n    if (/^[\\n\\t ,[\\]{}#&*!|>'\"%@`]|^[?-]$|^[?-][ \\t]|[\\n:][ \\t]|[ \\t]\\n|[\\n\\t ]#|[\\n\\t :]$/.test(value)) {\n        // not allowed:\n        // - '-' or '?'\n        // - start with an indicator character (except [?:-]) or /[?-] /\n        // - '\\n ', ': ' or ' \\n' anywhere\n        // - '#' not preceded by a non-space char\n        // - end with ' ' or ':'\n        return implicitKey || inFlow || !value.includes('\\n')\n            ? quotedString(value, ctx)\n            : blockString(item, ctx, onComment, onChompKeep);\n    }\n    if (!implicitKey &&\n        !inFlow &&\n        type !== Scalar.Scalar.PLAIN &&\n        value.includes('\\n')) {\n        // Where allowed & type not set explicitly, prefer block style for multiline strings\n        return blockString(item, ctx, onComment, onChompKeep);\n    }\n    if (containsDocumentMarker(value)) {\n        if (indent === '') {\n            ctx.forceBlockIndent = true;\n            return blockString(item, ctx, onComment, onChompKeep);\n        }\n        else if (implicitKey && indent === indentStep) {\n            return quotedString(value, ctx);\n        }\n    }\n    const str = value.replace(/\\n+/g, `$&\\n${indent}`);\n    // Verify that output will be parsed as a string, as e.g. plain numbers and\n    // booleans get parsed with those types in v1.2 (e.g. '42', 'true' & '0.9e-3'),\n    // and others in v1.1.\n    if (actualString) {\n        const test = (tag) => tag.default && tag.tag !== 'tag:yaml.org,2002:str' && tag.test?.test(str);\n        const { compat, tags } = ctx.doc.schema;\n        if (tags.some(test) || compat?.some(test))\n            return quotedString(value, ctx);\n    }\n    return implicitKey\n        ? str\n        : foldFlowLines.foldFlowLines(str, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false));\n}\nfunction stringifyString(item, ctx, onComment, onChompKeep) {\n    const { implicitKey, inFlow } = ctx;\n    const ss = typeof item.value === 'string'\n        ? item\n        : Object.assign({}, item, { value: String(item.value) });\n    let { type } = item;\n    if (type !== Scalar.Scalar.QUOTE_DOUBLE) {\n        // force double quotes on control characters & unpaired surrogates\n        if (/[\\x00-\\x08\\x0b-\\x1f\\x7f-\\x9f\\u{D800}-\\u{DFFF}]/u.test(ss.value))\n            type = Scalar.Scalar.QUOTE_DOUBLE;\n    }\n    const _stringify = (_type) => {\n        switch (_type) {\n            case Scalar.Scalar.BLOCK_FOLDED:\n            case Scalar.Scalar.BLOCK_LITERAL:\n                return implicitKey || inFlow\n                    ? quotedString(ss.value, ctx) // blocks are not valid inside flow containers\n                    : blockString(ss, ctx, onComment, onChompKeep);\n            case Scalar.Scalar.QUOTE_DOUBLE:\n                return doubleQuotedString(ss.value, ctx);\n            case Scalar.Scalar.QUOTE_SINGLE:\n                return singleQuotedString(ss.value, ctx);\n            case Scalar.Scalar.PLAIN:\n                return plainString(ss, ctx, onComment, onChompKeep);\n            default:\n                return null;\n        }\n    };\n    let res = _stringify(type);\n    if (res === null) {\n        const { defaultKeyType, defaultStringType } = ctx.options;\n        const t = (implicitKey && defaultKeyType) || defaultStringType;\n        res = _stringify(t);\n        if (res === null)\n            throw new Error(`Unsupported default string type ${t}`);\n    }\n    return res;\n}\n\nexports.stringifyString = stringifyString;\n", "'use strict';\n\nvar anchors = require('../doc/anchors.js');\nvar identity = require('../nodes/identity.js');\nvar stringifyComment = require('./stringifyComment.js');\nvar stringifyString = require('./stringifyString.js');\n\nfunction createStringifyContext(doc, options) {\n    const opt = Object.assign({\n        blockQuote: true,\n        commentString: stringifyComment.stringifyComment,\n        defaultKeyType: null,\n        defaultStringType: 'PLAIN',\n        directives: null,\n        doubleQuotedAsJSON: false,\n        doubleQuotedMinMultiLineLength: 40,\n        falseStr: 'false',\n        flowCollectionPadding: true,\n        indentSeq: true,\n        lineWidth: 80,\n        minContentWidth: 20,\n        nullStr: 'null',\n        simpleKeys: false,\n        singleQuote: null,\n        trailingComma: false,\n        trueStr: 'true',\n        verifyAliasOrder: true\n    }, doc.schema.toStringOptions, options);\n    let inFlow;\n    switch (opt.collectionStyle) {\n        case 'block':\n            inFlow = false;\n            break;\n        case 'flow':\n            inFlow = true;\n            break;\n        default:\n            inFlow = null;\n    }\n    return {\n        anchors: new Set(),\n        doc,\n        flowCollectionPadding: opt.flowCollectionPadding ? ' ' : '',\n        indent: '',\n        indentStep: typeof opt.indent === 'number' ? ' '.repeat(opt.indent) : '  ',\n        inFlow,\n        options: opt\n    };\n}\nfunction getTagObject(tags, item) {\n    if (item.tag) {\n        const match = tags.filter(t => t.tag === item.tag);\n        if (match.length > 0)\n            return match.find(t => t.format === item.format) ?? match[0];\n    }\n    let tagObj = undefined;\n    let obj;\n    if (identity.isScalar(item)) {\n        obj = item.value;\n        let match = tags.filter(t => t.identify?.(obj));\n        if (match.length > 1) {\n            const testMatch = match.filter(t => t.test);\n            if (testMatch.length > 0)\n                match = testMatch;\n        }\n        tagObj =\n            match.find(t => t.format === item.format) ?? match.find(t => !t.format);\n    }\n    else {\n        obj = item;\n        tagObj = tags.find(t => t.nodeClass && obj instanceof t.nodeClass);\n    }\n    if (!tagObj) {\n        const name = obj?.constructor?.name ?? (obj === null ? 'null' : typeof obj);\n        throw new Error(`Tag not resolved for ${name} value`);\n    }\n    return tagObj;\n}\n// needs to be called before value stringifier to allow for circular anchor refs\nfunction stringifyProps(node, tagObj, { anchors: anchors$1, doc }) {\n    if (!doc.directives)\n        return '';\n    const props = [];\n    const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor;\n    if (anchor && anchors.anchorIsValid(anchor)) {\n        anchors$1.add(anchor);\n        props.push(`&${anchor}`);\n    }\n    const tag = node.tag ?? (tagObj.default ? null : tagObj.tag);\n    if (tag)\n        props.push(doc.directives.tagString(tag));\n    return props.join(' ');\n}\nfunction stringify(item, ctx, onComment, onChompKeep) {\n    if (identity.isPair(item))\n        return item.toString(ctx, onComment, onChompKeep);\n    if (identity.isAlias(item)) {\n        if (ctx.doc.directives)\n            return item.toString(ctx);\n        if (ctx.resolvedAliases?.has(item)) {\n            throw new TypeError(`Cannot stringify circular structure without alias nodes`);\n        }\n        else {\n            if (ctx.resolvedAliases)\n                ctx.resolvedAliases.add(item);\n            else\n                ctx.resolvedAliases = new Set([item]);\n            item = item.resolve(ctx.doc);\n        }\n    }\n    let tagObj = undefined;\n    const node = identity.isNode(item)\n        ? item\n        : ctx.doc.createNode(item, { onTagObj: o => (tagObj = o) });\n    tagObj ?? (tagObj = getTagObject(ctx.doc.schema.tags, node));\n    const props = stringifyProps(node, tagObj, ctx);\n    if (props.length > 0)\n        ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1;\n    const str = typeof tagObj.stringify === 'function'\n        ? tagObj.stringify(node, ctx, onComment, onChompKeep)\n        : identity.isScalar(node)\n            ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep)\n            : node.toString(ctx, onComment, onChompKeep);\n    if (!props)\n        return str;\n    return identity.isScalar(node) || str[0] === '{' || str[0] === '['\n        ? `${props} ${str}`\n        : `${props}\\n${ctx.indent}${str}`;\n}\n\nexports.createStringifyContext = createStringifyContext;\nexports.stringify = stringify;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar Scalar = require('../nodes/Scalar.js');\nvar stringify = require('./stringify.js');\nvar stringifyComment = require('./stringifyComment.js');\n\nfunction stringifyPair({ key, value }, ctx, onComment, onChompKeep) {\n    const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;\n    let keyComment = (identity.isNode(key) && key.comment) || null;\n    if (simpleKeys) {\n        if (keyComment) {\n            throw new Error('With simple keys, key nodes cannot have comments');\n        }\n        if (identity.isCollection(key) || (!identity.isNode(key) && typeof key === 'object')) {\n            const msg = 'With simple keys, collection cannot be used as a key value';\n            throw new Error(msg);\n        }\n    }\n    let explicitKey = !simpleKeys &&\n        (!key ||\n            (keyComment && value == null && !ctx.inFlow) ||\n            identity.isCollection(key) ||\n            (identity.isScalar(key)\n                ? key.type === Scalar.Scalar.BLOCK_FOLDED || key.type === Scalar.Scalar.BLOCK_LITERAL\n                : typeof key === 'object'));\n    ctx = Object.assign({}, ctx, {\n        allNullValues: false,\n        implicitKey: !explicitKey && (simpleKeys || !allNullValues),\n        indent: indent + indentStep\n    });\n    let keyCommentDone = false;\n    let chompKeep = false;\n    let str = stringify.stringify(key, ctx, () => (keyCommentDone = true), () => (chompKeep = true));\n    if (!explicitKey && !ctx.inFlow && str.length > 1024) {\n        if (simpleKeys)\n            throw new Error('With simple keys, single line scalar must not span more than 1024 characters');\n        explicitKey = true;\n    }\n    if (ctx.inFlow) {\n        if (allNullValues || value == null) {\n            if (keyCommentDone && onComment)\n                onComment();\n            return str === '' ? '?' : explicitKey ? `? ${str}` : str;\n        }\n    }\n    else if ((allNullValues && !simpleKeys) || (value == null && explicitKey)) {\n        str = `? ${str}`;\n        if (keyComment && !keyCommentDone) {\n            str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment));\n        }\n        else if (chompKeep && onChompKeep)\n            onChompKeep();\n        return str;\n    }\n    if (keyCommentDone)\n        keyComment = null;\n    if (explicitKey) {\n        if (keyComment)\n            str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment));\n        str = `? ${str}\\n${indent}:`;\n    }\n    else {\n        str = `${str}:`;\n        if (keyComment)\n            str += stringifyComment.lineComment(str, ctx.indent, commentString(keyComment));\n    }\n    let vsb, vcb, valueComment;\n    if (identity.isNode(value)) {\n        vsb = !!value.spaceBefore;\n        vcb = value.commentBefore;\n        valueComment = value.comment;\n    }\n    else {\n        vsb = false;\n        vcb = null;\n        valueComment = null;\n        if (value && typeof value === 'object')\n            value = doc.createNode(value);\n    }\n    ctx.implicitKey = false;\n    if (!explicitKey && !keyComment && identity.isScalar(value))\n        ctx.indentAtStart = str.length + 1;\n    chompKeep = false;\n    if (!indentSeq &&\n        indentStep.length >= 2 &&\n        !ctx.inFlow &&\n        !explicitKey &&\n        identity.isSeq(value) &&\n        !value.flow &&\n        !value.tag &&\n        !value.anchor) {\n        // If indentSeq === false, consider '- ' as part of indentation where possible\n        ctx.indent = ctx.indent.substring(2);\n    }\n    let valueCommentDone = false;\n    const valueStr = stringify.stringify(value, ctx, () => (valueCommentDone = true), () => (chompKeep = true));\n    let ws = ' ';\n    if (keyComment || vsb || vcb) {\n        ws = vsb ? '\\n' : '';\n        if (vcb) {\n            const cs = commentString(vcb);\n            ws += `\\n${stringifyComment.indentComment(cs, ctx.indent)}`;\n        }\n        if (valueStr === '' && !ctx.inFlow) {\n            if (ws === '\\n' && valueComment)\n                ws = '\\n\\n';\n        }\n        else {\n            ws += `\\n${ctx.indent}`;\n        }\n    }\n    else if (!explicitKey && identity.isCollection(value)) {\n        const vs0 = valueStr[0];\n        const nl0 = valueStr.indexOf('\\n');\n        const hasNewline = nl0 !== -1;\n        const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0;\n        if (hasNewline || !flow) {\n            let hasPropsLine = false;\n            if (hasNewline && (vs0 === '&' || vs0 === '!')) {\n                let sp0 = valueStr.indexOf(' ');\n                if (vs0 === '&' &&\n                    sp0 !== -1 &&\n                    sp0 < nl0 &&\n                    valueStr[sp0 + 1] === '!') {\n                    sp0 = valueStr.indexOf(' ', sp0 + 1);\n                }\n                if (sp0 === -1 || nl0 < sp0)\n                    hasPropsLine = true;\n            }\n            if (!hasPropsLine)\n                ws = `\\n${ctx.indent}`;\n        }\n    }\n    else if (valueStr === '' || valueStr[0] === '\\n') {\n        ws = '';\n    }\n    str += ws + valueStr;\n    if (ctx.inFlow) {\n        if (valueCommentDone && onComment)\n            onComment();\n    }\n    else if (valueComment && !valueCommentDone) {\n        str += stringifyComment.lineComment(str, ctx.indent, commentString(valueComment));\n    }\n    else if (chompKeep && onChompKeep) {\n        onChompKeep();\n    }\n    return str;\n}\n\nexports.stringifyPair = stringifyPair;\n", "'use strict';\n\nvar node_process = require('process');\n\nfunction debug(logLevel, ...messages) {\n    if (logLevel === 'debug')\n        console.log(...messages);\n}\nfunction warn(logLevel, warning) {\n    if (logLevel === 'debug' || logLevel === 'warn') {\n        if (typeof node_process.emitWarning === 'function')\n            node_process.emitWarning(warning);\n        else\n            console.warn(warning);\n    }\n}\n\nexports.debug = debug;\nexports.warn = warn;\n", "'use strict';\n\nvar identity = require('../../nodes/identity.js');\nvar Scalar = require('../../nodes/Scalar.js');\n\n// If the value associated with a merge key is a single mapping node, each of\n// its key/value pairs is inserted into the current mapping, unless the key\n// already exists in it. If the value associated with the merge key is a\n// sequence, then this sequence is expected to contain mapping nodes and each\n// of these nodes is merged in turn according to its order in the sequence.\n// Keys in mapping nodes earlier in the sequence override keys specified in\n// later mapping nodes. -- http://yaml.org/type/merge.html\nconst MERGE_KEY = '<<';\nconst merge = {\n    identify: value => value === MERGE_KEY ||\n        (typeof value === 'symbol' && value.description === MERGE_KEY),\n    default: 'key',\n    tag: 'tag:yaml.org,2002:merge',\n    test: /^<<$/,\n    resolve: () => Object.assign(new Scalar.Scalar(Symbol(MERGE_KEY)), {\n        addToJSMap: addMergeToJSMap\n    }),\n    stringify: () => MERGE_KEY\n};\nconst isMergeKey = (ctx, key) => (merge.identify(key) ||\n    (identity.isScalar(key) &&\n        (!key.type || key.type === Scalar.Scalar.PLAIN) &&\n        merge.identify(key.value))) &&\n    ctx?.doc.schema.tags.some(tag => tag.tag === merge.tag && tag.default);\nfunction addMergeToJSMap(ctx, map, value) {\n    const source = resolveAliasValue(ctx, value);\n    if (identity.isSeq(source))\n        for (const it of source.items)\n            mergeValue(ctx, map, it);\n    else if (Array.isArray(source))\n        for (const it of source)\n            mergeValue(ctx, map, it);\n    else\n        mergeValue(ctx, map, source);\n}\nfunction mergeValue(ctx, map, value) {\n    const source = resolveAliasValue(ctx, value);\n    if (!identity.isMap(source))\n        throw new Error('Merge sources must be maps or map aliases');\n    const srcMap = source.toJSON(null, ctx, Map);\n    for (const [key, value] of srcMap) {\n        if (map instanceof Map) {\n            if (!map.has(key))\n                map.set(key, value);\n        }\n        else if (map instanceof Set) {\n            map.add(key);\n        }\n        else if (!Object.prototype.hasOwnProperty.call(map, key)) {\n            Object.defineProperty(map, key, {\n                value,\n                writable: true,\n                enumerable: true,\n                configurable: true\n            });\n        }\n    }\n    return map;\n}\nfunction resolveAliasValue(ctx, value) {\n    return ctx && identity.isAlias(value) ? value.resolve(ctx.doc, ctx) : value;\n}\n\nexports.addMergeToJSMap = addMergeToJSMap;\nexports.isMergeKey = isMergeKey;\nexports.merge = merge;\n", "'use strict';\n\nvar log = require('../log.js');\nvar merge = require('../schema/yaml-1.1/merge.js');\nvar stringify = require('../stringify/stringify.js');\nvar identity = require('./identity.js');\nvar toJS = require('./toJS.js');\n\nfunction addPairToJSMap(ctx, map, { key, value }) {\n    if (identity.isNode(key) && key.addToJSMap)\n        key.addToJSMap(ctx, map, value);\n    // TODO: Should drop this special case for bare << handling\n    else if (merge.isMergeKey(ctx, key))\n        merge.addMergeToJSMap(ctx, map, value);\n    else {\n        const jsKey = toJS.toJS(key, '', ctx);\n        if (map instanceof Map) {\n            map.set(jsKey, toJS.toJS(value, jsKey, ctx));\n        }\n        else if (map instanceof Set) {\n            map.add(jsKey);\n        }\n        else {\n            const stringKey = stringifyKey(key, jsKey, ctx);\n            const jsValue = toJS.toJS(value, stringKey, ctx);\n            if (stringKey in map)\n                Object.defineProperty(map, stringKey, {\n                    value: jsValue,\n                    writable: true,\n                    enumerable: true,\n                    configurable: true\n                });\n            else\n                map[stringKey] = jsValue;\n        }\n    }\n    return map;\n}\nfunction stringifyKey(key, jsKey, ctx) {\n    if (jsKey === null)\n        return '';\n    // eslint-disable-next-line @typescript-eslint/no-base-to-string\n    if (typeof jsKey !== 'object')\n        return String(jsKey);\n    if (identity.isNode(key) && ctx?.doc) {\n        const strCtx = stringify.createStringifyContext(ctx.doc, {});\n        strCtx.anchors = new Set();\n        for (const node of ctx.anchors.keys())\n            strCtx.anchors.add(node.anchor);\n        strCtx.inFlow = true;\n        strCtx.inStringifyKey = true;\n        const strKey = key.toString(strCtx);\n        if (!ctx.mapKeyWarned) {\n            let jsonStr = JSON.stringify(strKey);\n            if (jsonStr.length > 40)\n                jsonStr = jsonStr.substring(0, 36) + '...\"';\n            log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`);\n            ctx.mapKeyWarned = true;\n        }\n        return strKey;\n    }\n    return JSON.stringify(jsKey);\n}\n\nexports.addPairToJSMap = addPairToJSMap;\n", "'use strict';\n\nvar createNode = require('../doc/createNode.js');\nvar stringifyPair = require('../stringify/stringifyPair.js');\nvar addPairToJSMap = require('./addPairToJSMap.js');\nvar identity = require('./identity.js');\n\nfunction createPair(key, value, ctx) {\n    const k = createNode.createNode(key, undefined, ctx);\n    const v = createNode.createNode(value, undefined, ctx);\n    return new Pair(k, v);\n}\nclass Pair {\n    constructor(key, value = null) {\n        Object.defineProperty(this, identity.NODE_TYPE, { value: identity.PAIR });\n        this.key = key;\n        this.value = value;\n    }\n    clone(schema) {\n        let { key, value } = this;\n        if (identity.isNode(key))\n            key = key.clone(schema);\n        if (identity.isNode(value))\n            value = value.clone(schema);\n        return new Pair(key, value);\n    }\n    toJSON(_, ctx) {\n        const pair = ctx?.mapAsMap ? new Map() : {};\n        return addPairToJSMap.addPairToJSMap(ctx, pair, this);\n    }\n    toString(ctx, onComment, onChompKeep) {\n        return ctx?.doc\n            ? stringifyPair.stringifyPair(this, ctx, onComment, onChompKeep)\n            : JSON.stringify(this);\n    }\n}\n\nexports.Pair = Pair;\nexports.createPair = createPair;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar stringify = require('./stringify.js');\nvar stringifyComment = require('./stringifyComment.js');\n\nfunction stringifyCollection(collection, ctx, options) {\n    const flow = ctx.inFlow ?? collection.flow;\n    const stringify = flow ? stringifyFlowCollection : stringifyBlockCollection;\n    return stringify(collection, ctx, options);\n}\nfunction stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {\n    const { indent, options: { commentString } } = ctx;\n    const itemCtx = Object.assign({}, ctx, { indent: itemIndent, type: null });\n    let chompKeep = false; // flag for the preceding node's status\n    const lines = [];\n    for (let i = 0; i < items.length; ++i) {\n        const item = items[i];\n        let comment = null;\n        if (identity.isNode(item)) {\n            if (!chompKeep && item.spaceBefore)\n                lines.push('');\n            addCommentBefore(ctx, lines, item.commentBefore, chompKeep);\n            if (item.comment)\n                comment = item.comment;\n        }\n        else if (identity.isPair(item)) {\n            const ik = identity.isNode(item.key) ? item.key : null;\n            if (ik) {\n                if (!chompKeep && ik.spaceBefore)\n                    lines.push('');\n                addCommentBefore(ctx, lines, ik.commentBefore, chompKeep);\n            }\n        }\n        chompKeep = false;\n        let str = stringify.stringify(item, itemCtx, () => (comment = null), () => (chompKeep = true));\n        if (comment)\n            str += stringifyComment.lineComment(str, itemIndent, commentString(comment));\n        if (chompKeep && comment)\n            chompKeep = false;\n        lines.push(blockItemPrefix + str);\n    }\n    let str;\n    if (lines.length === 0) {\n        str = flowChars.start + flowChars.end;\n    }\n    else {\n        str = lines[0];\n        for (let i = 1; i < lines.length; ++i) {\n            const line = lines[i];\n            str += line ? `\\n${indent}${line}` : '\\n';\n        }\n    }\n    if (comment) {\n        str += '\\n' + stringifyComment.indentComment(commentString(comment), indent);\n        if (onComment)\n            onComment();\n    }\n    else if (chompKeep && onChompKeep)\n        onChompKeep();\n    return str;\n}\nfunction stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) {\n    const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx;\n    itemIndent += indentStep;\n    const itemCtx = Object.assign({}, ctx, {\n        indent: itemIndent,\n        inFlow: true,\n        type: null\n    });\n    let reqNewline = false;\n    let linesAtValue = 0;\n    const lines = [];\n    for (let i = 0; i < items.length; ++i) {\n        const item = items[i];\n        let comment = null;\n        if (identity.isNode(item)) {\n            if (item.spaceBefore)\n                lines.push('');\n            addCommentBefore(ctx, lines, item.commentBefore, false);\n            if (item.comment)\n                comment = item.comment;\n        }\n        else if (identity.isPair(item)) {\n            const ik = identity.isNode(item.key) ? item.key : null;\n            if (ik) {\n                if (ik.spaceBefore)\n                    lines.push('');\n                addCommentBefore(ctx, lines, ik.commentBefore, false);\n                if (ik.comment)\n                    reqNewline = true;\n            }\n            const iv = identity.isNode(item.value) ? item.value : null;\n            if (iv) {\n                if (iv.comment)\n                    comment = iv.comment;\n                if (iv.commentBefore)\n                    reqNewline = true;\n            }\n            else if (item.value == null && ik?.comment) {\n                comment = ik.comment;\n            }\n        }\n        if (comment)\n            reqNewline = true;\n        let str = stringify.stringify(item, itemCtx, () => (comment = null));\n        reqNewline || (reqNewline = lines.length > linesAtValue || str.includes('\\n'));\n        if (i < items.length - 1) {\n            str += ',';\n        }\n        else if (ctx.options.trailingComma) {\n            if (ctx.options.lineWidth > 0) {\n                reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) +\n                    (str.length + 2) >\n                    ctx.options.lineWidth);\n            }\n            if (reqNewline) {\n                str += ',';\n            }\n        }\n        if (comment)\n            str += stringifyComment.lineComment(str, itemIndent, commentString(comment));\n        lines.push(str);\n        linesAtValue = lines.length;\n    }\n    const { start, end } = flowChars;\n    if (lines.length === 0) {\n        return start + end;\n    }\n    else {\n        if (!reqNewline) {\n            const len = lines.reduce((sum, line) => sum + line.length + 2, 2);\n            reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;\n        }\n        if (reqNewline) {\n            let str = start;\n            for (const line of lines)\n                str += line ? `\\n${indentStep}${indent}${line}` : '\\n';\n            return `${str}\\n${indent}${end}`;\n        }\n        else {\n            return `${start}${fcPadding}${lines.join(' ')}${fcPadding}${end}`;\n        }\n    }\n}\nfunction addCommentBefore({ indent, options: { commentString } }, lines, comment, chompKeep) {\n    if (comment && chompKeep)\n        comment = comment.replace(/^\\n+/, '');\n    if (comment) {\n        const ic = stringifyComment.indentComment(commentString(comment), indent);\n        lines.push(ic.trimStart()); // Avoid double indent on first line\n    }\n}\n\nexports.stringifyCollection = stringifyCollection;\n", "'use strict';\n\nvar stringifyCollection = require('../stringify/stringifyCollection.js');\nvar addPairToJSMap = require('./addPairToJSMap.js');\nvar Collection = require('./Collection.js');\nvar identity = require('./identity.js');\nvar Pair = require('./Pair.js');\nvar Scalar = require('./Scalar.js');\n\nfunction findPair(items, key) {\n    const k = identity.isScalar(key) ? key.value : key;\n    for (const it of items) {\n        if (identity.isPair(it)) {\n            if (it.key === key || it.key === k)\n                return it;\n            if (identity.isScalar(it.key) && it.key.value === k)\n                return it;\n        }\n    }\n    return undefined;\n}\nclass YAMLMap extends Collection.Collection {\n    static get tagName() {\n        return 'tag:yaml.org,2002:map';\n    }\n    constructor(schema) {\n        super(identity.MAP, schema);\n        this.items = [];\n    }\n    /**\n     * A generic collection parsing method that can be extended\n     * to other node classes that inherit from YAMLMap\n     */\n    static from(schema, obj, ctx) {\n        const { keepUndefined, replacer } = ctx;\n        const map = new this(schema);\n        const add = (key, value) => {\n            if (typeof replacer === 'function')\n                value = replacer.call(obj, key, value);\n            else if (Array.isArray(replacer) && !replacer.includes(key))\n                return;\n            if (value !== undefined || keepUndefined)\n                map.items.push(Pair.createPair(key, value, ctx));\n        };\n        if (obj instanceof Map) {\n            for (const [key, value] of obj)\n                add(key, value);\n        }\n        else if (obj && typeof obj === 'object') {\n            for (const key of Object.keys(obj))\n                add(key, obj[key]);\n        }\n        if (typeof schema.sortMapEntries === 'function') {\n            map.items.sort(schema.sortMapEntries);\n        }\n        return map;\n    }\n    /**\n     * Adds a value to the collection.\n     *\n     * @param overwrite - If not set `true`, using a key that is already in the\n     *   collection will throw. Otherwise, overwrites the previous value.\n     */\n    add(pair, overwrite) {\n        let _pair;\n        if (identity.isPair(pair))\n            _pair = pair;\n        else if (!pair || typeof pair !== 'object' || !('key' in pair)) {\n            // In TypeScript, this never happens.\n            _pair = new Pair.Pair(pair, pair?.value);\n        }\n        else\n            _pair = new Pair.Pair(pair.key, pair.value);\n        const prev = findPair(this.items, _pair.key);\n        const sortEntries = this.schema?.sortMapEntries;\n        if (prev) {\n            if (!overwrite)\n                throw new Error(`Key ${_pair.key} already set`);\n            // For scalars, keep the old node & its comments and anchors\n            if (identity.isScalar(prev.value) && Scalar.isScalarValue(_pair.value))\n                prev.value.value = _pair.value;\n            else\n                prev.value = _pair.value;\n        }\n        else if (sortEntries) {\n            const i = this.items.findIndex(item => sortEntries(_pair, item) < 0);\n            if (i === -1)\n                this.items.push(_pair);\n            else\n                this.items.splice(i, 0, _pair);\n        }\n        else {\n            this.items.push(_pair);\n        }\n    }\n    delete(key) {\n        const it = findPair(this.items, key);\n        if (!it)\n            return false;\n        const del = this.items.splice(this.items.indexOf(it), 1);\n        return del.length > 0;\n    }\n    get(key, keepScalar) {\n        const it = findPair(this.items, key);\n        const node = it?.value;\n        return (!keepScalar && identity.isScalar(node) ? node.value : node) ?? undefined;\n    }\n    has(key) {\n        return !!findPair(this.items, key);\n    }\n    set(key, value) {\n        this.add(new Pair.Pair(key, value), true);\n    }\n    /**\n     * @param ctx - Conversion context, originally set in Document#toJS()\n     * @param {Class} Type - If set, forces the returned collection type\n     * @returns Instance of Type, Map, or Object\n     */\n    toJSON(_, ctx, Type) {\n        const map = Type ? new Type() : ctx?.mapAsMap ? new Map() : {};\n        if (ctx?.onCreate)\n            ctx.onCreate(map);\n        for (const item of this.items)\n            addPairToJSMap.addPairToJSMap(ctx, map, item);\n        return map;\n    }\n    toString(ctx, onComment, onChompKeep) {\n        if (!ctx)\n            return JSON.stringify(this);\n        for (const item of this.items) {\n            if (!identity.isPair(item))\n                throw new Error(`Map items must all be pairs; found ${JSON.stringify(item)} instead`);\n        }\n        if (!ctx.allNullValues && this.hasAllNullValues(false))\n            ctx = Object.assign({}, ctx, { allNullValues: true });\n        return stringifyCollection.stringifyCollection(this, ctx, {\n            blockItemPrefix: '',\n            flowChars: { start: '{', end: '}' },\n            itemIndent: ctx.indent || '',\n            onChompKeep,\n            onComment\n        });\n    }\n}\n\nexports.YAMLMap = YAMLMap;\nexports.findPair = findPair;\n", "'use strict';\n\nvar identity = require('../../nodes/identity.js');\nvar YAMLMap = require('../../nodes/YAMLMap.js');\n\nconst map = {\n    collection: 'map',\n    default: true,\n    nodeClass: YAMLMap.YAMLMap,\n    tag: 'tag:yaml.org,2002:map',\n    resolve(map, onError) {\n        if (!identity.isMap(map))\n            onError('Expected a mapping for this tag');\n        return map;\n    },\n    createNode: (schema, obj, ctx) => YAMLMap.YAMLMap.from(schema, obj, ctx)\n};\n\nexports.map = map;\n", "'use strict';\n\nvar createNode = require('../doc/createNode.js');\nvar stringifyCollection = require('../stringify/stringifyCollection.js');\nvar Collection = require('./Collection.js');\nvar identity = require('./identity.js');\nvar Scalar = require('./Scalar.js');\nvar toJS = require('./toJS.js');\n\nclass YAMLSeq extends Collection.Collection {\n    static get tagName() {\n        return 'tag:yaml.org,2002:seq';\n    }\n    constructor(schema) {\n        super(identity.SEQ, schema);\n        this.items = [];\n    }\n    add(value) {\n        this.items.push(value);\n    }\n    /**\n     * Removes a value from the collection.\n     *\n     * `key` must contain a representation of an integer for this to succeed.\n     * It may be wrapped in a `Scalar`.\n     *\n     * @returns `true` if the item was found and removed.\n     */\n    delete(key) {\n        const idx = asItemIndex(key);\n        if (typeof idx !== 'number')\n            return false;\n        const del = this.items.splice(idx, 1);\n        return del.length > 0;\n    }\n    get(key, keepScalar) {\n        const idx = asItemIndex(key);\n        if (typeof idx !== 'number')\n            return undefined;\n        const it = this.items[idx];\n        return !keepScalar && identity.isScalar(it) ? it.value : it;\n    }\n    /**\n     * Checks if the collection includes a value with the key `key`.\n     *\n     * `key` must contain a representation of an integer for this to succeed.\n     * It may be wrapped in a `Scalar`.\n     */\n    has(key) {\n        const idx = asItemIndex(key);\n        return typeof idx === 'number' && idx < this.items.length;\n    }\n    /**\n     * Sets a value in this collection. For `!!set`, `value` needs to be a\n     * boolean to add/remove the item from the set.\n     *\n     * If `key` does not contain a representation of an integer, this will throw.\n     * It may be wrapped in a `Scalar`.\n     */\n    set(key, value) {\n        const idx = asItemIndex(key);\n        if (typeof idx !== 'number')\n            throw new Error(`Expected a valid index, not ${key}.`);\n        const prev = this.items[idx];\n        if (identity.isScalar(prev) && Scalar.isScalarValue(value))\n            prev.value = value;\n        else\n            this.items[idx] = value;\n    }\n    toJSON(_, ctx) {\n        const seq = [];\n        if (ctx?.onCreate)\n            ctx.onCreate(seq);\n        let i = 0;\n        for (const item of this.items)\n            seq.push(toJS.toJS(item, String(i++), ctx));\n        return seq;\n    }\n    toString(ctx, onComment, onChompKeep) {\n        if (!ctx)\n            return JSON.stringify(this);\n        return stringifyCollection.stringifyCollection(this, ctx, {\n            blockItemPrefix: '- ',\n            flowChars: { start: '[', end: ']' },\n            itemIndent: (ctx.indent || '') + '  ',\n            onChompKeep,\n            onComment\n        });\n    }\n    static from(schema, obj, ctx) {\n        const { replacer } = ctx;\n        const seq = new this(schema);\n        if (obj && Symbol.iterator in Object(obj)) {\n            let i = 0;\n            for (let it of obj) {\n                if (typeof replacer === 'function') {\n                    const key = obj instanceof Set ? it : String(i++);\n                    it = replacer.call(obj, key, it);\n                }\n                seq.items.push(createNode.createNode(it, undefined, ctx));\n            }\n        }\n        return seq;\n    }\n}\nfunction asItemIndex(key) {\n    let idx = identity.isScalar(key) ? key.value : key;\n    if (idx && typeof idx === 'string')\n        idx = Number(idx);\n    return typeof idx === 'number' && Number.isInteger(idx) && idx >= 0\n        ? idx\n        : null;\n}\n\nexports.YAMLSeq = YAMLSeq;\n", "'use strict';\n\nvar identity = require('../../nodes/identity.js');\nvar YAMLSeq = require('../../nodes/YAMLSeq.js');\n\nconst seq = {\n    collection: 'seq',\n    default: true,\n    nodeClass: YAMLSeq.YAMLSeq,\n    tag: 'tag:yaml.org,2002:seq',\n    resolve(seq, onError) {\n        if (!identity.isSeq(seq))\n            onError('Expected a sequence for this tag');\n        return seq;\n    },\n    createNode: (schema, obj, ctx) => YAMLSeq.YAMLSeq.from(schema, obj, ctx)\n};\n\nexports.seq = seq;\n", "'use strict';\n\nvar stringifyString = require('../../stringify/stringifyString.js');\n\nconst string = {\n    identify: value => typeof value === 'string',\n    default: true,\n    tag: 'tag:yaml.org,2002:str',\n    resolve: str => str,\n    stringify(item, ctx, onComment, onChompKeep) {\n        ctx = Object.assign({ actualString: true }, ctx);\n        return stringifyString.stringifyString(item, ctx, onComment, onChompKeep);\n    }\n};\n\nexports.string = string;\n", "'use strict';\n\nvar Scalar = require('../../nodes/Scalar.js');\n\nconst nullTag = {\n    identify: value => value == null,\n    createNode: () => new Scalar.Scalar(null),\n    default: true,\n    tag: 'tag:yaml.org,2002:null',\n    test: /^(?:~|[Nn]ull|NULL)?$/,\n    resolve: () => new Scalar.Scalar(null),\n    stringify: ({ source }, ctx) => typeof source === 'string' && nullTag.test.test(source)\n        ? source\n        : ctx.options.nullStr\n};\n\nexports.nullTag = nullTag;\n", "'use strict';\n\nvar Scalar = require('../../nodes/Scalar.js');\n\nconst boolTag = {\n    identify: value => typeof value === 'boolean',\n    default: true,\n    tag: 'tag:yaml.org,2002:bool',\n    test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/,\n    resolve: str => new Scalar.Scalar(str[0] === 't' || str[0] === 'T'),\n    stringify({ source, value }, ctx) {\n        if (source && boolTag.test.test(source)) {\n            const sv = source[0] === 't' || source[0] === 'T';\n            if (value === sv)\n                return source;\n        }\n        return value ? ctx.options.trueStr : ctx.options.falseStr;\n    }\n};\n\nexports.boolTag = boolTag;\n", "'use strict';\n\nfunction stringifyNumber({ format, minFractionDigits, tag, value }) {\n    if (typeof value === 'bigint')\n        return String(value);\n    const num = typeof value === 'number' ? value : Number(value);\n    if (!isFinite(num))\n        return isNaN(num) ? '.nan' : num < 0 ? '-.inf' : '.inf';\n    let n = Object.is(value, -0) ? '-0' : JSON.stringify(value);\n    if (!format &&\n        minFractionDigits &&\n        (!tag || tag === 'tag:yaml.org,2002:float') &&\n        /^-?\\d/.test(n) &&\n        !n.includes('e')) {\n        let i = n.indexOf('.');\n        if (i < 0) {\n            i = n.length;\n            n += '.';\n        }\n        let d = minFractionDigits - (n.length - i - 1);\n        while (d-- > 0)\n            n += '0';\n    }\n    return n;\n}\n\nexports.stringifyNumber = stringifyNumber;\n", "'use strict';\n\nvar Scalar = require('../../nodes/Scalar.js');\nvar stringifyNumber = require('../../stringify/stringifyNumber.js');\n\nconst floatNaN = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    test: /^(?:[-+]?\\.(?:inf|Inf|INF)|\\.nan|\\.NaN|\\.NAN)$/,\n    resolve: str => str.slice(-3).toLowerCase() === 'nan'\n        ? NaN\n        : str[0] === '-'\n            ? Number.NEGATIVE_INFINITY\n            : Number.POSITIVE_INFINITY,\n    stringify: stringifyNumber.stringifyNumber\n};\nconst floatExp = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    format: 'EXP',\n    test: /^[-+]?(?:\\.[0-9]+|[0-9]+(?:\\.[0-9]*)?)[eE][-+]?[0-9]+$/,\n    resolve: str => parseFloat(str),\n    stringify(node) {\n        const num = Number(node.value);\n        return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);\n    }\n};\nconst float = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    test: /^[-+]?(?:\\.[0-9]+|[0-9]+\\.[0-9]*)$/,\n    resolve(str) {\n        const node = new Scalar.Scalar(parseFloat(str));\n        const dot = str.indexOf('.');\n        if (dot !== -1 && str[str.length - 1] === '0')\n            node.minFractionDigits = str.length - dot - 1;\n        return node;\n    },\n    stringify: stringifyNumber.stringifyNumber\n};\n\nexports.float = float;\nexports.floatExp = floatExp;\nexports.floatNaN = floatNaN;\n", "'use strict';\n\nvar stringifyNumber = require('../../stringify/stringifyNumber.js');\n\nconst intIdentify = (value) => typeof value === 'bigint' || Number.isInteger(value);\nconst intResolve = (str, offset, radix, { intAsBigInt }) => (intAsBigInt ? BigInt(str) : parseInt(str.substring(offset), radix));\nfunction intStringify(node, radix, prefix) {\n    const { value } = node;\n    if (intIdentify(value) && value >= 0)\n        return prefix + value.toString(radix);\n    return stringifyNumber.stringifyNumber(node);\n}\nconst intOct = {\n    identify: value => intIdentify(value) && value >= 0,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    format: 'OCT',\n    test: /^0o[0-7]+$/,\n    resolve: (str, _onError, opt) => intResolve(str, 2, 8, opt),\n    stringify: node => intStringify(node, 8, '0o')\n};\nconst int = {\n    identify: intIdentify,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    test: /^[-+]?[0-9]+$/,\n    resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt),\n    stringify: stringifyNumber.stringifyNumber\n};\nconst intHex = {\n    identify: value => intIdentify(value) && value >= 0,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    format: 'HEX',\n    test: /^0x[0-9a-fA-F]+$/,\n    resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt),\n    stringify: node => intStringify(node, 16, '0x')\n};\n\nexports.int = int;\nexports.intHex = intHex;\nexports.intOct = intOct;\n", "'use strict';\n\nvar map = require('../common/map.js');\nvar _null = require('../common/null.js');\nvar seq = require('../common/seq.js');\nvar string = require('../common/string.js');\nvar bool = require('./bool.js');\nvar float = require('./float.js');\nvar int = require('./int.js');\n\nconst schema = [\n    map.map,\n    seq.seq,\n    string.string,\n    _null.nullTag,\n    bool.boolTag,\n    int.intOct,\n    int.int,\n    int.intHex,\n    float.floatNaN,\n    float.floatExp,\n    float.float\n];\n\nexports.schema = schema;\n", "'use strict';\n\nvar Scalar = require('../../nodes/Scalar.js');\nvar map = require('../common/map.js');\nvar seq = require('../common/seq.js');\n\nfunction intIdentify(value) {\n    return typeof value === 'bigint' || Number.isInteger(value);\n}\nconst stringifyJSON = ({ value }) => JSON.stringify(value);\nconst jsonScalars = [\n    {\n        identify: value => typeof value === 'string',\n        default: true,\n        tag: 'tag:yaml.org,2002:str',\n        resolve: str => str,\n        stringify: stringifyJSON\n    },\n    {\n        identify: value => value == null,\n        createNode: () => new Scalar.Scalar(null),\n        default: true,\n        tag: 'tag:yaml.org,2002:null',\n        test: /^null$/,\n        resolve: () => null,\n        stringify: stringifyJSON\n    },\n    {\n        identify: value => typeof value === 'boolean',\n        default: true,\n        tag: 'tag:yaml.org,2002:bool',\n        test: /^true$|^false$/,\n        resolve: str => str === 'true',\n        stringify: stringifyJSON\n    },\n    {\n        identify: intIdentify,\n        default: true,\n        tag: 'tag:yaml.org,2002:int',\n        test: /^-?(?:0|[1-9][0-9]*)$/,\n        resolve: (str, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str) : parseInt(str, 10),\n        stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value)\n    },\n    {\n        identify: value => typeof value === 'number',\n        default: true,\n        tag: 'tag:yaml.org,2002:float',\n        test: /^-?(?:0|[1-9][0-9]*)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/,\n        resolve: str => parseFloat(str),\n        stringify: stringifyJSON\n    }\n];\nconst jsonError = {\n    default: true,\n    tag: '',\n    test: /^/,\n    resolve(str, onError) {\n        onError(`Unresolved plain scalar ${JSON.stringify(str)}`);\n        return str;\n    }\n};\nconst schema = [map.map, seq.seq].concat(jsonScalars, jsonError);\n\nexports.schema = schema;\n", "'use strict';\n\nvar node_buffer = require('buffer');\nvar Scalar = require('../../nodes/Scalar.js');\nvar stringifyString = require('../../stringify/stringifyString.js');\n\nconst binary = {\n    identify: value => value instanceof Uint8Array, // Buffer inherits from Uint8Array\n    default: false,\n    tag: 'tag:yaml.org,2002:binary',\n    /**\n     * Returns a Buffer in node and an Uint8Array in browsers\n     *\n     * To use the resulting buffer as an image, you'll want to do something like:\n     *\n     *   const blob = new Blob([buffer], { type: 'image/jpeg' })\n     *   document.querySelector('#photo').src = URL.createObjectURL(blob)\n     */\n    resolve(src, onError) {\n        if (typeof node_buffer.Buffer === 'function') {\n            return node_buffer.Buffer.from(src, 'base64');\n        }\n        else if (typeof atob === 'function') {\n            // On IE 11, atob() can't handle newlines\n            const str = atob(src.replace(/[\\n\\r]/g, ''));\n            const buffer = new Uint8Array(str.length);\n            for (let i = 0; i < str.length; ++i)\n                buffer[i] = str.charCodeAt(i);\n            return buffer;\n        }\n        else {\n            onError('This environment does not support reading binary tags; either Buffer or atob is required');\n            return src;\n        }\n    },\n    stringify({ comment, type, value }, ctx, onComment, onChompKeep) {\n        if (!value)\n            return '';\n        const buf = value; // checked earlier by binary.identify()\n        let str;\n        if (typeof node_buffer.Buffer === 'function') {\n            str =\n                buf instanceof node_buffer.Buffer\n                    ? buf.toString('base64')\n                    : node_buffer.Buffer.from(buf.buffer).toString('base64');\n        }\n        else if (typeof btoa === 'function') {\n            let s = '';\n            for (let i = 0; i < buf.length; ++i)\n                s += String.fromCharCode(buf[i]);\n            str = btoa(s);\n        }\n        else {\n            throw new Error('This environment does not support writing binary tags; either Buffer or btoa is required');\n        }\n        type ?? (type = Scalar.Scalar.BLOCK_LITERAL);\n        if (type !== Scalar.Scalar.QUOTE_DOUBLE) {\n            const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth);\n            const n = Math.ceil(str.length / lineWidth);\n            const lines = new Array(n);\n            for (let i = 0, o = 0; i < n; ++i, o += lineWidth) {\n                lines[i] = str.substr(o, lineWidth);\n            }\n            str = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? '\\n' : ' ');\n        }\n        return stringifyString.stringifyString({ comment, type, value: str }, ctx, onComment, onChompKeep);\n    }\n};\n\nexports.binary = binary;\n", "'use strict';\n\nvar identity = require('../../nodes/identity.js');\nvar Pair = require('../../nodes/Pair.js');\nvar Scalar = require('../../nodes/Scalar.js');\nvar YAMLSeq = require('../../nodes/YAMLSeq.js');\n\nfunction resolvePairs(seq, onError) {\n    if (identity.isSeq(seq)) {\n        for (let i = 0; i < seq.items.length; ++i) {\n            let item = seq.items[i];\n            if (identity.isPair(item))\n                continue;\n            else if (identity.isMap(item)) {\n                if (item.items.length > 1)\n                    onError('Each pair must have its own sequence indicator');\n                const pair = item.items[0] || new Pair.Pair(new Scalar.Scalar(null));\n                if (item.commentBefore)\n                    pair.key.commentBefore = pair.key.commentBefore\n                        ? `${item.commentBefore}\\n${pair.key.commentBefore}`\n                        : item.commentBefore;\n                if (item.comment) {\n                    const cn = pair.value ?? pair.key;\n                    cn.comment = cn.comment\n                        ? `${item.comment}\\n${cn.comment}`\n                        : item.comment;\n                }\n                item = pair;\n            }\n            seq.items[i] = identity.isPair(item) ? item : new Pair.Pair(item);\n        }\n    }\n    else\n        onError('Expected a sequence for this tag');\n    return seq;\n}\nfunction createPairs(schema, iterable, ctx) {\n    const { replacer } = ctx;\n    const pairs = new YAMLSeq.YAMLSeq(schema);\n    pairs.tag = 'tag:yaml.org,2002:pairs';\n    let i = 0;\n    if (iterable && Symbol.iterator in Object(iterable))\n        for (let it of iterable) {\n            if (typeof replacer === 'function')\n                it = replacer.call(iterable, String(i++), it);\n            let key, value;\n            if (Array.isArray(it)) {\n                if (it.length === 2) {\n                    key = it[0];\n                    value = it[1];\n                }\n                else\n                    throw new TypeError(`Expected [key, value] tuple: ${it}`);\n            }\n            else if (it && it instanceof Object) {\n                const keys = Object.keys(it);\n                if (keys.length === 1) {\n                    key = keys[0];\n                    value = it[key];\n                }\n                else {\n                    throw new TypeError(`Expected tuple with one key, not ${keys.length} keys`);\n                }\n            }\n            else {\n                key = it;\n            }\n            pairs.items.push(Pair.createPair(key, value, ctx));\n        }\n    return pairs;\n}\nconst pairs = {\n    collection: 'seq',\n    default: false,\n    tag: 'tag:yaml.org,2002:pairs',\n    resolve: resolvePairs,\n    createNode: createPairs\n};\n\nexports.createPairs = createPairs;\nexports.pairs = pairs;\nexports.resolvePairs = resolvePairs;\n", "'use strict';\n\nvar identity = require('../../nodes/identity.js');\nvar toJS = require('../../nodes/toJS.js');\nvar YAMLMap = require('../../nodes/YAMLMap.js');\nvar YAMLSeq = require('../../nodes/YAMLSeq.js');\nvar pairs = require('./pairs.js');\n\nclass YAMLOMap extends YAMLSeq.YAMLSeq {\n    constructor() {\n        super();\n        this.add = YAMLMap.YAMLMap.prototype.add.bind(this);\n        this.delete = YAMLMap.YAMLMap.prototype.delete.bind(this);\n        this.get = YAMLMap.YAMLMap.prototype.get.bind(this);\n        this.has = YAMLMap.YAMLMap.prototype.has.bind(this);\n        this.set = YAMLMap.YAMLMap.prototype.set.bind(this);\n        this.tag = YAMLOMap.tag;\n    }\n    /**\n     * If `ctx` is given, the return type is actually `Map<unknown, unknown>`,\n     * but TypeScript won't allow widening the signature of a child method.\n     */\n    toJSON(_, ctx) {\n        if (!ctx)\n            return super.toJSON(_);\n        const map = new Map();\n        if (ctx?.onCreate)\n            ctx.onCreate(map);\n        for (const pair of this.items) {\n            let key, value;\n            if (identity.isPair(pair)) {\n                key = toJS.toJS(pair.key, '', ctx);\n                value = toJS.toJS(pair.value, key, ctx);\n            }\n            else {\n                key = toJS.toJS(pair, '', ctx);\n            }\n            if (map.has(key))\n                throw new Error('Ordered maps must not include duplicate keys');\n            map.set(key, value);\n        }\n        return map;\n    }\n    static from(schema, iterable, ctx) {\n        const pairs$1 = pairs.createPairs(schema, iterable, ctx);\n        const omap = new this();\n        omap.items = pairs$1.items;\n        return omap;\n    }\n}\nYAMLOMap.tag = 'tag:yaml.org,2002:omap';\nconst omap = {\n    collection: 'seq',\n    identify: value => value instanceof Map,\n    nodeClass: YAMLOMap,\n    default: false,\n    tag: 'tag:yaml.org,2002:omap',\n    resolve(seq, onError) {\n        const pairs$1 = pairs.resolvePairs(seq, onError);\n        const seenKeys = [];\n        for (const { key } of pairs$1.items) {\n            if (identity.isScalar(key)) {\n                if (seenKeys.includes(key.value)) {\n                    onError(`Ordered maps must not include duplicate keys: ${key.value}`);\n                }\n                else {\n                    seenKeys.push(key.value);\n                }\n            }\n        }\n        return Object.assign(new YAMLOMap(), pairs$1);\n    },\n    createNode: (schema, iterable, ctx) => YAMLOMap.from(schema, iterable, ctx)\n};\n\nexports.YAMLOMap = YAMLOMap;\nexports.omap = omap;\n", "'use strict';\n\nvar Scalar = require('../../nodes/Scalar.js');\n\nfunction boolStringify({ value, source }, ctx) {\n    const boolObj = value ? trueTag : falseTag;\n    if (source && boolObj.test.test(source))\n        return source;\n    return value ? ctx.options.trueStr : ctx.options.falseStr;\n}\nconst trueTag = {\n    identify: value => value === true,\n    default: true,\n    tag: 'tag:yaml.org,2002:bool',\n    test: /^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/,\n    resolve: () => new Scalar.Scalar(true),\n    stringify: boolStringify\n};\nconst falseTag = {\n    identify: value => value === false,\n    default: true,\n    tag: 'tag:yaml.org,2002:bool',\n    test: /^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/,\n    resolve: () => new Scalar.Scalar(false),\n    stringify: boolStringify\n};\n\nexports.falseTag = falseTag;\nexports.trueTag = trueTag;\n", "'use strict';\n\nvar Scalar = require('../../nodes/Scalar.js');\nvar stringifyNumber = require('../../stringify/stringifyNumber.js');\n\nconst floatNaN = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    test: /^(?:[-+]?\\.(?:inf|Inf|INF)|\\.nan|\\.NaN|\\.NAN)$/,\n    resolve: (str) => str.slice(-3).toLowerCase() === 'nan'\n        ? NaN\n        : str[0] === '-'\n            ? Number.NEGATIVE_INFINITY\n            : Number.POSITIVE_INFINITY,\n    stringify: stringifyNumber.stringifyNumber\n};\nconst floatExp = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    format: 'EXP',\n    test: /^[-+]?(?:[0-9][0-9_]*)?(?:\\.[0-9_]*)?[eE][-+]?[0-9]+$/,\n    resolve: (str) => parseFloat(str.replace(/_/g, '')),\n    stringify(node) {\n        const num = Number(node.value);\n        return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);\n    }\n};\nconst float = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    test: /^[-+]?(?:[0-9][0-9_]*)?\\.[0-9_]*$/,\n    resolve(str) {\n        const node = new Scalar.Scalar(parseFloat(str.replace(/_/g, '')));\n        const dot = str.indexOf('.');\n        if (dot !== -1) {\n            const f = str.substring(dot + 1).replace(/_/g, '');\n            if (f[f.length - 1] === '0')\n                node.minFractionDigits = f.length;\n        }\n        return node;\n    },\n    stringify: stringifyNumber.stringifyNumber\n};\n\nexports.float = float;\nexports.floatExp = floatExp;\nexports.floatNaN = floatNaN;\n", "'use strict';\n\nvar stringifyNumber = require('../../stringify/stringifyNumber.js');\n\nconst intIdentify = (value) => typeof value === 'bigint' || Number.isInteger(value);\nfunction intResolve(str, offset, radix, { intAsBigInt }) {\n    const sign = str[0];\n    if (sign === '-' || sign === '+')\n        offset += 1;\n    str = str.substring(offset).replace(/_/g, '');\n    if (intAsBigInt) {\n        switch (radix) {\n            case 2:\n                str = `0b${str}`;\n                break;\n            case 8:\n                str = `0o${str}`;\n                break;\n            case 16:\n                str = `0x${str}`;\n                break;\n        }\n        const n = BigInt(str);\n        return sign === '-' ? BigInt(-1) * n : n;\n    }\n    const n = parseInt(str, radix);\n    return sign === '-' ? -1 * n : n;\n}\nfunction intStringify(node, radix, prefix) {\n    const { value } = node;\n    if (intIdentify(value)) {\n        const str = value.toString(radix);\n        return value < 0 ? '-' + prefix + str.substr(1) : prefix + str;\n    }\n    return stringifyNumber.stringifyNumber(node);\n}\nconst intBin = {\n    identify: intIdentify,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    format: 'BIN',\n    test: /^[-+]?0b[0-1_]+$/,\n    resolve: (str, _onError, opt) => intResolve(str, 2, 2, opt),\n    stringify: node => intStringify(node, 2, '0b')\n};\nconst intOct = {\n    identify: intIdentify,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    format: 'OCT',\n    test: /^[-+]?0[0-7_]+$/,\n    resolve: (str, _onError, opt) => intResolve(str, 1, 8, opt),\n    stringify: node => intStringify(node, 8, '0')\n};\nconst int = {\n    identify: intIdentify,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    test: /^[-+]?[0-9][0-9_]*$/,\n    resolve: (str, _onError, opt) => intResolve(str, 0, 10, opt),\n    stringify: stringifyNumber.stringifyNumber\n};\nconst intHex = {\n    identify: intIdentify,\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    format: 'HEX',\n    test: /^[-+]?0x[0-9a-fA-F_]+$/,\n    resolve: (str, _onError, opt) => intResolve(str, 2, 16, opt),\n    stringify: node => intStringify(node, 16, '0x')\n};\n\nexports.int = int;\nexports.intBin = intBin;\nexports.intHex = intHex;\nexports.intOct = intOct;\n", "'use strict';\n\nvar identity = require('../../nodes/identity.js');\nvar Pair = require('../../nodes/Pair.js');\nvar YAMLMap = require('../../nodes/YAMLMap.js');\n\nclass YAMLSet extends YAMLMap.YAMLMap {\n    constructor(schema) {\n        super(schema);\n        this.tag = YAMLSet.tag;\n    }\n    add(key) {\n        let pair;\n        if (identity.isPair(key))\n            pair = key;\n        else if (key &&\n            typeof key === 'object' &&\n            'key' in key &&\n            'value' in key &&\n            key.value === null)\n            pair = new Pair.Pair(key.key, null);\n        else\n            pair = new Pair.Pair(key, null);\n        const prev = YAMLMap.findPair(this.items, pair.key);\n        if (!prev)\n            this.items.push(pair);\n    }\n    /**\n     * If `keepPair` is `true`, returns the Pair matching `key`.\n     * Otherwise, returns the value of that Pair's key.\n     */\n    get(key, keepPair) {\n        const pair = YAMLMap.findPair(this.items, key);\n        return !keepPair && identity.isPair(pair)\n            ? identity.isScalar(pair.key)\n                ? pair.key.value\n                : pair.key\n            : pair;\n    }\n    set(key, value) {\n        if (typeof value !== 'boolean')\n            throw new Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof value}`);\n        const prev = YAMLMap.findPair(this.items, key);\n        if (prev && !value) {\n            this.items.splice(this.items.indexOf(prev), 1);\n        }\n        else if (!prev && value) {\n            this.items.push(new Pair.Pair(key));\n        }\n    }\n    toJSON(_, ctx) {\n        return super.toJSON(_, ctx, Set);\n    }\n    toString(ctx, onComment, onChompKeep) {\n        if (!ctx)\n            return JSON.stringify(this);\n        if (this.hasAllNullValues(true))\n            return super.toString(Object.assign({}, ctx, { allNullValues: true }), onComment, onChompKeep);\n        else\n            throw new Error('Set items must all have null values');\n    }\n    static from(schema, iterable, ctx) {\n        const { replacer } = ctx;\n        const set = new this(schema);\n        if (iterable && Symbol.iterator in Object(iterable))\n            for (let value of iterable) {\n                if (typeof replacer === 'function')\n                    value = replacer.call(iterable, value, value);\n                set.items.push(Pair.createPair(value, null, ctx));\n            }\n        return set;\n    }\n}\nYAMLSet.tag = 'tag:yaml.org,2002:set';\nconst set = {\n    collection: 'map',\n    identify: value => value instanceof Set,\n    nodeClass: YAMLSet,\n    default: false,\n    tag: 'tag:yaml.org,2002:set',\n    createNode: (schema, iterable, ctx) => YAMLSet.from(schema, iterable, ctx),\n    resolve(map, onError) {\n        if (identity.isMap(map)) {\n            if (map.hasAllNullValues(true))\n                return Object.assign(new YAMLSet(), map);\n            else\n                onError('Set items must all have null values');\n        }\n        else\n            onError('Expected a mapping for this tag');\n        return map;\n    }\n};\n\nexports.YAMLSet = YAMLSet;\nexports.set = set;\n", "'use strict';\n\nvar stringifyNumber = require('../../stringify/stringifyNumber.js');\n\n/** Internal types handle bigint as number, because TS can't figure it out. */\nfunction parseSexagesimal(str, asBigInt) {\n    const sign = str[0];\n    const parts = sign === '-' || sign === '+' ? str.substring(1) : str;\n    const num = (n) => asBigInt ? BigInt(n) : Number(n);\n    const res = parts\n        .replace(/_/g, '')\n        .split(':')\n        .reduce((res, p) => res * num(60) + num(p), num(0));\n    return (sign === '-' ? num(-1) * res : res);\n}\n/**\n * hhhh:mm:ss.sss\n *\n * Internal types handle bigint as number, because TS can't figure it out.\n */\nfunction stringifySexagesimal(node) {\n    let { value } = node;\n    let num = (n) => n;\n    if (typeof value === 'bigint')\n        num = n => BigInt(n);\n    else if (isNaN(value) || !isFinite(value))\n        return stringifyNumber.stringifyNumber(node);\n    let sign = '';\n    if (value < 0) {\n        sign = '-';\n        value *= num(-1);\n    }\n    const _60 = num(60);\n    const parts = [value % _60]; // seconds, including ms\n    if (value < 60) {\n        parts.unshift(0); // at least one : is required\n    }\n    else {\n        value = (value - parts[0]) / _60;\n        parts.unshift(value % _60); // minutes\n        if (value >= 60) {\n            value = (value - parts[0]) / _60;\n            parts.unshift(value); // hours\n        }\n    }\n    return (sign +\n        parts\n            .map(n => String(n).padStart(2, '0'))\n            .join(':')\n            .replace(/000000\\d*$/, '') // % 60 may introduce error\n    );\n}\nconst intTime = {\n    identify: value => typeof value === 'bigint' || Number.isInteger(value),\n    default: true,\n    tag: 'tag:yaml.org,2002:int',\n    format: 'TIME',\n    test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/,\n    resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt),\n    stringify: stringifySexagesimal\n};\nconst floatTime = {\n    identify: value => typeof value === 'number',\n    default: true,\n    tag: 'tag:yaml.org,2002:float',\n    format: 'TIME',\n    test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*$/,\n    resolve: str => parseSexagesimal(str, false),\n    stringify: stringifySexagesimal\n};\nconst timestamp = {\n    identify: value => value instanceof Date,\n    default: true,\n    tag: 'tag:yaml.org,2002:timestamp',\n    // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part\n    // may be omitted altogether, resulting in a date format. In such a case, the time part is\n    // assumed to be 00:00:00Z (start of day, UTC).\n    test: RegExp('^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})' + // YYYY-Mm-Dd\n        '(?:' + // time is optional\n        '(?:t|T|[ \\\\t]+)' + // t | T | whitespace\n        '([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\\\.[0-9]+)?)' + // Hh:Mm:Ss(.ss)?\n        '(?:[ \\\\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?' + // Z | +5 | -03:30\n        ')?$'),\n    resolve(str) {\n        const match = str.match(timestamp.test);\n        if (!match)\n            throw new Error('!!timestamp expects a date, starting with yyyy-mm-dd');\n        const [, year, month, day, hour, minute, second] = match.map(Number);\n        const millisec = match[7] ? Number((match[7] + '00').substr(1, 3)) : 0;\n        let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec);\n        const tz = match[8];\n        if (tz && tz !== 'Z') {\n            let d = parseSexagesimal(tz, false);\n            if (Math.abs(d) < 30)\n                d *= 60;\n            date -= 60000 * d;\n        }\n        return new Date(date);\n    },\n    stringify: ({ value }) => value?.toISOString().replace(/(T00:00:00)?\\.000Z$/, '') ?? ''\n};\n\nexports.floatTime = floatTime;\nexports.intTime = intTime;\nexports.timestamp = timestamp;\n", "'use strict';\n\nvar map = require('../common/map.js');\nvar _null = require('../common/null.js');\nvar seq = require('../common/seq.js');\nvar string = require('../common/string.js');\nvar binary = require('./binary.js');\nvar bool = require('./bool.js');\nvar float = require('./float.js');\nvar int = require('./int.js');\nvar merge = require('./merge.js');\nvar omap = require('./omap.js');\nvar pairs = require('./pairs.js');\nvar set = require('./set.js');\nvar timestamp = require('./timestamp.js');\n\nconst schema = [\n    map.map,\n    seq.seq,\n    string.string,\n    _null.nullTag,\n    bool.trueTag,\n    bool.falseTag,\n    int.intBin,\n    int.intOct,\n    int.int,\n    int.intHex,\n    float.floatNaN,\n    float.floatExp,\n    float.float,\n    binary.binary,\n    merge.merge,\n    omap.omap,\n    pairs.pairs,\n    set.set,\n    timestamp.intTime,\n    timestamp.floatTime,\n    timestamp.timestamp\n];\n\nexports.schema = schema;\n", "'use strict';\n\nvar map = require('./common/map.js');\nvar _null = require('./common/null.js');\nvar seq = require('./common/seq.js');\nvar string = require('./common/string.js');\nvar bool = require('./core/bool.js');\nvar float = require('./core/float.js');\nvar int = require('./core/int.js');\nvar schema = require('./core/schema.js');\nvar schema$1 = require('./json/schema.js');\nvar binary = require('./yaml-1.1/binary.js');\nvar merge = require('./yaml-1.1/merge.js');\nvar omap = require('./yaml-1.1/omap.js');\nvar pairs = require('./yaml-1.1/pairs.js');\nvar schema$2 = require('./yaml-1.1/schema.js');\nvar set = require('./yaml-1.1/set.js');\nvar timestamp = require('./yaml-1.1/timestamp.js');\n\nconst schemas = new Map([\n    ['core', schema.schema],\n    ['failsafe', [map.map, seq.seq, string.string]],\n    ['json', schema$1.schema],\n    ['yaml11', schema$2.schema],\n    ['yaml-1.1', schema$2.schema]\n]);\nconst tagsByName = {\n    binary: binary.binary,\n    bool: bool.boolTag,\n    float: float.float,\n    floatExp: float.floatExp,\n    floatNaN: float.floatNaN,\n    floatTime: timestamp.floatTime,\n    int: int.int,\n    intHex: int.intHex,\n    intOct: int.intOct,\n    intTime: timestamp.intTime,\n    map: map.map,\n    merge: merge.merge,\n    null: _null.nullTag,\n    omap: omap.omap,\n    pairs: pairs.pairs,\n    seq: seq.seq,\n    set: set.set,\n    timestamp: timestamp.timestamp\n};\nconst coreKnownTags = {\n    'tag:yaml.org,2002:binary': binary.binary,\n    'tag:yaml.org,2002:merge': merge.merge,\n    'tag:yaml.org,2002:omap': omap.omap,\n    'tag:yaml.org,2002:pairs': pairs.pairs,\n    'tag:yaml.org,2002:set': set.set,\n    'tag:yaml.org,2002:timestamp': timestamp.timestamp\n};\nfunction getTags(customTags, schemaName, addMergeTag) {\n    const schemaTags = schemas.get(schemaName);\n    if (schemaTags && !customTags) {\n        return addMergeTag && !schemaTags.includes(merge.merge)\n            ? schemaTags.concat(merge.merge)\n            : schemaTags.slice();\n    }\n    let tags = schemaTags;\n    if (!tags) {\n        if (Array.isArray(customTags))\n            tags = [];\n        else {\n            const keys = Array.from(schemas.keys())\n                .filter(key => key !== 'yaml11')\n                .map(key => JSON.stringify(key))\n                .join(', ');\n            throw new Error(`Unknown schema \"${schemaName}\"; use one of ${keys} or define customTags array`);\n        }\n    }\n    if (Array.isArray(customTags)) {\n        for (const tag of customTags)\n            tags = tags.concat(tag);\n    }\n    else if (typeof customTags === 'function') {\n        tags = customTags(tags.slice());\n    }\n    if (addMergeTag)\n        tags = tags.concat(merge.merge);\n    return tags.reduce((tags, tag) => {\n        const tagObj = typeof tag === 'string' ? tagsByName[tag] : tag;\n        if (!tagObj) {\n            const tagName = JSON.stringify(tag);\n            const keys = Object.keys(tagsByName)\n                .map(key => JSON.stringify(key))\n                .join(', ');\n            throw new Error(`Unknown custom tag ${tagName}; use one of ${keys}`);\n        }\n        if (!tags.includes(tagObj))\n            tags.push(tagObj);\n        return tags;\n    }, []);\n}\n\nexports.coreKnownTags = coreKnownTags;\nexports.getTags = getTags;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar map = require('./common/map.js');\nvar seq = require('./common/seq.js');\nvar string = require('./common/string.js');\nvar tags = require('./tags.js');\n\nconst sortMapEntriesByKey = (a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0;\nclass Schema {\n    constructor({ compat, customTags, merge, resolveKnownTags, schema, sortMapEntries, toStringDefaults }) {\n        this.compat = Array.isArray(compat)\n            ? tags.getTags(compat, 'compat')\n            : compat\n                ? tags.getTags(null, compat)\n                : null;\n        this.name = (typeof schema === 'string' && schema) || 'core';\n        this.knownTags = resolveKnownTags ? tags.coreKnownTags : {};\n        this.tags = tags.getTags(customTags, this.name, merge);\n        this.toStringOptions = toStringDefaults ?? null;\n        Object.defineProperty(this, identity.MAP, { value: map.map });\n        Object.defineProperty(this, identity.SCALAR, { value: string.string });\n        Object.defineProperty(this, identity.SEQ, { value: seq.seq });\n        // Used by createMap()\n        this.sortMapEntries =\n            typeof sortMapEntries === 'function'\n                ? sortMapEntries\n                : sortMapEntries === true\n                    ? sortMapEntriesByKey\n                    : null;\n    }\n    clone() {\n        const copy = Object.create(Schema.prototype, Object.getOwnPropertyDescriptors(this));\n        copy.tags = this.tags.slice();\n        return copy;\n    }\n}\n\nexports.Schema = Schema;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar stringify = require('./stringify.js');\nvar stringifyComment = require('./stringifyComment.js');\n\nfunction stringifyDocument(doc, options) {\n    const lines = [];\n    let hasDirectives = options.directives === true;\n    if (options.directives !== false && doc.directives) {\n        const dir = doc.directives.toString(doc);\n        if (dir) {\n            lines.push(dir);\n            hasDirectives = true;\n        }\n        else if (doc.directives.docStart)\n            hasDirectives = true;\n    }\n    if (hasDirectives)\n        lines.push('---');\n    const ctx = stringify.createStringifyContext(doc, options);\n    const { commentString } = ctx.options;\n    if (doc.commentBefore) {\n        if (lines.length !== 1)\n            lines.unshift('');\n        const cs = commentString(doc.commentBefore);\n        lines.unshift(stringifyComment.indentComment(cs, ''));\n    }\n    let chompKeep = false;\n    let contentComment = null;\n    if (doc.contents) {\n        if (identity.isNode(doc.contents)) {\n            if (doc.contents.spaceBefore && hasDirectives)\n                lines.push('');\n            if (doc.contents.commentBefore) {\n                const cs = commentString(doc.contents.commentBefore);\n                lines.push(stringifyComment.indentComment(cs, ''));\n            }\n            // top-level block scalars need to be indented if followed by a comment\n            ctx.forceBlockIndent = !!doc.comment;\n            contentComment = doc.contents.comment;\n        }\n        const onChompKeep = contentComment ? undefined : () => (chompKeep = true);\n        let body = stringify.stringify(doc.contents, ctx, () => (contentComment = null), onChompKeep);\n        if (contentComment)\n            body += stringifyComment.lineComment(body, '', commentString(contentComment));\n        if ((body[0] === '|' || body[0] === '>') &&\n            lines[lines.length - 1] === '---') {\n            // Top-level block scalars with a preceding doc marker ought to use the\n            // same line for their header.\n            lines[lines.length - 1] = `--- ${body}`;\n        }\n        else\n            lines.push(body);\n    }\n    else {\n        lines.push(stringify.stringify(doc.contents, ctx));\n    }\n    if (doc.directives?.docEnd) {\n        if (doc.comment) {\n            const cs = commentString(doc.comment);\n            if (cs.includes('\\n')) {\n                lines.push('...');\n                lines.push(stringifyComment.indentComment(cs, ''));\n            }\n            else {\n                lines.push(`... ${cs}`);\n            }\n        }\n        else {\n            lines.push('...');\n        }\n    }\n    else {\n        let dc = doc.comment;\n        if (dc && chompKeep)\n            dc = dc.replace(/^\\n+/, '');\n        if (dc) {\n            if ((!chompKeep || contentComment) && lines[lines.length - 1] !== '')\n                lines.push('');\n            lines.push(stringifyComment.indentComment(commentString(dc), ''));\n        }\n    }\n    return lines.join('\\n') + '\\n';\n}\n\nexports.stringifyDocument = stringifyDocument;\n", "'use strict';\n\nvar Alias = require('../nodes/Alias.js');\nvar Collection = require('../nodes/Collection.js');\nvar identity = require('../nodes/identity.js');\nvar Pair = require('../nodes/Pair.js');\nvar toJS = require('../nodes/toJS.js');\nvar Schema = require('../schema/Schema.js');\nvar stringifyDocument = require('../stringify/stringifyDocument.js');\nvar anchors = require('./anchors.js');\nvar applyReviver = require('./applyReviver.js');\nvar createNode = require('./createNode.js');\nvar directives = require('./directives.js');\n\nclass Document {\n    constructor(value, replacer, options) {\n        /** A comment before this Document */\n        this.commentBefore = null;\n        /** A comment immediately after this Document */\n        this.comment = null;\n        /** Errors encountered during parsing. */\n        this.errors = [];\n        /** Warnings encountered during parsing. */\n        this.warnings = [];\n        Object.defineProperty(this, identity.NODE_TYPE, { value: identity.DOC });\n        let _replacer = null;\n        if (typeof replacer === 'function' || Array.isArray(replacer)) {\n            _replacer = replacer;\n        }\n        else if (options === undefined && replacer) {\n            options = replacer;\n            replacer = undefined;\n        }\n        const opt = Object.assign({\n            intAsBigInt: false,\n            keepSourceTokens: false,\n            logLevel: 'warn',\n            prettyErrors: true,\n            strict: true,\n            stringKeys: false,\n            uniqueKeys: true,\n            version: '1.2'\n        }, options);\n        this.options = opt;\n        let { version } = opt;\n        if (options?._directives) {\n            this.directives = options._directives.atDocument();\n            if (this.directives.yaml.explicit)\n                version = this.directives.yaml.version;\n        }\n        else\n            this.directives = new directives.Directives({ version });\n        this.setSchema(version, options);\n        // @ts-expect-error We can't really know that this matches Contents.\n        this.contents =\n            value === undefined ? null : this.createNode(value, _replacer, options);\n    }\n    /**\n     * Create a deep copy of this Document and its contents.\n     *\n     * Custom Node values that inherit from `Object` still refer to their original instances.\n     */\n    clone() {\n        const copy = Object.create(Document.prototype, {\n            [identity.NODE_TYPE]: { value: identity.DOC }\n        });\n        copy.commentBefore = this.commentBefore;\n        copy.comment = this.comment;\n        copy.errors = this.errors.slice();\n        copy.warnings = this.warnings.slice();\n        copy.options = Object.assign({}, this.options);\n        if (this.directives)\n            copy.directives = this.directives.clone();\n        copy.schema = this.schema.clone();\n        // @ts-expect-error We can't really know that this matches Contents.\n        copy.contents = identity.isNode(this.contents)\n            ? this.contents.clone(copy.schema)\n            : this.contents;\n        if (this.range)\n            copy.range = this.range.slice();\n        return copy;\n    }\n    /** Adds a value to the document. */\n    add(value) {\n        if (assertCollection(this.contents))\n            this.contents.add(value);\n    }\n    /** Adds a value to the document. */\n    addIn(path, value) {\n        if (assertCollection(this.contents))\n            this.contents.addIn(path, value);\n    }\n    /**\n     * Create a new `Alias` node, ensuring that the target `node` has the required anchor.\n     *\n     * If `node` already has an anchor, `name` is ignored.\n     * Otherwise, the `node.anchor` value will be set to `name`,\n     * or if an anchor with that name is already present in the document,\n     * `name` will be used as a prefix for a new unique anchor.\n     * If `name` is undefined, the generated anchor will use 'a' as a prefix.\n     */\n    createAlias(node, name) {\n        if (!node.anchor) {\n            const prev = anchors.anchorNames(this);\n            node.anchor =\n                // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n                !name || prev.has(name) ? anchors.findNewAnchor(name || 'a', prev) : name;\n        }\n        return new Alias.Alias(node.anchor);\n    }\n    createNode(value, replacer, options) {\n        let _replacer = undefined;\n        if (typeof replacer === 'function') {\n            value = replacer.call({ '': value }, '', value);\n            _replacer = replacer;\n        }\n        else if (Array.isArray(replacer)) {\n            const keyToStr = (v) => typeof v === 'number' || v instanceof String || v instanceof Number;\n            const asStr = replacer.filter(keyToStr).map(String);\n            if (asStr.length > 0)\n                replacer = replacer.concat(asStr);\n            _replacer = replacer;\n        }\n        else if (options === undefined && replacer) {\n            options = replacer;\n            replacer = undefined;\n        }\n        const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {};\n        const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors(this, \n        // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n        anchorPrefix || 'a');\n        const ctx = {\n            aliasDuplicateObjects: aliasDuplicateObjects ?? true,\n            keepUndefined: keepUndefined ?? false,\n            onAnchor,\n            onTagObj,\n            replacer: _replacer,\n            schema: this.schema,\n            sourceObjects\n        };\n        const node = createNode.createNode(value, tag, ctx);\n        if (flow && identity.isCollection(node))\n            node.flow = true;\n        setAnchors();\n        return node;\n    }\n    /**\n     * Convert a key and a value into a `Pair` using the current schema,\n     * recursively wrapping all values as `Scalar` or `Collection` nodes.\n     */\n    createPair(key, value, options = {}) {\n        const k = this.createNode(key, null, options);\n        const v = this.createNode(value, null, options);\n        return new Pair.Pair(k, v);\n    }\n    /**\n     * Removes a value from the document.\n     * @returns `true` if the item was found and removed.\n     */\n    delete(key) {\n        return assertCollection(this.contents) ? this.contents.delete(key) : false;\n    }\n    /**\n     * Removes a value from the document.\n     * @returns `true` if the item was found and removed.\n     */\n    deleteIn(path) {\n        if (Collection.isEmptyPath(path)) {\n            if (this.contents == null)\n                return false;\n            // @ts-expect-error Presumed impossible if Strict extends false\n            this.contents = null;\n            return true;\n        }\n        return assertCollection(this.contents)\n            ? this.contents.deleteIn(path)\n            : false;\n    }\n    /**\n     * Returns item at `key`, or `undefined` if not found. By default unwraps\n     * scalar values from their surrounding node; to disable set `keepScalar` to\n     * `true` (collections are always returned intact).\n     */\n    get(key, keepScalar) {\n        return identity.isCollection(this.contents)\n            ? this.contents.get(key, keepScalar)\n            : undefined;\n    }\n    /**\n     * Returns item at `path`, or `undefined` if not found. By default unwraps\n     * scalar values from their surrounding node; to disable set `keepScalar` to\n     * `true` (collections are always returned intact).\n     */\n    getIn(path, keepScalar) {\n        if (Collection.isEmptyPath(path))\n            return !keepScalar && identity.isScalar(this.contents)\n                ? this.contents.value\n                : this.contents;\n        return identity.isCollection(this.contents)\n            ? this.contents.getIn(path, keepScalar)\n            : undefined;\n    }\n    /**\n     * Checks if the document includes a value with the key `key`.\n     */\n    has(key) {\n        return identity.isCollection(this.contents) ? this.contents.has(key) : false;\n    }\n    /**\n     * Checks if the document includes a value at `path`.\n     */\n    hasIn(path) {\n        if (Collection.isEmptyPath(path))\n            return this.contents !== undefined;\n        return identity.isCollection(this.contents) ? this.contents.hasIn(path) : false;\n    }\n    /**\n     * Sets a value in this document. For `!!set`, `value` needs to be a\n     * boolean to add/remove the item from the set.\n     */\n    set(key, value) {\n        if (this.contents == null) {\n            // @ts-expect-error We can't really know that this matches Contents.\n            this.contents = Collection.collectionFromPath(this.schema, [key], value);\n        }\n        else if (assertCollection(this.contents)) {\n            this.contents.set(key, value);\n        }\n    }\n    /**\n     * Sets a value in this document. For `!!set`, `value` needs to be a\n     * boolean to add/remove the item from the set.\n     */\n    setIn(path, value) {\n        if (Collection.isEmptyPath(path)) {\n            // @ts-expect-error We can't really know that this matches Contents.\n            this.contents = value;\n        }\n        else if (this.contents == null) {\n            // @ts-expect-error We can't really know that this matches Contents.\n            this.contents = Collection.collectionFromPath(this.schema, Array.from(path), value);\n        }\n        else if (assertCollection(this.contents)) {\n            this.contents.setIn(path, value);\n        }\n    }\n    /**\n     * Change the YAML version and schema used by the document.\n     * A `null` version disables support for directives, explicit tags, anchors, and aliases.\n     * It also requires the `schema` option to be given as a `Schema` instance value.\n     *\n     * Overrides all previously set schema options.\n     */\n    setSchema(version, options = {}) {\n        if (typeof version === 'number')\n            version = String(version);\n        let opt;\n        switch (version) {\n            case '1.1':\n                if (this.directives)\n                    this.directives.yaml.version = '1.1';\n                else\n                    this.directives = new directives.Directives({ version: '1.1' });\n                opt = { resolveKnownTags: false, schema: 'yaml-1.1' };\n                break;\n            case '1.2':\n            case 'next':\n                if (this.directives)\n                    this.directives.yaml.version = version;\n                else\n                    this.directives = new directives.Directives({ version });\n                opt = { resolveKnownTags: true, schema: 'core' };\n                break;\n            case null:\n                if (this.directives)\n                    delete this.directives;\n                opt = null;\n                break;\n            default: {\n                const sv = JSON.stringify(version);\n                throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`);\n            }\n        }\n        // Not using `instanceof Schema` to allow for duck typing\n        if (options.schema instanceof Object)\n            this.schema = options.schema;\n        else if (opt)\n            this.schema = new Schema.Schema(Object.assign(opt, options));\n        else\n            throw new Error(`With a null YAML version, the { schema: Schema } option is required`);\n    }\n    // json & jsonArg are only used from toJSON()\n    toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {\n        const ctx = {\n            anchors: new Map(),\n            doc: this,\n            keep: !json,\n            mapAsMap: mapAsMap === true,\n            mapKeyWarned: false,\n            maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100\n        };\n        const res = toJS.toJS(this.contents, jsonArg ?? '', ctx);\n        if (typeof onAnchor === 'function')\n            for (const { count, res } of ctx.anchors.values())\n                onAnchor(res, count);\n        return typeof reviver === 'function'\n            ? applyReviver.applyReviver(reviver, { '': res }, '', res)\n            : res;\n    }\n    /**\n     * A JSON representation of the document `contents`.\n     *\n     * @param jsonArg Used by `JSON.stringify` to indicate the array index or\n     *   property name.\n     */\n    toJSON(jsonArg, onAnchor) {\n        return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor });\n    }\n    /** A YAML representation of the document. */\n    toString(options = {}) {\n        if (this.errors.length > 0)\n            throw new Error('Document with errors cannot be stringified');\n        if ('indent' in options &&\n            (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) {\n            const s = JSON.stringify(options.indent);\n            throw new Error(`\"indent\" option must be a positive integer, not ${s}`);\n        }\n        return stringifyDocument.stringifyDocument(this, options);\n    }\n}\nfunction assertCollection(contents) {\n    if (identity.isCollection(contents))\n        return true;\n    throw new Error('Expected a YAML collection as document contents');\n}\n\nexports.Document = Document;\n", "'use strict';\n\nclass YAMLError extends Error {\n    constructor(name, pos, code, message) {\n        super();\n        this.name = name;\n        this.code = code;\n        this.message = message;\n        this.pos = pos;\n    }\n}\nclass YAMLParseError extends YAMLError {\n    constructor(pos, code, message) {\n        super('YAMLParseError', pos, code, message);\n    }\n}\nclass YAMLWarning extends YAMLError {\n    constructor(pos, code, message) {\n        super('YAMLWarning', pos, code, message);\n    }\n}\nconst prettifyError = (src, lc) => (error) => {\n    if (error.pos[0] === -1)\n        return;\n    error.linePos = error.pos.map(pos => lc.linePos(pos));\n    const { line, col } = error.linePos[0];\n    error.message += ` at line ${line}, column ${col}`;\n    let ci = col - 1;\n    let lineStr = src\n        .substring(lc.lineStarts[line - 1], lc.lineStarts[line])\n        .replace(/[\\n\\r]+$/, '');\n    // Trim to max 80 chars, keeping col position near the middle\n    if (ci >= 60 && lineStr.length > 80) {\n        const trimStart = Math.min(ci - 39, lineStr.length - 79);\n        lineStr = '\u2026' + lineStr.substring(trimStart);\n        ci -= trimStart - 1;\n    }\n    if (lineStr.length > 80)\n        lineStr = lineStr.substring(0, 79) + '\u2026';\n    // Include previous line in context if pointing at line start\n    if (line > 1 && /^ *$/.test(lineStr.substring(0, ci))) {\n        // Regexp won't match if start is trimmed\n        let prev = src.substring(lc.lineStarts[line - 2], lc.lineStarts[line - 1]);\n        if (prev.length > 80)\n            prev = prev.substring(0, 79) + '\u2026\\n';\n        lineStr = prev + lineStr;\n    }\n    if (/[^ ]/.test(lineStr)) {\n        let count = 1;\n        const end = error.linePos[1];\n        if (end?.line === line && end.col > col) {\n            count = Math.max(1, Math.min(end.col - col, 80 - ci));\n        }\n        const pointer = ' '.repeat(ci) + '^'.repeat(count);\n        error.message += `:\\n\\n${lineStr}\\n${pointer}\\n`;\n    }\n};\n\nexports.YAMLError = YAMLError;\nexports.YAMLParseError = YAMLParseError;\nexports.YAMLWarning = YAMLWarning;\nexports.prettifyError = prettifyError;\n", "'use strict';\n\nfunction resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {\n    let spaceBefore = false;\n    let atNewline = startOnNewline;\n    let hasSpace = startOnNewline;\n    let comment = '';\n    let commentSep = '';\n    let hasNewline = false;\n    let reqSpace = false;\n    let tab = null;\n    let anchor = null;\n    let tag = null;\n    let newlineAfterProp = null;\n    let comma = null;\n    let found = null;\n    let start = null;\n    for (const token of tokens) {\n        if (reqSpace) {\n            if (token.type !== 'space' &&\n                token.type !== 'newline' &&\n                token.type !== 'comma')\n                onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');\n            reqSpace = false;\n        }\n        if (tab) {\n            if (atNewline && token.type !== 'comment' && token.type !== 'newline') {\n                onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');\n            }\n            tab = null;\n        }\n        switch (token.type) {\n            case 'space':\n                // At the doc level, tabs at line start may be parsed\n                // as leading white space rather than indentation.\n                // In a flow collection, only the parser handles indent.\n                if (!flow &&\n                    (indicator !== 'doc-start' || next?.type !== 'flow-collection') &&\n                    token.source.includes('\\t')) {\n                    tab = token;\n                }\n                hasSpace = true;\n                break;\n            case 'comment': {\n                if (!hasSpace)\n                    onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters');\n                const cb = token.source.substring(1) || ' ';\n                if (!comment)\n                    comment = cb;\n                else\n                    comment += commentSep + cb;\n                commentSep = '';\n                atNewline = false;\n                break;\n            }\n            case 'newline':\n                if (atNewline) {\n                    if (comment)\n                        comment += token.source;\n                    else if (!found || indicator !== 'seq-item-ind')\n                        spaceBefore = true;\n                }\n                else\n                    commentSep += token.source;\n                atNewline = true;\n                hasNewline = true;\n                if (anchor || tag)\n                    newlineAfterProp = token;\n                hasSpace = true;\n                break;\n            case 'anchor':\n                if (anchor)\n                    onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');\n                if (token.source.endsWith(':'))\n                    onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true);\n                anchor = token;\n                start ?? (start = token.offset);\n                atNewline = false;\n                hasSpace = false;\n                reqSpace = true;\n                break;\n            case 'tag': {\n                if (tag)\n                    onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag');\n                tag = token;\n                start ?? (start = token.offset);\n                atNewline = false;\n                hasSpace = false;\n                reqSpace = true;\n                break;\n            }\n            case indicator:\n                // Could here handle preceding comments differently\n                if (anchor || tag)\n                    onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);\n                if (found)\n                    onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);\n                found = token;\n                atNewline =\n                    indicator === 'seq-item-ind' || indicator === 'explicit-key-ind';\n                hasSpace = false;\n                break;\n            case 'comma':\n                if (flow) {\n                    if (comma)\n                        onError(token, 'UNEXPECTED_TOKEN', `Unexpected , in ${flow}`);\n                    comma = token;\n                    atNewline = false;\n                    hasSpace = false;\n                    break;\n                }\n            // else fallthrough\n            default:\n                onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.type} token`);\n                atNewline = false;\n                hasSpace = false;\n        }\n    }\n    const last = tokens[tokens.length - 1];\n    const end = last ? last.offset + last.source.length : offset;\n    if (reqSpace &&\n        next &&\n        next.type !== 'space' &&\n        next.type !== 'newline' &&\n        next.type !== 'comma' &&\n        (next.type !== 'scalar' || next.source !== '')) {\n        onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');\n    }\n    if (tab &&\n        ((atNewline && tab.indent <= parentIndent) ||\n            next?.type === 'block-map' ||\n            next?.type === 'block-seq'))\n        onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');\n    return {\n        comma,\n        found,\n        spaceBefore,\n        comment,\n        hasNewline,\n        anchor,\n        tag,\n        newlineAfterProp,\n        end,\n        start: start ?? end\n    };\n}\n\nexports.resolveProps = resolveProps;\n", "'use strict';\n\nfunction containsNewline(key) {\n    if (!key)\n        return null;\n    switch (key.type) {\n        case 'alias':\n        case 'scalar':\n        case 'double-quoted-scalar':\n        case 'single-quoted-scalar':\n            if (key.source.includes('\\n'))\n                return true;\n            if (key.end)\n                for (const st of key.end)\n                    if (st.type === 'newline')\n                        return true;\n            return false;\n        case 'flow-collection':\n            for (const it of key.items) {\n                for (const st of it.start)\n                    if (st.type === 'newline')\n                        return true;\n                if (it.sep)\n                    for (const st of it.sep)\n                        if (st.type === 'newline')\n                            return true;\n                if (containsNewline(it.key) || containsNewline(it.value))\n                    return true;\n            }\n            return false;\n        default:\n            return true;\n    }\n}\n\nexports.containsNewline = containsNewline;\n", "'use strict';\n\nvar utilContainsNewline = require('./util-contains-newline.js');\n\nfunction flowIndentCheck(indent, fc, onError) {\n    if (fc?.type === 'flow-collection') {\n        const end = fc.end[0];\n        if (end.indent === indent &&\n            (end.source === ']' || end.source === '}') &&\n            utilContainsNewline.containsNewline(fc)) {\n            const msg = 'Flow end indicator should be more indented than parent';\n            onError(end, 'BAD_INDENT', msg, true);\n        }\n    }\n}\n\nexports.flowIndentCheck = flowIndentCheck;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\n\nfunction mapIncludes(ctx, items, search) {\n    const { uniqueKeys } = ctx.options;\n    if (uniqueKeys === false)\n        return false;\n    const isEqual = typeof uniqueKeys === 'function'\n        ? uniqueKeys\n        : (a, b) => a === b || (identity.isScalar(a) && identity.isScalar(b) && a.value === b.value);\n    return items.some(pair => isEqual(pair.key, search));\n}\n\nexports.mapIncludes = mapIncludes;\n", "'use strict';\n\nvar Pair = require('../nodes/Pair.js');\nvar YAMLMap = require('../nodes/YAMLMap.js');\nvar resolveProps = require('./resolve-props.js');\nvar utilContainsNewline = require('./util-contains-newline.js');\nvar utilFlowIndentCheck = require('./util-flow-indent-check.js');\nvar utilMapIncludes = require('./util-map-includes.js');\n\nconst startColMsg = 'All mapping items must start at the same column';\nfunction resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, tag) {\n    const NodeClass = tag?.nodeClass ?? YAMLMap.YAMLMap;\n    const map = new NodeClass(ctx.schema);\n    if (ctx.atRoot)\n        ctx.atRoot = false;\n    let offset = bm.offset;\n    let commentEnd = null;\n    for (const collItem of bm.items) {\n        const { start, key, sep, value } = collItem;\n        // key properties\n        const keyProps = resolveProps.resolveProps(start, {\n            indicator: 'explicit-key-ind',\n            next: key ?? sep?.[0],\n            offset,\n            onError,\n            parentIndent: bm.indent,\n            startOnNewline: true\n        });\n        const implicitKey = !keyProps.found;\n        if (implicitKey) {\n            if (key) {\n                if (key.type === 'block-seq')\n                    onError(offset, 'BLOCK_AS_IMPLICIT_KEY', 'A block sequence may not be used as an implicit map key');\n                else if ('indent' in key && key.indent !== bm.indent)\n                    onError(offset, 'BAD_INDENT', startColMsg);\n            }\n            if (!keyProps.anchor && !keyProps.tag && !sep) {\n                commentEnd = keyProps.end;\n                if (keyProps.comment) {\n                    if (map.comment)\n                        map.comment += '\\n' + keyProps.comment;\n                    else\n                        map.comment = keyProps.comment;\n                }\n                continue;\n            }\n            if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) {\n                onError(key ?? start[start.length - 1], 'MULTILINE_IMPLICIT_KEY', 'Implicit keys need to be on a single line');\n            }\n        }\n        else if (keyProps.found?.indent !== bm.indent) {\n            onError(offset, 'BAD_INDENT', startColMsg);\n        }\n        // key value\n        ctx.atKey = true;\n        const keyStart = keyProps.end;\n        const keyNode = key\n            ? composeNode(ctx, key, keyProps, onError)\n            : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError);\n        if (ctx.schema.compat)\n            utilFlowIndentCheck.flowIndentCheck(bm.indent, key, onError);\n        ctx.atKey = false;\n        if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))\n            onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');\n        // value properties\n        const valueProps = resolveProps.resolveProps(sep ?? [], {\n            indicator: 'map-value-ind',\n            next: value,\n            offset: keyNode.range[2],\n            onError,\n            parentIndent: bm.indent,\n            startOnNewline: !key || key.type === 'block-scalar'\n        });\n        offset = valueProps.end;\n        if (valueProps.found) {\n            if (implicitKey) {\n                if (value?.type === 'block-map' && !valueProps.hasNewline)\n                    onError(offset, 'BLOCK_AS_IMPLICIT_KEY', 'Nested mappings are not allowed in compact mappings');\n                if (ctx.options.strict &&\n                    keyProps.start < valueProps.found.offset - 1024)\n                    onError(keyNode.range, 'KEY_OVER_1024_CHARS', 'The : indicator must be at most 1024 chars after the start of an implicit block mapping key');\n            }\n            // value value\n            const valueNode = value\n                ? composeNode(ctx, value, valueProps, onError)\n                : composeEmptyNode(ctx, offset, sep, null, valueProps, onError);\n            if (ctx.schema.compat)\n                utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);\n            offset = valueNode.range[2];\n            const pair = new Pair.Pair(keyNode, valueNode);\n            if (ctx.options.keepSourceTokens)\n                pair.srcToken = collItem;\n            map.items.push(pair);\n        }\n        else {\n            // key with no value\n            if (implicitKey)\n                onError(keyNode.range, 'MISSING_CHAR', 'Implicit map keys need to be followed by map values');\n            if (valueProps.comment) {\n                if (keyNode.comment)\n                    keyNode.comment += '\\n' + valueProps.comment;\n                else\n                    keyNode.comment = valueProps.comment;\n            }\n            const pair = new Pair.Pair(keyNode);\n            if (ctx.options.keepSourceTokens)\n                pair.srcToken = collItem;\n            map.items.push(pair);\n        }\n    }\n    if (commentEnd && commentEnd < offset)\n        onError(commentEnd, 'IMPOSSIBLE', 'Map comment with trailing content');\n    map.range = [bm.offset, offset, commentEnd ?? offset];\n    return map;\n}\n\nexports.resolveBlockMap = resolveBlockMap;\n", "'use strict';\n\nvar YAMLSeq = require('../nodes/YAMLSeq.js');\nvar resolveProps = require('./resolve-props.js');\nvar utilFlowIndentCheck = require('./util-flow-indent-check.js');\n\nfunction resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, tag) {\n    const NodeClass = tag?.nodeClass ?? YAMLSeq.YAMLSeq;\n    const seq = new NodeClass(ctx.schema);\n    if (ctx.atRoot)\n        ctx.atRoot = false;\n    if (ctx.atKey)\n        ctx.atKey = false;\n    let offset = bs.offset;\n    let commentEnd = null;\n    for (const { start, value } of bs.items) {\n        const props = resolveProps.resolveProps(start, {\n            indicator: 'seq-item-ind',\n            next: value,\n            offset,\n            onError,\n            parentIndent: bs.indent,\n            startOnNewline: true\n        });\n        if (!props.found) {\n            if (props.anchor || props.tag || value) {\n                if (value?.type === 'block-seq')\n                    onError(props.end, 'BAD_INDENT', 'All sequence items must start at the same column');\n                else\n                    onError(offset, 'MISSING_CHAR', 'Sequence item without - indicator');\n            }\n            else {\n                commentEnd = props.end;\n                if (props.comment)\n                    seq.comment = props.comment;\n                continue;\n            }\n        }\n        const node = value\n            ? composeNode(ctx, value, props, onError)\n            : composeEmptyNode(ctx, props.end, start, null, props, onError);\n        if (ctx.schema.compat)\n            utilFlowIndentCheck.flowIndentCheck(bs.indent, value, onError);\n        offset = node.range[2];\n        seq.items.push(node);\n    }\n    seq.range = [bs.offset, offset, commentEnd ?? offset];\n    return seq;\n}\n\nexports.resolveBlockSeq = resolveBlockSeq;\n", "'use strict';\n\nfunction resolveEnd(end, offset, reqSpace, onError) {\n    let comment = '';\n    if (end) {\n        let hasSpace = false;\n        let sep = '';\n        for (const token of end) {\n            const { source, type } = token;\n            switch (type) {\n                case 'space':\n                    hasSpace = true;\n                    break;\n                case 'comment': {\n                    if (reqSpace && !hasSpace)\n                        onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters');\n                    const cb = source.substring(1) || ' ';\n                    if (!comment)\n                        comment = cb;\n                    else\n                        comment += sep + cb;\n                    sep = '';\n                    break;\n                }\n                case 'newline':\n                    if (comment)\n                        sep += source;\n                    hasSpace = true;\n                    break;\n                default:\n                    onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${type} at node end`);\n            }\n            offset += source.length;\n        }\n    }\n    return { comment, offset };\n}\n\nexports.resolveEnd = resolveEnd;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar Pair = require('../nodes/Pair.js');\nvar YAMLMap = require('../nodes/YAMLMap.js');\nvar YAMLSeq = require('../nodes/YAMLSeq.js');\nvar resolveEnd = require('./resolve-end.js');\nvar resolveProps = require('./resolve-props.js');\nvar utilContainsNewline = require('./util-contains-newline.js');\nvar utilMapIncludes = require('./util-map-includes.js');\n\nconst blockMsg = 'Block collections are not allowed within flow collections';\nconst isBlock = (token) => token && (token.type === 'block-map' || token.type === 'block-seq');\nfunction resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError, tag) {\n    const isMap = fc.start.source === '{';\n    const fcName = isMap ? 'flow map' : 'flow sequence';\n    const NodeClass = (tag?.nodeClass ?? (isMap ? YAMLMap.YAMLMap : YAMLSeq.YAMLSeq));\n    const coll = new NodeClass(ctx.schema);\n    coll.flow = true;\n    const atRoot = ctx.atRoot;\n    if (atRoot)\n        ctx.atRoot = false;\n    if (ctx.atKey)\n        ctx.atKey = false;\n    let offset = fc.offset + fc.start.source.length;\n    for (let i = 0; i < fc.items.length; ++i) {\n        const collItem = fc.items[i];\n        const { start, key, sep, value } = collItem;\n        const props = resolveProps.resolveProps(start, {\n            flow: fcName,\n            indicator: 'explicit-key-ind',\n            next: key ?? sep?.[0],\n            offset,\n            onError,\n            parentIndent: fc.indent,\n            startOnNewline: false\n        });\n        if (!props.found) {\n            if (!props.anchor && !props.tag && !sep && !value) {\n                if (i === 0 && props.comma)\n                    onError(props.comma, 'UNEXPECTED_TOKEN', `Unexpected , in ${fcName}`);\n                else if (i < fc.items.length - 1)\n                    onError(props.start, 'UNEXPECTED_TOKEN', `Unexpected empty item in ${fcName}`);\n                if (props.comment) {\n                    if (coll.comment)\n                        coll.comment += '\\n' + props.comment;\n                    else\n                        coll.comment = props.comment;\n                }\n                offset = props.end;\n                continue;\n            }\n            if (!isMap && ctx.options.strict && utilContainsNewline.containsNewline(key))\n                onError(key, // checked by containsNewline()\n                'MULTILINE_IMPLICIT_KEY', 'Implicit keys of flow sequence pairs need to be on a single line');\n        }\n        if (i === 0) {\n            if (props.comma)\n                onError(props.comma, 'UNEXPECTED_TOKEN', `Unexpected , in ${fcName}`);\n        }\n        else {\n            if (!props.comma)\n                onError(props.start, 'MISSING_CHAR', `Missing , between ${fcName} items`);\n            if (props.comment) {\n                let prevItemComment = '';\n                loop: for (const st of start) {\n                    switch (st.type) {\n                        case 'comma':\n                        case 'space':\n                            break;\n                        case 'comment':\n                            prevItemComment = st.source.substring(1);\n                            break loop;\n                        default:\n                            break loop;\n                    }\n                }\n                if (prevItemComment) {\n                    let prev = coll.items[coll.items.length - 1];\n                    if (identity.isPair(prev))\n                        prev = prev.value ?? prev.key;\n                    if (prev.comment)\n                        prev.comment += '\\n' + prevItemComment;\n                    else\n                        prev.comment = prevItemComment;\n                    props.comment = props.comment.substring(prevItemComment.length + 1);\n                }\n            }\n        }\n        if (!isMap && !sep && !props.found) {\n            // item is a value in a seq\n            // \u2192 key & sep are empty, start does not include ? or :\n            const valueNode = value\n                ? composeNode(ctx, value, props, onError)\n                : composeEmptyNode(ctx, props.end, sep, null, props, onError);\n            coll.items.push(valueNode);\n            offset = valueNode.range[2];\n            if (isBlock(value))\n                onError(valueNode.range, 'BLOCK_IN_FLOW', blockMsg);\n        }\n        else {\n            // item is a key+value pair\n            // key value\n            ctx.atKey = true;\n            const keyStart = props.end;\n            const keyNode = key\n                ? composeNode(ctx, key, props, onError)\n                : composeEmptyNode(ctx, keyStart, start, null, props, onError);\n            if (isBlock(key))\n                onError(keyNode.range, 'BLOCK_IN_FLOW', blockMsg);\n            ctx.atKey = false;\n            // value properties\n            const valueProps = resolveProps.resolveProps(sep ?? [], {\n                flow: fcName,\n                indicator: 'map-value-ind',\n                next: value,\n                offset: keyNode.range[2],\n                onError,\n                parentIndent: fc.indent,\n                startOnNewline: false\n            });\n            if (valueProps.found) {\n                if (!isMap && !props.found && ctx.options.strict) {\n                    if (sep)\n                        for (const st of sep) {\n                            if (st === valueProps.found)\n                                break;\n                            if (st.type === 'newline') {\n                                onError(st, 'MULTILINE_IMPLICIT_KEY', 'Implicit keys of flow sequence pairs need to be on a single line');\n                                break;\n                            }\n                        }\n                    if (props.start < valueProps.found.offset - 1024)\n                        onError(valueProps.found, 'KEY_OVER_1024_CHARS', 'The : indicator must be at most 1024 chars after the start of an implicit flow sequence key');\n                }\n            }\n            else if (value) {\n                if ('source' in value && value.source?.[0] === ':')\n                    onError(value, 'MISSING_CHAR', `Missing space after : in ${fcName}`);\n                else\n                    onError(valueProps.start, 'MISSING_CHAR', `Missing , or : between ${fcName} items`);\n            }\n            // value value\n            const valueNode = value\n                ? composeNode(ctx, value, valueProps, onError)\n                : valueProps.found\n                    ? composeEmptyNode(ctx, valueProps.end, sep, null, valueProps, onError)\n                    : null;\n            if (valueNode) {\n                if (isBlock(value))\n                    onError(valueNode.range, 'BLOCK_IN_FLOW', blockMsg);\n            }\n            else if (valueProps.comment) {\n                if (keyNode.comment)\n                    keyNode.comment += '\\n' + valueProps.comment;\n                else\n                    keyNode.comment = valueProps.comment;\n            }\n            const pair = new Pair.Pair(keyNode, valueNode);\n            if (ctx.options.keepSourceTokens)\n                pair.srcToken = collItem;\n            if (isMap) {\n                const map = coll;\n                if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))\n                    onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');\n                map.items.push(pair);\n            }\n            else {\n                const map = new YAMLMap.YAMLMap(ctx.schema);\n                map.flow = true;\n                map.items.push(pair);\n                const endRange = (valueNode ?? keyNode).range;\n                map.range = [keyNode.range[0], endRange[1], endRange[2]];\n                coll.items.push(map);\n            }\n            offset = valueNode ? valueNode.range[2] : valueProps.end;\n        }\n    }\n    const expectedEnd = isMap ? '}' : ']';\n    const [ce, ...ee] = fc.end;\n    let cePos = offset;\n    if (ce?.source === expectedEnd)\n        cePos = ce.offset + ce.source.length;\n    else {\n        const name = fcName[0].toUpperCase() + fcName.substring(1);\n        const msg = atRoot\n            ? `${name} must end with a ${expectedEnd}`\n            : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`;\n        onError(offset, atRoot ? 'MISSING_CHAR' : 'BAD_INDENT', msg);\n        if (ce && ce.source.length !== 1)\n            ee.unshift(ce);\n    }\n    if (ee.length > 0) {\n        const end = resolveEnd.resolveEnd(ee, cePos, ctx.options.strict, onError);\n        if (end.comment) {\n            if (coll.comment)\n                coll.comment += '\\n' + end.comment;\n            else\n                coll.comment = end.comment;\n        }\n        coll.range = [fc.offset, cePos, end.offset];\n    }\n    else {\n        coll.range = [fc.offset, cePos, cePos];\n    }\n    return coll;\n}\n\nexports.resolveFlowCollection = resolveFlowCollection;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar Scalar = require('../nodes/Scalar.js');\nvar YAMLMap = require('../nodes/YAMLMap.js');\nvar YAMLSeq = require('../nodes/YAMLSeq.js');\nvar resolveBlockMap = require('./resolve-block-map.js');\nvar resolveBlockSeq = require('./resolve-block-seq.js');\nvar resolveFlowCollection = require('./resolve-flow-collection.js');\n\nfunction resolveCollection(CN, ctx, token, onError, tagName, tag) {\n    const coll = token.type === 'block-map'\n        ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag)\n        : token.type === 'block-seq'\n            ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag)\n            : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag);\n    const Coll = coll.constructor;\n    // If we got a tagName matching the class, or the tag name is '!',\n    // then use the tagName from the node class used to create it.\n    if (tagName === '!' || tagName === Coll.tagName) {\n        coll.tag = Coll.tagName;\n        return coll;\n    }\n    if (tagName)\n        coll.tag = tagName;\n    return coll;\n}\nfunction composeCollection(CN, ctx, token, props, onError) {\n    const tagToken = props.tag;\n    const tagName = !tagToken\n        ? null\n        : ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg));\n    if (token.type === 'block-seq') {\n        const { anchor, newlineAfterProp: nl } = props;\n        const lastProp = anchor && tagToken\n            ? anchor.offset > tagToken.offset\n                ? anchor\n                : tagToken\n            : (anchor ?? tagToken);\n        if (lastProp && (!nl || nl.offset < lastProp.offset)) {\n            const message = 'Missing newline after block sequence props';\n            onError(lastProp, 'MISSING_CHAR', message);\n        }\n    }\n    const expType = token.type === 'block-map'\n        ? 'map'\n        : token.type === 'block-seq'\n            ? 'seq'\n            : token.start.source === '{'\n                ? 'map'\n                : 'seq';\n    // shortcut: check if it's a generic YAMLMap or YAMLSeq\n    // before jumping into the custom tag logic.\n    if (!tagToken ||\n        !tagName ||\n        tagName === '!' ||\n        (tagName === YAMLMap.YAMLMap.tagName && expType === 'map') ||\n        (tagName === YAMLSeq.YAMLSeq.tagName && expType === 'seq')) {\n        return resolveCollection(CN, ctx, token, onError, tagName);\n    }\n    let tag = ctx.schema.tags.find(t => t.tag === tagName && t.collection === expType);\n    if (!tag) {\n        const kt = ctx.schema.knownTags[tagName];\n        if (kt?.collection === expType) {\n            ctx.schema.tags.push(Object.assign({}, kt, { default: false }));\n            tag = kt;\n        }\n        else {\n            if (kt) {\n                onError(tagToken, 'BAD_COLLECTION_TYPE', `${kt.tag} used for ${expType} collection, but expects ${kt.collection ?? 'scalar'}`, true);\n            }\n            else {\n                onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, true);\n            }\n            return resolveCollection(CN, ctx, token, onError, tagName);\n        }\n    }\n    const coll = resolveCollection(CN, ctx, token, onError, tagName, tag);\n    const res = tag.resolve?.(coll, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg), ctx.options) ?? coll;\n    const node = identity.isNode(res)\n        ? res\n        : new Scalar.Scalar(res);\n    node.range = coll.range;\n    node.tag = tagName;\n    if (tag?.format)\n        node.format = tag.format;\n    return node;\n}\n\nexports.composeCollection = composeCollection;\n", "'use strict';\n\nvar Scalar = require('../nodes/Scalar.js');\n\nfunction resolveBlockScalar(ctx, scalar, onError) {\n    const start = scalar.offset;\n    const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError);\n    if (!header)\n        return { value: '', type: null, comment: '', range: [start, start, start] };\n    const type = header.mode === '>' ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL;\n    const lines = scalar.source ? splitLines(scalar.source) : [];\n    // determine the end of content & start of chomping\n    let chompStart = lines.length;\n    for (let i = lines.length - 1; i >= 0; --i) {\n        const content = lines[i][1];\n        if (content === '' || content === '\\r')\n            chompStart = i;\n        else\n            break;\n    }\n    // shortcut for empty contents\n    if (chompStart === 0) {\n        const value = header.chomp === '+' && lines.length > 0\n            ? '\\n'.repeat(Math.max(1, lines.length - 1))\n            : '';\n        let end = start + header.length;\n        if (scalar.source)\n            end += scalar.source.length;\n        return { value, type, comment: header.comment, range: [start, end, end] };\n    }\n    // find the indentation level to trim from start\n    let trimIndent = scalar.indent + header.indent;\n    let offset = scalar.offset + header.length;\n    let contentStart = 0;\n    for (let i = 0; i < chompStart; ++i) {\n        const [indent, content] = lines[i];\n        if (content === '' || content === '\\r') {\n            if (header.indent === 0 && indent.length > trimIndent)\n                trimIndent = indent.length;\n        }\n        else {\n            if (indent.length < trimIndent) {\n                const message = 'Block scalars with more-indented leading empty lines must use an explicit indentation indicator';\n                onError(offset + indent.length, 'MISSING_CHAR', message);\n            }\n            if (header.indent === 0)\n                trimIndent = indent.length;\n            contentStart = i;\n            if (trimIndent === 0 && !ctx.atRoot) {\n                const message = 'Block scalar values in collections must be indented';\n                onError(offset, 'BAD_INDENT', message);\n            }\n            break;\n        }\n        offset += indent.length + content.length + 1;\n    }\n    // include trailing more-indented empty lines in content\n    for (let i = lines.length - 1; i >= chompStart; --i) {\n        if (lines[i][0].length > trimIndent)\n            chompStart = i + 1;\n    }\n    let value = '';\n    let sep = '';\n    let prevMoreIndented = false;\n    // leading whitespace is kept intact\n    for (let i = 0; i < contentStart; ++i)\n        value += lines[i][0].slice(trimIndent) + '\\n';\n    for (let i = contentStart; i < chompStart; ++i) {\n        let [indent, content] = lines[i];\n        offset += indent.length + content.length + 1;\n        const crlf = content[content.length - 1] === '\\r';\n        if (crlf)\n            content = content.slice(0, -1);\n        /* istanbul ignore if already caught in lexer */\n        if (content && indent.length < trimIndent) {\n            const src = header.indent\n                ? 'explicit indentation indicator'\n                : 'first line';\n            const message = `Block scalar lines must not be less indented than their ${src}`;\n            onError(offset - content.length - (crlf ? 2 : 1), 'BAD_INDENT', message);\n            indent = '';\n        }\n        if (type === Scalar.Scalar.BLOCK_LITERAL) {\n            value += sep + indent.slice(trimIndent) + content;\n            sep = '\\n';\n        }\n        else if (indent.length > trimIndent || content[0] === '\\t') {\n            // more-indented content within a folded block\n            if (sep === ' ')\n                sep = '\\n';\n            else if (!prevMoreIndented && sep === '\\n')\n                sep = '\\n\\n';\n            value += sep + indent.slice(trimIndent) + content;\n            sep = '\\n';\n            prevMoreIndented = true;\n        }\n        else if (content === '') {\n            // empty line\n            if (sep === '\\n')\n                value += '\\n';\n            else\n                sep = '\\n';\n        }\n        else {\n            value += sep + content;\n            sep = ' ';\n            prevMoreIndented = false;\n        }\n    }\n    switch (header.chomp) {\n        case '-':\n            break;\n        case '+':\n            for (let i = chompStart; i < lines.length; ++i)\n                value += '\\n' + lines[i][0].slice(trimIndent);\n            if (value[value.length - 1] !== '\\n')\n                value += '\\n';\n            break;\n        default:\n            value += '\\n';\n    }\n    const end = start + header.length + scalar.source.length;\n    return { value, type, comment: header.comment, range: [start, end, end] };\n}\nfunction parseBlockScalarHeader({ offset, props }, strict, onError) {\n    /* istanbul ignore if should not happen */\n    if (props[0].type !== 'block-scalar-header') {\n        onError(props[0], 'IMPOSSIBLE', 'Block scalar header not found');\n        return null;\n    }\n    const { source } = props[0];\n    const mode = source[0];\n    let indent = 0;\n    let chomp = '';\n    let error = -1;\n    for (let i = 1; i < source.length; ++i) {\n        const ch = source[i];\n        if (!chomp && (ch === '-' || ch === '+'))\n            chomp = ch;\n        else {\n            const n = Number(ch);\n            if (!indent && n)\n                indent = n;\n            else if (error === -1)\n                error = offset + i;\n        }\n    }\n    if (error !== -1)\n        onError(error, 'UNEXPECTED_TOKEN', `Block scalar header includes extra characters: ${source}`);\n    let hasSpace = false;\n    let comment = '';\n    let length = source.length;\n    for (let i = 1; i < props.length; ++i) {\n        const token = props[i];\n        switch (token.type) {\n            case 'space':\n                hasSpace = true;\n            // fallthrough\n            case 'newline':\n                length += token.source.length;\n                break;\n            case 'comment':\n                if (strict && !hasSpace) {\n                    const message = 'Comments must be separated from other tokens by white space characters';\n                    onError(token, 'MISSING_CHAR', message);\n                }\n                length += token.source.length;\n                comment = token.source.substring(1);\n                break;\n            case 'error':\n                onError(token, 'UNEXPECTED_TOKEN', token.message);\n                length += token.source.length;\n                break;\n            /* istanbul ignore next should not happen */\n            default: {\n                const message = `Unexpected token in block scalar header: ${token.type}`;\n                onError(token, 'UNEXPECTED_TOKEN', message);\n                const ts = token.source;\n                if (ts && typeof ts === 'string')\n                    length += ts.length;\n            }\n        }\n    }\n    return { mode, indent, chomp, comment, length };\n}\n/** @returns Array of lines split up as `[indent, content]` */\nfunction splitLines(source) {\n    const split = source.split(/\\n( *)/);\n    const first = split[0];\n    const m = first.match(/^( *)/);\n    const line0 = m?.[1]\n        ? [m[1], first.slice(m[1].length)]\n        : ['', first];\n    const lines = [line0];\n    for (let i = 1; i < split.length; i += 2)\n        lines.push([split[i], split[i + 1]]);\n    return lines;\n}\n\nexports.resolveBlockScalar = resolveBlockScalar;\n", "'use strict';\n\nvar Scalar = require('../nodes/Scalar.js');\nvar resolveEnd = require('./resolve-end.js');\n\nfunction resolveFlowScalar(scalar, strict, onError) {\n    const { offset, type, source, end } = scalar;\n    let _type;\n    let value;\n    const _onError = (rel, code, msg) => onError(offset + rel, code, msg);\n    switch (type) {\n        case 'scalar':\n            _type = Scalar.Scalar.PLAIN;\n            value = plainValue(source, _onError);\n            break;\n        case 'single-quoted-scalar':\n            _type = Scalar.Scalar.QUOTE_SINGLE;\n            value = singleQuotedValue(source, _onError);\n            break;\n        case 'double-quoted-scalar':\n            _type = Scalar.Scalar.QUOTE_DOUBLE;\n            value = doubleQuotedValue(source, _onError);\n            break;\n        /* istanbul ignore next should not happen */\n        default:\n            onError(scalar, 'UNEXPECTED_TOKEN', `Expected a flow scalar value, but found: ${type}`);\n            return {\n                value: '',\n                type: null,\n                comment: '',\n                range: [offset, offset + source.length, offset + source.length]\n            };\n    }\n    const valueEnd = offset + source.length;\n    const re = resolveEnd.resolveEnd(end, valueEnd, strict, onError);\n    return {\n        value,\n        type: _type,\n        comment: re.comment,\n        range: [offset, valueEnd, re.offset]\n    };\n}\nfunction plainValue(source, onError) {\n    let badChar = '';\n    switch (source[0]) {\n        /* istanbul ignore next should not happen */\n        case '\\t':\n            badChar = 'a tab character';\n            break;\n        case ',':\n            badChar = 'flow indicator character ,';\n            break;\n        case '%':\n            badChar = 'directive indicator character %';\n            break;\n        case '|':\n        case '>': {\n            badChar = `block scalar indicator ${source[0]}`;\n            break;\n        }\n        case '@':\n        case '`': {\n            badChar = `reserved character ${source[0]}`;\n            break;\n        }\n    }\n    if (badChar)\n        onError(0, 'BAD_SCALAR_START', `Plain value cannot start with ${badChar}`);\n    return unfoldLines(source);\n}\nfunction singleQuotedValue(source, onError) {\n    if (source[source.length - 1] !== \"'\" || source.length === 1)\n        onError(source.length, 'MISSING_CHAR', \"Missing closing 'quote\");\n    return unfoldLines(source.slice(1, -1)).replace(/''/g, \"'\");\n}\nfunction unfoldLines(source) {\n    const line = /(.*?)\\r?\\n/sy;\n    let match = line.exec(source);\n    if (!match)\n        return source;\n    /**\n     * The negative lookbehinds in these RegExps are to\n     * prevent causing a polynomial search time in certain cases.\n     *\n     * The try-catch is for Safari < 16.4 and other old browsers:\n     * https://caniuse.com/js-regexp-lookbehind\n     */\n    let trimEnd, trimBoth;\n    try {\n        trimEnd = new RegExp('(?<![ \\t])[ \\t]+$');\n        trimBoth = new RegExp('^[ \\t]+|(?<![ \\t])[ \\t]+$', 'g');\n    }\n    catch {\n        trimEnd = /[ \\t]+$/;\n        trimBoth = /^[ \\t]+|[ \\t]+$/g;\n    }\n    let res = match[1].replace(trimEnd, '');\n    let sep = ' ';\n    let pos = line.lastIndex;\n    while ((match = line.exec(source))) {\n        const lm = match[1].replace(trimBoth, '');\n        if (lm === '') {\n            if (sep === '\\n')\n                res += sep;\n            else\n                sep = '\\n';\n        }\n        else {\n            res += sep + lm;\n            sep = ' ';\n        }\n        pos = line.lastIndex;\n    }\n    const last = /[ \\t]*(.*)/sy;\n    last.lastIndex = pos;\n    match = last.exec(source);\n    return res + sep + (match?.[1] ?? '');\n}\nfunction doubleQuotedValue(source, onError) {\n    let res = '';\n    for (let i = 1; i < source.length - 1; ++i) {\n        const ch = source[i];\n        if (ch === '\\r' && source[i + 1] === '\\n')\n            continue;\n        if (ch === '\\n') {\n            const { fold, offset } = foldNewline(source, i);\n            res += fold;\n            i = offset;\n        }\n        else if (ch === '\\\\') {\n            let next = source[++i];\n            const cc = escapeCodes[next];\n            if (cc)\n                res += cc;\n            else if (next === '\\n') {\n                // skip escaped newlines, but still trim the following line\n                next = source[i + 1];\n                while (next === ' ' || next === '\\t')\n                    next = source[++i + 1];\n            }\n            else if (next === '\\r' && source[i + 1] === '\\n') {\n                // skip escaped CRLF newlines, but still trim the following line\n                next = source[++i + 1];\n                while (next === ' ' || next === '\\t')\n                    next = source[++i + 1];\n            }\n            else if (next === 'x' || next === 'u' || next === 'U') {\n                const length = next === 'x' ? 2 : next === 'u' ? 4 : 8;\n                res += parseCharCode(source, i + 1, length, onError);\n                i += length;\n            }\n            else {\n                const raw = source.substr(i - 1, 2);\n                onError(i - 1, 'BAD_DQ_ESCAPE', `Invalid escape sequence ${raw}`);\n                res += raw;\n            }\n        }\n        else if (ch === ' ' || ch === '\\t') {\n            // trim trailing whitespace\n            const wsStart = i;\n            let next = source[i + 1];\n            while (next === ' ' || next === '\\t')\n                next = source[++i + 1];\n            if (next !== '\\n' && !(next === '\\r' && source[i + 2] === '\\n'))\n                res += i > wsStart ? source.slice(wsStart, i + 1) : ch;\n        }\n        else {\n            res += ch;\n        }\n    }\n    if (source[source.length - 1] !== '\"' || source.length === 1)\n        onError(source.length, 'MISSING_CHAR', 'Missing closing \"quote');\n    return res;\n}\n/**\n * Fold a single newline into a space, multiple newlines to N - 1 newlines.\n * Presumes `source[offset] === '\\n'`\n */\nfunction foldNewline(source, offset) {\n    let fold = '';\n    let ch = source[offset + 1];\n    while (ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r') {\n        if (ch === '\\r' && source[offset + 2] !== '\\n')\n            break;\n        if (ch === '\\n')\n            fold += '\\n';\n        offset += 1;\n        ch = source[offset + 1];\n    }\n    if (!fold)\n        fold = ' ';\n    return { fold, offset };\n}\nconst escapeCodes = {\n    '0': '\\0', // null character\n    a: '\\x07', // bell character\n    b: '\\b', // backspace\n    e: '\\x1b', // escape character\n    f: '\\f', // form feed\n    n: '\\n', // line feed\n    r: '\\r', // carriage return\n    t: '\\t', // horizontal tab\n    v: '\\v', // vertical tab\n    N: '\\u0085', // Unicode next line\n    _: '\\u00a0', // Unicode non-breaking space\n    L: '\\u2028', // Unicode line separator\n    P: '\\u2029', // Unicode paragraph separator\n    ' ': ' ',\n    '\"': '\"',\n    '/': '/',\n    '\\\\': '\\\\',\n    '\\t': '\\t'\n};\nfunction parseCharCode(source, offset, length, onError) {\n    const cc = source.substr(offset, length);\n    const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);\n    const code = ok ? parseInt(cc, 16) : NaN;\n    try {\n        return String.fromCodePoint(code);\n    }\n    catch {\n        const raw = source.substr(offset - 2, length + 2);\n        onError(offset - 2, 'BAD_DQ_ESCAPE', `Invalid escape sequence ${raw}`);\n        return raw;\n    }\n}\n\nexports.resolveFlowScalar = resolveFlowScalar;\n", "'use strict';\n\nvar identity = require('../nodes/identity.js');\nvar Scalar = require('../nodes/Scalar.js');\nvar resolveBlockScalar = require('./resolve-block-scalar.js');\nvar resolveFlowScalar = require('./resolve-flow-scalar.js');\n\nfunction composeScalar(ctx, token, tagToken, onError) {\n    const { value, type, comment, range } = token.type === 'block-scalar'\n        ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError)\n        : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError);\n    const tagName = tagToken\n        ? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))\n        : null;\n    let tag;\n    if (ctx.options.stringKeys && ctx.atKey) {\n        tag = ctx.schema[identity.SCALAR];\n    }\n    else if (tagName)\n        tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError);\n    else if (token.type === 'scalar')\n        tag = findScalarTagByTest(ctx, value, token, onError);\n    else\n        tag = ctx.schema[identity.SCALAR];\n    let scalar;\n    try {\n        const res = tag.resolve(value, msg => onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg), ctx.options);\n        scalar = identity.isScalar(res) ? res : new Scalar.Scalar(res);\n    }\n    catch (error) {\n        const msg = error instanceof Error ? error.message : String(error);\n        onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg);\n        scalar = new Scalar.Scalar(value);\n    }\n    scalar.range = range;\n    scalar.source = value;\n    if (type)\n        scalar.type = type;\n    if (tagName)\n        scalar.tag = tagName;\n    if (tag.format)\n        scalar.format = tag.format;\n    if (comment)\n        scalar.comment = comment;\n    return scalar;\n}\nfunction findScalarTagByName(schema, value, tagName, tagToken, onError) {\n    if (tagName === '!')\n        return schema[identity.SCALAR]; // non-specific tag\n    const matchWithTest = [];\n    for (const tag of schema.tags) {\n        if (!tag.collection && tag.tag === tagName) {\n            if (tag.default && tag.test)\n                matchWithTest.push(tag);\n            else\n                return tag;\n        }\n    }\n    for (const tag of matchWithTest)\n        if (tag.test?.test(value))\n            return tag;\n    const kt = schema.knownTags[tagName];\n    if (kt && !kt.collection) {\n        // Ensure that the known tag is available for stringifying,\n        // but does not get used by default.\n        schema.tags.push(Object.assign({}, kt, { default: false, test: undefined }));\n        return kt;\n    }\n    onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, tagName !== 'tag:yaml.org,2002:str');\n    return schema[identity.SCALAR];\n}\nfunction findScalarTagByTest({ atKey, directives, schema }, value, token, onError) {\n    const tag = schema.tags.find(tag => (tag.default === true || (atKey && tag.default === 'key')) &&\n        tag.test?.test(value)) || schema[identity.SCALAR];\n    if (schema.compat) {\n        const compat = schema.compat.find(tag => tag.default && tag.test?.test(value)) ??\n            schema[identity.SCALAR];\n        if (tag.tag !== compat.tag) {\n            const ts = directives.tagString(tag.tag);\n            const cs = directives.tagString(compat.tag);\n            const msg = `Value may be parsed as either ${ts} or ${cs}`;\n            onError(token, 'TAG_RESOLVE_FAILED', msg, true);\n        }\n    }\n    return tag;\n}\n\nexports.composeScalar = composeScalar;\n", "'use strict';\n\nfunction emptyScalarPosition(offset, before, pos) {\n    if (before) {\n        pos ?? (pos = before.length);\n        for (let i = pos - 1; i >= 0; --i) {\n            let st = before[i];\n            switch (st.type) {\n                case 'space':\n                case 'comment':\n                case 'newline':\n                    offset -= st.source.length;\n                    continue;\n            }\n            // Technically, an empty scalar is immediately after the last non-empty\n            // node, but it's more useful to place it after any whitespace.\n            st = before[++i];\n            while (st?.type === 'space') {\n                offset += st.source.length;\n                st = before[++i];\n            }\n            break;\n        }\n    }\n    return offset;\n}\n\nexports.emptyScalarPosition = emptyScalarPosition;\n", "'use strict';\n\nvar Alias = require('../nodes/Alias.js');\nvar identity = require('../nodes/identity.js');\nvar composeCollection = require('./compose-collection.js');\nvar composeScalar = require('./compose-scalar.js');\nvar resolveEnd = require('./resolve-end.js');\nvar utilEmptyScalarPosition = require('./util-empty-scalar-position.js');\n\nconst CN = { composeNode, composeEmptyNode };\nfunction composeNode(ctx, token, props, onError) {\n    const atKey = ctx.atKey;\n    const { spaceBefore, comment, anchor, tag } = props;\n    let node;\n    let isSrcToken = true;\n    switch (token.type) {\n        case 'alias':\n            node = composeAlias(ctx, token, onError);\n            if (anchor || tag)\n                onError(token, 'ALIAS_PROPS', 'An alias node must not specify any properties');\n            break;\n        case 'scalar':\n        case 'single-quoted-scalar':\n        case 'double-quoted-scalar':\n        case 'block-scalar':\n            node = composeScalar.composeScalar(ctx, token, tag, onError);\n            if (anchor)\n                node.anchor = anchor.source.substring(1);\n            break;\n        case 'block-map':\n        case 'block-seq':\n        case 'flow-collection':\n            try {\n                node = composeCollection.composeCollection(CN, ctx, token, props, onError);\n                if (anchor)\n                    node.anchor = anchor.source.substring(1);\n            }\n            catch (error) {\n                // Almost certainly here due to a stack overflow\n                const message = error instanceof Error ? error.message : String(error);\n                onError(token, 'RESOURCE_EXHAUSTION', message);\n            }\n            break;\n        default: {\n            const message = token.type === 'error'\n                ? token.message\n                : `Unsupported token (type: ${token.type})`;\n            onError(token, 'UNEXPECTED_TOKEN', message);\n            isSrcToken = false;\n        }\n    }\n    node ?? (node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError));\n    if (anchor && node.anchor === '')\n        onError(anchor, 'BAD_ALIAS', 'Anchor cannot be an empty string');\n    if (atKey &&\n        ctx.options.stringKeys &&\n        (!identity.isScalar(node) ||\n            typeof node.value !== 'string' ||\n            (node.tag && node.tag !== 'tag:yaml.org,2002:str'))) {\n        const msg = 'With stringKeys, all keys must be strings';\n        onError(tag ?? token, 'NON_STRING_KEY', msg);\n    }\n    if (spaceBefore)\n        node.spaceBefore = true;\n    if (comment) {\n        if (token.type === 'scalar' && token.source === '')\n            node.comment = comment;\n        else\n            node.commentBefore = comment;\n    }\n    // @ts-expect-error Type checking misses meaning of isSrcToken\n    if (ctx.options.keepSourceTokens && isSrcToken)\n        node.srcToken = token;\n    return node;\n}\nfunction composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag, end }, onError) {\n    const token = {\n        type: 'scalar',\n        offset: utilEmptyScalarPosition.emptyScalarPosition(offset, before, pos),\n        indent: -1,\n        source: ''\n    };\n    const node = composeScalar.composeScalar(ctx, token, tag, onError);\n    if (anchor) {\n        node.anchor = anchor.source.substring(1);\n        if (node.anchor === '')\n            onError(anchor, 'BAD_ALIAS', 'Anchor cannot be an empty string');\n    }\n    if (spaceBefore)\n        node.spaceBefore = true;\n    if (comment) {\n        node.comment = comment;\n        node.range[2] = end;\n    }\n    return node;\n}\nfunction composeAlias({ options }, { offset, source, end }, onError) {\n    const alias = new Alias.Alias(source.substring(1));\n    if (alias.source === '')\n        onError(offset, 'BAD_ALIAS', 'Alias cannot be an empty string');\n    if (alias.source.endsWith(':'))\n        onError(offset + source.length - 1, 'BAD_ALIAS', 'Alias ending in : is ambiguous', true);\n    const valueEnd = offset + source.length;\n    const re = resolveEnd.resolveEnd(end, valueEnd, options.strict, onError);\n    alias.range = [offset, valueEnd, re.offset];\n    if (re.comment)\n        alias.comment = re.comment;\n    return alias;\n}\n\nexports.composeEmptyNode = composeEmptyNode;\nexports.composeNode = composeNode;\n", "'use strict';\n\nvar Document = require('../doc/Document.js');\nvar composeNode = require('./compose-node.js');\nvar resolveEnd = require('./resolve-end.js');\nvar resolveProps = require('./resolve-props.js');\n\nfunction composeDoc(options, directives, { offset, start, value, end }, onError) {\n    const opts = Object.assign({ _directives: directives }, options);\n    const doc = new Document.Document(undefined, opts);\n    const ctx = {\n        atKey: false,\n        atRoot: true,\n        directives: doc.directives,\n        options: doc.options,\n        schema: doc.schema\n    };\n    const props = resolveProps.resolveProps(start, {\n        indicator: 'doc-start',\n        next: value ?? end?.[0],\n        offset,\n        onError,\n        parentIndent: 0,\n        startOnNewline: true\n    });\n    if (props.found) {\n        doc.directives.docStart = true;\n        if (value &&\n            (value.type === 'block-map' || value.type === 'block-seq') &&\n            !props.hasNewline)\n            onError(props.end, 'MISSING_CHAR', 'Block collection cannot start on same line with directives-end marker');\n    }\n    // @ts-expect-error If Contents is set, let's trust the user\n    doc.contents = value\n        ? composeNode.composeNode(ctx, value, props, onError)\n        : composeNode.composeEmptyNode(ctx, props.end, start, null, props, onError);\n    const contentEnd = doc.contents.range[2];\n    const re = resolveEnd.resolveEnd(end, contentEnd, false, onError);\n    if (re.comment)\n        doc.comment = re.comment;\n    doc.range = [offset, contentEnd, re.offset];\n    return doc;\n}\n\nexports.composeDoc = composeDoc;\n", "'use strict';\n\nvar node_process = require('process');\nvar directives = require('../doc/directives.js');\nvar Document = require('../doc/Document.js');\nvar errors = require('../errors.js');\nvar identity = require('../nodes/identity.js');\nvar composeDoc = require('./compose-doc.js');\nvar resolveEnd = require('./resolve-end.js');\n\nfunction getErrorPos(src) {\n    if (typeof src === 'number')\n        return [src, src + 1];\n    if (Array.isArray(src))\n        return src.length === 2 ? src : [src[0], src[1]];\n    const { offset, source } = src;\n    return [offset, offset + (typeof source === 'string' ? source.length : 1)];\n}\nfunction parsePrelude(prelude) {\n    let comment = '';\n    let atComment = false;\n    let afterEmptyLine = false;\n    for (let i = 0; i < prelude.length; ++i) {\n        const source = prelude[i];\n        switch (source[0]) {\n            case '#':\n                comment +=\n                    (comment === '' ? '' : afterEmptyLine ? '\\n\\n' : '\\n') +\n                        (source.substring(1) || ' ');\n                atComment = true;\n                afterEmptyLine = false;\n                break;\n            case '%':\n                if (prelude[i + 1]?.[0] !== '#')\n                    i += 1;\n                atComment = false;\n                break;\n            default:\n                // This may be wrong after doc-end, but in that case it doesn't matter\n                if (!atComment)\n                    afterEmptyLine = true;\n                atComment = false;\n        }\n    }\n    return { comment, afterEmptyLine };\n}\n/**\n * Compose a stream of CST nodes into a stream of YAML Documents.\n *\n * ```ts\n * import { Composer, Parser } from 'yaml'\n *\n * const src: string = ...\n * const tokens = new Parser().parse(src)\n * const docs = new Composer().compose(tokens)\n * ```\n */\nclass Composer {\n    constructor(options = {}) {\n        this.doc = null;\n        this.atDirectives = false;\n        this.prelude = [];\n        this.errors = [];\n        this.warnings = [];\n        this.onError = (source, code, message, warning) => {\n            const pos = getErrorPos(source);\n            if (warning)\n                this.warnings.push(new errors.YAMLWarning(pos, code, message));\n            else\n                this.errors.push(new errors.YAMLParseError(pos, code, message));\n        };\n        // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n        this.directives = new directives.Directives({ version: options.version || '1.2' });\n        this.options = options;\n    }\n    decorate(doc, afterDoc) {\n        const { comment, afterEmptyLine } = parsePrelude(this.prelude);\n        //console.log({ dc: doc.comment, prelude, comment })\n        if (comment) {\n            const dc = doc.contents;\n            if (afterDoc) {\n                doc.comment = doc.comment ? `${doc.comment}\\n${comment}` : comment;\n            }\n            else if (afterEmptyLine || doc.directives.docStart || !dc) {\n                doc.commentBefore = comment;\n            }\n            else if (identity.isCollection(dc) && !dc.flow && dc.items.length > 0) {\n                let it = dc.items[0];\n                if (identity.isPair(it))\n                    it = it.key;\n                const cb = it.commentBefore;\n                it.commentBefore = cb ? `${comment}\\n${cb}` : comment;\n            }\n            else {\n                const cb = dc.commentBefore;\n                dc.commentBefore = cb ? `${comment}\\n${cb}` : comment;\n            }\n        }\n        if (afterDoc) {\n            for (let i = 0; i < this.errors.length; ++i)\n                doc.errors.push(this.errors[i]);\n            for (let i = 0; i < this.warnings.length; ++i)\n                doc.warnings.push(this.warnings[i]);\n        }\n        else {\n            doc.errors = this.errors;\n            doc.warnings = this.warnings;\n        }\n        this.prelude = [];\n        this.errors = [];\n        this.warnings = [];\n    }\n    /**\n     * Current stream status information.\n     *\n     * Mostly useful at the end of input for an empty stream.\n     */\n    streamInfo() {\n        return {\n            comment: parsePrelude(this.prelude).comment,\n            directives: this.directives,\n            errors: this.errors,\n            warnings: this.warnings\n        };\n    }\n    /**\n     * Compose tokens into documents.\n     *\n     * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document.\n     * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly.\n     */\n    *compose(tokens, forceDoc = false, endOffset = -1) {\n        for (const token of tokens)\n            yield* this.next(token);\n        yield* this.end(forceDoc, endOffset);\n    }\n    /** Advance the composer by one CST token. */\n    *next(token) {\n        if (node_process.env.LOG_STREAM)\n            console.dir(token, { depth: null });\n        switch (token.type) {\n            case 'directive':\n                this.directives.add(token.source, (offset, message, warning) => {\n                    const pos = getErrorPos(token);\n                    pos[0] += offset;\n                    this.onError(pos, 'BAD_DIRECTIVE', message, warning);\n                });\n                this.prelude.push(token.source);\n                this.atDirectives = true;\n                break;\n            case 'document': {\n                const doc = composeDoc.composeDoc(this.options, this.directives, token, this.onError);\n                if (this.atDirectives && !doc.directives.docStart)\n                    this.onError(token, 'MISSING_CHAR', 'Missing directives-end/doc-start indicator line');\n                this.decorate(doc, false);\n                if (this.doc)\n                    yield this.doc;\n                this.doc = doc;\n                this.atDirectives = false;\n                break;\n            }\n            case 'byte-order-mark':\n            case 'space':\n                break;\n            case 'comment':\n            case 'newline':\n                this.prelude.push(token.source);\n                break;\n            case 'error': {\n                const msg = token.source\n                    ? `${token.message}: ${JSON.stringify(token.source)}`\n                    : token.message;\n                const error = new errors.YAMLParseError(getErrorPos(token), 'UNEXPECTED_TOKEN', msg);\n                if (this.atDirectives || !this.doc)\n                    this.errors.push(error);\n                else\n                    this.doc.errors.push(error);\n                break;\n            }\n            case 'doc-end': {\n                if (!this.doc) {\n                    const msg = 'Unexpected doc-end without preceding document';\n                    this.errors.push(new errors.YAMLParseError(getErrorPos(token), 'UNEXPECTED_TOKEN', msg));\n                    break;\n                }\n                this.doc.directives.docEnd = true;\n                const end = resolveEnd.resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError);\n                this.decorate(this.doc, true);\n                if (end.comment) {\n                    const dc = this.doc.comment;\n                    this.doc.comment = dc ? `${dc}\\n${end.comment}` : end.comment;\n                }\n                this.doc.range[2] = end.offset;\n                break;\n            }\n            default:\n                this.errors.push(new errors.YAMLParseError(getErrorPos(token), 'UNEXPECTED_TOKEN', `Unsupported token ${token.type}`));\n        }\n    }\n    /**\n     * Call at end of input to yield any remaining document.\n     *\n     * @param forceDoc - If the stream contains no document, still emit a final document including any comments and directives that would be applied to a subsequent document.\n     * @param endOffset - Should be set if `forceDoc` is also set, to set the document range end and to indicate errors correctly.\n     */\n    *end(forceDoc = false, endOffset = -1) {\n        if (this.doc) {\n            this.decorate(this.doc, true);\n            yield this.doc;\n            this.doc = null;\n        }\n        else if (forceDoc) {\n            const opts = Object.assign({ _directives: this.directives }, this.options);\n            const doc = new Document.Document(undefined, opts);\n            if (this.atDirectives)\n                this.onError(endOffset, 'MISSING_CHAR', 'Missing directives-end indicator line');\n            doc.range = [0, endOffset, endOffset];\n            this.decorate(doc, false);\n            yield doc;\n        }\n    }\n}\n\nexports.Composer = Composer;\n", "'use strict';\n\nvar resolveBlockScalar = require('../compose/resolve-block-scalar.js');\nvar resolveFlowScalar = require('../compose/resolve-flow-scalar.js');\nvar errors = require('../errors.js');\nvar stringifyString = require('../stringify/stringifyString.js');\n\nfunction resolveAsScalar(token, strict = true, onError) {\n    if (token) {\n        const _onError = (pos, code, message) => {\n            const offset = typeof pos === 'number' ? pos : Array.isArray(pos) ? pos[0] : pos.offset;\n            if (onError)\n                onError(offset, code, message);\n            else\n                throw new errors.YAMLParseError([offset, offset + 1], code, message);\n        };\n        switch (token.type) {\n            case 'scalar':\n            case 'single-quoted-scalar':\n            case 'double-quoted-scalar':\n                return resolveFlowScalar.resolveFlowScalar(token, strict, _onError);\n            case 'block-scalar':\n                return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError);\n        }\n    }\n    return null;\n}\n/**\n * Create a new scalar token with `value`\n *\n * Values that represent an actual string but may be parsed as a different type should use a `type` other than `'PLAIN'`,\n * as this function does not support any schema operations and won't check for such conflicts.\n *\n * @param value The string representation of the value, which will have its content properly indented.\n * @param context.end Comments and whitespace after the end of the value, or after the block scalar header. If undefined, a newline will be added.\n * @param context.implicitKey Being within an implicit key may affect the resolved type of the token's value.\n * @param context.indent The indent level of the token.\n * @param context.inFlow Is this scalar within a flow collection? This may affect the resolved type of the token's value.\n * @param context.offset The offset position of the token.\n * @param context.type The preferred type of the scalar token. If undefined, the previous type of the `token` will be used, defaulting to `'PLAIN'`.\n */\nfunction createScalarToken(value, context) {\n    const { implicitKey = false, indent, inFlow = false, offset = -1, type = 'PLAIN' } = context;\n    const source = stringifyString.stringifyString({ type, value }, {\n        implicitKey,\n        indent: indent > 0 ? ' '.repeat(indent) : '',\n        inFlow,\n        options: { blockQuote: true, lineWidth: -1 }\n    });\n    const end = context.end ?? [\n        { type: 'newline', offset: -1, indent, source: '\\n' }\n    ];\n    switch (source[0]) {\n        case '|':\n        case '>': {\n            const he = source.indexOf('\\n');\n            const head = source.substring(0, he);\n            const body = source.substring(he + 1) + '\\n';\n            const props = [\n                { type: 'block-scalar-header', offset, indent, source: head }\n            ];\n            if (!addEndtoBlockProps(props, end))\n                props.push({ type: 'newline', offset: -1, indent, source: '\\n' });\n            return { type: 'block-scalar', offset, indent, props, source: body };\n        }\n        case '\"':\n            return { type: 'double-quoted-scalar', offset, indent, source, end };\n        case \"'\":\n            return { type: 'single-quoted-scalar', offset, indent, source, end };\n        default:\n            return { type: 'scalar', offset, indent, source, end };\n    }\n}\n/**\n * Set the value of `token` to the given string `value`, overwriting any previous contents and type that it may have.\n *\n * Best efforts are made to retain any comments previously associated with the `token`,\n * though all contents within a collection's `items` will be overwritten.\n *\n * Values that represent an actual string but may be parsed as a different type should use a `type` other than `'PLAIN'`,\n * as this function does not support any schema operations and won't check for such conflicts.\n *\n * @param token Any token. If it does not include an `indent` value, the value will be stringified as if it were an implicit key.\n * @param value The string representation of the value, which will have its content properly indented.\n * @param context.afterKey In most cases, values after a key should have an additional level of indentation.\n * @param context.implicitKey Being within an implicit key may affect the resolved type of the token's value.\n * @param context.inFlow Being within a flow collection may affect the resolved type of the token's value.\n * @param context.type The preferred type of the scalar token. If undefined, the previous type of the `token` will be used, defaulting to `'PLAIN'`.\n */\nfunction setScalarValue(token, value, context = {}) {\n    let { afterKey = false, implicitKey = false, inFlow = false, type } = context;\n    let indent = 'indent' in token ? token.indent : null;\n    if (afterKey && typeof indent === 'number')\n        indent += 2;\n    if (!type)\n        switch (token.type) {\n            case 'single-quoted-scalar':\n                type = 'QUOTE_SINGLE';\n                break;\n            case 'double-quoted-scalar':\n                type = 'QUOTE_DOUBLE';\n                break;\n            case 'block-scalar': {\n                const header = token.props[0];\n                if (header.type !== 'block-scalar-header')\n                    throw new Error('Invalid block scalar header');\n                type = header.source[0] === '>' ? 'BLOCK_FOLDED' : 'BLOCK_LITERAL';\n                break;\n            }\n            default:\n                type = 'PLAIN';\n        }\n    const source = stringifyString.stringifyString({ type, value }, {\n        implicitKey: implicitKey || indent === null,\n        indent: indent !== null && indent > 0 ? ' '.repeat(indent) : '',\n        inFlow,\n        options: { blockQuote: true, lineWidth: -1 }\n    });\n    switch (source[0]) {\n        case '|':\n        case '>':\n            setBlockScalarValue(token, source);\n            break;\n        case '\"':\n            setFlowScalarValue(token, source, 'double-quoted-scalar');\n            break;\n        case \"'\":\n            setFlowScalarValue(token, source, 'single-quoted-scalar');\n            break;\n        default:\n            setFlowScalarValue(token, source, 'scalar');\n    }\n}\nfunction setBlockScalarValue(token, source) {\n    const he = source.indexOf('\\n');\n    const head = source.substring(0, he);\n    const body = source.substring(he + 1) + '\\n';\n    if (token.type === 'block-scalar') {\n        const header = token.props[0];\n        if (header.type !== 'block-scalar-header')\n            throw new Error('Invalid block scalar header');\n        header.source = head;\n        token.source = body;\n    }\n    else {\n        const { offset } = token;\n        const indent = 'indent' in token ? token.indent : -1;\n        const props = [\n            { type: 'block-scalar-header', offset, indent, source: head }\n        ];\n        if (!addEndtoBlockProps(props, 'end' in token ? token.end : undefined))\n            props.push({ type: 'newline', offset: -1, indent, source: '\\n' });\n        for (const key of Object.keys(token))\n            if (key !== 'type' && key !== 'offset')\n                delete token[key];\n        Object.assign(token, { type: 'block-scalar', indent, props, source: body });\n    }\n}\n/** @returns `true` if last token is a newline */\nfunction addEndtoBlockProps(props, end) {\n    if (end)\n        for (const st of end)\n            switch (st.type) {\n                case 'space':\n                case 'comment':\n                    props.push(st);\n                    break;\n                case 'newline':\n                    props.push(st);\n                    return true;\n            }\n    return false;\n}\nfunction setFlowScalarValue(token, source, type) {\n    switch (token.type) {\n        case 'scalar':\n        case 'double-quoted-scalar':\n        case 'single-quoted-scalar':\n            token.type = type;\n            token.source = source;\n            break;\n        case 'block-scalar': {\n            const end = token.props.slice(1);\n            let oa = source.length;\n            if (token.props[0].type === 'block-scalar-header')\n                oa -= token.props[0].source.length;\n            for (const tok of end)\n                tok.offset += oa;\n            delete token.props;\n            Object.assign(token, { type, source, end });\n            break;\n        }\n        case 'block-map':\n        case 'block-seq': {\n            const offset = token.offset + source.length;\n            const nl = { type: 'newline', offset, indent: token.indent, source: '\\n' };\n            delete token.items;\n            Object.assign(token, { type, source, end: [nl] });\n            break;\n        }\n        default: {\n            const indent = 'indent' in token ? token.indent : -1;\n            const end = 'end' in token && Array.isArray(token.end)\n                ? token.end.filter(st => st.type === 'space' ||\n                    st.type === 'comment' ||\n                    st.type === 'newline')\n                : [];\n            for (const key of Object.keys(token))\n                if (key !== 'type' && key !== 'offset')\n                    delete token[key];\n            Object.assign(token, { type, indent, source, end });\n        }\n    }\n}\n\nexports.createScalarToken = createScalarToken;\nexports.resolveAsScalar = resolveAsScalar;\nexports.setScalarValue = setScalarValue;\n", "'use strict';\n\n/**\n * Stringify a CST document, token, or collection item\n *\n * Fair warning: This applies no validation whatsoever, and\n * simply concatenates the sources in their logical order.\n */\nconst stringify = (cst) => 'type' in cst ? stringifyToken(cst) : stringifyItem(cst);\nfunction stringifyToken(token) {\n    switch (token.type) {\n        case 'block-scalar': {\n            let res = '';\n            for (const tok of token.props)\n                res += stringifyToken(tok);\n            return res + token.source;\n        }\n        case 'block-map':\n        case 'block-seq': {\n            let res = '';\n            for (const item of token.items)\n                res += stringifyItem(item);\n            return res;\n        }\n        case 'flow-collection': {\n            let res = token.start.source;\n            for (const item of token.items)\n                res += stringifyItem(item);\n            for (const st of token.end)\n                res += st.source;\n            return res;\n        }\n        case 'document': {\n            let res = stringifyItem(token);\n            if (token.end)\n                for (const st of token.end)\n                    res += st.source;\n            return res;\n        }\n        default: {\n            let res = token.source;\n            if ('end' in token && token.end)\n                for (const st of token.end)\n                    res += st.source;\n            return res;\n        }\n    }\n}\nfunction stringifyItem({ start, key, sep, value }) {\n    let res = '';\n    for (const st of start)\n        res += st.source;\n    if (key)\n        res += stringifyToken(key);\n    if (sep)\n        for (const st of sep)\n            res += st.source;\n    if (value)\n        res += stringifyToken(value);\n    return res;\n}\n\nexports.stringify = stringify;\n", "'use strict';\n\nconst BREAK = Symbol('break visit');\nconst SKIP = Symbol('skip children');\nconst REMOVE = Symbol('remove item');\n/**\n * Apply a visitor to a CST document or item.\n *\n * Walks through the tree (depth-first) starting from the root, calling a\n * `visitor` function with two arguments when entering each item:\n *   - `item`: The current item, which included the following members:\n *     - `start: SourceToken[]` \u2013 Source tokens before the key or value,\n *       possibly including its anchor or tag.\n *     - `key?: Token | null` \u2013 Set for pair values. May then be `null`, if\n *       the key before the `:` separator is empty.\n *     - `sep?: SourceToken[]` \u2013 Source tokens between the key and the value,\n *       which should include the `:` map value indicator if `value` is set.\n *     - `value?: Token` \u2013 The value of a sequence item, or of a map pair.\n *   - `path`: The steps from the root to the current node, as an array of\n *     `['key' | 'value', number]` tuples.\n *\n * The return value of the visitor may be used to control the traversal:\n *   - `undefined` (default): Do nothing and continue\n *   - `visit.SKIP`: Do not visit the children of this token, continue with\n *      next sibling\n *   - `visit.BREAK`: Terminate traversal completely\n *   - `visit.REMOVE`: Remove the current item, then continue with the next one\n *   - `number`: Set the index of the next step. This is useful especially if\n *     the index of the current token has changed.\n *   - `function`: Define the next visitor for this item. After the original\n *     visitor is called on item entry, next visitors are called after handling\n *     a non-empty `key` and when exiting the item.\n */\nfunction visit(cst, visitor) {\n    if ('type' in cst && cst.type === 'document')\n        cst = { start: cst.start, value: cst.value };\n    _visit(Object.freeze([]), cst, visitor);\n}\n// Without the `as symbol` casts, TS declares these in the `visit`\n// namespace using `var`, but then complains about that because\n// `unique symbol` must be `const`.\n/** Terminate visit traversal completely */\nvisit.BREAK = BREAK;\n/** Do not visit the children of the current item */\nvisit.SKIP = SKIP;\n/** Remove the current item */\nvisit.REMOVE = REMOVE;\n/** Find the item at `path` from `cst` as the root */\nvisit.itemAtPath = (cst, path) => {\n    let item = cst;\n    for (const [field, index] of path) {\n        const tok = item?.[field];\n        if (tok && 'items' in tok) {\n            item = tok.items[index];\n        }\n        else\n            return undefined;\n    }\n    return item;\n};\n/**\n * Get the immediate parent collection of the item at `path` from `cst` as the root.\n *\n * Throws an error if the collection is not found, which should never happen if the item itself exists.\n */\nvisit.parentCollection = (cst, path) => {\n    const parent = visit.itemAtPath(cst, path.slice(0, -1));\n    const field = path[path.length - 1][0];\n    const coll = parent?.[field];\n    if (coll && 'items' in coll)\n        return coll;\n    throw new Error('Parent collection not found');\n};\nfunction _visit(path, item, visitor) {\n    let ctrl = visitor(item, path);\n    if (typeof ctrl === 'symbol')\n        return ctrl;\n    for (const field of ['key', 'value']) {\n        const token = item[field];\n        if (token && 'items' in token) {\n            for (let i = 0; i < token.items.length; ++i) {\n                const ci = _visit(Object.freeze(path.concat([[field, i]])), token.items[i], visitor);\n                if (typeof ci === 'number')\n                    i = ci - 1;\n                else if (ci === BREAK)\n                    return BREAK;\n                else if (ci === REMOVE) {\n                    token.items.splice(i, 1);\n                    i -= 1;\n                }\n            }\n            if (typeof ctrl === 'function' && field === 'key')\n                ctrl = ctrl(item, path);\n        }\n    }\n    return typeof ctrl === 'function' ? ctrl(item, path) : ctrl;\n}\n\nexports.visit = visit;\n", "'use strict';\n\nvar cstScalar = require('./cst-scalar.js');\nvar cstStringify = require('./cst-stringify.js');\nvar cstVisit = require('./cst-visit.js');\n\n/** The byte order mark */\nconst BOM = '\\u{FEFF}';\n/** Start of doc-mode */\nconst DOCUMENT = '\\x02'; // C0: Start of Text\n/** Unexpected end of flow-mode */\nconst FLOW_END = '\\x18'; // C0: Cancel\n/** Next token is a scalar value */\nconst SCALAR = '\\x1f'; // C0: Unit Separator\n/** @returns `true` if `token` is a flow or block collection */\nconst isCollection = (token) => !!token && 'items' in token;\n/** @returns `true` if `token` is a flow or block scalar; not an alias */\nconst isScalar = (token) => !!token &&\n    (token.type === 'scalar' ||\n        token.type === 'single-quoted-scalar' ||\n        token.type === 'double-quoted-scalar' ||\n        token.type === 'block-scalar');\n/* istanbul ignore next */\n/** Get a printable representation of a lexer token */\nfunction prettyToken(token) {\n    switch (token) {\n        case BOM:\n            return '<BOM>';\n        case DOCUMENT:\n            return '<DOC>';\n        case FLOW_END:\n            return '<FLOW_END>';\n        case SCALAR:\n            return '<SCALAR>';\n        default:\n            return JSON.stringify(token);\n    }\n}\n/** Identify the type of a lexer token. May return `null` for unknown tokens. */\nfunction tokenType(source) {\n    switch (source) {\n        case BOM:\n            return 'byte-order-mark';\n        case DOCUMENT:\n            return 'doc-mode';\n        case FLOW_END:\n            return 'flow-error-end';\n        case SCALAR:\n            return 'scalar';\n        case '---':\n            return 'doc-start';\n        case '...':\n            return 'doc-end';\n        case '':\n        case '\\n':\n        case '\\r\\n':\n            return 'newline';\n        case '-':\n            return 'seq-item-ind';\n        case '?':\n            return 'explicit-key-ind';\n        case ':':\n            return 'map-value-ind';\n        case '{':\n            return 'flow-map-start';\n        case '}':\n            return 'flow-map-end';\n        case '[':\n            return 'flow-seq-start';\n        case ']':\n            return 'flow-seq-end';\n        case ',':\n            return 'comma';\n    }\n    switch (source[0]) {\n        case ' ':\n        case '\\t':\n            return 'space';\n        case '#':\n            return 'comment';\n        case '%':\n            return 'directive-line';\n        case '*':\n            return 'alias';\n        case '&':\n            return 'anchor';\n        case '!':\n            return 'tag';\n        case \"'\":\n            return 'single-quoted-scalar';\n        case '\"':\n            return 'double-quoted-scalar';\n        case '|':\n        case '>':\n            return 'block-scalar-header';\n    }\n    return null;\n}\n\nexports.createScalarToken = cstScalar.createScalarToken;\nexports.resolveAsScalar = cstScalar.resolveAsScalar;\nexports.setScalarValue = cstScalar.setScalarValue;\nexports.stringify = cstStringify.stringify;\nexports.visit = cstVisit.visit;\nexports.BOM = BOM;\nexports.DOCUMENT = DOCUMENT;\nexports.FLOW_END = FLOW_END;\nexports.SCALAR = SCALAR;\nexports.isCollection = isCollection;\nexports.isScalar = isScalar;\nexports.prettyToken = prettyToken;\nexports.tokenType = tokenType;\n", "'use strict';\n\nvar cst = require('./cst.js');\n\n/*\nSTART -> stream\n\nstream\n  directive -> line-end -> stream\n  indent + line-end -> stream\n  [else] -> line-start\n\nline-end\n  comment -> line-end\n  newline -> .\n  input-end -> END\n\nline-start\n  doc-start -> doc\n  doc-end -> stream\n  [else] -> indent -> block-start\n\nblock-start\n  seq-item-start -> block-start\n  explicit-key-start -> block-start\n  map-value-start -> block-start\n  [else] -> doc\n\ndoc\n  line-end -> line-start\n  spaces -> doc\n  anchor -> doc\n  tag -> doc\n  flow-start -> flow -> doc\n  flow-end -> error -> doc\n  seq-item-start -> error -> doc\n  explicit-key-start -> error -> doc\n  map-value-start -> doc\n  alias -> doc\n  quote-start -> quoted-scalar -> doc\n  block-scalar-header -> line-end -> block-scalar(min) -> line-start\n  [else] -> plain-scalar(false, min) -> doc\n\nflow\n  line-end -> flow\n  spaces -> flow\n  anchor -> flow\n  tag -> flow\n  flow-start -> flow -> flow\n  flow-end -> .\n  seq-item-start -> error -> flow\n  explicit-key-start -> flow\n  map-value-start -> flow\n  alias -> flow\n  quote-start -> quoted-scalar -> flow\n  comma -> flow\n  [else] -> plain-scalar(true, 0) -> flow\n\nquoted-scalar\n  quote-end -> .\n  [else] -> quoted-scalar\n\nblock-scalar(min)\n  newline + peek(indent < min) -> .\n  [else] -> block-scalar(min)\n\nplain-scalar(is-flow, min)\n  scalar-end(is-flow) -> .\n  peek(newline + (indent < min)) -> .\n  [else] -> plain-scalar(min)\n*/\nfunction isEmpty(ch) {\n    switch (ch) {\n        case undefined:\n        case ' ':\n        case '\\n':\n        case '\\r':\n        case '\\t':\n            return true;\n        default:\n            return false;\n    }\n}\nconst hexDigits = new Set('0123456789ABCDEFabcdef');\nconst tagChars = new Set(\"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()\");\nconst flowIndicatorChars = new Set(',[]{}');\nconst invalidAnchorChars = new Set(' ,[]{}\\n\\r\\t');\nconst isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch);\n/**\n * Splits an input string into lexical tokens, i.e. smaller strings that are\n * easily identifiable by `tokens.tokenType()`.\n *\n * Lexing starts always in a \"stream\" context. Incomplete input may be buffered\n * until a complete token can be emitted.\n *\n * In addition to slices of the original input, the following control characters\n * may also be emitted:\n *\n * - `\\x02` (Start of Text): A document starts with the next token\n * - `\\x18` (Cancel): Unexpected end of flow-mode (indicates an error)\n * - `\\x1f` (Unit Separator): Next token is a scalar value\n * - `\\u{FEFF}` (Byte order mark): Emitted separately outside documents\n */\nclass Lexer {\n    constructor() {\n        /**\n         * Flag indicating whether the end of the current buffer marks the end of\n         * all input\n         */\n        this.atEnd = false;\n        /**\n         * Explicit indent set in block scalar header, as an offset from the current\n         * minimum indent, so e.g. set to 1 from a header `|2+`. Set to -1 if not\n         * explicitly set.\n         */\n        this.blockScalarIndent = -1;\n        /**\n         * Block scalars that include a + (keep) chomping indicator in their header\n         * include trailing empty lines, which are otherwise excluded from the\n         * scalar's contents.\n         */\n        this.blockScalarKeep = false;\n        /** Current input */\n        this.buffer = '';\n        /**\n         * Flag noting whether the map value indicator : can immediately follow this\n         * node within a flow context.\n         */\n        this.flowKey = false;\n        /** Count of surrounding flow collection levels. */\n        this.flowLevel = 0;\n        /**\n         * Minimum level of indentation required for next lines to be parsed as a\n         * part of the current scalar value.\n         */\n        this.indentNext = 0;\n        /** Indentation level of the current line. */\n        this.indentValue = 0;\n        /** Position of the next \\n character. */\n        this.lineEndPos = null;\n        /** Stores the state of the lexer if reaching the end of incpomplete input */\n        this.next = null;\n        /** A pointer to `buffer`; the current position of the lexer. */\n        this.pos = 0;\n    }\n    /**\n     * Generate YAML tokens from the `source` string. If `incomplete`,\n     * a part of the last line may be left as a buffer for the next call.\n     *\n     * @returns A generator of lexical tokens\n     */\n    *lex(source, incomplete = false) {\n        if (source) {\n            if (typeof source !== 'string')\n                throw TypeError('source is not a string');\n            this.buffer = this.buffer ? this.buffer + source : source;\n            this.lineEndPos = null;\n        }\n        this.atEnd = !incomplete;\n        let next = this.next ?? 'stream';\n        while (next && (incomplete || this.hasChars(1)))\n            next = yield* this.parseNext(next);\n    }\n    atLineEnd() {\n        let i = this.pos;\n        let ch = this.buffer[i];\n        while (ch === ' ' || ch === '\\t')\n            ch = this.buffer[++i];\n        if (!ch || ch === '#' || ch === '\\n')\n            return true;\n        if (ch === '\\r')\n            return this.buffer[i + 1] === '\\n';\n        return false;\n    }\n    charAt(n) {\n        return this.buffer[this.pos + n];\n    }\n    continueScalar(offset) {\n        let ch = this.buffer[offset];\n        if (this.indentNext > 0) {\n            let indent = 0;\n            while (ch === ' ')\n                ch = this.buffer[++indent + offset];\n            if (ch === '\\r') {\n                const next = this.buffer[indent + offset + 1];\n                if (next === '\\n' || (!next && !this.atEnd))\n                    return offset + indent + 1;\n            }\n            return ch === '\\n' || indent >= this.indentNext || (!ch && !this.atEnd)\n                ? offset + indent\n                : -1;\n        }\n        if (ch === '-' || ch === '.') {\n            const dt = this.buffer.substr(offset, 3);\n            if ((dt === '---' || dt === '...') && isEmpty(this.buffer[offset + 3]))\n                return -1;\n        }\n        return offset;\n    }\n    getLine() {\n        let end = this.lineEndPos;\n        if (typeof end !== 'number' || (end !== -1 && end < this.pos)) {\n            end = this.buffer.indexOf('\\n', this.pos);\n            this.lineEndPos = end;\n        }\n        if (end === -1)\n            return this.atEnd ? this.buffer.substring(this.pos) : null;\n        if (this.buffer[end - 1] === '\\r')\n            end -= 1;\n        return this.buffer.substring(this.pos, end);\n    }\n    hasChars(n) {\n        return this.pos + n <= this.buffer.length;\n    }\n    setNext(state) {\n        this.buffer = this.buffer.substring(this.pos);\n        this.pos = 0;\n        this.lineEndPos = null;\n        this.next = state;\n        return null;\n    }\n    peek(n) {\n        return this.buffer.substr(this.pos, n);\n    }\n    *parseNext(next) {\n        switch (next) {\n            case 'stream':\n                return yield* this.parseStream();\n            case 'line-start':\n                return yield* this.parseLineStart();\n            case 'block-start':\n                return yield* this.parseBlockStart();\n            case 'doc':\n                return yield* this.parseDocument();\n            case 'flow':\n                return yield* this.parseFlowCollection();\n            case 'quoted-scalar':\n                return yield* this.parseQuotedScalar();\n            case 'block-scalar':\n                return yield* this.parseBlockScalar();\n            case 'plain-scalar':\n                return yield* this.parsePlainScalar();\n        }\n    }\n    *parseStream() {\n        let line = this.getLine();\n        if (line === null)\n            return this.setNext('stream');\n        if (line[0] === cst.BOM) {\n            yield* this.pushCount(1);\n            line = line.substring(1);\n        }\n        if (line[0] === '%') {\n            let dirEnd = line.length;\n            let cs = line.indexOf('#');\n            while (cs !== -1) {\n                const ch = line[cs - 1];\n                if (ch === ' ' || ch === '\\t') {\n                    dirEnd = cs - 1;\n                    break;\n                }\n                else {\n                    cs = line.indexOf('#', cs + 1);\n                }\n            }\n            while (true) {\n                const ch = line[dirEnd - 1];\n                if (ch === ' ' || ch === '\\t')\n                    dirEnd -= 1;\n                else\n                    break;\n            }\n            const n = (yield* this.pushCount(dirEnd)) + (yield* this.pushSpaces(true));\n            yield* this.pushCount(line.length - n); // possible comment\n            this.pushNewline();\n            return 'stream';\n        }\n        if (this.atLineEnd()) {\n            const sp = yield* this.pushSpaces(true);\n            yield* this.pushCount(line.length - sp);\n            yield* this.pushNewline();\n            return 'stream';\n        }\n        yield cst.DOCUMENT;\n        return yield* this.parseLineStart();\n    }\n    *parseLineStart() {\n        const ch = this.charAt(0);\n        if (!ch && !this.atEnd)\n            return this.setNext('line-start');\n        if (ch === '-' || ch === '.') {\n            if (!this.atEnd && !this.hasChars(4))\n                return this.setNext('line-start');\n            const s = this.peek(3);\n            if ((s === '---' || s === '...') && isEmpty(this.charAt(3))) {\n                yield* this.pushCount(3);\n                this.indentValue = 0;\n                this.indentNext = 0;\n                return s === '---' ? 'doc' : 'stream';\n            }\n        }\n        this.indentValue = yield* this.pushSpaces(false);\n        if (this.indentNext > this.indentValue && !isEmpty(this.charAt(1)))\n            this.indentNext = this.indentValue;\n        return yield* this.parseBlockStart();\n    }\n    *parseBlockStart() {\n        const [ch0, ch1] = this.peek(2);\n        if (!ch1 && !this.atEnd)\n            return this.setNext('block-start');\n        if ((ch0 === '-' || ch0 === '?' || ch0 === ':') && isEmpty(ch1)) {\n            const n = (yield* this.pushCount(1)) + (yield* this.pushSpaces(true));\n            this.indentNext = this.indentValue + 1;\n            this.indentValue += n;\n            return 'block-start';\n        }\n        return 'doc';\n    }\n    *parseDocument() {\n        yield* this.pushSpaces(true);\n        const line = this.getLine();\n        if (line === null)\n            return this.setNext('doc');\n        let n = yield* this.pushIndicators();\n        switch (line[n]) {\n            case '#':\n                yield* this.pushCount(line.length - n);\n            // fallthrough\n            case undefined:\n                yield* this.pushNewline();\n                return yield* this.parseLineStart();\n            case '{':\n            case '[':\n                yield* this.pushCount(1);\n                this.flowKey = false;\n                this.flowLevel = 1;\n                return 'flow';\n            case '}':\n            case ']':\n                // this is an error\n                yield* this.pushCount(1);\n                return 'doc';\n            case '*':\n                yield* this.pushUntil(isNotAnchorChar);\n                return 'doc';\n            case '\"':\n            case \"'\":\n                return yield* this.parseQuotedScalar();\n            case '|':\n            case '>':\n                n += yield* this.parseBlockScalarHeader();\n                n += yield* this.pushSpaces(true);\n                yield* this.pushCount(line.length - n);\n                yield* this.pushNewline();\n                return yield* this.parseBlockScalar();\n            default:\n                return yield* this.parsePlainScalar();\n        }\n    }\n    *parseFlowCollection() {\n        let nl, sp;\n        let indent = -1;\n        do {\n            nl = yield* this.pushNewline();\n            if (nl > 0) {\n                sp = yield* this.pushSpaces(false);\n                this.indentValue = indent = sp;\n            }\n            else {\n                sp = 0;\n            }\n            sp += yield* this.pushSpaces(true);\n        } while (nl + sp > 0);\n        const line = this.getLine();\n        if (line === null)\n            return this.setNext('flow');\n        if ((indent !== -1 && indent < this.indentNext && line[0] !== '#') ||\n            (indent === 0 &&\n                (line.startsWith('---') || line.startsWith('...')) &&\n                isEmpty(line[3]))) {\n            // Allowing for the terminal ] or } at the same (rather than greater)\n            // indent level as the initial [ or { is technically invalid, but\n            // failing here would be surprising to users.\n            const atFlowEndMarker = indent === this.indentNext - 1 &&\n                this.flowLevel === 1 &&\n                (line[0] === ']' || line[0] === '}');\n            if (!atFlowEndMarker) {\n                // this is an error\n                this.flowLevel = 0;\n                yield cst.FLOW_END;\n                return yield* this.parseLineStart();\n            }\n        }\n        let n = 0;\n        while (line[n] === ',') {\n            n += yield* this.pushCount(1);\n            n += yield* this.pushSpaces(true);\n            this.flowKey = false;\n        }\n        n += yield* this.pushIndicators();\n        switch (line[n]) {\n            case undefined:\n                return 'flow';\n            case '#':\n                yield* this.pushCount(line.length - n);\n                return 'flow';\n            case '{':\n            case '[':\n                yield* this.pushCount(1);\n                this.flowKey = false;\n                this.flowLevel += 1;\n                return 'flow';\n            case '}':\n            case ']':\n                yield* this.pushCount(1);\n                this.flowKey = true;\n                this.flowLevel -= 1;\n                return this.flowLevel ? 'flow' : 'doc';\n            case '*':\n                yield* this.pushUntil(isNotAnchorChar);\n                return 'flow';\n            case '\"':\n            case \"'\":\n                this.flowKey = true;\n                return yield* this.parseQuotedScalar();\n            case ':': {\n                const next = this.charAt(1);\n                if (this.flowKey || isEmpty(next) || next === ',') {\n                    this.flowKey = false;\n                    yield* this.pushCount(1);\n                    yield* this.pushSpaces(true);\n                    return 'flow';\n                }\n            }\n            // fallthrough\n            default:\n                this.flowKey = false;\n                return yield* this.parsePlainScalar();\n        }\n    }\n    *parseQuotedScalar() {\n        const quote = this.charAt(0);\n        let end = this.buffer.indexOf(quote, this.pos + 1);\n        if (quote === \"'\") {\n            while (end !== -1 && this.buffer[end + 1] === \"'\")\n                end = this.buffer.indexOf(\"'\", end + 2);\n        }\n        else {\n            // double-quote\n            while (end !== -1) {\n                let n = 0;\n                while (this.buffer[end - 1 - n] === '\\\\')\n                    n += 1;\n                if (n % 2 === 0)\n                    break;\n                end = this.buffer.indexOf('\"', end + 1);\n            }\n        }\n        // Only looking for newlines within the quotes\n        const qb = this.buffer.substring(0, end);\n        let nl = qb.indexOf('\\n', this.pos);\n        if (nl !== -1) {\n            while (nl !== -1) {\n                const cs = this.continueScalar(nl + 1);\n                if (cs === -1)\n                    break;\n                nl = qb.indexOf('\\n', cs);\n            }\n            if (nl !== -1) {\n                // this is an error caused by an unexpected unindent\n                end = nl - (qb[nl - 1] === '\\r' ? 2 : 1);\n            }\n        }\n        if (end === -1) {\n            if (!this.atEnd)\n                return this.setNext('quoted-scalar');\n            end = this.buffer.length;\n        }\n        yield* this.pushToIndex(end + 1, false);\n        return this.flowLevel ? 'flow' : 'doc';\n    }\n    *parseBlockScalarHeader() {\n        this.blockScalarIndent = -1;\n        this.blockScalarKeep = false;\n        let i = this.pos;\n        while (true) {\n            const ch = this.buffer[++i];\n            if (ch === '+')\n                this.blockScalarKeep = true;\n            else if (ch > '0' && ch <= '9')\n                this.blockScalarIndent = Number(ch) - 1;\n            else if (ch !== '-')\n                break;\n        }\n        return yield* this.pushUntil(ch => isEmpty(ch) || ch === '#');\n    }\n    *parseBlockScalar() {\n        let nl = this.pos - 1; // may be -1 if this.pos === 0\n        let indent = 0;\n        let ch;\n        loop: for (let i = this.pos; (ch = this.buffer[i]); ++i) {\n            switch (ch) {\n                case ' ':\n                    indent += 1;\n                    break;\n                case '\\n':\n                    nl = i;\n                    indent = 0;\n                    break;\n                case '\\r': {\n                    const next = this.buffer[i + 1];\n                    if (!next && !this.atEnd)\n                        return this.setNext('block-scalar');\n                    if (next === '\\n')\n                        break;\n                } // fallthrough\n                default:\n                    break loop;\n            }\n        }\n        if (!ch && !this.atEnd)\n            return this.setNext('block-scalar');\n        if (indent >= this.indentNext) {\n            if (this.blockScalarIndent === -1)\n                this.indentNext = indent;\n            else {\n                this.indentNext =\n                    this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext);\n            }\n            do {\n                const cs = this.continueScalar(nl + 1);\n                if (cs === -1)\n                    break;\n                nl = this.buffer.indexOf('\\n', cs);\n            } while (nl !== -1);\n            if (nl === -1) {\n                if (!this.atEnd)\n                    return this.setNext('block-scalar');\n                nl = this.buffer.length;\n            }\n        }\n        // Trailing insufficiently indented tabs are invalid.\n        // To catch that during parsing, we include them in the block scalar value.\n        let i = nl + 1;\n        ch = this.buffer[i];\n        while (ch === ' ')\n            ch = this.buffer[++i];\n        if (ch === '\\t') {\n            while (ch === '\\t' || ch === ' ' || ch === '\\r' || ch === '\\n')\n                ch = this.buffer[++i];\n            nl = i - 1;\n        }\n        else if (!this.blockScalarKeep) {\n            do {\n                let i = nl - 1;\n                let ch = this.buffer[i];\n                if (ch === '\\r')\n                    ch = this.buffer[--i];\n                const lastChar = i; // Drop the line if last char not more indented\n                while (ch === ' ')\n                    ch = this.buffer[--i];\n                if (ch === '\\n' && i >= this.pos && i + 1 + indent > lastChar)\n                    nl = i;\n                else\n                    break;\n            } while (true);\n        }\n        yield cst.SCALAR;\n        yield* this.pushToIndex(nl + 1, true);\n        return yield* this.parseLineStart();\n    }\n    *parsePlainScalar() {\n        const inFlow = this.flowLevel > 0;\n        let end = this.pos - 1;\n        let i = this.pos - 1;\n        let ch;\n        while ((ch = this.buffer[++i])) {\n            if (ch === ':') {\n                const next = this.buffer[i + 1];\n                if (isEmpty(next) || (inFlow && flowIndicatorChars.has(next)))\n                    break;\n                end = i;\n            }\n            else if (isEmpty(ch)) {\n                let next = this.buffer[i + 1];\n                if (ch === '\\r') {\n                    if (next === '\\n') {\n                        i += 1;\n                        ch = '\\n';\n                        next = this.buffer[i + 1];\n                    }\n                    else\n                        end = i;\n                }\n                if (next === '#' || (inFlow && flowIndicatorChars.has(next)))\n                    break;\n                if (ch === '\\n') {\n                    const cs = this.continueScalar(i + 1);\n                    if (cs === -1)\n                        break;\n                    i = Math.max(i, cs - 2); // to advance, but still account for ' #'\n                }\n            }\n            else {\n                if (inFlow && flowIndicatorChars.has(ch))\n                    break;\n                end = i;\n            }\n        }\n        if (!ch && !this.atEnd)\n            return this.setNext('plain-scalar');\n        yield cst.SCALAR;\n        yield* this.pushToIndex(end + 1, true);\n        return inFlow ? 'flow' : 'doc';\n    }\n    *pushCount(n) {\n        if (n > 0) {\n            yield this.buffer.substr(this.pos, n);\n            this.pos += n;\n            return n;\n        }\n        return 0;\n    }\n    *pushToIndex(i, allowEmpty) {\n        const s = this.buffer.slice(this.pos, i);\n        if (s) {\n            yield s;\n            this.pos += s.length;\n            return s.length;\n        }\n        else if (allowEmpty)\n            yield '';\n        return 0;\n    }\n    *pushIndicators() {\n        let n = 0;\n        loop: while (true) {\n            switch (this.charAt(0)) {\n                case '!':\n                    n += yield* this.pushTag();\n                    n += yield* this.pushSpaces(true);\n                    continue loop;\n                case '&':\n                    n += yield* this.pushUntil(isNotAnchorChar);\n                    n += yield* this.pushSpaces(true);\n                    continue loop;\n                case '-': // this is an error\n                case '?': // this is an error outside flow collections\n                case ':': {\n                    const inFlow = this.flowLevel > 0;\n                    const ch1 = this.charAt(1);\n                    if (isEmpty(ch1) || (inFlow && flowIndicatorChars.has(ch1))) {\n                        if (!inFlow)\n                            this.indentNext = this.indentValue + 1;\n                        else if (this.flowKey)\n                            this.flowKey = false;\n                        n += yield* this.pushCount(1);\n                        n += yield* this.pushSpaces(true);\n                        continue loop;\n                    }\n                }\n            }\n            break loop;\n        }\n        return n;\n    }\n    *pushTag() {\n        if (this.charAt(1) === '<') {\n            let i = this.pos + 2;\n            let ch = this.buffer[i];\n            while (!isEmpty(ch) && ch !== '>')\n                ch = this.buffer[++i];\n            return yield* this.pushToIndex(ch === '>' ? i + 1 : i, false);\n        }\n        else {\n            let i = this.pos + 1;\n            let ch = this.buffer[i];\n            while (ch) {\n                if (tagChars.has(ch))\n                    ch = this.buffer[++i];\n                else if (ch === '%' &&\n                    hexDigits.has(this.buffer[i + 1]) &&\n                    hexDigits.has(this.buffer[i + 2])) {\n                    ch = this.buffer[(i += 3)];\n                }\n                else\n                    break;\n            }\n            return yield* this.pushToIndex(i, false);\n        }\n    }\n    *pushNewline() {\n        const ch = this.buffer[this.pos];\n        if (ch === '\\n')\n            return yield* this.pushCount(1);\n        else if (ch === '\\r' && this.charAt(1) === '\\n')\n            return yield* this.pushCount(2);\n        else\n            return 0;\n    }\n    *pushSpaces(allowTabs) {\n        let i = this.pos - 1;\n        let ch;\n        do {\n            ch = this.buffer[++i];\n        } while (ch === ' ' || (allowTabs && ch === '\\t'));\n        const n = i - this.pos;\n        if (n > 0) {\n            yield this.buffer.substr(this.pos, n);\n            this.pos = i;\n        }\n        return n;\n    }\n    *pushUntil(test) {\n        let i = this.pos;\n        let ch = this.buffer[i];\n        while (!test(ch))\n            ch = this.buffer[++i];\n        return yield* this.pushToIndex(i, false);\n    }\n}\n\nexports.Lexer = Lexer;\n", "'use strict';\n\n/**\n * Tracks newlines during parsing in order to provide an efficient API for\n * determining the one-indexed `{ line, col }` position for any offset\n * within the input.\n */\nclass LineCounter {\n    constructor() {\n        this.lineStarts = [];\n        /**\n         * Should be called in ascending order. Otherwise, call\n         * `lineCounter.lineStarts.sort()` before calling `linePos()`.\n         */\n        this.addNewLine = (offset) => this.lineStarts.push(offset);\n        /**\n         * Performs a binary search and returns the 1-indexed { line, col }\n         * position of `offset`. If `line === 0`, `addNewLine` has never been\n         * called or `offset` is before the first known newline.\n         */\n        this.linePos = (offset) => {\n            let low = 0;\n            let high = this.lineStarts.length;\n            while (low < high) {\n                const mid = (low + high) >> 1; // Math.floor((low + high) / 2)\n                if (this.lineStarts[mid] < offset)\n                    low = mid + 1;\n                else\n                    high = mid;\n            }\n            if (this.lineStarts[low] === offset)\n                return { line: low + 1, col: 1 };\n            if (low === 0)\n                return { line: 0, col: offset };\n            const start = this.lineStarts[low - 1];\n            return { line: low, col: offset - start + 1 };\n        };\n    }\n}\n\nexports.LineCounter = LineCounter;\n", "'use strict';\n\nvar node_process = require('process');\nvar cst = require('./cst.js');\nvar lexer = require('./lexer.js');\n\nfunction includesToken(list, type) {\n    for (let i = 0; i < list.length; ++i)\n        if (list[i].type === type)\n            return true;\n    return false;\n}\nfunction findNonEmptyIndex(list) {\n    for (let i = 0; i < list.length; ++i) {\n        switch (list[i].type) {\n            case 'space':\n            case 'comment':\n            case 'newline':\n                break;\n            default:\n                return i;\n        }\n    }\n    return -1;\n}\nfunction isFlowToken(token) {\n    switch (token?.type) {\n        case 'alias':\n        case 'scalar':\n        case 'single-quoted-scalar':\n        case 'double-quoted-scalar':\n        case 'flow-collection':\n            return true;\n        default:\n            return false;\n    }\n}\nfunction getPrevProps(parent) {\n    switch (parent.type) {\n        case 'document':\n            return parent.start;\n        case 'block-map': {\n            const it = parent.items[parent.items.length - 1];\n            return it.sep ?? it.start;\n        }\n        case 'block-seq':\n            return parent.items[parent.items.length - 1].start;\n        /* istanbul ignore next should not happen */\n        default:\n            return [];\n    }\n}\n/** Note: May modify input array */\nfunction getFirstKeyStartProps(prev) {\n    if (prev.length === 0)\n        return [];\n    let i = prev.length;\n    loop: while (--i >= 0) {\n        switch (prev[i].type) {\n            case 'doc-start':\n            case 'explicit-key-ind':\n            case 'map-value-ind':\n            case 'seq-item-ind':\n            case 'newline':\n                break loop;\n        }\n    }\n    while (prev[++i]?.type === 'space') {\n        /* loop */\n    }\n    return prev.splice(i, prev.length);\n}\nfunction arrayPushArray(target, source) {\n    // May exhaust call stack with large `source` array\n    if (source.length < 1e5)\n        Array.prototype.push.apply(target, source);\n    else\n        for (let i = 0; i < source.length; ++i)\n            target.push(source[i]);\n}\nfunction fixFlowSeqItems(fc) {\n    if (fc.start.type === 'flow-seq-start') {\n        for (const it of fc.items) {\n            if (it.sep &&\n                !it.value &&\n                !includesToken(it.start, 'explicit-key-ind') &&\n                !includesToken(it.sep, 'map-value-ind')) {\n                if (it.key)\n                    it.value = it.key;\n                delete it.key;\n                if (isFlowToken(it.value)) {\n                    if (it.value.end)\n                        arrayPushArray(it.value.end, it.sep);\n                    else\n                        it.value.end = it.sep;\n                }\n                else\n                    arrayPushArray(it.start, it.sep);\n                delete it.sep;\n            }\n        }\n    }\n}\n/**\n * A YAML concrete syntax tree (CST) parser\n *\n * ```ts\n * const src: string = ...\n * for (const token of new Parser().parse(src)) {\n *   // token: Token\n * }\n * ```\n *\n * To use the parser with a user-provided lexer:\n *\n * ```ts\n * function* parse(source: string, lexer: Lexer) {\n *   const parser = new Parser()\n *   for (const lexeme of lexer.lex(source))\n *     yield* parser.next(lexeme)\n *   yield* parser.end()\n * }\n *\n * const src: string = ...\n * const lexer = new Lexer()\n * for (const token of parse(src, lexer)) {\n *   // token: Token\n * }\n * ```\n */\nclass Parser {\n    /**\n     * @param onNewLine - If defined, called separately with the start position of\n     *   each new line (in `parse()`, including the start of input).\n     */\n    constructor(onNewLine) {\n        /** If true, space and sequence indicators count as indentation */\n        this.atNewLine = true;\n        /** If true, next token is a scalar value */\n        this.atScalar = false;\n        /** Current indentation level */\n        this.indent = 0;\n        /** Current offset since the start of parsing */\n        this.offset = 0;\n        /** On the same line with a block map key */\n        this.onKeyLine = false;\n        /** Top indicates the node that's currently being built */\n        this.stack = [];\n        /** The source of the current token, set in parse() */\n        this.source = '';\n        /** The type of the current token, set in parse() */\n        this.type = '';\n        // Must be defined after `next()`\n        this.lexer = new lexer.Lexer();\n        this.onNewLine = onNewLine;\n    }\n    /**\n     * Parse `source` as a YAML stream.\n     * If `incomplete`, a part of the last line may be left as a buffer for the next call.\n     *\n     * Errors are not thrown, but yielded as `{ type: 'error', message }` tokens.\n     *\n     * @returns A generator of tokens representing each directive, document, and other structure.\n     */\n    *parse(source, incomplete = false) {\n        if (this.onNewLine && this.offset === 0)\n            this.onNewLine(0);\n        for (const lexeme of this.lexer.lex(source, incomplete))\n            yield* this.next(lexeme);\n        if (!incomplete)\n            yield* this.end();\n    }\n    /**\n     * Advance the parser by the `source` of one lexical token.\n     */\n    *next(source) {\n        this.source = source;\n        if (node_process.env.LOG_TOKENS)\n            console.log('|', cst.prettyToken(source));\n        if (this.atScalar) {\n            this.atScalar = false;\n            yield* this.step();\n            this.offset += source.length;\n            return;\n        }\n        const type = cst.tokenType(source);\n        if (!type) {\n            const message = `Not a YAML token: ${source}`;\n            yield* this.pop({ type: 'error', offset: this.offset, message, source });\n            this.offset += source.length;\n        }\n        else if (type === 'scalar') {\n            this.atNewLine = false;\n            this.atScalar = true;\n            this.type = 'scalar';\n        }\n        else {\n            this.type = type;\n            yield* this.step();\n            switch (type) {\n                case 'newline':\n                    this.atNewLine = true;\n                    this.indent = 0;\n                    if (this.onNewLine)\n                        this.onNewLine(this.offset + source.length);\n                    break;\n                case 'space':\n                    if (this.atNewLine && source[0] === ' ')\n                        this.indent += source.length;\n                    break;\n                case 'explicit-key-ind':\n                case 'map-value-ind':\n                case 'seq-item-ind':\n                    if (this.atNewLine)\n                        this.indent += source.length;\n                    break;\n                case 'doc-mode':\n                case 'flow-error-end':\n                    return;\n                default:\n                    this.atNewLine = false;\n            }\n            this.offset += source.length;\n        }\n    }\n    /** Call at end of input to push out any remaining constructions */\n    *end() {\n        while (this.stack.length > 0)\n            yield* this.pop();\n    }\n    get sourceToken() {\n        const st = {\n            type: this.type,\n            offset: this.offset,\n            indent: this.indent,\n            source: this.source\n        };\n        return st;\n    }\n    *step() {\n        const top = this.peek(1);\n        if (this.type === 'doc-end' && top?.type !== 'doc-end') {\n            while (this.stack.length > 0)\n                yield* this.pop();\n            this.stack.push({\n                type: 'doc-end',\n                offset: this.offset,\n                source: this.source\n            });\n            return;\n        }\n        if (!top)\n            return yield* this.stream();\n        switch (top.type) {\n            case 'document':\n                return yield* this.document(top);\n            case 'alias':\n            case 'scalar':\n            case 'single-quoted-scalar':\n            case 'double-quoted-scalar':\n                return yield* this.scalar(top);\n            case 'block-scalar':\n                return yield* this.blockScalar(top);\n            case 'block-map':\n                return yield* this.blockMap(top);\n            case 'block-seq':\n                return yield* this.blockSequence(top);\n            case 'flow-collection':\n                return yield* this.flowCollection(top);\n            case 'doc-end':\n                return yield* this.documentEnd(top);\n        }\n        /* istanbul ignore next should not happen */\n        yield* this.pop();\n    }\n    peek(n) {\n        return this.stack[this.stack.length - n];\n    }\n    *pop(error) {\n        const token = error ?? this.stack.pop();\n        /* istanbul ignore if should not happen */\n        if (!token) {\n            const message = 'Tried to pop an empty stack';\n            yield { type: 'error', offset: this.offset, source: '', message };\n        }\n        else if (this.stack.length === 0) {\n            yield token;\n        }\n        else {\n            const top = this.peek(1);\n            if (token.type === 'block-scalar') {\n                // Block scalars use their parent rather than header indent\n                token.indent = 'indent' in top ? top.indent : 0;\n            }\n            else if (token.type === 'flow-collection' && top.type === 'document') {\n                // Ignore all indent for top-level flow collections\n                token.indent = 0;\n            }\n            if (token.type === 'flow-collection')\n                fixFlowSeqItems(token);\n            switch (top.type) {\n                case 'document':\n                    top.value = token;\n                    break;\n                case 'block-scalar':\n                    top.props.push(token); // error\n                    break;\n                case 'block-map': {\n                    const it = top.items[top.items.length - 1];\n                    if (it.value) {\n                        top.items.push({ start: [], key: token, sep: [] });\n                        this.onKeyLine = true;\n                        return;\n                    }\n                    else if (it.sep) {\n                        it.value = token;\n                    }\n                    else {\n                        Object.assign(it, { key: token, sep: [] });\n                        this.onKeyLine = !it.explicitKey;\n                        return;\n                    }\n                    break;\n                }\n                case 'block-seq': {\n                    const it = top.items[top.items.length - 1];\n                    if (it.value)\n                        top.items.push({ start: [], value: token });\n                    else\n                        it.value = token;\n                    break;\n                }\n                case 'flow-collection': {\n                    const it = top.items[top.items.length - 1];\n                    if (!it || it.value)\n                        top.items.push({ start: [], key: token, sep: [] });\n                    else if (it.sep)\n                        it.value = token;\n                    else\n                        Object.assign(it, { key: token, sep: [] });\n                    return;\n                }\n                /* istanbul ignore next should not happen */\n                default:\n                    yield* this.pop();\n                    yield* this.pop(token);\n            }\n            if ((top.type === 'document' ||\n                top.type === 'block-map' ||\n                top.type === 'block-seq') &&\n                (token.type === 'block-map' || token.type === 'block-seq')) {\n                const last = token.items[token.items.length - 1];\n                if (last &&\n                    !last.sep &&\n                    !last.value &&\n                    last.start.length > 0 &&\n                    findNonEmptyIndex(last.start) === -1 &&\n                    (token.indent === 0 ||\n                        last.start.every(st => st.type !== 'comment' || st.indent < token.indent))) {\n                    if (top.type === 'document')\n                        top.end = last.start;\n                    else\n                        top.items.push({ start: last.start });\n                    token.items.splice(-1, 1);\n                }\n            }\n        }\n    }\n    *stream() {\n        switch (this.type) {\n            case 'directive-line':\n                yield { type: 'directive', offset: this.offset, source: this.source };\n                return;\n            case 'byte-order-mark':\n            case 'space':\n            case 'comment':\n            case 'newline':\n                yield this.sourceToken;\n                return;\n            case 'doc-mode':\n            case 'doc-start': {\n                const doc = {\n                    type: 'document',\n                    offset: this.offset,\n                    start: []\n                };\n                if (this.type === 'doc-start')\n                    doc.start.push(this.sourceToken);\n                this.stack.push(doc);\n                return;\n            }\n        }\n        yield {\n            type: 'error',\n            offset: this.offset,\n            message: `Unexpected ${this.type} token in YAML stream`,\n            source: this.source\n        };\n    }\n    *document(doc) {\n        if (doc.value)\n            return yield* this.lineEnd(doc);\n        switch (this.type) {\n            case 'doc-start': {\n                if (findNonEmptyIndex(doc.start) !== -1) {\n                    yield* this.pop();\n                    yield* this.step();\n                }\n                else\n                    doc.start.push(this.sourceToken);\n                return;\n            }\n            case 'anchor':\n            case 'tag':\n            case 'space':\n            case 'comment':\n            case 'newline':\n                doc.start.push(this.sourceToken);\n                return;\n        }\n        const bv = this.startBlockValue(doc);\n        if (bv)\n            this.stack.push(bv);\n        else {\n            yield {\n                type: 'error',\n                offset: this.offset,\n                message: `Unexpected ${this.type} token in YAML document`,\n                source: this.source\n            };\n        }\n    }\n    *scalar(scalar) {\n        if (this.type === 'map-value-ind') {\n            const prev = getPrevProps(this.peek(2));\n            const start = getFirstKeyStartProps(prev);\n            let sep;\n            if (scalar.end) {\n                sep = scalar.end;\n                sep.push(this.sourceToken);\n                delete scalar.end;\n            }\n            else\n                sep = [this.sourceToken];\n            const map = {\n                type: 'block-map',\n                offset: scalar.offset,\n                indent: scalar.indent,\n                items: [{ start, key: scalar, sep }]\n            };\n            this.onKeyLine = true;\n            this.stack[this.stack.length - 1] = map;\n        }\n        else\n            yield* this.lineEnd(scalar);\n    }\n    *blockScalar(scalar) {\n        switch (this.type) {\n            case 'space':\n            case 'comment':\n            case 'newline':\n                scalar.props.push(this.sourceToken);\n                return;\n            case 'scalar':\n                scalar.source = this.source;\n                // block-scalar source includes trailing newline\n                this.atNewLine = true;\n                this.indent = 0;\n                if (this.onNewLine) {\n                    let nl = this.source.indexOf('\\n') + 1;\n                    while (nl !== 0) {\n                        this.onNewLine(this.offset + nl);\n                        nl = this.source.indexOf('\\n', nl) + 1;\n                    }\n                }\n                yield* this.pop();\n                break;\n            /* istanbul ignore next should not happen */\n            default:\n                yield* this.pop();\n                yield* this.step();\n        }\n    }\n    *blockMap(map) {\n        const it = map.items[map.items.length - 1];\n        // it.sep is true-ish if pair already has key or : separator\n        switch (this.type) {\n            case 'newline':\n                this.onKeyLine = false;\n                if (it.value) {\n                    const end = 'end' in it.value ? it.value.end : undefined;\n                    const last = Array.isArray(end) ? end[end.length - 1] : undefined;\n                    if (last?.type === 'comment')\n                        end?.push(this.sourceToken);\n                    else\n                        map.items.push({ start: [this.sourceToken] });\n                }\n                else if (it.sep) {\n                    it.sep.push(this.sourceToken);\n                }\n                else {\n                    it.start.push(this.sourceToken);\n                }\n                return;\n            case 'space':\n            case 'comment':\n                if (it.value) {\n                    map.items.push({ start: [this.sourceToken] });\n                }\n                else if (it.sep) {\n                    it.sep.push(this.sourceToken);\n                }\n                else {\n                    if (this.atIndentedComment(it.start, map.indent)) {\n                        const prev = map.items[map.items.length - 2];\n                        const end = prev?.value?.end;\n                        if (Array.isArray(end)) {\n                            arrayPushArray(end, it.start);\n                            end.push(this.sourceToken);\n                            map.items.pop();\n                            return;\n                        }\n                    }\n                    it.start.push(this.sourceToken);\n                }\n                return;\n        }\n        if (this.indent >= map.indent) {\n            const atMapIndent = !this.onKeyLine && this.indent === map.indent;\n            const atNextItem = atMapIndent &&\n                (it.sep || it.explicitKey) &&\n                this.type !== 'seq-item-ind';\n            // For empty nodes, assign newline-separated not indented empty tokens to following node\n            let start = [];\n            if (atNextItem && it.sep && !it.value) {\n                const nl = [];\n                for (let i = 0; i < it.sep.length; ++i) {\n                    const st = it.sep[i];\n                    switch (st.type) {\n                        case 'newline':\n                            nl.push(i);\n                            break;\n                        case 'space':\n                            break;\n                        case 'comment':\n                            if (st.indent > map.indent)\n                                nl.length = 0;\n                            break;\n                        default:\n                            nl.length = 0;\n                    }\n                }\n                if (nl.length >= 2)\n                    start = it.sep.splice(nl[1]);\n            }\n            switch (this.type) {\n                case 'anchor':\n                case 'tag':\n                    if (atNextItem || it.value) {\n                        start.push(this.sourceToken);\n                        map.items.push({ start });\n                        this.onKeyLine = true;\n                    }\n                    else if (it.sep) {\n                        it.sep.push(this.sourceToken);\n                    }\n                    else {\n                        it.start.push(this.sourceToken);\n                    }\n                    return;\n                case 'explicit-key-ind':\n                    if (!it.sep && !it.explicitKey) {\n                        it.start.push(this.sourceToken);\n                        it.explicitKey = true;\n                    }\n                    else if (atNextItem || it.value) {\n                        start.push(this.sourceToken);\n                        map.items.push({ start, explicitKey: true });\n                    }\n                    else {\n                        this.stack.push({\n                            type: 'block-map',\n                            offset: this.offset,\n                            indent: this.indent,\n                            items: [{ start: [this.sourceToken], explicitKey: true }]\n                        });\n                    }\n                    this.onKeyLine = true;\n                    return;\n                case 'map-value-ind':\n                    if (it.explicitKey) {\n                        if (!it.sep) {\n                            if (includesToken(it.start, 'newline')) {\n                                Object.assign(it, { key: null, sep: [this.sourceToken] });\n                            }\n                            else {\n                                const start = getFirstKeyStartProps(it.start);\n                                this.stack.push({\n                                    type: 'block-map',\n                                    offset: this.offset,\n                                    indent: this.indent,\n                                    items: [{ start, key: null, sep: [this.sourceToken] }]\n                                });\n                            }\n                        }\n                        else if (it.value) {\n                            map.items.push({ start: [], key: null, sep: [this.sourceToken] });\n                        }\n                        else if (includesToken(it.sep, 'map-value-ind')) {\n                            this.stack.push({\n                                type: 'block-map',\n                                offset: this.offset,\n                                indent: this.indent,\n                                items: [{ start, key: null, sep: [this.sourceToken] }]\n                            });\n                        }\n                        else if (isFlowToken(it.key) &&\n                            !includesToken(it.sep, 'newline')) {\n                            const start = getFirstKeyStartProps(it.start);\n                            const key = it.key;\n                            const sep = it.sep;\n                            sep.push(this.sourceToken);\n                            // @ts-expect-error type guard is wrong here\n                            delete it.key;\n                            // @ts-expect-error type guard is wrong here\n                            delete it.sep;\n                            this.stack.push({\n                                type: 'block-map',\n                                offset: this.offset,\n                                indent: this.indent,\n                                items: [{ start, key, sep }]\n                            });\n                        }\n                        else if (start.length > 0) {\n                            // Not actually at next item\n                            it.sep = it.sep.concat(start, this.sourceToken);\n                        }\n                        else {\n                            it.sep.push(this.sourceToken);\n                        }\n                    }\n                    else {\n                        if (!it.sep) {\n                            Object.assign(it, { key: null, sep: [this.sourceToken] });\n                        }\n                        else if (it.value || atNextItem) {\n                            map.items.push({ start, key: null, sep: [this.sourceToken] });\n                        }\n                        else if (includesToken(it.sep, 'map-value-ind')) {\n                            this.stack.push({\n                                type: 'block-map',\n                                offset: this.offset,\n                                indent: this.indent,\n                                items: [{ start: [], key: null, sep: [this.sourceToken] }]\n                            });\n                        }\n                        else {\n                            it.sep.push(this.sourceToken);\n                        }\n                    }\n                    this.onKeyLine = true;\n                    return;\n                case 'alias':\n                case 'scalar':\n                case 'single-quoted-scalar':\n                case 'double-quoted-scalar': {\n                    const fs = this.flowScalar(this.type);\n                    if (atNextItem || it.value) {\n                        map.items.push({ start, key: fs, sep: [] });\n                        this.onKeyLine = true;\n                    }\n                    else if (it.sep) {\n                        this.stack.push(fs);\n                    }\n                    else {\n                        Object.assign(it, { key: fs, sep: [] });\n                        this.onKeyLine = true;\n                    }\n                    return;\n                }\n                default: {\n                    const bv = this.startBlockValue(map);\n                    if (bv) {\n                        if (bv.type === 'block-seq') {\n                            if (!it.explicitKey &&\n                                it.sep &&\n                                !includesToken(it.sep, 'newline')) {\n                                yield* this.pop({\n                                    type: 'error',\n                                    offset: this.offset,\n                                    message: 'Unexpected block-seq-ind on same line with key',\n                                    source: this.source\n                                });\n                                return;\n                            }\n                        }\n                        else if (atMapIndent) {\n                            map.items.push({ start });\n                        }\n                        this.stack.push(bv);\n                        return;\n                    }\n                }\n            }\n        }\n        yield* this.pop();\n        yield* this.step();\n    }\n    *blockSequence(seq) {\n        const it = seq.items[seq.items.length - 1];\n        switch (this.type) {\n            case 'newline':\n                if (it.value) {\n                    const end = 'end' in it.value ? it.value.end : undefined;\n                    const last = Array.isArray(end) ? end[end.length - 1] : undefined;\n                    if (last?.type === 'comment')\n                        end?.push(this.sourceToken);\n                    else\n                        seq.items.push({ start: [this.sourceToken] });\n                }\n                else\n                    it.start.push(this.sourceToken);\n                return;\n            case 'space':\n            case 'comment':\n                if (it.value)\n                    seq.items.push({ start: [this.sourceToken] });\n                else {\n                    if (this.atIndentedComment(it.start, seq.indent)) {\n                        const prev = seq.items[seq.items.length - 2];\n                        const end = prev?.value?.end;\n                        if (Array.isArray(end)) {\n                            arrayPushArray(end, it.start);\n                            end.push(this.sourceToken);\n                            seq.items.pop();\n                            return;\n                        }\n                    }\n                    it.start.push(this.sourceToken);\n                }\n                return;\n            case 'anchor':\n            case 'tag':\n                if (it.value || this.indent <= seq.indent)\n                    break;\n                it.start.push(this.sourceToken);\n                return;\n            case 'seq-item-ind':\n                if (this.indent !== seq.indent)\n                    break;\n                if (it.value || includesToken(it.start, 'seq-item-ind'))\n                    seq.items.push({ start: [this.sourceToken] });\n                else\n                    it.start.push(this.sourceToken);\n                return;\n        }\n        if (this.indent > seq.indent) {\n            const bv = this.startBlockValue(seq);\n            if (bv) {\n                this.stack.push(bv);\n                return;\n            }\n        }\n        yield* this.pop();\n        yield* this.step();\n    }\n    *flowCollection(fc) {\n        const it = fc.items[fc.items.length - 1];\n        if (this.type === 'flow-error-end') {\n            let top;\n            do {\n                yield* this.pop();\n                top = this.peek(1);\n            } while (top?.type === 'flow-collection');\n        }\n        else if (fc.end.length === 0) {\n            switch (this.type) {\n                case 'comma':\n                case 'explicit-key-ind':\n                    if (!it || it.sep)\n                        fc.items.push({ start: [this.sourceToken] });\n                    else\n                        it.start.push(this.sourceToken);\n                    return;\n                case 'map-value-ind':\n                    if (!it || it.value)\n                        fc.items.push({ start: [], key: null, sep: [this.sourceToken] });\n                    else if (it.sep)\n                        it.sep.push(this.sourceToken);\n                    else\n                        Object.assign(it, { key: null, sep: [this.sourceToken] });\n                    return;\n                case 'space':\n                case 'comment':\n                case 'newline':\n                case 'anchor':\n                case 'tag':\n                    if (!it || it.value)\n                        fc.items.push({ start: [this.sourceToken] });\n                    else if (it.sep)\n                        it.sep.push(this.sourceToken);\n                    else\n                        it.start.push(this.sourceToken);\n                    return;\n                case 'alias':\n                case 'scalar':\n                case 'single-quoted-scalar':\n                case 'double-quoted-scalar': {\n                    const fs = this.flowScalar(this.type);\n                    if (!it || it.value)\n                        fc.items.push({ start: [], key: fs, sep: [] });\n                    else if (it.sep)\n                        this.stack.push(fs);\n                    else\n                        Object.assign(it, { key: fs, sep: [] });\n                    return;\n                }\n                case 'flow-map-end':\n                case 'flow-seq-end':\n                    fc.end.push(this.sourceToken);\n                    return;\n            }\n            const bv = this.startBlockValue(fc);\n            /* istanbul ignore else should not happen */\n            if (bv)\n                this.stack.push(bv);\n            else {\n                yield* this.pop();\n                yield* this.step();\n            }\n        }\n        else {\n            const parent = this.peek(2);\n            if (parent.type === 'block-map' &&\n                ((this.type === 'map-value-ind' && parent.indent === fc.indent) ||\n                    (this.type === 'newline' &&\n                        !parent.items[parent.items.length - 1].sep))) {\n                yield* this.pop();\n                yield* this.step();\n            }\n            else if (this.type === 'map-value-ind' &&\n                parent.type !== 'flow-collection') {\n                const prev = getPrevProps(parent);\n                const start = getFirstKeyStartProps(prev);\n                fixFlowSeqItems(fc);\n                const sep = fc.end.splice(1, fc.end.length);\n                sep.push(this.sourceToken);\n                const map = {\n                    type: 'block-map',\n                    offset: fc.offset,\n                    indent: fc.indent,\n                    items: [{ start, key: fc, sep }]\n                };\n                this.onKeyLine = true;\n                this.stack[this.stack.length - 1] = map;\n            }\n            else {\n                yield* this.lineEnd(fc);\n            }\n        }\n    }\n    flowScalar(type) {\n        if (this.onNewLine) {\n            let nl = this.source.indexOf('\\n') + 1;\n            while (nl !== 0) {\n                this.onNewLine(this.offset + nl);\n                nl = this.source.indexOf('\\n', nl) + 1;\n            }\n        }\n        return {\n            type,\n            offset: this.offset,\n            indent: this.indent,\n            source: this.source\n        };\n    }\n    startBlockValue(parent) {\n        switch (this.type) {\n            case 'alias':\n            case 'scalar':\n            case 'single-quoted-scalar':\n            case 'double-quoted-scalar':\n                return this.flowScalar(this.type);\n            case 'block-scalar-header':\n                return {\n                    type: 'block-scalar',\n                    offset: this.offset,\n                    indent: this.indent,\n                    props: [this.sourceToken],\n                    source: ''\n                };\n            case 'flow-map-start':\n            case 'flow-seq-start':\n                return {\n                    type: 'flow-collection',\n                    offset: this.offset,\n                    indent: this.indent,\n                    start: this.sourceToken,\n                    items: [],\n                    end: []\n                };\n            case 'seq-item-ind':\n                return {\n                    type: 'block-seq',\n                    offset: this.offset,\n                    indent: this.indent,\n                    items: [{ start: [this.sourceToken] }]\n                };\n            case 'explicit-key-ind': {\n                this.onKeyLine = true;\n                const prev = getPrevProps(parent);\n                const start = getFirstKeyStartProps(prev);\n                start.push(this.sourceToken);\n                return {\n                    type: 'block-map',\n                    offset: this.offset,\n                    indent: this.indent,\n                    items: [{ start, explicitKey: true }]\n                };\n            }\n            case 'map-value-ind': {\n                this.onKeyLine = true;\n                const prev = getPrevProps(parent);\n                const start = getFirstKeyStartProps(prev);\n                return {\n                    type: 'block-map',\n                    offset: this.offset,\n                    indent: this.indent,\n                    items: [{ start, key: null, sep: [this.sourceToken] }]\n                };\n            }\n        }\n        return null;\n    }\n    atIndentedComment(start, indent) {\n        if (this.type !== 'comment')\n            return false;\n        if (this.indent <= indent)\n            return false;\n        return start.every(st => st.type === 'newline' || st.type === 'space');\n    }\n    *documentEnd(docEnd) {\n        if (this.type !== 'doc-mode') {\n            if (docEnd.end)\n                docEnd.end.push(this.sourceToken);\n            else\n                docEnd.end = [this.sourceToken];\n            if (this.type === 'newline')\n                yield* this.pop();\n        }\n    }\n    *lineEnd(token) {\n        switch (this.type) {\n            case 'comma':\n            case 'doc-start':\n            case 'doc-end':\n            case 'flow-seq-end':\n            case 'flow-map-end':\n            case 'map-value-ind':\n                yield* this.pop();\n                yield* this.step();\n                break;\n            case 'newline':\n                this.onKeyLine = false;\n            // fallthrough\n            case 'space':\n            case 'comment':\n            default:\n                // all other values are errors\n                if (token.end)\n                    token.end.push(this.sourceToken);\n                else\n                    token.end = [this.sourceToken];\n                if (this.type === 'newline')\n                    yield* this.pop();\n        }\n    }\n}\n\nexports.Parser = Parser;\n", "'use strict';\n\nvar composer = require('./compose/composer.js');\nvar Document = require('./doc/Document.js');\nvar errors = require('./errors.js');\nvar log = require('./log.js');\nvar identity = require('./nodes/identity.js');\nvar lineCounter = require('./parse/line-counter.js');\nvar parser = require('./parse/parser.js');\n\nfunction parseOptions(options) {\n    const prettyErrors = options.prettyErrors !== false;\n    const lineCounter$1 = options.lineCounter || (prettyErrors && new lineCounter.LineCounter()) || null;\n    return { lineCounter: lineCounter$1, prettyErrors };\n}\n/**\n * Parse the input as a stream of YAML documents.\n *\n * Documents should be separated from each other by `...` or `---` marker lines.\n *\n * @returns If an empty `docs` array is returned, it will be of type\n *   EmptyStream and contain additional stream information. In\n *   TypeScript, you should use `'empty' in docs` as a type guard for it.\n */\nfunction parseAllDocuments(source, options = {}) {\n    const { lineCounter, prettyErrors } = parseOptions(options);\n    const parser$1 = new parser.Parser(lineCounter?.addNewLine);\n    const composer$1 = new composer.Composer(options);\n    const docs = Array.from(composer$1.compose(parser$1.parse(source)));\n    if (prettyErrors && lineCounter)\n        for (const doc of docs) {\n            doc.errors.forEach(errors.prettifyError(source, lineCounter));\n            doc.warnings.forEach(errors.prettifyError(source, lineCounter));\n        }\n    if (docs.length > 0)\n        return docs;\n    return Object.assign([], { empty: true }, composer$1.streamInfo());\n}\n/** Parse an input string into a single YAML.Document */\nfunction parseDocument(source, options = {}) {\n    const { lineCounter, prettyErrors } = parseOptions(options);\n    const parser$1 = new parser.Parser(lineCounter?.addNewLine);\n    const composer$1 = new composer.Composer(options);\n    // `doc` is always set by compose.end(true) at the very latest\n    let doc = null;\n    for (const _doc of composer$1.compose(parser$1.parse(source), true, source.length)) {\n        if (!doc)\n            doc = _doc;\n        else if (doc.options.logLevel !== 'silent') {\n            doc.errors.push(new errors.YAMLParseError(_doc.range.slice(0, 2), 'MULTIPLE_DOCS', 'Source contains multiple documents; please use YAML.parseAllDocuments()'));\n            break;\n        }\n    }\n    if (prettyErrors && lineCounter) {\n        doc.errors.forEach(errors.prettifyError(source, lineCounter));\n        doc.warnings.forEach(errors.prettifyError(source, lineCounter));\n    }\n    return doc;\n}\nfunction parse(src, reviver, options) {\n    let _reviver = undefined;\n    if (typeof reviver === 'function') {\n        _reviver = reviver;\n    }\n    else if (options === undefined && reviver && typeof reviver === 'object') {\n        options = reviver;\n    }\n    const doc = parseDocument(src, options);\n    if (!doc)\n        return null;\n    doc.warnings.forEach(warning => log.warn(doc.options.logLevel, warning));\n    if (doc.errors.length > 0) {\n        if (doc.options.logLevel !== 'silent')\n            throw doc.errors[0];\n        else\n            doc.errors = [];\n    }\n    return doc.toJS(Object.assign({ reviver: _reviver }, options));\n}\nfunction stringify(value, replacer, options) {\n    let _replacer = null;\n    if (typeof replacer === 'function' || Array.isArray(replacer)) {\n        _replacer = replacer;\n    }\n    else if (options === undefined && replacer) {\n        options = replacer;\n    }\n    if (typeof options === 'string')\n        options = options.length;\n    if (typeof options === 'number') {\n        const indent = Math.round(options);\n        options = indent < 1 ? undefined : indent > 8 ? { indent: 8 } : { indent };\n    }\n    if (value === undefined) {\n        const { keepUndefined } = options ?? replacer ?? {};\n        if (!keepUndefined)\n            return undefined;\n    }\n    if (identity.isDocument(value) && !_replacer)\n        return value.toString(options);\n    return new Document.Document(value, _replacer, options).toString(options);\n}\n\nexports.parse = parse;\nexports.parseAllDocuments = parseAllDocuments;\nexports.parseDocument = parseDocument;\nexports.stringify = stringify;\n", "'use strict';\n\nvar composer = require('./compose/composer.js');\nvar Document = require('./doc/Document.js');\nvar Schema = require('./schema/Schema.js');\nvar errors = require('./errors.js');\nvar Alias = require('./nodes/Alias.js');\nvar identity = require('./nodes/identity.js');\nvar Pair = require('./nodes/Pair.js');\nvar Scalar = require('./nodes/Scalar.js');\nvar YAMLMap = require('./nodes/YAMLMap.js');\nvar YAMLSeq = require('./nodes/YAMLSeq.js');\nvar cst = require('./parse/cst.js');\nvar lexer = require('./parse/lexer.js');\nvar lineCounter = require('./parse/line-counter.js');\nvar parser = require('./parse/parser.js');\nvar publicApi = require('./public-api.js');\nvar visit = require('./visit.js');\n\n\n\nexports.Composer = composer.Composer;\nexports.Document = Document.Document;\nexports.Schema = Schema.Schema;\nexports.YAMLError = errors.YAMLError;\nexports.YAMLParseError = errors.YAMLParseError;\nexports.YAMLWarning = errors.YAMLWarning;\nexports.Alias = Alias.Alias;\nexports.isAlias = identity.isAlias;\nexports.isCollection = identity.isCollection;\nexports.isDocument = identity.isDocument;\nexports.isMap = identity.isMap;\nexports.isNode = identity.isNode;\nexports.isPair = identity.isPair;\nexports.isScalar = identity.isScalar;\nexports.isSeq = identity.isSeq;\nexports.Pair = Pair.Pair;\nexports.Scalar = Scalar.Scalar;\nexports.YAMLMap = YAMLMap.YAMLMap;\nexports.YAMLSeq = YAMLSeq.YAMLSeq;\nexports.CST = cst;\nexports.Lexer = lexer.Lexer;\nexports.LineCounter = lineCounter.LineCounter;\nexports.Parser = parser.Parser;\nexports.parse = publicApi.parse;\nexports.parseAllDocuments = publicApi.parseAllDocuments;\nexports.parseDocument = publicApi.parseDocument;\nexports.stringify = publicApi.stringify;\nexports.visit = visit.visit;\nexports.visitAsync = visit.visitAsync;\n", "/**\n * Per-project worker preamble: `.pai/worker.md` at the project root (git\n * toplevel of the cwd; the cwd itself outside git). Its text is appended to the\n * worker's system prompt right after the contract, so a project's standing\n * rules ride on every worker started there.\n */\nimport { execFileSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport const PREAMBLE_REL = \".pai/worker.md\";\nexport const PREAMBLE_MAX_BYTES = 16 * 1024;\n\nexport interface Preamble {\n  path: string;\n  text: string;\n  lines: number;\n  hash: string;\n}\n\nexport type PreambleResult = { preamble: Preamble | null } | { refused: string };\n\nexport function projectRoot(cwd: string): string {\n  try {\n    return execFileSync(\"git\", [\"rev-parse\", \"--show-toplevel\"], {\n      cwd,\n      encoding: \"utf8\",\n      stdio: [\"ignore\", \"pipe\", \"ignore\"],\n    }).trim();\n  } catch {\n    return cwd;\n  }\n}\n\n/** Missing or empty file: no preamble. Over PREAMBLE_MAX_BYTES: refused, never truncated. */\nexport function loadPreamble(cwd: string): PreambleResult {\n  const path = join(projectRoot(cwd), PREAMBLE_REL);\n  if (!existsSync(path)) return { preamble: null };\n  const size = statSync(path).size;\n  if (size > PREAMBLE_MAX_BYTES) {\n    return { refused: `${path} is ${size} bytes, over the ${PREAMBLE_MAX_BYTES} byte limit \u2014 shorten it` };\n  }\n  const text = readFileSync(path, \"utf8\");\n  if (!text.trim()) return { preamble: null };\n  const hash = createHash(\"sha256\").update(text).digest(\"hex\").slice(0, 8);\n  return { preamble: { path, text: text.trim(), lines: text.trimEnd().split(\"\\n\").length, hash } };\n}\n\n/**\n * The system-prompt block: a fixed header plus the raw file text, nothing run-specific\n * (no ids, paths, hashes), so every worker of a project shares one cacheable prefix.\n */\nexport function preambleBlock(p: Preamble): string {\n  return `## Project worker rules\\n\\n${p.text}`;\n}\n\nexport function preambleLine(p: Preamble | null): string {\n  return p ? `preamble: ${PREAMBLE_REL} (${p.lines} lines)` : \"preamble: none\";\n}\n", "/**\n * types.ts \u2014 Unified Notification Framework type definitions\n *\n * Defines the channel registry, event routing, and configuration schema\n * for PAI's notification subsystem.\n */\n\n// ---------------------------------------------------------------------------\n// Channel identifiers\n// ---------------------------------------------------------------------------\n\nexport type ChannelId = \"ntfy\" | \"whatsapp\" | \"macos\" | \"voice\" | \"cli\";\n\n// ---------------------------------------------------------------------------\n// Notification event types\n// ---------------------------------------------------------------------------\n\n/**\n * The semantic type of a notification event.\n * Used to route events to the appropriate channels.\n */\nexport type NotificationEvent =\n  | \"error\"\n  | \"progress\"\n  | \"completion\"\n  | \"info\"\n  | \"debug\";\n\n// ---------------------------------------------------------------------------\n// Notification mode\n// ---------------------------------------------------------------------------\n\n/**\n * The current notification mode.\n *\n * - \"auto\"      \u2014 Use the per-event routing table (default)\n * - \"voice\"     \u2014 All events go to voice (WhatsApp TTS)\n * - \"whatsapp\"  \u2014 All events go to WhatsApp text\n * - \"ntfy\"      \u2014 All events go to ntfy.sh\n * - \"macos\"     \u2014 All events go to macOS notifications\n * - \"cli\"       \u2014 All events go to CLI stdout only\n * - \"off\"       \u2014 Suppress all notifications\n */\nexport type NotificationMode =\n  | \"auto\"\n  | \"voice\"\n  | \"whatsapp\"\n  | \"ntfy\"\n  | \"macos\"\n  | \"cli\"\n  | \"off\";\n\n// ---------------------------------------------------------------------------\n// Per-channel configuration\n// ---------------------------------------------------------------------------\n\nexport interface NtfyChannelConfig {\n  enabled: boolean;\n  /** ntfy.sh topic URL, e.g. \"https://ntfy.sh/my-topic\" */\n  url?: string;\n  /** ntfy priority: min | low | default | high | urgent */\n  priority?: \"min\" | \"low\" | \"default\" | \"high\" | \"urgent\";\n}\n\nexport interface WhatsAppChannelConfig {\n  enabled: boolean;\n  /** Optional recipient (phone, JID, or contact name). Omit for self-chat. */\n  recipient?: string;\n}\n\nexport interface MacOsChannelConfig {\n  enabled: boolean;\n}\n\nexport interface VoiceChannelConfig {\n  enabled: boolean;\n  /** Kokoro voice name, e.g. \"bm_george\", \"af_bella\". Default: \"bm_george\" */\n  voiceName?: string;\n}\n\nexport interface CliChannelConfig {\n  enabled: boolean;\n}\n\nexport interface ChannelConfigs {\n  ntfy: NtfyChannelConfig;\n  whatsapp: WhatsAppChannelConfig;\n  macos: MacOsChannelConfig;\n  voice: VoiceChannelConfig;\n  cli: CliChannelConfig;\n}\n\n// ---------------------------------------------------------------------------\n// Routing table\n// ---------------------------------------------------------------------------\n\n/**\n * Maps each event type to the ordered list of channels that should receive it.\n * Only channels that are enabled in `channels` and present in this list are used.\n */\nexport type RoutingTable = {\n  [K in NotificationEvent]: ChannelId[];\n};\n\nexport const DEFAULT_ROUTING: RoutingTable = {\n  error:      [\"whatsapp\", \"macos\", \"ntfy\", \"cli\"],\n  completion: [\"whatsapp\", \"macos\", \"ntfy\", \"cli\"],\n  info:       [\"cli\"],\n  progress:   [\"cli\"],\n  debug:      [],\n};\n\n// ---------------------------------------------------------------------------\n// Top-level notification config (embedded in PaiDaemonConfig)\n// ---------------------------------------------------------------------------\n\nexport interface NotificationConfig {\n  /** Current routing mode. Default: \"auto\" */\n  mode: NotificationMode;\n  /** Per-channel configuration */\n  channels: ChannelConfigs;\n  /** Event \u2192 channel routing (used in \"auto\" mode) */\n  routing: RoutingTable;\n}\n\nexport const DEFAULT_CHANNELS: ChannelConfigs = {\n  ntfy: {\n    enabled: false,\n    url: undefined,\n    priority: \"default\",\n  },\n  whatsapp: {\n    enabled: true,\n    recipient: undefined,\n  },\n  macos: {\n    enabled: true,\n  },\n  voice: {\n    enabled: false,\n    voiceName: \"bm_george\",\n  },\n  cli: {\n    enabled: true,\n  },\n};\n\nexport const DEFAULT_NOTIFICATION_CONFIG: NotificationConfig = {\n  mode: \"auto\",\n  channels: DEFAULT_CHANNELS,\n  routing: DEFAULT_ROUTING,\n};\n\n// ---------------------------------------------------------------------------\n// Notification payload\n// ---------------------------------------------------------------------------\n\nexport interface NotificationPayload {\n  /** Semantic event type \u2014 used for routing */\n  event: NotificationEvent;\n  /** The notification message body */\n  message: string;\n  /** Optional title (used by macOS, ntfy) */\n  title?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Provider interface\n// ---------------------------------------------------------------------------\n\nexport interface NotificationProvider {\n  readonly channelId: ChannelId;\n  /**\n   * Send a notification.\n   * Returns true on success, false on failure (failure is non-fatal).\n   */\n  send(payload: NotificationPayload, config: NotificationConfig): Promise<boolean>;\n}\n\n// ---------------------------------------------------------------------------\n// Send result\n// ---------------------------------------------------------------------------\n\nexport interface SendResult {\n  channelsAttempted: ChannelId[];\n  channelsSucceeded: ChannelId[];\n  channelsFailed: ChannelId[];\n  mode: NotificationMode;\n}\n", "/**\n * types.ts \u2014 Task Bus type definitions\n *\n * Defines the provider registry, ownership resolution, and configuration schema\n * for PAI's cross-session task subsystem.\n *\n * The task bus routes work between PAI sessions through an external tracker.\n * A session files a task; a routine reads it later and dispatches it to the\n * session that owns it \u2014 spawning one if none is running.\n *\n * See docs/task-bus.md for the architecture and its constraints.\n */\n\n// ---------------------------------------------------------------------------\n// Provider identifiers\n// ---------------------------------------------------------------------------\n\nexport type ProviderId = \"todoist\";\n\n// ---------------------------------------------------------------------------\n// Ownership\n// ---------------------------------------------------------------------------\n\n/**\n * Prefix marking a tracker label as a PAI ownership assertion.\n * A task labelled `pai:acme-api` is owned by the `acme-api` project.\n */\nexport const OWNER_LABEL_PREFIX = \"pai:\";\n\n/**\n * How a task's owner was determined. Recorded so the routine can explain\n * itself, and so a mis-resolution is diagnosable rather than silent.\n *\n * - \"label\"     \u2014 an explicit `pai:<project>` label (authoritative)\n * - \"container\" \u2014 the enclosing sub-project name matched a PAI alias (fallback)\n * - \"none\"      \u2014 unresolved; the task stays in the findings inbox\n */\nexport type OwnerSource = \"label\" | \"container\" | \"none\";\n\nexport interface TaskOwner {\n  /** Resolved PAI project short name, e.g. \"acme-api\". Null when UNROUTED. */\n  project: string | null;\n  /** Absolute path to the project root. Null when UNROUTED. */\n  rootPath: string | null;\n  source: OwnerSource;\n  /**\n   * The raw string that resolution was attempted against, kept for diagnostics\n   * when `source` is \"none\" \u2014 e.g. a \"Reading List \uD83D\uDCDA\" container matches no\n   * PAI project, which is expected rather than a fault.\n   */\n  rawHint?: string;\n}\n\n/** An unresolved owner. UNROUTED is a normal state, not an error. */\nexport const UNROUTED: TaskOwner = {\n  project: null,\n  rootPath: null,\n  source: \"none\",\n};\n\n// ---------------------------------------------------------------------------\n// Tasks\n// ---------------------------------------------------------------------------\n\nexport type TaskPriority = \"p1\" | \"p2\" | \"p3\" | \"p4\";\n\nexport interface Task {\n  /** Provider-native task ID. Opaque; never parsed. */\n  id: string;\n  title: string;\n  /**\n   * Full procedure AND reasoning \u2014 enough that the task is actionable months\n   * later, or by the user alone, without re-deriving anything. Enforced at\n   * filing time rather than left to discipline.\n   */\n  body: string;\n  owner: TaskOwner;\n  /** ISO 8601 date or datetime. Null when the task has no due date. */\n  due: string | null;\n  /**\n   * The tracker's own recurrence text, e.g. \"every day at 08:00\". Null for a\n   * one-off.\n   *\n   * Kept verbatim rather than parsed into a rule because it is also the only\n   * way to write a recurrence back: Todoist re-parses this string, and it is\n   * what lets a due date be restored without destroying the recurrence.\n   */\n  recurrence?: string | null;\n  priority: TaskPriority;\n  labels: string[];\n  /**\n   * Stable reference to the artifact this task is about. Prefer a `hook://`\n   * URL over a filesystem path \u2014 it survives renames and moves, and opens in\n   * DEVONthink To Go on iOS.\n   */\n  sourceUrl?: string;\n  /** True for organizational headers that cannot be completed. */\n  isHeader?: boolean;\n}\n\n/** A task being filed. `owner` is a project short name, resolved on write. */\nexport interface NewTask {\n  title: string;\n  body: string;\n  owner?: string | null;\n  due?: string;\n  priority?: TaskPriority;\n  labels?: string[];\n  sourceUrl?: string;\n  /**\n   * Sub-project to file into, created if absent.\n   *\n   * The convention is one sub-project per PAI project under the bus root. It\n   * previously existed only in the shape of the data, so every session had to\n   * re-derive it \u2014 and a session that inferred cautiously filed flat instead,\n   * which is exactly the pile the convention prevents.\n   */\n  into?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Provider interface\n// ---------------------------------------------------------------------------\n\nexport interface ListOptions {\n  /** Only tasks due on or before this ISO date. Omit for all open tasks. */\n  dueBefore?: string;\n  /** Restrict to one resolved owner. Omit for every owner. */\n  owner?: string;\n  /** Include tasks that resolved to UNROUTED. Default: true. */\n  includeUnrouted?: boolean;\n  limit?: number;\n}\n\nexport interface TaskProvider {\n  readonly providerId: ProviderId;\n\n  /**\n   * False when no credential is configured. The bus degrades to a no-op\n   * rather than failing \u2014 a user without a tracker still gets working PAI.\n   */\n  isConfigured(): boolean;\n\n  listOpen(opts: ListOptions): Promise<Task[]>;\n  add(task: NewTask): Promise<Task>;\n  complete(id: string): Promise<void>;\n\n  /**\n   * Rewrite a task's due date through the tracker's natural-language field.\n   *\n   * Optional, and deliberately expressed as a string rather than a date: a\n   * recurring task's schedule and its next occurrence are the same field, so\n   * moving the date without the rule silently downgrades a routine to a one-off.\n   * A provider that cannot express both at once should not offer this.\n   */\n  setDue?(id: string, dueString: string): Promise<void>;\n\n  /**\n   * Sub-projects under the bus root \u2014 the set of addresses a task can be filed\n   * against, one per session.\n   *\n   * Optional because it is not universal: a tracker may address work by tag or\n   * list rather than by nested project, and forcing a nesting concept onto one\n   * that has none would mean faking it. A provider without these simply does\n   * not offer session-scoped inboxes, and callers say so rather than failing.\n   */\n  listSubProjects?(): Promise<Array<{ id: string; name: string }>>;\n  findOrCreateSubProject?(name: string): Promise<{ id: string; created: boolean }>;\n\n  /**\n   * The comment thread on a task, oldest first.\n   *\n   * Optional because not every tracker has threaded comments. Where it exists,\n   * the thread is usually where the reasoning lives \u2014 the question, the answer,\n   * the correction \u2014 and completing the task takes it out of view. That is what\n   * the archive exists to keep.\n   */\n  listComments?(taskId: string): Promise<Array<{ id: string; content: string; postedAt?: string }>>;\n\n  /**\n   * One task by id, whether open or completed.\n   *\n   * Needed because archiving runs at or after completion, and a completed task\n   * is gone from `listOpen` \u2014 which is exactly the moment its discussion stops\n   * being visible and most needs keeping.\n   */\n  getTask?(id: string): Promise<Task | null>;\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\nexport interface TodoistProviderConfig {\n  enabled: boolean;\n  /**\n   * API token. Resolution order is apiKey \u2192 TODOIST_API_KEY env \u2192 unconfigured.\n   *\n   * Never read this from another tool's config file. PAI ships as a product;\n   * scraping ~/.claude.json for a key belonging to the Todoist MCP is not\n   * acceptable even though the key is sitting there.\n   */\n  apiKey?: string;\n  /**\n   * Tracker project ID that roots the bus (the \"Claude \uD83E\uDD16\" project).\n   *\n   * Stored as an ID, never a name. Todoist's project search silently returns\n   * zero results for names containing emoji \u2014 resolving by name would report\n   * \"no tasks\" instead of failing, which is the exact class of silent failure\n   * this subsystem exists to surface.\n   */\n  rootProjectId?: string;\n  /** Section ID for the findings inbox. Tasks land here when UNROUTED. */\n  findingsSectionId?: string;\n}\n\nexport interface TaskConfig {\n  /** Master switch. When false the bus is inert. */\n  enabled: boolean;\n  providers: {\n    todoist: TodoistProviderConfig;\n  };\n  /**\n   * Dispatch work to the owning session automatically, spawning one if absent.\n   * Requires AIBroker. When false \u2014 or when AIBroker is unavailable \u2014 PAI\n   * reports which project owns each task and leaves acting to the user.\n   */\n  autoDispatch: boolean;\n\n  /**\n   * Seconds AIBroker may spend on a single dispatch, spawn included.\n   *\n   * Passed down to the transport so both sides share one deadline. Raise it on\n   * a loaded machine where sessions are slow to start accepting input.\n   */\n  dispatchTimeoutSecs?: number;\n\n  /**\n   * Project a task goes to when it carries the bare `pai` marker and its\n   * location says nothing \u2014 an Inbox capture, typically.\n   *\n   * This is the one thing a task's location cannot express: \"an AI should take\n   * this, and I do not know which one yet\". Everything else is answered by the\n   * project the task sits in.\n   *\n   * Unset means such a task stays UNROUTED, which is a legitimate choice: it\n   * then surfaces in the findings inbox for triage rather than being guessed at.\n   */\n  defaultOwner?: string;\n}\n\nexport const DEFAULT_TASK_CONFIG: TaskConfig = {\n  enabled: false,\n  providers: {\n    todoist: {\n      enabled: false,\n    },\n  },\n  autoDispatch: false,\n};\n\n// ---------------------------------------------------------------------------\n// Dispatch results\n// ---------------------------------------------------------------------------\n\n/**\n * What happened to one task during a dispatch run.\n *\n * - \"delivered\"    \u2014 sent to an already-running session, and confirmed submitted\n * - \"queued\"       \u2014 typed into a live session that was mid-turn, so submission\n *                    could not be confirmed inside the window. This is delivery:\n *                    Claude Code holds typed input until the current turn ends.\n *                    Never retried \u2014 the text is already in the input box, so a\n *                    second attempt is a second copy, not a retry. One trigger\n *                    arrived three times on 2026-08-01 for exactly that reason.\n * - \"spawned\"      \u2014 none running; one was launched, came up, and received it\n * - \"unrouted\"     \u2014 no owner resolved; left in the findings inbox\n * - \"unlaunchable\" \u2014 an owner resolved but no PAI alias exists to launch it\n * - \"unreachable\"  \u2014 a session was launched but never became ready to accept input\n * - \"skipped\"      \u2014 autoDispatch is off, or no transport; reported only\n *\n * `unlaunchable` and `unreachable` are distinct because the fixes differ:\n * the first is a setup gap (register an alias), the second is a runtime\n * failure (find out why the session did not come up). Collapsing them would\n * send users looking in the wrong place.\n *\n * None of these are errors. A task that could not be delivered is a routing\n * result to report, not an exception to throw.\n */\nexport type DispatchOutcome =\n  | \"delivered\"\n  | \"queued\"\n  | \"spawned\"\n  | \"unrouted\"\n  | \"unlaunchable\"\n  | \"unreachable\"\n  | \"skipped\";\n\nexport interface DispatchResult {\n  task: Task;\n  outcome: DispatchOutcome;\n  /** Session the task reached, when it reached one. */\n  session?: string;\n  /** Why the task did not reach a session. Present on failure outcomes. */\n  reason?: string;\n}\n", "/**\n * runtime-paths.ts \u2014 the shared files PAI talks to the running daemon through.\n *\n * Sockets, logs and pidfiles live at fixed absolute paths under /tmp, and every\n * one of them is shared with a *live* daemon. That makes them the same hazard\n * as any other piece of real user state a test can reach: writing one from a\n * test does not fail, it corrupts something already running, silently.\n *\n * The home guard in test/setup-home-guard.ts does not cover them. It works by\n * redirecting HOME, so it protects paths *derived* from the home directory and\n * nothing else \u2014 a hardcoded \"/tmp/pai.sock\" walks straight past it. Verified\n * on 2026-08-01 by snapshotting all five and running the suite: nothing writes\n * to them today. But \"no test does this yet\" is not a property, and the next\n * daemon test to be written would clobber the live socket with nothing to stop\n * it.\n *\n * So each path is read from an environment variable with the historical /tmp\n * value as its default. Behaviour is unchanged for every real invocation; the\n * test setup overrides the variables and the whole class becomes unreachable\n * rather than merely absent, the same way the home guard works.\n *\n * Read at call time, not at module load: the test setup file runs before any\n * module is imported, but resolving these eagerly would still bake in whatever\n * the environment held at import and make the override order matter.\n */\n\nimport { paiHomePath } from \"./config/pai-home.js\";\n\n/** IPC socket for the PAI daemon. */\nexport function paiSocketPath(): string {\n  return process.env.PAI_SOCKET_PATH ?? \"/tmp/pai.sock\";\n}\n\n/** IPC socket for the AIBroker daemon \u2014 read by PAI, owned by AIBroker. */\nexport function aibrokerSocketPath(): string {\n  return process.env.PAI_AIBROKER_SOCKET_PATH ?? \"/tmp/aibroker.sock\";\n}\n\n/** Where the daemon's stdout and stderr are collected. */\nexport function daemonLogPath(): string {\n  return process.env.PAI_DAEMON_LOG_PATH ?? \"/tmp/pai-daemon.log\";\n}\n\n/** Pidfile for the running daemon. */\nexport function daemonPidPath(): string {\n  return process.env.PAI_DAEMON_PID_PATH ?? paiHomePath(\"daemon.pid\");\n}\n\n/** Where the launchd scheduler tick writes its output. */\nexport function schedulerLogPath(): string {\n  return process.env.PAI_SCHEDULER_LOG_PATH ?? \"/tmp/pai-scheduler.log\";\n}\n\n/**\n * Where the status line caches what it may reuse between renders: the\n * per-provider plan-usage snapshots and the Claude Code version. Shared with\n * every live session's status line, so a test writing here would be feeding\n * fixtures to the real thing. Read by statusline-command.sh as PAI_CACHE_DIR.\n */\nexport function paiCacheDir(): string {\n  return process.env.PAI_CACHE_DIR ?? \"/tmp/claude\";\n}\n\n/**\n * Every runtime path, for the test guard to redirect in one place.\n *\n * Listed here rather than in the guard so that adding a path and forgetting to\n * protect it is not possible: the guard iterates this.\n */\nexport const RUNTIME_PATH_ENV_VARS = [\n  \"PAI_SOCKET_PATH\",\n  \"PAI_AIBROKER_SOCKET_PATH\",\n  \"PAI_DAEMON_LOG_PATH\",\n  \"PAI_DAEMON_PID_PATH\",\n  \"PAI_SCHEDULER_LOG_PATH\",\n  \"PAI_CACHE_DIR\",\n] as const;\n", "/**\n * test-isolation.ts \u2014 the mechanism behind \"tests never touch the live PAI\n * config and never send real notifications\".\n *\n * Observed 2026-10-19: a vitest run rewrote the operator's live config.yaml\n * and pushed real notifications to their phone. A convention (\"give your\n * test a temp home\") did not hold, so the writers refuse instead.\n *\n * The real home comes from os.userInfo().homedir (the passwd entry), NOT\n * $HOME / os.homedir(): tests redirect those, and a test that leaks past the\n * redirect is exactly what this guard catches.\n */\n\nimport { userInfo } from \"node:os\";\nimport { join, resolve, sep } from \"node:path\";\n\n/** True under vitest, NODE_ENV=test or bun test. */\nexport function isTestRunner(): boolean {\n  const e = process.env;\n  return !!e.VITEST || e.NODE_ENV === \"test\" || !!e.BUN_TEST;\n}\n\nfunction realHome(): string | null {\n  try {\n    return userInfo().homedir;\n  } catch {\n    return null;\n  }\n}\n\n/** Throw if a test runner is about to write a live PAI file (PAI home tree or ~/.claude.json). */\nexport function assertTestWriteAllowed(path: string): void {\n  if (!isTestRunner()) return;\n  const home = realHome();\n  if (!home) return;\n  const target = resolve(path);\n  const paiHomes = [join(home, \".claude\", \"pai\"), join(home, \".pai\")];\n  const claudeJson = join(home, \".claude.json\");\n  if (target === claudeJson || target.startsWith(claudeJson + \".\") || paiHomes.some((h) => target === h || target.startsWith(h + sep))) {\n    throw new Error(\n      `refusing to write ${path}: test-isolation rule \u2014 tests must never write the live PAI config; ` +\n        `use a temp PAI home (PAI_HOME=<mkdtemp dir>) instead`\n    );\n  }\n}\n", "/**\n * json-store.ts \u2014 read/write JSON config files without destroying them\n *\n * The failure this exists to prevent:\n *\n *     try { return JSON.parse(read(path)); } catch { return {}; }\n *     ... later ...\n *     write(path, JSON.stringify(ourData));\n *\n * An unreadable file becomes an empty object, and the next write makes that\n * permanent. Silently, exit code 0. This shape appeared three times in this\n * repo against three different files, and twice in AIBroker.\n *\n * The distinction that matters is between *missing* and *unreadable*:\n *\n *   missing     \u2014 legitimate first run. Start fresh; writing is safe.\n *   unreadable  \u2014 the file exists and we could not parse it. Those bytes are\n *                 the only copy of something. Never overwrite them.\n *\n * Collapsing the second into the first is the bug.\n *\n * NOT everything deserves this guard. For a transient buffer \u2014 an undelivered\n * message queue, a cache \u2014 starting fresh IS the correct recovery, and\n * refusing to write would disable the feature permanently. Use `writeJsonAtomic`\n * alone there: it still prevents a crash from truncating a good file, without\n * blocking recovery. Reserve `readJsonStrict` for data a user cannot rebuild.\n */\n\nimport {\n  existsSync,\n  readFileSync,\n  writeFileSync,\n  copyFileSync,\n  renameSync,\n  unlinkSync,\n  mkdirSync,\n  chmodSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { assertTestWriteAllowed } from \"./test-isolation.js\";\n\n/**\n * Read a JSON file, distinguishing \"absent\" from \"damaged\".\n *\n * @param path   file to read\n * @param label  how to name it to the user, e.g. \"~/.claude.json\"\n * @throws if the file exists but cannot be read or parsed\n */\nexport function readJsonStrict(path: string, label = path): Record<string, unknown> {\n  if (!existsSync(path)) return {};\n\n  let raw: string;\n  try {\n    raw = readFileSync(path, \"utf8\");\n  } catch (e) {\n    throw new Error(\n      `Could not read ${label}: ${e instanceof Error ? e.message : String(e)}\\n` +\n      `Refusing to continue \u2014 writing now would replace its contents with ours alone.`\n    );\n  }\n\n  try {\n    return JSON.parse(raw) as Record<string, unknown>;\n  } catch (e) {\n    throw new Error(\n      `${label} exists but is not valid JSON: ${e instanceof Error ? e.message : String(e)}\\n` +\n      `Refusing to continue \u2014 overwriting it would destroy whatever it holds.\\n` +\n      `Repair the file, or move it aside and re-run this command.`\n    );\n  }\n}\n\n/**\n * Leaf values that mean \"type word\", not data.\n *\n * A config whose top-level keys map to bare type words is a serialized schema,\n * not a configuration \u2014 the exact shape found over the live config on\n * 2026-09-18 (399 bytes: `socketPath: string`, `indexIntervalSecs: int`, \u2026).\n * No real config carries three of these at its top level; a dump carries\n * nothing else.\n */\nconst SCHEMA_TYPE_WORDS = new Set([\n  \"string\", \"int\", \"integer\", \"number\", \"bool\", \"boolean\",\n  \"float\", \"double\", \"object\", \"array\", \"any\", \"unknown\",\n]);\n\nfunction schemaDumpHits(data: Record<string, unknown>): number {\n  let hits = 0;\n  for (const v of Object.values(data)) {\n    if (typeof v === \"string\" && SCHEMA_TYPE_WORDS.has(v.trim())) hits += 1;\n  }\n  return hits;\n}\n\n/**\n * Write JSON without risking the existing file.\n *\n * Keeps a `.bak-pai` copy of the previous contents, then writes to a temp file\n * and renames. Rename is atomic within a filesystem, so a crash mid-write\n * leaves the original intact rather than truncated \u2014 which is how these files\n * become corrupt in the first place.\n */\nexport function writeJsonAtomic(\n  path: string,\n  data: Record<string, unknown>,\n  opts: { backup?: boolean; label?: string } = {}\n): void {\n  assertTestWriteAllowed(path);\n  const { backup = true, label = path } = opts;\n  if (schemaDumpHits(data) >= 3) {\n    throw new Error(\n      `${label}: refusing to write \u2014 the object looks like a type schema, not values ` +\n        `(several top-level keys map to bare type words). That is the corruption ` +\n        `signature of 2026-09-18: the caller serialized the schema instead of the ` +\n        `config. Nothing was written.`\n    );\n  }\n  const serialized = JSON.stringify(data, null, 2) + \"\\n\";\n\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n\n  if (backup && existsSync(path)) {\n    try {\n      copyFileSync(path, `${path}.bak-pai`);\n      chmodSync(`${path}.bak-pai`, 0o600);\n    } catch (e) {\n      throw new Error(\n        `Could not back up ${label}: ${e instanceof Error ? e.message : String(e)}\\n` +\n        `Refusing to write without a backup.`\n      );\n    }\n  }\n\n  const tmp = `${path}.tmp-pai-${process.pid}`;\n  try {\n    writeFileSync(tmp, serialized, { encoding: \"utf8\", mode: 0o600 });\n    renameSync(tmp, path);\n    chmodSync(path, 0o600);\n  } catch (e) {\n    try { if (existsSync(tmp)) unlinkSync(tmp); } catch { /* best effort */ }\n    throw new Error(\n      `Failed to write ${label}: ${e instanceof Error ? e.message : String(e)}\\n` +\n      `The original is unchanged.`\n    );\n  }\n}\n", "/**\n * yaml-store.ts \u2014 generic, comment-preserving YAML file helpers shared by\n * every PAI config file that has a YAML form (config.yaml, voices.yaml;\n * workers.yaml keeps its own writer in workers-config.ts because it needs\n * provider-specific key-quoting, but its atomic-write tail is this module's\n * writeYamlFileAtomic).\n */\n\nimport {\n  existsSync,\n  readFileSync,\n  writeFileSync,\n  copyFileSync,\n  renameSync,\n  unlinkSync,\n  mkdirSync,\n  chmodSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { assertTestWriteAllowed } from \"./test-isolation.js\";\nimport { Document, YAMLMap, parseDocument, type Node, type Pair, type Scalar } from \"yaml\";\n\nexport class YamlStoreError extends Error {}\n\n/**\n * Validate `text` parses as YAML, back up any existing file to `.bak-pai`,\n * then write via temp file + rename (atomic within a filesystem) at mode\n * 0600 \u2014 every PAI-owned YAML file can carry secrets, key or no key.\n */\nexport function writeYamlFileAtomic(path: string, text: string, opts: { label?: string } = {}): void {\n  assertTestWriteAllowed(path);\n  const label = opts.label ?? path;\n  const parsed = parseDocument(text);\n  if (parsed.errors.length) {\n    throw new YamlStoreError(`refusing to write ${label}: ${parsed.errors[0].message} (previous file left unchanged)`);\n  }\n\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  if (existsSync(path)) {\n    try {\n      copyFileSync(path, `${path}.bak-pai`);\n    } catch (e) {\n      throw new YamlStoreError(\n        `Could not back up ${label}: ${e instanceof Error ? e.message : String(e)}\\nRefusing to write without a backup.`\n      );\n    }\n  }\n  const tmp = `${path}.tmp-pai-${process.pid}`;\n  try {\n    writeFileSync(tmp, text, { encoding: \"utf8\", mode: 0o600 });\n    renameSync(tmp, path);\n    chmodSync(path, 0o600);\n  } catch (e) {\n    try {\n      if (existsSync(tmp)) unlinkSync(tmp);\n    } catch {\n      /* best effort */\n    }\n    throw new YamlStoreError(`Failed to write ${label}: ${e instanceof Error ? e.message : String(e)}`);\n  }\n}\n\n/** Deep-equal for plain JSON-shaped values (objects/arrays/scalars). */\nexport function deepEqualJson(a: unknown, b: unknown): boolean {\n  return JSON.stringify(a) === JSON.stringify(b);\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\n/**\n * Apply the diff between `before` and `after` (arbitrary nested plain JSON\n * objects) onto a YAML Document, touching only the leaves/keys that actually\n * changed \u2014 this is what lets hand-written comments elsewhere in the file\n * survive a `set`/`unset`. Arrays and non-object values are replaced\n * wholesale when they differ; only plain objects are diffed key by key.\n */\nexport function syncPlainObjectIntoYamlDoc(\n  doc: Document,\n  before: unknown,\n  after: unknown,\n  path: (string | number)[] = []\n): void {\n  if (isPlainObject(after) && isPlainObject(before)) {\n    for (const key of Object.keys(after)) {\n      syncPlainObjectIntoYamlDoc(doc, before[key], after[key], [...path, key]);\n    }\n    for (const key of Object.keys(before)) {\n      if (!(key in after)) doc.deleteIn([...path, key]);\n    }\n    return;\n  }\n  if (after === undefined) {\n    if (before !== undefined) doc.deleteIn(path);\n    return;\n  }\n  if (!deepEqualJson(before, after)) {\n    doc.setIn(path, after);\n  }\n}\n\n/**\n * Attach a `# text` comment directly above the key at `path` inside a YAML\n * Document \u2014 used for the annotated section headers `pai config yaml`\n * writes. No-op if the path does not resolve to a mapping entry (e.g. the\n * key was omitted from the document).\n */\nexport function setYamlKeyComment(doc: Document, path: (string | number)[], text: string): void {\n  const parentPath = path.slice(0, -1);\n  const lastKey = path[path.length - 1];\n  const parent = parentPath.length ? doc.getIn(parentPath, true) : doc.contents;\n  if (!(parent instanceof YAMLMap)) return;\n  const pair = (parent.items as Pair[]).find((p) => (p.key as Scalar)?.value === lastKey);\n  if (pair) {\n    (pair.key as Node & { commentBefore?: string }).commentBefore = ` ${text}`;\n  }\n}\n", "/**\n * main-config.ts \u2014 the dual-format (config.yaml / config.json) engine behind\n * the main PAI config file. Deliberately has NO dependency on\n * src/daemon/config.ts (which owns path resolution, PaiDaemonConfig and\n * DEFAULTS) so that module can import this one for loadConfig() without a\n * cycle; every path this module touches is passed in explicitly.\n *\n * Precedence, same shape as workers.yaml/workers/config.ts: config.yaml wins\n * when it exists, config.json otherwise. `pai config yaml` (see\n * src/cli/commands/config.ts) performs the one-time JSON\u2192YAML conversion;\n * every writer that goes through readMainConfigRaw/writeMainConfigRaw (see\n * src/daemon/config.ts) keeps working unmodified either way.\n */\n\nimport { existsSync, readFileSync, renameSync, chmodSync } from \"node:fs\";\nimport { dirname, join, basename } from \"node:path\";\nimport { Document, parseDocument } from \"yaml\";\nimport { readJsonStrict, writeJsonAtomic } from \"./json-store.js\";\nimport { writeYamlFileAtomic, syncPlainObjectIntoYamlDoc, deepEqualJson, setYamlKeyComment } from \"./yaml-store.js\";\n\nexport class MainConfigError extends Error {}\n\n/** The config.yaml sibling of a given config.json path (same directory). */\nexport function yamlSiblingPath(jsonPath: string): string {\n  return join(dirname(jsonPath), \"config.yaml\");\n}\n\n/** The path a user should be pointed at: config.yaml when it exists (same\n *  precedence as readDualFormatConfigRaw), else config.json. */\nexport function resolvedMainConfigPath(jsonPath: string, yamlPath: string = yamlSiblingPath(jsonPath)): string {\n  return existsSync(yamlPath) ? yamlPath : jsonPath;\n}\n\n/**\n * Read the raw main config: config.yaml if it exists, else config.json\n * (readJsonStrict \u2014 missing is `{}`, damaged throws), matching\n * readWorkersSection's precedence for workers.yaml/JSON.\n */\nexport function readDualFormatConfigRaw(jsonPath: string, yamlPath: string = yamlSiblingPath(jsonPath)): Record<string, unknown> {\n  if (existsSync(yamlPath)) {\n    let text: string;\n    try {\n      text = readFileSync(yamlPath, \"utf8\");\n    } catch (e) {\n      throw new MainConfigError(`Could not read ${yamlPath}: ${e instanceof Error ? e.message : String(e)}`);\n    }\n    const doc = parseDocument(text);\n    if (doc.errors.length) {\n      throw new MainConfigError(`${yamlPath}: ${doc.errors[0].message}`);\n    }\n    const parsed = doc.toJS();\n    if (parsed === null || parsed === undefined) return {};\n    if (typeof parsed !== \"object\" || Array.isArray(parsed)) {\n      throw new MainConfigError(`${yamlPath}: top level must be a mapping`);\n    }\n    return parsed as Record<string, unknown>;\n  }\n  return readJsonStrict(jsonPath, jsonPath);\n}\n\n/**\n * Write the raw main config back: comment-preserving diff-sync into\n * config.yaml when it exists, else plain writeJsonAtomic to config.json \u2014\n * this is the one seam every writer of the main config (CLI commands,\n * identity, notifications, setup, workers) routes through, so a config.yaml\n * on disk is honored no matter which of them made the change.\n */\nexport function writeDualFormatConfigRaw(\n  jsonPath: string,\n  raw: Record<string, unknown>,\n  yamlPath: string = yamlSiblingPath(jsonPath)\n): void {\n  if (existsSync(yamlPath)) {\n    let text: string;\n    try {\n      text = readFileSync(yamlPath, \"utf8\");\n    } catch (e) {\n      throw new MainConfigError(`Could not read ${yamlPath}: ${e instanceof Error ? e.message : String(e)}`);\n    }\n    const doc = parseDocument(text);\n    if (doc.errors.length) {\n      throw new MainConfigError(`${yamlPath}: ${doc.errors[0].message}`);\n    }\n    const before = (doc.toJS() ?? {}) as Record<string, unknown>;\n    syncPlainObjectIntoYamlDoc(doc, before, raw);\n    try {\n      writeYamlFileAtomic(yamlPath, String(doc), { label: yamlPath });\n    } catch (e) {\n      throw new MainConfigError(e instanceof Error ? e.message : String(e));\n    }\n    return;\n  }\n  writeJsonAtomic(jsonPath, raw, { label: jsonPath });\n}\n\n// ---------------------------------------------------------------------------\n// JSON \u2192 YAML migration (\"pai config yaml\")\n// ---------------------------------------------------------------------------\n\n/**\n * Short annotation for every top-level PaiDaemonConfig field, shown as a `#`\n * comment above the field in the generated config.yaml. Mirrors (and must be\n * kept in sync with) the doc comments on PaiDaemonConfig in\n * src/daemon/config.ts \u2014 kept as a separate short-form table rather than\n * parsed from the TS source at runtime.\n */\nexport const MAIN_CONFIG_SECTION_COMMENTS: Record<string, string> = {\n  socketPath: \"Unix Domain Socket path for daemon IPC.\",\n  indexIntervalSecs: \"How often the daemon re-indexes changed files, in seconds.\",\n  embedIntervalSecs: \"How often the daemon runs the embedding pass, in seconds.\",\n  embedOnStartup:\n    \"Run an embed pass 60s after daemon start. Off by default: with a large backlog it makes every restart a CPU storm.\",\n  embedMaxMinutes: \"Wall-clock budget of one embed pass in minutes (default 60); the backlog continues next cycle.\",\n  embedMaxLoad: \"Load average above which an embed pass yields (default 8). Also yields while a local-model worker runs.\",\n  passPriority: 'Priority of scheduled index/embed passes: \"background\" (default; low-priority child process) or \"normal\" (in-process).',\n  maintenanceHour: \"Local hour (0-23) to anchor the recurring index/embed cycle to, so maintenance runs in a fixed window.\",\n  quiet: \"Quiet mode (`pai daemon quiet on|off`): defer maintenance jobs; { on, until? }.\",\n  storageBackend: 'Storage backend: \"sqlite\" (default) or \"postgres\".',\n  postgres: 'PostgreSQL connection settings, used when storageBackend is \"postgres\".',\n  embeddingModel: \"Embedding model name, used for semantic/hybrid search.\",\n  embedding:\n    'Embedding backend: backend (\"transformers-cpu-q8\" or \"ollama-f16\"), model, ollama.baseUrl. Switching needs `pai memory reembed`.',\n  logLevel: \"Daemon log level: debug, info, warn, or error.\",\n  obsidian: \"Obsidian sync into the PAI-generated vault: { enabled } (default false). Gates only writing; vault indexing is vaultIndex.\",\n  vaultIndex: \"Read-only indexing of vaultPath for memory search: { enabled } (default true). Independent of obsidian.enabled.\",\n  vaultPath: \"Obsidian vault root path for zettelkasten indexing, if any.\",\n  vaultProjectId: \"Registry project_id used for vault chunks in memory_chunks. Default: auto-detected.\",\n  notifications: \"Notification subsystem configuration.\",\n  search: \"Search defaults, applied when an MCP tool or CLI call doesn't specify one.\",\n  tasks: \"Task bus \u2014 optional external tracker for cross-session work.\",\n  identity:\n    'Who \"me\" is \u2014 addresses that count as the user\\'s own. Empty by default and never guessed: nothing is self-addressed until this is set.',\n  workers:\n    \"Non-provider worker settings (pane, routing, tree, cache keepalive, fallback). Providers/classes/mcp_sets live in workers.yaml, not here.\",\n};\n\n/** Keys whose leading-underscore string value is a JSON stand-in for a\n *  comment (e.g. `\"_comment\": \"...\"`, `\"_deliverToNote\": \"...\"`). Stripped\n *  from the generated YAML and re-attached as a real `#` comment above the\n *  nearest remaining sibling key in the same object. */\nfunction isCommentKey(key: string): boolean {\n  return key.startsWith(\"_\");\n}\n\ninterface CollectedComment {\n  path: (string | number)[];\n  text: string;\n}\n\n/** Recursively strip `_`-prefixed comment keys out of a plain JSON value,\n *  returning the cleaned value plus where each stripped comment belongs. */\nfunction stripUnderscoreComments(value: unknown, path: (string | number)[] = []): { cleaned: unknown; comments: CollectedComment[] } {\n  if (Array.isArray(value)) {\n    const comments: CollectedComment[] = [];\n    const cleaned = value.map((v, i) => {\n      const r = stripUnderscoreComments(v, [...path, i]);\n      comments.push(...r.comments);\n      return r.cleaned;\n    });\n    return { cleaned, comments };\n  }\n  if (typeof value === \"object\" && value !== null) {\n    const obj = value as Record<string, unknown>;\n    const cleaned: Record<string, unknown> = {};\n    const comments: CollectedComment[] = [];\n    const localComments: string[] = [];\n    for (const [k, v] of Object.entries(obj)) {\n      if (isCommentKey(k)) {\n        if (typeof v === \"string\" && v.trim()) localComments.push(v.trim());\n        continue;\n      }\n      const r = stripUnderscoreComments(v, [...path, k]);\n      cleaned[k] = r.cleaned;\n      comments.push(...r.comments);\n    }\n    // Attach every local comment above the first remaining key in this\n    // object (in source order) \u2014 good enough for the JSON-workaround shape\n    // this exists to convert (a `_comment` sibling explaining the object).\n    const firstKey = Object.keys(cleaned)[0];\n    if (localComments.length && firstKey !== undefined) {\n      comments.unshift({ path: [...path, firstKey], text: localComments.join(\" \") });\n    }\n    return { cleaned, comments };\n  }\n  return { cleaned: value, comments: [] };\n}\n\nexport interface MainConfigMigrateResult {\n  yamlPath: string;\n  yamlText: string;\n  /** Path the pre-migration JSON was renamed to; null on a dry run. */\n  backupPath: string | null;\n  dryRun: boolean;\n}\n\n/**\n * `pai config yaml`: convert config.json to config.yaml. Strips `_`-prefixed\n * JSON-comment-workaround keys into real `#` comments, adds a short\n * annotation above every top-level section (MAIN_CONFIG_SECTION_COMMENTS),\n * verifies the generated YAML re-parses to the exact same data before\n * writing anything, then renames the JSON aside to\n * `config.json.migrated-<YYYY-MM-DD>` (never deleted). Refuses when\n * config.yaml already exists unless `force`. `dryRun` computes and returns\n * the would-be YAML text without touching either file.\n */\nexport function migrateMainConfigToYaml(\n  jsonPath: string,\n  opts: { dryRun?: boolean; force?: boolean; yamlPath?: string; sectionComments?: Record<string, string> } = {}\n): MainConfigMigrateResult {\n  const yamlPath = opts.yamlPath ?? yamlSiblingPath(jsonPath);\n  if (existsSync(yamlPath) && !opts.force) {\n    throw new MainConfigError(`${yamlPath} already exists \u2014 edit it directly, or pass --force to regenerate it from ${jsonPath}`);\n  }\n  if (!existsSync(jsonPath)) {\n    throw new MainConfigError(`${jsonPath} does not exist \u2014 nothing to migrate`);\n  }\n\n  const raw = readJsonStrict(jsonPath, jsonPath);\n  const { cleaned, comments } = stripUnderscoreComments(raw);\n  const cleanedObj = cleaned as Record<string, unknown>;\n\n  const doc = new Document(cleanedObj);\n  const sectionComments = opts.sectionComments ?? MAIN_CONFIG_SECTION_COMMENTS;\n  for (const [key, text] of Object.entries(sectionComments)) {\n    if (key in cleanedObj) setYamlKeyComment(doc, [key], text);\n  }\n  for (const c of comments) {\n    setYamlKeyComment(doc, c.path, c.text);\n  }\n  const yamlText = String(doc);\n\n  // Round-trip check: the generated YAML must re-parse to exactly the data\n  // it was built from before anything is written or renamed.\n  const reparsed = parseDocument(yamlText).toJS();\n  if (!deepEqualJson(reparsed, cleanedObj)) {\n    throw new MainConfigError(\n      `${yamlPath}: generated YAML does not re-parse to the same data as ${jsonPath} \u2014 aborting, nothing written`\n    );\n  }\n\n  if (opts.dryRun) {\n    return { yamlPath, yamlText, backupPath: null, dryRun: true };\n  }\n\n  writeYamlFileAtomic(yamlPath, yamlText, { label: yamlPath });\n\n  const stamp = new Date().toISOString().slice(0, 10);\n  const backupPath = join(dirname(jsonPath), `${basename(jsonPath)}.migrated-${stamp}`);\n  renameSync(jsonPath, backupPath);\n  chmodSync(backupPath, 0o600);\n\n  return { yamlPath, yamlText, backupPath, dryRun: false };\n}\n", "/**\n * config.ts \u2014 Configuration loader for PAI Daemon\n *\n * Loads config from ~/.pai/config.json (the pre-2026-09-19 location\n * was ~/.config/pai/config.json, briefly ~/.pai.json in between \u2014\n * see paiConfigFilePath/migrateConfigFile).\n * Deep-merges with defaults so partial configs work fine.\n * Expands ~ in path values at runtime.\n */\n\nimport { existsSync, mkdirSync, writeFileSync, chmodSync, readdirSync } from \"node:fs\";\nimport { homedir, userInfo } from \"node:os\";\nimport { join, dirname, basename } from \"node:path\";\nimport type { NotificationConfig } from \"../notifications/types.js\";\nimport { DEFAULT_NOTIFICATION_CONFIG } from \"../notifications/types.js\";\nimport type { TaskConfig } from \"../tasks/types.js\";\nimport { DEFAULT_TASK_CONFIG } from \"../tasks/types.js\";\nimport { paiSocketPath } from \"../runtime-paths.js\";\nimport {\n  paiHomePath,\n  resolvePaiFile,\n  migratePaiFile,\n  PaiFileMigrationError,\n  type MigrateFileResult,\n} from \"../config/pai-home.js\";\nimport {\n  yamlSiblingPath,\n  readDualFormatConfigRaw,\n  writeDualFormatConfigRaw,\n  MainConfigError,\n} from \"../config/main-config.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SearchConfig {\n  /** Default search mode: 'keyword', 'semantic', or 'hybrid'. Default: 'keyword'. */\n  mode: \"keyword\" | \"semantic\" | \"hybrid\";\n  /** Enable cross-encoder reranking by default. Default: true. */\n  rerank: boolean;\n  /** Recency boost half-life in days. 0 = off. Default: 90. */\n  recencyBoostDays: number;\n  /** Default max results. Default: 10. */\n  defaultLimit: number;\n  /** Default snippet length for MCP results. Default: 200. */\n  snippetLength: number;\n}\n\nexport interface PostgresConfig {\n  /** Connection string \u2014 if set, overrides individual host/port/etc. fields */\n  connectionString?: string;\n  /** Postgres host (default: \"localhost\") */\n  host?: string;\n  /** Postgres port (default: 5432) */\n  port?: number;\n  /** Postgres database name (default: \"pai\") */\n  database?: string;\n  /** Postgres user (default: \"pai\") */\n  user?: string;\n  /** Postgres password (default: \"pai\") */\n  password?: string;\n  /** Maximum pool connections (default: 5) */\n  maxConnections?: number;\n  /** Connection timeout in ms (default: 5000) */\n  connectionTimeoutMs?: number;\n}\n\n/**\n * Idle-triggered prompt-cache keepalive for interactive Claude Code sessions\n * (see docs/cache-keepalive.md, \"Interactive sessions\"). Distinct from\n * `workers.cacheKeepaliveSecs` (src/workers/config.ts), which re-arms a\n * *worker provider's* cache via trivial worker spawns on a fixed timer: this\n * beats a live interactive session only when it has actually gone idle long\n * enough to risk its 1h ephemeral cache expiring, and only within working\n * hours \u2014 a fixed timer would beat while the user is active (no-op, wasted\n * quota) or overnight (never pays back before the next real prompt anyway).\n */\nexport interface SessionsCacheKeepaliveConfig {\n  /** Off by default \u2014 arming it is the operator's explicit call. */\n  enabled: boolean;\n  /** Beat a session once it has been idle at least this long. Must be < the\n   *  provider's cache TTL (60 for the 1h ephemeral cache) or the beat is too\n   *  late to matter. */\n  idleMinutes: number;\n  /** Cap on consecutive beats per idle stretch; resets when the user prompts\n   *  the session again for real (not with the keepalive word itself). */\n  maxBeats: number;\n  /** Local time window \"HH:MM-HH:MM\" outside which no beats are sent. */\n  activeHours: string;\n  /** Skip sessions whose context is too small to be worth a beat. */\n  minContextTokens: number;\n  /** The exact word typed into the session; kept to one word so the\n   *  UserPromptSubmit hook can recognise it and answer minimally. */\n  prompt: string;\n}\n\nexport interface SessionsConfig {\n  cacheKeepalive: SessionsCacheKeepaliveConfig;\n}\n\nexport interface EmbeddingConfig {\n  /** Backend id: \"transformers-cpu-q8\" (default, in-process) or \"ollama-f16\". */\n  backend: string;\n  /** Model name for the backend. Ollama: the model name on the server\n   *  (default \"arctic-embed-m-v1.5-f16\"). Unset for transformers-cpu-q8,\n   *  which uses embeddingModel. */\n  model?: string;\n  ollama: { baseUrl: string };\n}\n\nexport interface DashboardConfig {\n  /** Serve the read-only status dashboard. Default: true. */\n  enabled: boolean;\n  /** TCP port. Default: 8770 (8765-8768 are taken by other local services). */\n  port: number;\n  /** Bind address. Default \"127.0.0.1\"; add the tailnet IP to reach it from a phone. */\n  bind: string;\n}\n\nexport interface ObsidianConfig {\n  /** Gates only the writing side (sync, master notes, symlinks). Default: false. */\n  enabled: boolean;\n}\n\nexport interface VaultIndexConfig {\n  /** Read-only indexing of `vaultPath` for memory search. Independent of `obsidian.enabled`. Default: true. */\n  enabled: boolean;\n}\n\nexport interface RouterConfig {\n  /** Keep `pai router` (127.0.0.1:8790) running under the daemon. Default: false. */\n  enabled: boolean;\n  /**\n   * Listen scope: \"loopback\" (default \u2014 127.0.0.1 only, as always), or beyond\n   * it: \"tailscale\" (the utun address inside 100.64.0.0/10) or an explicit\n   * IPv4 \u2014 never 0.0.0.0. A wider bind adds a second listener on that address\n   * for the cockpit, which then needs the token from\n   * ~/.pai/router-cockpit.token (printed once as a full URL at\n   * startup); /v1 keeps answering only loopback and tailnet remotes.\n   */\n  bind: string;\n}\n\nexport interface PaiDaemonConfig {\n  /** Unix Domain Socket path for IPC */\n  socketPath: string;\n\n  /** Index schedule interval in seconds (default: 300 = 5 minutes) */\n  indexIntervalSecs: number;\n\n  /** Embedding schedule interval in seconds (default: 600 = 10 minutes) */\n  embedIntervalSecs: number;\n  /** Obsidian vault sync interval in seconds (default: 1800 = 30 minutes); syncs only if a hook flagged a change. */\n  obsidianSyncIntervalSecs: number;\n  /** Run an embed pass 60s after daemon start. Off by default: with a large\n   *  backlog it makes every restart a CPU storm, and it ignores the interval. */\n  embedOnStartup: boolean;\n  /** Wall-clock budget of one embed pass in minutes (default 60); the rest waits for the next cycle. */\n  embedMaxMinutes?: number;\n  /** Load average (1 min) above which an embed pass yields (default 8). */\n  embedMaxLoad?: number;\n\n  /** Local hour (0-23) to anchor the recurring index/embed cycle to. When unset,\n   *  the cycle is anchored to daemon start, so a daytime restart pins every\n   *  later pass to daytime too \u2014 a 24h interval does not by itself mean \"at\n   *  night\". Set this to run maintenance in a fixed window regardless of when\n   *  the machine last booted. */\n  maintenanceHour?: number;\n\n  /** Priority of the scheduled index/embed passes. \"background\" (default) runs\n   *  them in a child under `taskpolicy -b nice -n 19` (macOS) or\n   *  `nice -n 19 ionice -c3` (Linux); the daemon itself, which serves MCP,\n   *  keeps normal priority. \"normal\" runs them in-process. */\n  passPriority: \"background\" | \"normal\";\n\n  /** Storage backend: \"sqlite\" (default) or \"postgres\" */\n  storageBackend: \"sqlite\" | \"postgres\";\n\n  /** PostgreSQL connection config (used when storageBackend = \"postgres\") */\n  postgres?: PostgresConfig;\n\n  /** Embedding model name (used for semantic/hybrid search) */\n  embeddingModel: string;\n\n  /** Embedding backend selection (see src/memory/backends/). */\n  embedding: EmbeddingConfig;\n\n  /** Log level */\n  logLevel: \"debug\" | \"info\" | \"warn\" | \"error\";\n\n  /** Obsidian vault root path for zettelkasten indexing. If set, vault indexing runs alongside project indexing. */\n  vaultPath?: string;\n\n  /** Registry project_id to use for vault chunks in memory_chunks. Default: auto-detected. */\n  vaultProjectId?: number;\n\n  /** Notification subsystem configuration */\n  notifications: NotificationConfig;\n\n  /** Search defaults \u2014 applied when MCP tool or CLI doesn't specify a value */\n  search: SearchConfig;\n\n  /** Task bus \u2014 optional external tracker for cross-session work */\n  tasks: TaskConfig;\n\n  /** Who \"me\" is \u2014 addresses that count as the user's own. */\n  identity: IdentityConfig;\n\n  /** Interactive-session settings (currently just the cache keepalive). */\n  sessions: SessionsConfig;\n\n  /** Read-only memory status dashboard (src/daemon/dashboard.ts). */\n  dashboard: DashboardConfig;\n\n  /** Obsidian sync switch (off by default). */\n  obsidian: ObsidianConfig;\n\n  /** Vault index switch (on by default). */\n  vaultIndex: VaultIndexConfig;\n\n  /** Model router supervised by the daemon (src/router/supervisor.ts). */\n  router: RouterConfig;\n}\n\n/**\n * The user's own identity, for anything that delivers back to them.\n *\n * This exists so \"my own address\" is a fact the system can check rather than\n * something a model infers from context. An assistant deciding on the spot\n * whether an address looks like the user's is exactly the judgement that should\n * not be re-made per message.\n *\n * Empty by default and never guessed at install time: an empty `selfEmails`\n * means nothing is self-addressed, so anything reading this fails closed.\n */\nexport interface IdentityConfig {\n  /**\n   * Where digests and \"mail me X\" requests are delivered.\n   *\n   * Must be a mailbox separate from the account doing the sending. Gmail files\n   * a message sent from an account to itself \u2014 or to one of its own domain\n   * aliases \u2014 under Sent only, and it never reaches the inbox. The send reports\n   * success, so this fails silently and looks exactly like delivery. Observed\n   * 2026-08-01: owner@example.ch \u2192 owner@example.de, sent fine, invisible.\n   *\n   * Where a separate mailbox is not available, deliver by writing the message\n   * and adding the INBOX label to it rather than relying on the send path.\n   */\n  deliverTo?: string;\n\n  /**\n   * Every address that counts as the user's own.\n   *\n   * Used as an allowlist by anything that may act without review \u2014 outbound\n   * mail being the case that motivated it. Membership is the whole test: an\n   * address that is not listed is not the user's, however similar it looks.\n   * Plus-aliases and domain aliases must be listed explicitly rather than\n   * pattern-matched, because the patterns that would match them also match\n   * addresses belonging to other people.\n   */\n  selfEmails: string[];\n\n  /** The account used to send on the user's behalf, when one is configured. */\n  sendingAccount?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Per-user Postgres isolation\n// ---------------------------------------------------------------------------\n\n/** Derive a per-user Postgres database name: pai_<username> */\nfunction perUserDbName(): string {\n  const username = userInfo().username;\n  // Sanitize: only allow alphanumeric and underscore for Postgres identifiers\n  const safe = username.replace(/[^a-zA-Z0-9_]/g, \"_\").toLowerCase();\n  return `pai_${safe}`;\n}\n\n/** Derive the per-user connection string */\nfunction perUserConnectionString(): string {\n  const db = perUserDbName();\n  return `postgresql://pai:pai@localhost:5432/${db}`;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\n/**\n * idleMinutes=50 sits under the 60-minute ephemeral-1h TTL with margin for\n * scheduler jitter; maxBeats=6 (~5h of coverage at one beat per idle stretch)\n * and activeHours 08:00-22:00 keep an unattended overnight machine from\n * beating all night for a session nobody will return to before the cache\n * would have expired anyway.\n */\nexport const DEFAULT_SESSIONS_CACHE_KEEPALIVE: SessionsCacheKeepaliveConfig = {\n  enabled: false,\n  idleMinutes: 50,\n  maxBeats: 6,\n  activeHours: \"08:00-22:00\",\n  minContextTokens: 20_000,\n  prompt: \"keepalive\",\n};\n\nexport const DEFAULTS: PaiDaemonConfig = {\n  socketPath: paiSocketPath(),\n  indexIntervalSecs: 300,\n  embedIntervalSecs: 600,\n  obsidianSyncIntervalSecs: 1800,\n  embedOnStartup: false,\n  embedMaxMinutes: 60,\n  embedMaxLoad: 8,\n  passPriority: \"background\",\n  storageBackend: \"sqlite\",\n  postgres: {\n    connectionString: perUserConnectionString(),\n    maxConnections: 5,\n    connectionTimeoutMs: 5000,\n  },\n  embeddingModel: \"Snowflake/snowflake-arctic-embed-m-v1.5\",\n  embedding: { backend: \"transformers-cpu-q8\", ollama: { baseUrl: \"http://127.0.0.1:11434\" } },\n  logLevel: \"info\",\n  notifications: DEFAULT_NOTIFICATION_CONFIG,\n  tasks: DEFAULT_TASK_CONFIG,\n  // Deliberately empty. An install must not guess who the user is: a wrong\n  // guess here is an address that can be mailed without review.\n  identity: { selfEmails: [] },\n  sessions: { cacheKeepalive: { ...DEFAULT_SESSIONS_CACHE_KEEPALIVE } },\n  dashboard: { enabled: true, port: 8770, bind: \"127.0.0.1\" },\n  obsidian: { enabled: false },\n  vaultIndex: { enabled: true },\n  router: { enabled: false, bind: \"loopback\" },\n  search: {\n    mode: \"keyword\",\n    rerank: true,\n    recencyBoostDays: 90,\n    defaultLimit: 10,\n    snippetLength: 200,\n  },\n};\n\n/** Config template \u2014 generated at runtime so the DB name is per-user */\nfunction configTemplate(): string {\n  return `{\n  \"socketPath\": \"/tmp/pai.sock\",\n  \"indexIntervalSecs\": 300,\n  \"embedIntervalSecs\": 600,\n  \"storageBackend\": \"sqlite\",\n  \"postgres\": {\n    \"connectionString\": \"${perUserConnectionString()}\",\n    \"maxConnections\": 5,\n    \"connectionTimeoutMs\": 5000\n  },\n  \"embeddingModel\": \"Snowflake/snowflake-arctic-embed-m-v1.5\",\n  \"logLevel\": \"info\",\n  \"vaultPath\": \"\",\n  \"vaultProjectId\": 0,\n  \"search\": {\n    \"mode\": \"keyword\",\n    \"rerank\": true,\n    \"recencyBoostDays\": 90,\n    \"defaultLimit\": 10,\n    \"snippetLength\": 200\n  }\n}\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/** Expand a leading ~ to the real home directory */\nexport function expandHome(p: string): string {\n  if (p === \"~\" || p.startsWith(\"~/\") || p.startsWith(\"~\\\\\")) {\n    return join(homedir(), p.slice(1));\n  }\n  return p;\n}\n\n/** Canonical location since 2026-09-19: under the PAI_HOME namespace dir,\n *  so nothing PAI writes can collide with a file Claude Code itself owns. */\nconst NEW_CONFIG_FILE = paiHomePath(\"config.json\");\n\n/** Briefly the canonical location between 2026-09-19's two migrations \u2014\n *  read during the transition, never written to again. */\nconst OLD_CONFIG_FILE = join(homedir(), \".claude\", \"pai.json\");\n\n/** Where the config lived before 2026-09-19 \u2014 read during the transition,\n *  never written to once NEW_CONFIG_FILE exists (see `pai config migrate`). */\nconst LEGACY_CONFIG_FILE = join(homedir(), \".config\", \"pai\", \"config.json\");\n\n/**\n * The path any read/write of the PAI config actually uses: PAI_CONFIG_FILE\n * (tests, power users) first, else the new PAI_HOME location if it exists,\n * else the most recent old location that is actually on disk (printing a\n * one-time notice), else the new location (the target a first write creates).\n */\nexport function paiConfigFilePath(): string {\n  const override = process.env.PAI_CONFIG_FILE;\n  if (override) return override;\n  return resolvePaiFile(NEW_CONFIG_FILE, [OLD_CONFIG_FILE, LEGACY_CONFIG_FILE], \"pai config migrate\");\n}\n\nexport const CONFIG_FILE = paiConfigFilePath();\nexport const CONFIG_DIR = dirname(CONFIG_FILE);\n\n/** config.yaml \u2014 the canonical main config once `pai config yaml` has run,\n *  read/written by readMainConfigRaw/writeMainConfigRaw instead of\n *  CONFIG_FILE whenever it exists (see src/config/main-config.ts). */\nexport function paiConfigYamlFilePath(): string {\n  return yamlSiblingPath(CONFIG_FILE);\n}\n\n/**\n * Read the raw main config object exactly as every non-typed writer\n * (identity, notifications, obsidian, setup, workers, memory settings) needs\n * it: config.yaml when it exists, else CONFIG_FILE. `path` overrides the\n * JSON location (tests, or workers/config.ts's parametrized CONFIG_FILE) \u2014\n * its YAML sibling (same dir, `config.yaml`) is what gets preferred.\n */\nexport function readMainConfigRaw(path: string = CONFIG_FILE): Record<string, unknown> {\n  try {\n    return readDualFormatConfigRaw(path);\n  } catch (e) {\n    throw e instanceof MainConfigError ? new Error(e.message) : e;\n  }\n}\n\n/**\n * Write the raw main config object back through the same seam: a\n * comment-preserving sync into config.yaml when it exists, else a plain\n * writeJsonAtomic to `path`. Every writer of the main config must call this\n * instead of touching CONFIG_FILE directly, so a config.yaml on disk is\n * honored no matter which of them made the change.\n */\nexport function writeMainConfigRaw(raw: Record<string, unknown>, path: string = CONFIG_FILE): void {\n  try {\n    writeDualFormatConfigRaw(path, raw);\n  } catch (e) {\n    throw e instanceof MainConfigError ? new Error(e.message) : e;\n  }\n}\n\nexport const ConfigMigrationError = PaiFileMigrationError;\nexport type ConfigMigrateResult = MigrateFileResult;\n\n/**\n * `pai config migrate`: move config.json (from ~/.pai.json or\n * ~/.config/pai/config.json, whichever is found) to ~/.pai/config.json\n * byte-for-byte, verify the copy, then rename the old file aside as\n * config.json.migrated-<YYYYMMDD> (never deleted).\n */\nexport function migrateConfigFile(opts: { dryRun?: boolean } = {}): ConfigMigrateResult {\n  const result = migratePaiFile(NEW_CONFIG_FILE, [OLD_CONFIG_FILE, LEGACY_CONFIG_FILE], opts);\n  if (!result.dryRun && result.fromPath) {\n    // config.json can hold secrets (e.g. the Postgres password) \u2014 lock down\n    // both the new location and the `.migrated-<stamp>` copy left behind.\n    if (existsSync(result.toPath)) chmodSync(result.toPath, 0o600);\n    const oldDir = dirname(result.fromPath);\n    const oldName = basename(result.fromPath);\n    for (const entry of readdirSync(oldDir)) {\n      if (entry.startsWith(`${oldName}.migrated-`)) chmodSync(join(oldDir, entry), 0o600);\n    }\n  }\n  return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deep merge (handles nested objects, not arrays)\n// ---------------------------------------------------------------------------\n\nfunction deepMerge<T extends object>(\n  target: T,\n  source: Record<string, unknown>\n): T {\n  const result = { ...target };\n  for (const key of Object.keys(source)) {\n    const srcVal = source[key];\n    if (srcVal === undefined || srcVal === null) continue;\n    const tgtVal = (target as Record<string, unknown>)[key];\n    if (\n      typeof srcVal === \"object\" &&\n      !Array.isArray(srcVal) &&\n      typeof tgtVal === \"object\" &&\n      tgtVal !== null &&\n      !Array.isArray(tgtVal)\n    ) {\n      (result as Record<string, unknown>)[key] = deepMerge(\n        tgtVal as object,\n        srcVal as Record<string, unknown>\n      );\n    } else {\n      (result as Record<string, unknown>)[key] = srcVal;\n    }\n  }\n  return result;\n}\n\n// ---------------------------------------------------------------------------\n// Config loader\n// ---------------------------------------------------------------------------\n\n/**\n * Load configuration: config.yaml (see paiConfigYamlFilePath) if it exists,\n * else CONFIG_FILE (see paiConfigFilePath), else defaults. Returns defaults\n * deep-merged with any values found in the file.\n */\nexport function loadConfig(): PaiDaemonConfig {\n  if (!existsSync(CONFIG_FILE) && !existsSync(paiConfigYamlFilePath())) {\n    return { ...DEFAULTS };\n  }\n\n  let parsed: Record<string, unknown>;\n  try {\n    parsed = readMainConfigRaw();\n  } catch (e) {\n    process.stderr.write(\n      `[pai-daemon] Could not read config: ${e instanceof Error ? e.message : String(e)}\\n`\n    );\n    return { ...DEFAULTS };\n  }\n\n  // Compat: config.json may use \"obsidianVaultPath\" (legacy key) instead of \"vaultPath\".\n  // Map it across so the daemon picks it up correctly.\n  if (parsed.obsidianVaultPath && !parsed.vaultPath) {\n    parsed.vaultPath = parsed.obsidianVaultPath;\n    process.stderr.write(\n      `[pai-daemon] Config: mapped obsidianVaultPath \u2192 vaultPath (${parsed.vaultPath})\\n`\n    );\n  }\n\n  return deepMerge(DEFAULTS, parsed);\n}\n\n/**\n * Ensure CONFIG_DIR exists and write a default config template to CONFIG_FILE\n * if none exists yet. Call this only from the `serve` command.\n */\nexport function ensureConfigDir(): void {\n  if (!existsSync(CONFIG_DIR)) {\n    mkdirSync(CONFIG_DIR, { recursive: true });\n    process.stderr.write(\n      `[pai-daemon] Created config directory: ${CONFIG_DIR}\\n`\n    );\n  }\n\n  if (!existsSync(CONFIG_FILE) && !existsSync(paiConfigYamlFilePath())) {\n    try {\n      writeFileSync(CONFIG_FILE, configTemplate(), \"utf-8\");\n      process.stderr.write(\n        `[pai-daemon] Wrote default config to: ${CONFIG_FILE}\\n`\n      );\n    } catch (e) {\n      process.stderr.write(\n        `[pai-daemon] Could not write default config: ${e}\\n`\n      );\n    }\n  }\n}\n", "/**\n * Status sink: write the session handover (\"## Continue\") into a forge issue\n * instead of Notes/TODO.md. Opt-in per project via `.pai/project.yaml`:\n *\n *   status:\n *     sink: forge            # todo (default) | forge\n *     forge:\n *       anchor: 283          # issue number of the anchor\n *       marker: CASELEAF-HANDOVER-ANCHOR-V1   # must occur in the anchor body\n *       repo: OWNER/NAME     # optional; default from `git remote get-url origin`\n *\n * Anchor format: the anchor body carries the line `handover: #<n>`, naming the\n * day's handover issue. If no such line exists, the first `#<n>` after the\n * marker is used. The handover issue body holds the section\n * `<!-- pai:continue --> ... <!-- /pai:continue -->`, replaced or appended.\n * Never comments, never closes. Token: config `forge.token`, else env\n * FORGE_TOKEN, else AIBROKER_FORGE_TOKEN; never printed.\n *\n * Synchronous (callers are hooks) via curl, 5 s budget for the whole write.\n */\nimport { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse } from \"yaml\";\nimport { projectRoot } from \"../workers/preamble.js\";\nimport { readMainConfigRaw } from \"../daemon/config.js\";\n\nexport const PROJECT_CONFIG_REL = \".pai/project.yaml\";\nexport const SECTION_OPEN = \"<!-- pai:continue -->\";\nexport const SECTION_CLOSE = \"<!-- /pai:continue -->\";\nexport const FORGE_BUDGET_MS = 5000;\n\nexport interface StatusConfig {\n  sink: \"todo\" | \"forge\";\n  anchor?: number;\n  marker?: string;\n  repo?: string;\n}\n\nexport interface HttpResult { status: number; text: string }\n/** Injected in tests. Throws on transport failure. */\nexport type Http = (method: \"GET\" | \"PATCH\" | \"POST\" | \"DELETE\", url: string, token: string, body: string | undefined, timeoutMs: number) => HttpResult;\n\nexport class ForgeError extends Error {}\n\nexport function parseStatusConfig(text: string): StatusConfig {\n  const doc = (parse(text) ?? {}) as { status?: { sink?: unknown; forge?: Record<string, unknown> } };\n  const forge = doc.status?.forge ?? {};\n  const anchor = Number(forge.anchor);\n  return {\n    sink: doc.status?.sink === \"forge\" ? \"forge\" : \"todo\",\n    anchor: Number.isInteger(anchor) && anchor > 0 ? anchor : undefined,\n    marker: typeof forge.marker === \"string\" && forge.marker ? forge.marker : undefined,\n    repo: typeof forge.repo === \"string\" && forge.repo ? forge.repo : undefined,\n  };\n}\n\nexport function loadStatusConfig(cwd: string): StatusConfig {\n  const path = join(projectRoot(cwd), PROJECT_CONFIG_REL);\n  if (!existsSync(path)) return { sink: \"todo\" };\n  try {\n    return parseStatusConfig(readFileSync(path, \"utf8\"));\n  } catch (e) {\n    throw new ForgeError(`${PROJECT_CONFIG_REL} unreadable (${(e as Error).message.split(\"\\n\")[0]})`);\n  }\n}\n\n/** https://host/git/OWNER/REPO.git -> API base https://host/git/api/v1 + \"OWNER/REPO\". */\nexport function deriveRepo(originUrl: string): { apiBase: string; repo: string } {\n  const m = originUrl.trim().match(/^(https?):\\/\\/(?:[^@/]+@)?([^/]+)\\/(.*?)\\/?$/);\n  if (m) {\n    const parts = m[3].replace(/\\.git$/, \"\").split(\"/\");\n    if (parts.length >= 2) {\n      const prefix = parts.slice(0, -2).join(\"/\");\n      return { apiBase: `${m[1]}://${m[2]}${prefix ? `/${prefix}` : \"\"}/api/v1`, repo: parts.slice(-2).join(\"/\") };\n    }\n  }\n  throw new ForgeError(\"cannot derive forge API from origin URL\");\n}\n\nfunction resolveToken(): string | undefined {\n  try {\n    const t = (readMainConfigRaw().forge as { token?: unknown } | undefined)?.token;\n    if (typeof t === \"string\" && t) return t;\n  } catch { /* unreadable config: fall through to env */ }\n  return process.env.FORGE_TOKEN || process.env.AIBROKER_FORGE_TOKEN || undefined;\n}\n\n/** Real transport: curl; the token goes in on stdin so it never shows in ps. */\nexport const curlHttp: Http = (method, url, token, body, timeoutMs) => {\n  const cfg = [\n    `url = \"${url}\"`,\n    `request = \"${method}\"`,\n    `header = \"Authorization: token ${token}\"`,\n    `header = \"Content-Type: application/json\"`,\n    `max-time = ${Math.max(0.5, timeoutMs / 1000)}`,\n    `silent`,\n    `write-out = \"\\\\n%{http_code}\"`,\n  ].join(\"\\n\");\n  const argv = body !== undefined ? [\"-K\", \"-\", \"--data-raw\", body] : [\"-K\", \"-\"];\n  const r = spawnSync(\"curl\", argv, { input: cfg, encoding: \"utf8\", timeout: timeoutMs + 500, maxBuffer: 16 * 1024 * 1024 });\n  if (r.error || r.status !== 0) throw new ForgeError(`curl failed (${r.error?.message ?? `exit ${r.status}`})`);\n  const at = r.stdout.lastIndexOf(\"\\n\");\n  return { status: Number(r.stdout.slice(at + 1)), text: r.stdout.slice(0, at) };\n};\n\n/** `handover: #<n>` line wins; else the first `#<n>` after the marker. */\nexport function extractHandoverNumber(anchorBody: string, marker: string): number | undefined {\n  const line = anchorBody.match(/^\\s*handover:\\s*#(\\d+)/im);\n  if (line) return Number(line[1]);\n  const after = anchorBody.slice(anchorBody.indexOf(marker) + marker.length).match(/#(\\d+)/);\n  return after ? Number(after[1]) : undefined;\n}\n\n/** Replace the delimited section, or append it; everything else stays byte-identical. */\nexport function upsertSection(body: string, markdown: string, tag = \"continue\"): string {\n  const open = tag === \"continue\" ? SECTION_OPEN : `<!-- pai:${tag} -->`;\n  const close = tag === \"continue\" ? SECTION_CLOSE : `<!-- /pai:${tag} -->`;\n  const section = `${open}\\n${markdown.trim()}\\n${close}`;\n  const a = body.indexOf(open);\n  const b = a === -1 ? -1 : body.indexOf(close, a);\n  if (b !== -1) return body.slice(0, a) + section + body.slice(b + close.length);\n  const sep = body === \"\" || body.endsWith(\"\\n\\n\") ? \"\" : body.endsWith(\"\\n\") ? \"\\n\" : \"\\n\\n\";\n  return body + sep + section + \"\\n\";\n}\n\nexport interface ForgeDeps { http?: Http; token?: string; originUrl?: string; dryRun?: boolean }\n\ntype Method = \"GET\" | \"PATCH\" | \"POST\" | \"DELETE\";\n\n/** One forge repo + token + 5 s budget; dry run records mutating calls in `planned` instead of sending them. */\nfunction forgeClient(cwd: string, cfg: StatusConfig, deps: ForgeDeps) {\n  const token = deps.token ?? resolveToken();\n  if (!token) throw new ForgeError(\"no forge token\");\n  const http = deps.http ?? curlHttp;\n  let origin = deps.originUrl;\n  if (!origin) {\n    try {\n      origin = execFileSync(\"git\", [\"remote\", \"get-url\", \"origin\"], { cwd, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"] });\n    } catch { throw new ForgeError(\"no origin remote\"); }\n  }\n  const derived = deriveRepo(origin);\n  const repo = cfg.repo ?? derived.repo;\n  const deadline = Date.now() + FORGE_BUDGET_MS;\n  const planned: string[] = [];\n  const call = (method: Method, path: string, body?: string): HttpResult => {\n    if (deps.dryRun && method !== \"GET\") {\n      planned.push(`${method} ${derived.apiBase}/repos/${repo}/${path}${body ? ` ${body}` : \"\"}`);\n      return { status: 200, text: \"{}\" };\n    }\n    const left = deadline - Date.now();\n    if (left < 300) throw new ForgeError(\"time budget exhausted\");\n    let r: HttpResult;\n    try { r = http(method, `${derived.apiBase}/repos/${repo}/${path}`, token, body, left); }\n    catch (e) { throw e instanceof ForgeError ? e : new ForgeError(`request failed (${(e as Error).message})`); }\n    if (r.status < 200 || r.status >= 300) throw new ForgeError(`HTTP ${r.status} on ${method} ${path}`);\n    return r;\n  };\n  return { repo, call, planned };\n}\n\nfunction json<T>(r: HttpResult): T {\n  try { return JSON.parse(r.text) as T; } catch { throw new ForgeError(\"unparseable forge response\"); }\n}\n\n/** Returns the issue number written (or, dry run, that would be). Throws ForgeError with the reason. */\nexport function writeContinueToForge(cwd: string, cfg: StatusConfig, markdown: string, deps: ForgeDeps = {}): { issue: number; body: string } {\n  return writeSectionToForge(cwd, cfg, markdown, \"continue\", deps);\n}\n\n/** Replace/append section `tag` in the day's handover issue; the rest of the body is untouched. */\nexport function writeSectionToForge(cwd: string, cfg: StatusConfig, markdown: string, tag: string, deps: ForgeDeps = {}): { issue: number; body: string } {\n  if (!cfg.anchor || !cfg.marker) throw new ForgeError(\"status.forge.anchor/marker missing\");\n  const { call } = forgeClient(cwd, cfg, deps);\n  const bodyOf = (n: number): string => String(json<{ body?: unknown }>(call(\"GET\", `issues/${n}`)).body ?? \"\");\n  const anchorBody = bodyOf(cfg.anchor);\n  if (!anchorBody.includes(cfg.marker)) throw new ForgeError(`anchor #${cfg.anchor} lacks marker`);\n  const handover = extractHandoverNumber(anchorBody, cfg.marker);\n  if (!handover) throw new ForgeError(`no handover number in anchor #${cfg.anchor}`);\n  const next = upsertSection(bodyOf(handover), markdown, tag);\n  if (!deps.dryRun) call(\"PATCH\", `issues/${handover}`, JSON.stringify({ body: next }));\n  return { issue: handover, body: next };\n}\n\n// Claim convention (identical on the AIBroker side): assignee CLAIM_ASSIGNEE + label CLAIM_LABEL.\n// PAI never closes issues.\nexport const CLAIM_ASSIGNEE = \"claude\";\nexport const CLAIM_LABEL = \"in-progress\";\n\nexport interface ClaimResult { repo: string; issue: number; message: string; planned: string[] }\nexport interface ClaimRecord { issue: number; repo: string; at: string }\n\ninterface IssueView { assignees?: Array<{ login: string }> | null; labels?: Array<{ id: number; name: string }> | null }\n\n/** Claim (add=true) or release (add=false) issue `n`; other assignees and labels are left alone. */\nexport function setClaim(cwd: string, cfg: StatusConfig, n: number, add: boolean, deps: ForgeDeps = {}): ClaimResult {\n  const { repo, call, planned } = forgeClient(cwd, cfg, deps);\n  const issue = json<IssueView>(call(\"GET\", `issues/${n}`));\n  const logins = (issue.assignees ?? []).map((a) => a.login);\n  const next = add ? [...new Set([...logins, CLAIM_ASSIGNEE])] : logins.filter((l) => l !== CLAIM_ASSIGNEE);\n  if (next.length !== logins.length || next.some((l, i) => l !== logins[i])) {\n    call(\"PATCH\", `issues/${n}`, JSON.stringify({ assignees: next }));\n  }\n  const verb = add ? \"claimed\" : \"released\";\n  let message = `${verb} ${repo}#${n}`;\n  const has = (issue.labels ?? []).find((l) => l.name === CLAIM_LABEL);\n  if (add && !has) {\n    const label = json<Array<{ id: number; name: string }>>(call(\"GET\", \"labels?limit=100\")).find((l) => l.name === CLAIM_LABEL);\n    if (label) call(\"POST\", `issues/${n}/labels`, JSON.stringify({ labels: [label.id] }));\n    else message += ` (assignee only: label \"${CLAIM_LABEL}\" does not exist in ${repo})`;\n  } else if (!add && has) {\n    call(\"DELETE\", `issues/${n}/labels/${has.id}`);\n  }\n  return { repo, issue: n, message, planned };\n}\n\n/** Open issues in the repo currently carrying the claim label. */\nexport function listClaims(cwd: string, cfg: StatusConfig, deps: ForgeDeps = {}): Array<{ issue: number; title: string; assignees: string[] }> {\n  const { call } = forgeClient(cwd, cfg, deps);\n  const rows = json<Array<{ number: number; title: string; pull_request?: unknown } & IssueView>>(\n    call(\"GET\", `issues?state=open&type=issues&labels=${CLAIM_LABEL}&limit=50`),\n  );\n  return rows.map((r) => ({ issue: r.number, title: r.title, assignees: (r.assignees ?? []).map((a) => a.login) }));\n}\n\n/** Release every claim best-effort; returns the per-issue errors (never throws). */\nexport function releaseClaims(cwd: string, claims: ClaimRecord[], deps: ForgeDeps = {}): string[] {\n  const errors: string[] = [];\n  for (const c of claims) {\n    try { setClaim(cwd, { ...loadStatusConfig(cwd), repo: c.repo }, c.issue, false, deps); }\n    catch (e) { errors.push(`${c.repo}#${c.issue}: ${(e as Error).message}`); }\n  }\n  return errors;\n}\n\n// ---- stale report: open issues that landed in the integration branch or went idle ----\n\nexport type Git = (args: string[]) => string;\nexport interface StaleRow { issue: number; title: string; kind: \"landed\" | \"idle\"; via?: string; idleDays: number; claim: string[] }\nexport interface StaleDeps extends ForgeDeps { git?: Git; now?: number }\n\n/** One shared sink for the CLI/MCP report and the daemon section. */\nexport function formatStale(rows: StaleRow[]): string {\n  if (!rows.length) return \"No stale issues.\";\n  return rows\n    .map((r) => `- #${r.issue} ${r.title} - ${r.kind === \"landed\" ? `landed via ${r.via}` : `idle ${r.idleDays} days`}${r.claim.length ? ` [${r.claim.join(\", \")}]` : \"\"}`)\n    .join(\"\\n\");\n}\n\n/**\n * Open issues that either landed or idle >= days. Landed first, then idle by age.\n * Landed (deterministic; a plain mention like \"see #283\" never counts):\n *  (a) a branch merged into the integration branch whose name has <n> as a whole token (split on / - _ .), or\n *  (b) an integration-branch commit whose subject has a closing reference (fixes|closes|resolves #<n>).\n * A trailing \"(#<n>)\" is a plain reference and does not count. The configured anchor issue is never listed.\n */\nexport function listStale(cwd: string, cfg: StatusConfig, days = 7, deps: StaleDeps = {}): StaleRow[] {\n  const { call } = forgeClient(cwd, cfg, deps);\n  const git: Git = deps.git ?? ((a) => execFileSync(\"git\", a, { cwd, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], maxBuffer: 64 * 1024 * 1024 }));\n  const gitTry = (a: string[]): string => { try { return git(a); } catch { return \"\"; } };\n  type Row = { number: number; title: string; updated_at: string } & IssueView;\n  const rows: Row[] = [];\n  for (let page = 1; page <= 10; page++) {\n    const chunk = json<Row[]>(call(\"GET\", `issues?state=open&type=issues&limit=50&page=${page}`));\n    rows.push(...chunk);\n    if (chunk.length < 50) break;\n  }\n  const refs = new Set(gitTry([\"for-each-ref\", \"--format=%(refname:short)\"]).split(\"\\n\"));\n  let integ = [\"develop\", \"origin/develop\"].find((b) => refs.has(b));\n  if (!integ) {\n    let def = \"main\";\n    try { def = json<{ default_branch?: string }>(call(\"GET\", \"\")).default_branch || \"main\"; } catch { /* repo info unavailable: main */ }\n    integ = refs.has(def) ? def : refs.has(`origin/${def}`) ? `origin/${def}` : def;\n  }\n  const tip = gitTry([\"rev-parse\", integ]).trim();\n  const merged = gitTry([\"branch\", \"-a\", \"--merged\", integ, \"--format=%(refname:short) %(objectname)\"])\n    .split(\"\\n\").map((l) => l.trim().split(\" \")).filter(([n, sha]) => n && sha && sha !== tip && n !== integ && !n.endsWith(\"/HEAD\") && n !== `origin/${integ}`)\n    .map(([n]) => n);\n  const now = deps.now ?? Date.now();\n  const out: StaleRow[] = [];\n  for (const r of rows) {\n    const n = r.number;\n    if (n === cfg.anchor) continue;\n    const idleDays = Math.floor((now - Date.parse(r.updated_at)) / 86_400_000);\n    const claim = [...((r.assignees ?? []).some((a) => a.login === CLAIM_ASSIGNEE) ? [`assignee ${CLAIM_ASSIGNEE}`] : []), ...((r.labels ?? []).some((l) => l.name === CLAIM_LABEL) ? [CLAIM_LABEL] : [])];\n    const branch = merged.find((b) => b.split(/[/\\-_.]/).includes(String(n)));\n    const closing = new RegExp(`\\\\b(fixes|fix|closes|close|resolves|resolve)\\\\s+#${n}\\\\b`, \"i\");\n    const commit = branch ? undefined : gitTry([\"log\", integ, \"-E\", `--grep=#${n}([^0-9]|$)`, \"--format=%h %s\"]).split(\"\\n\").find((l) => closing.test(l.slice(l.indexOf(\" \") + 1)));\n    if (branch || commit) out.push({ issue: n, title: r.title, kind: \"landed\", via: branch ?? `commit ${commit}`, idleDays, claim });\n    else if (idleDays >= days) out.push({ issue: n, title: r.title, kind: \"idle\", idleDays, claim });\n  }\n  return out.sort((a, b) => (a.kind === b.kind ? b.idleDays - a.idleDays : a.kind === \"landed\" ? -1 : 1));\n}\n", "/**\n * status.ts \u2014 the live per-worker status file.\n *\n * One JSON file per run, rewritten atomically on every turn, read by `ps`,\n * `follow`, the status line and the pane logic. Same field set the Python\n * runner wrote, plus `provider` (routing is multi-provider now) and `session`\n * (the AIBroker identity of the launching session, see scope.ts).\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport {\n  existsSync,\n  mkdirSync,\n  readFileSync,\n  readdirSync,\n  renameSync,\n  rmSync,\n  statSync,\n  writeFileSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { shortText } from \"./args.js\";\nimport { statusPath } from \"./paths.js\";\nimport { releaseClaims, type ClaimRecord } from \"../session/forge-sink.js\";\nimport type { WaitingOn } from \"./waiting.js\";\nimport type { Denial } from \"./denials.js\";\n\nexport interface WorkerSessionRef {\n  /** AIBroker/iTerm session id (the iTerm UUID). */\n  id: string;\n  /** AIBroker name of that session, when it has one. */\n  name: string;\n}\n\nexport interface WorkerStatus {\n  id: string;\n  pid: number;\n  /** pid of the `pai worker run` client process; `pid` may differ (host/child pid). */\n  runPid?: number;\n  label: string;\n  /** Latest `pai worker goal` / `say --goal` note to the worker; never replaces the label. */\n  note?: string;\n  /** Notes not yet handed to the worker; the PostToolUse hook drains them (note-delivery.ts). */\n  notePending?: string[];\n  /** When the last pending notes reached the worker (nowStamp). */\n  noteDeliveredAt?: string;\n  cwd: string;\n  /** iTerm session id of the launching terminal (\"\" outside iTerm). */\n  term: string;\n  provider: string;\n  model: string;\n  state: \"queued\" | \"running\" | \"done\" | \"failed\" | \"killed\" | \"lost\";\n  started: string;\n  updated: string;\n  turns: number;\n  tools: number;\n  last: string;\n  rc: number | null;\n  secs: number | null;\n  /** Launching session resolved through AIBroker, when it was. */\n  session?: WorkerSessionRef | null;\n  /** Session-profile name of the launching session: the key of its `max_parallel` gate. */\n  sessionKey?: string;\n  /** Claude Code session id (system/init) \u2014 what `resume` continues. */\n  claudeSession?: string | null;\n  /**\n   * Claude Code session id of the orchestrator whose Bash launched this run,\n   * when the status line's session map knew it (see scope.ts). Distinct from\n   * claudeSession, which is the run's own session.\n   */\n  spawnerSession?: string | null;\n  /** Follow-pane outcome of the run start: opened, or why not. */\n  pane?: { opened: boolean; reason?: string; session?: string; placement?: \"split\" } | null;\n  /** Context meter: tokens of the last assistant turn (input+cache+output). */\n  contextTokens?: number | null;\n  /** Current context fill: input+cache_read+cache_creation of the latest assistant turn. */\n  contextUsed?: number | null;\n  /** Context meter: window size (init model info or provider default). */\n  contextWindow?: number | null;\n  /** Path the prompt was read from via --spec (\"-\" for stdin), when it was. */\n  spec?: string | null;\n  /**\n   * Caller's --output-format, when the run was headless. A json/stream-json\n   * caller reads its own result and is notified on process exit by the\n   * harness \u2014 supervision.ts uses this to skip the duplicate relay send.\n   */\n  outputFormat?: \"text\" | \"json\" | \"stream-json\";\n  /** Chain this stage belongs to (the chain id), when it is a chain stage. */\n  parent?: string;\n  /** Class name of the chain stage (\"draft\", \"implement\", \u2026). */\n  stage?: string;\n  /** Worktree this run executed in, when it got one (see worktree.ts). */\n  worktreeDir?: string | null;\n  /** Branch the worker committed on (worker/<id>), set on worktree runs. */\n  branch?: string | null;\n  /** Set on a resumed worker: the id whose worktree it took over (see worktree.ts adoptTree). */\n  resumedFrom?: string;\n  /** Set on the old id when a resume took its worktree: that id owns the tree now. */\n  resumedBy?: string;\n  /** Commit the branch started from (worktree base) for the commits count. */\n  worktreeBase?: string | null;\n  /** Whether `worktreeBase` is a snapshot of uncommitted checkout state, not HEAD. */\n  worktreeSnapshot?: boolean | null;\n  /** Commits the worker made on its branch; set with `branch` on success. */\n  commits?: number | null;\n  /** Set by `pai worker merge` once the branch landed in the original checkout. */\n  merged?: boolean;\n  /** Set by `pai worker gc`: the hidden ref (refs/pai-archive/<id>) holding the leftover worktree's state. */\n  archived?: string | null;\n  /**\n   * How the run came to be: \"spawn\" (a `pai worker run` subagent) or \"chat\"\n   * (the terminal's interactive pane itself, tracked like a worker). Absent\n   * on statuses written before the flag existed \u2014 read as a spawn.\n   */\n  origin?: \"spawn\" | \"chat\";\n  /**\n   * Set while the start gate holds this run for the previous run on its\n   * provider to warm the prompt cache (stagger.ts); state stays \"running\" so\n   * wait/kill/pane logic still sees a live run, `last` carries the line.\n   */\n  warmup?: { for: string; provider: string; elapsedSec: number; limitSec: number };\n  /** Set while the capacity gate queues this run behind the provider cap (capacity.ts); `last` carries the line. */\n  capacity?: { provider: string; cap: number; total: number; position: number; elapsedSec: number; queued?: number; behind?: string; sinceMs?: number };\n  /** Where the routing policy sent this run and why (policy.ts); absent without a policy. */\n  route?: { policy: string; reason: string; local: boolean };\n  /** \"provider/model (session: launched on glm)\" \u2014 how the provider was chosen; set at spawn. */\n  routeWhy?: string;\n  /**\n   * Why budget routing put this run on its provider instead of a fuller one\n   * ahead of it (\"glm: anthropic 96% until 08:02\"); absent when no switch\n   * happened. Resolved once at spawn \u2014 a running worker is never switched.\n   */\n  switchReason?: string;\n  /** Fall-through link: the local run this cloud run repeats, and (on the local run) its repeat. */\n  fallThroughOf?: string;\n  fallThroughTo?: string;\n  /** Which contract/parser this run used for its final report (see report.ts). */\n  reportFormat?: \"json\" | \"ag2\";\n  /** Whether the final AG2 report passed `aibroker agentish check`; unset for json reports or when no validator ran. */\n  reportValid?: boolean;\n  /** Validation error messages, when reportValid is false. */\n  reportErrors?: string[];\n  /** Whether the headless prompt got the end-of-turn \"final message must be\u2026\" trailer line. */\n  promptTrailer?: boolean;\n  /** The project preamble (.pai/worker.md) appended to this run's prompt: path + short content hash. */\n  preamble?: { path: string; hash: string };\n  /** The provider contract addendum appended to this run's prompt: path + sha256 prefix. */\n  contractAddendum?: { path: string; sha: string };\n  /** Whether an invalid AG2 final message triggered the one bounded re-ask (see run.ts). */\n  reportRetried?: boolean;\n  /**\n   * Consecutive failed tool executions in this run, maintained by the runner's\n   * stream loop (0 after any success). Absent on old status files \u2014 every\n   * reader must treat that as \"no signal\".\n   */\n  consecFails?: number | null;\n  /** Hook/permission denials the run hit (capped list, see denials.ts) and their true total. */\n  denials?: Denial[];\n  denialCount?: number;\n  /**\n   * Seconds of the most recent single long sleep observed in a Bash tool_use\n   * (see parseSleepSecs), cleared when the command completes. Absent on old\n   * status files.\n   */\n  sleepSec?: number | null;\n  /** Epoch ms the run is stopped at (deadline.ts); absent = no limit. */\n  deadlineAt?: number | null;\n  /** Limit in minutes the deadline was derived from. */\n  maxMinutes?: number | null;\n  /** The loop guard stopped this run (state failed, `last` says why). */\n  loopStopped?: boolean;\n  /** The deadline stopped this run (state failed, `last` says why). */\n  timedOut?: boolean;\n  /** Launching process (the parent of `pai worker run`) at start, and its command name. */\n  parentPid?: number;\n  parentComm?: string;\n  parentBackground?: boolean;\n  /** Why the run ended (endreason.ts): exit, deadline, pai-worker-kill, parent-gone, external-signal. */\n  endReason?: string;\n  /** Who asked for the stop, when known (kill caller, daemon:<reason>). */\n  endBy?: string;\n  /** Detail for the reason (signal, sender note). */\n  endNote?: string;\n  /** Stated reason and force flag of a `pai worker kill` (endReason pai-worker-kill). */\n  endKillReason?: string;\n  endForced?: boolean;\n  /**\n   * What the worker is blocked on right now: written by `pai worker wait-on`\n   * while it waits (null once it returns), rendered by waiting.ts.\n   */\n  waitingOn?: WaitingOn | null;\n  /** Forge issues this worker claimed (`pai issue claim` inside it); released when it ends unless claimsContinue. */\n  claims?: ClaimRecord[];\n  /** The final report said the work continues (r=~): claims stay. Set by finaliseRun before the end write. */\n  claimsContinue?: boolean;\n  /** Per-issue errors from the best-effort release at worker end. */\n  claimReleaseErrors?: string[];\n  /** Ever granted clickr desktop controls (see controls.ts) \u2014 the runner returns them best-effort on exit. */\n  controlsHeld?: boolean;\n  /**\n   * Set when the task ran through the worker session pool (pool.ts) instead\n   * of a fresh claude spawn: task index within its slot, the slot's claude\n   * session id (shared across tasks), and the host pid. `pid` names the slot\n   * process \u2014 killing it kills the whole slot, not just this task.\n   */\n  pool?: { task: number; slotSession: string | null; hostPid: number; clientPid?: number; slot?: string; warm?: \"warm\" | \"cold\"; firstShare?: number };\n}\n\n/** Label a worker gets when launched with neither --label nor a prompt. */\nexport const UNLABELED = \"unlabeled\";\n\n/**\n * Whether a status is the terminal's interactive chat pane, not a spawned\n * task worker. `origin` is the real discriminator; the fallback catches\n * running entries from before the flag existed (unlabeled, no turns yet).\n * Removable once every pane runs code that writes `origin`.\n */\nexport function isChatPane(s: Pick<WorkerStatus, \"origin\" | \"label\" | \"turns\">): boolean {\n  return (\n    s.origin === \"chat\" ||\n    (!s.origin && (s.label === UNLABELED || s.label === \"(no prompt)\") && s.turns === 0)\n  );\n}\n\n/** Context-meter percentage 0\u2013100, null when the numbers are missing. */\nexport function contextPercent(s: Pick<WorkerStatus, \"contextTokens\" | \"contextWindow\">): number | null {\n  if (!s.contextTokens || !s.contextWindow) return null;\n  return Math.round((s.contextTokens / s.contextWindow) * 100);\n}\n\n/** Compact token count: 84k, 200k, 900. */\nexport function fmtContextK(n: number): string {\n  return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);\n}\n\n/** Footer token count: 48k, 1M, 1.5M. */\nexport function fmtTokens(n: number): string {\n  if (n >= 1_000_000) return `${Number((n / 1_000_000).toFixed(1))}M`;\n  return fmtContextK(n);\n}\n\n/** `<used> / <window>`, or just `<used>` when the window is unknown; \"\" with no reading. */\nexport function contextFooter(s: Pick<WorkerStatus, \"contextUsed\" | \"contextWindow\">): string {\n  if (!s.contextUsed) return \"\";\n  return s.contextWindow ? `${fmtTokens(s.contextUsed)} / ${fmtTokens(s.contextWindow)}` : fmtTokens(s.contextUsed);\n}\n\n/**\n * The one context-meter label, shared by every renderer so identical\n * numbers always render identically: `ctx 84k/200k (42%)` \u2014 used-style\n * percent. Empty when the numbers are missing (callers drop the part).\n */\nexport function contextLabel(\n  s: Pick<WorkerStatus, \"contextTokens\" | \"contextWindow\">\n): string {\n  const pct = contextPercent(s);\n  if (pct === null) return \"\";\n  return `ctx ${fmtContextK(s.contextTokens ?? 0)}/${fmtContextK(s.contextWindow ?? 0)} (${pct}%)`;\n}\n\n/** Timestamp format shared by status files and the ledger. */\nexport function nowStamp(d: Date = new Date()): string {\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  return (\n    `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` +\n    `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`\n  );\n}\n\n// second-resolution ids repeat when two workers/chains start together in one\n// process; a repeat gets a monotonic suffix so files never collide\nlet lastId = \"\";\nlet idSeq = 0;\n\nexport function newWorkerId(d: Date = new Date(), pid = process.pid): string {\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  const base = `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}-${pid}`;\n  if (base === lastId) {\n    idSeq += 1;\n    return `${base}-${String(idSeq).padStart(2, \"0\")}`;\n  }\n  lastId = base;\n  idSeq = 0;\n  return base;\n}\n\n// Temp names must be unique per writer, not just per worker: two processes\n// legitimately write the same id at the same moment (`pai worker kill` marking\n// a run killed while the run's own SIGTERM handler writes its terminal state).\n// With one shared `<id>.status.tmp` they clobber each other's temp file and the\n// loser's rename fails with ENOENT \u2014 observed live on 2026-09-19.\nlet tmpSeq = 0;\n\n/** A temp path only this call owns \u2014 never the same string twice. */\nexport function statusTmpPath(logDir: string, id: string): string {\n  return `${statusPath(logDir, id)}.${process.pid}.${tmpSeq++}.tmp`;\n}\n\n// The label this process first wrote for a given worker id: run.ts holds one\n// unchanging label in memory for the whole run and passes it to every\n// periodic saveStatus call, so a later call whose label still equals this\n// baseline is that unmodified write, never an intentional change \u2014 on-disk\n// then wins, so an external `pai worker goal` relabel between two of a\n// worker's own writes survives instead of being reverted by the next one. A\n// call whose label differs from the baseline (or the first call for an id in\n// this process, which has no baseline yet) IS the intentional change and\n// always wins \u2014 exactly the one-shot `pai worker goal` process itself.\nconst firstWrittenLabel = new Map<string, string>();\n\n/** Write status atomically (temp + rename) and stamp `updated`. */\nexport function saveStatus(logDir: string, status: WorkerStatus, d: Date = new Date(), external = false): void {\n  status.updated = nowStamp(d);\n  const path = statusPath(logDir, status.id);\n  const baseline = firstWrittenLabel.get(status.id);\n  if (baseline === undefined) {\n    firstWrittenLabel.set(status.id, status.label);\n  }\n  // `external`: a one-shot writer of another process (goal, the note hook) whose copy is the fresh load, so nothing on disk may override it.\n  const onDisk = baseline === undefined || external ? undefined : loadStatus(logDir, status.id);\n  if (onDisk) {\n    if (status.label === baseline && onDisk.label !== status.label) status.label = onDisk.label;\n    // Set by another process (`pai worker controls`); a true flag is never dropped by a stale in-memory copy.\n    if (onDisk.controlsHeld) status.controlsHeld = true;\n    // Written by `pai worker goal` (another process); the runner's copy never sets it.\n    if (onDisk.note !== undefined) status.note = onDisk.note;\n    if (onDisk.notePending !== undefined) status.notePending = onDisk.notePending;\n    if (onDisk.noteDeliveredAt !== undefined) status.noteDeliveredAt = onDisk.noteDeliveredAt;\n    // Written by `pai worker wait-on` (another process); the runner's in-memory copy never knows it.\n    if (status.waitingOn === undefined && onDisk.waitingOn) status.waitingOn = onDisk.waitingOn;\n    // Recorded by `pai issue claim` (another process).\n    if (status.claims === undefined && onDisk.claims) status.claims = onDisk.claims;\n  }\n  // The one owner of \"an ended worker holds no claims\" (like its pane, below): every end path saves through here.\n  // \"queued\" is not ended: a run parked at the capacity gate keeps its claims.\n  if (status.state !== \"running\" && status.state !== \"queued\" && status.claims?.length && !status.claimsContinue) {\n    const errors = releaseClaims(status.cwd, status.claims);\n    status.claims = [];\n    if (errors.length) {\n      status.claimReleaseErrors = errors;\n      process.stderr.write(`claim release failed: ${errors.join(\"; \")}\\n`);\n    }\n  }\n  const tmp = statusTmpPath(logDir, status.id);\n  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });\n  try {\n    writeFileSync(tmp, JSON.stringify(status), \"utf8\");\n    renameSync(tmp, path);\n  } catch (e) {\n    try {\n      rmSync(tmp, { force: true });\n    } catch {\n      // nothing to clean up\n    }\n    throw e;\n  }\n}\n\n/**\n * Set a worker's goal (the operator's `--label`), atomically, through the\n * same saveStatus path everything else writes with. Used by `pai worker\n * goal` and the `--goal` option of `say`: a fresh call in a fresh process, so\n * it always carries no baseline yet and always wins over the worker's own\n * next periodic write (see saveStatus above).\n */\nexport function setWorkerLabel(logDir: string, id: string, label: string): WorkerStatus {\n  const status = loadStatus(logDir, id);\n  if (!status) throw new Error(`no worker named \"${id}\"`);\n  status.label = label;\n  saveStatus(logDir, status);\n  return status;\n}\n\n/**\n * Record a note to a running worker (`pai worker goal`, `say --goal`). The\n * label stays the worker's task; the note is shown beside it, never instead.\n */\nexport function setWorkerNote(logDir: string, id: string, note: string): WorkerStatus {\n  const status = loadStatus(logDir, id);\n  if (!status) throw new Error(`no worker named \"${id}\"`);\n  status.note = note;\n  status.notePending = [...(status.notePending ?? []), note];\n  saveStatus(logDir, status, new Date(), true);\n  return status;\n}\n\n/** Load every status file in the logDir, oldest id first, skipping damage. */\nexport function loadStatuses(logDir: string): WorkerStatus[] {\n  if (!existsSync(logDir)) return [];\n  const out: WorkerStatus[] = [];\n  for (const name of readdirSync(logDir).sort()) {\n    if (!name.endsWith(\".status\")) continue;\n    try {\n      out.push(JSON.parse(readFileSync(join(logDir, name), \"utf8\")) as WorkerStatus);\n    } catch {\n      // a half-written or damaged status file is not worth a crash\n    }\n  }\n  return out;\n}\n\nconst statusCache = new Map<string, { mtimeMs: number; size: number; status: WorkerStatus | null; checked: number }>();\n/** A cached terminal status is trusted this long without a stat: a finished worker's file no longer changes. */\nexport const TERMINAL_RECHECK_MS = 5 * 60_000;\n\n/**\n * loadStatuses for the daemon's periodic tasks: a status file is parsed again only when its mtime or size\n * changed, so a tick over thousands of historical workers costs one readdir and one stat each, not a parse.\n * The returned objects are shared with the cache: read-only.\n */\nexport function loadStatusesCached(logDir: string, now = Date.now()): WorkerStatus[] {\n  if (!existsSync(logDir)) return [];\n  const out: WorkerStatus[] = [];\n  const seen = new Set<string>();\n  for (const name of readdirSync(logDir).sort()) {\n    if (!name.endsWith(\".status\")) continue;\n    const path = join(logDir, name);\n    seen.add(path);\n    try {\n      let hit = statusCache.get(path);\n      if (hit?.status && isTerminal(hit.status.state) && now - hit.checked < TERMINAL_RECHECK_MS) {\n        out.push(hit.status);\n        continue;\n      }\n      const st = statSync(path);\n      if (!hit || hit.mtimeMs !== st.mtimeMs || hit.size !== st.size) {\n        let status: WorkerStatus | null = null;\n        try {\n          status = JSON.parse(readFileSync(path, \"utf8\")) as WorkerStatus;\n        } catch {\n          // a half-written or damaged status file is not worth a crash\n        }\n        hit = { mtimeMs: st.mtimeMs, size: st.size, status, checked: now };\n        statusCache.set(path, hit);\n      }\n      hit.checked = now;\n      if (hit.status) out.push(hit.status);\n    } catch {\n      statusCache.delete(path);\n    }\n  }\n  for (const k of statusCache.keys()) if (k.startsWith(logDir) && !seen.has(k)) statusCache.delete(k);\n  return out;\n}\n\nexport function loadStatus(logDir: string, id: string): WorkerStatus | null {\n  const path = statusPath(logDir, id);\n  if (!existsSync(path)) return null;\n  try {\n    return JSON.parse(readFileSync(path, \"utf8\")) as WorkerStatus;\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Wait for a worker to record its own terminal state (the run's signal\n * handler writes killed/rc/secs). Returns that status, or null when it is\n * still live (\"running\" or \"queued\") after `timeoutMs` \u2014 the caller then\n * writes the state itself.\n */\nexport async function waitForTerminalStatus(\n  logDir: string,\n  id: string,\n  timeoutMs = 2000,\n  stepMs = 50\n): Promise<WorkerStatus | null> {\n  const deadline = Date.now() + timeoutMs;\n  for (;;) {\n    const s = loadStatus(logDir, id);\n    if (s && s.state !== \"running\" && s.state !== \"queued\") return s;\n    if (Date.now() >= deadline) return null;\n    await new Promise((r) => setTimeout(r, stepMs));\n  }\n}\n\n/**\n * Signal-0 liveness probe. EPERM means the pid exists but belongs to another\n * user: `epermAlive` picks whether that counts as alive.\n */\nexport function pidAlive(pid: number, opts: { epermAlive?: boolean } = {}): boolean {\n  try {\n    process.kill(pid, 0);\n    return true;\n  } catch (e) {\n    return !!opts.epermAlive && (e as NodeJS.ErrnoException).code === \"EPERM\";\n  }\n}\n\nexport function alive(pid: number | null | undefined): boolean {\n  if (!pid || pid <= 0) return false;\n  return pidAlive(pid);\n}\n\n/**\n * Whether `status.pid` still names the process that was started as this\n * worker, not a later, unrelated process the OS handed the same pid to after\n * the worker exited. `alive()` alone can't tell these apart \u2014 a pid observed\n * live on 2026-09-19 (worker 20260919-083549-90913) had been reused, and\n * `pai worker kill` signalled the wrong, unrelated process. `ps -o lstart=`\n * reads the running process's actual start time and compares it against the\n * status file's own `started` stamp; a reused pid almost never starts within\n * 120s of the original, so a wider drift means \"different process\".\n */\nexport type OwnerFields = Pick<WorkerStatus, \"pid\" | \"started\"> & { id?: string };\n\n/**\n * Process creation time encoded in a worker id (`YYYYMMDD-HHMMSS-<pid>`), only\n * when the id's pid is the recorded pid. `started` is rewritten when a queued\n * worker acquires its provider slot, so it can be minutes after the process\n * began; the id is minted at spawn and never changes.\n */\nexport function spawnTimeOf(status: OwnerFields): number | undefined {\n  const m = /^(\\d{4})(\\d{2})(\\d{2})-(\\d{2})(\\d{2})(\\d{2})-(\\d+)(?:-\\d+)?$/.exec(status.id ?? \"\");\n  if (!m || Number(m[7]) !== status.pid) return undefined;\n  return new Date(+m[1]!, +m[2]! - 1, +m[3]!, +m[4]!, +m[5]!, +m[6]!).getTime();\n}\n\nexport function ownsPid(status: OwnerFields): boolean {\n  if (status.pid <= 0 || !alive(status.pid)) return false;\n  try {\n    const out = execFileSync(\"ps\", [\"-o\", \"lstart=\", \"-p\", String(status.pid)], {\n      encoding: \"utf8\",\n      env: { ...process.env, LC_ALL: \"C\" },\n    }).trim();\n    const psStart = Date.parse(out);\n    const started = spawnTimeOf(status) ?? Date.parse(status.started.replace(\" \", \"T\"));\n    if (Number.isNaN(psStart) || Number.isNaN(started)) return alive(status.pid);\n    return Math.abs(psStart - started) <= 120_000;\n  } catch {\n    // pid exited between the alive() check and the ps call, or ps failed for\n    // another reason \u2014 the pid-existence check is still better than nothing.\n    return alive(status.pid);\n  }\n}\n\nconst ownsCache = new Map<string, number>();\n/** How long a verified pid ownership is trusted by the daemon's periodic tasks. */\nexport const OWNS_TTL_MS = 60_000;\n\n/**\n * ownsPid for periodic daemon tasks: a pid that was verified (one blocking `ps`) is trusted for a minute while\n * it still exists, so a tick does not spawn a `ps` per running worker. Kill paths keep using ownsPid.\n */\nexport function ownsPidCached(status: OwnerFields, now = Date.now()): boolean {\n  if (status.pid <= 0 || !alive(status.pid)) return false;\n  const key = `${status.pid}:${status.started}`;\n  const at = ownsCache.get(key);\n  if (at !== undefined && now - at < OWNS_TTL_MS) return true;\n  const ok = ownsPid(status);\n  if (ok) ownsCache.set(key, now);\n  else ownsCache.delete(key);\n  if (ownsCache.size > 512) for (const [k, t] of ownsCache) if (now - t >= OWNS_TTL_MS) ownsCache.delete(k);\n  return ok;\n}\n\n/** Any state a worker never leaves on its own: everything but \"running\" and \"queued\". */\nexport function isTerminal(state: WorkerStatus[\"state\"] | undefined): boolean {\n  return state !== undefined && state !== \"running\" && state !== \"queued\";\n}\n\n/** Whether a status is both marked live (\"running\", or \"queued\" at the capacity gate) and still owns its recorded pid. */\nexport function isLive(\n  status: OwnerFields & Pick<WorkerStatus, \"state\">,\n  owns: (s: OwnerFields) => boolean = ownsPid\n): boolean {\n  return (status.state === \"running\" || status.state === \"queued\") && owns(status);\n}\n\n/** How long a \"running\" status may sit with a dead pid before it is declared lost. */\nexport const LOST_GRACE_MS = 2 * 60_000;\n\n/**\n * Rewrite every status that is \"running\" or \"queued\" with a dead pid and has\n * not been updated for `graceMs` to the terminal state \"lost\" (crashed or\n * killed runs that never wrote a final state), so no reader counts it.\n * Returns the ids rewritten. The one place that does this; ps, the status\n * line and the daemon call it before reading.\n */\nexport function reconcileStatuses(\n  logDir: string,\n  now: Date = new Date(),\n  graceMs: number = LOST_GRACE_MS,\n  owns: (s: OwnerFields) => boolean = ownsPid\n): string[] {\n  const lost: string[] = [];\n  for (const s of loadStatuses(logDir)) {\n    if ((s.state !== \"running\" && s.state !== \"queued\") || owns(s)) continue;\n    const upd = Date.parse((s.updated ?? \"\").replace(\" \", \"T\"));\n    if (Number.isNaN(upd) || now.getTime() - upd < graceMs) continue;\n    saveStatus(logDir, { ...s, state: \"lost\", rc: null, last: \"process ended without a final status\" }, now);\n    lost.push(s.id);\n  }\n  return lost;\n}\n\n/** Record a start that died before the claude process ran: failed, with the error in `last`. */\nexport function markStartFailed(logDir: string, status: WorkerStatus, err: unknown): void {\n  status.state = \"failed\";\n  status.rc = status.rc ?? 1;\n  status.last = \"failed to start: \" + (err instanceof Error ? err.message : String(err)).replace(/\\s+/g, \" \").slice(0, 200);\n  saveStatus(logDir, status);\n}\n\n/** \"42s\" under 90s, \"7m\" above \u2014 the coarse age the table and bar show. */\nexport function ageOf(ts: string, now: Date = new Date()): string {\n  const t = Date.parse(ts.replace(\" \", \"T\"));\n  if (Number.isNaN(t)) return \"?\";\n  const s = Math.max(0, Math.floor((now.getTime() - t) / 1000));\n  return s < 90 ? `${s}s` : `${Math.floor(s / 60)}m`;\n}\n\n/** Precise elapsed time: \"45s\", \"1m 23s\", \"12m 05s\", \"1h 02m 05s\" \u2014 seconds zero-padded after a larger unit. */\nexport function elapsedOf(ts: string, now: Date = new Date()): string {\n  const t = Date.parse(ts.replace(\" \", \"T\"));\n  if (Number.isNaN(t)) return \"?\";\n  const secs = Math.max(0, Math.floor((now.getTime() - t) / 1000));\n  const hours = Math.floor(secs / 3600);\n  const mins = Math.floor((secs % 3600) / 60);\n  const sec = secs % 60;\n  if (hours > 0) return `${hours}h ${String(mins).padStart(2, \"0\")}m ${String(sec).padStart(2, \"0\")}s`;\n  if (mins > 0) return `${mins}m ${String(sec).padStart(2, \"0\")}s`;\n  return `${sec}s`;\n}\n\n/** Shorter sleeps are legitimate polling \u2014 not worth a supervision event. */\nexport const SLEEP_FLOOR_SECS = 60;\n\nconst SLEEP_RE = /^\\s*sleep\\s+([0-9]+(?:\\.[0-9]+)?)([smhd]?)\\s*(?:$|[;&|])/;\n\n/** s/m/h/d suffix to seconds multiplier, shared by every `sleep N<unit>` parse below. */\nfunction sleepUnitMultiplier(unit: string): number {\n  return unit === \"m\" ? 60 : unit === \"h\" ? 3600 : unit === \"d\" ? 86_400 : 1;\n}\n\n/**\n * Seconds of a `sleep N` (N plain or with an s/m/h/d suffix) that stands alone\n * or leads a compound command (`sleep 150; cat f`, `sleep 2m && tail f`,\n * `sleep 90 || true`, `sleep 60 &`), or null when no long sleep leads: anchored\n * to the start of the command, so a filename containing \"sleep\" or an\n * `echo sleep 500` never trips it, and floored at SLEEP_FLOOR_SECS so ordinary\n * polling stays invisible.\n */\nexport function parseSleepSecs(command: string): number | null {\n  const m = command.match(SLEEP_RE);\n  if (!m) return null;\n  const secs = Number(m[1]) * sleepUnitMultiplier(m[2]);\n  return Number.isFinite(secs) && secs >= SLEEP_FLOOR_SECS ? secs : null;\n}\n\n/**\n * Every command-segment boundary a shell recognises before a new simple\n * command can start: the very start of the string, `;`, `&`/`&&`, `|`/`||`,\n * a newline, an opening `(` or `{` (also covers `$(...)`, which ends in `(`),\n * or the keywords `do`/`then`. Segment end is the mirror image plus a closing\n * `)`, so `sleep 90` inside `$(sleep 90)` or `(sleep 90)` still counts.\n */\n// The terminator is a lookahead, not a consumed match: two sleeps sharing one\n// separator (`sleep 65; sleep 590`) both need that `;` available as their own\n// boundary \u2014 matchAll never revisits a character a previous match consumed.\nconst SLEEP_SEGMENT_RE =\n  /(?:^|[;&|\\n(){]|\\bdo\\b|\\bthen\\b)\\s*sleep\\s+([0-9]+(?:\\.[0-9]+)?)([smhd]?)(?=\\s*(?:$|[;&|)\\n]))/g;\n\n/**\n * The longest `sleep N` found as its OWN command segment anywhere in\n * `command` \u2014 not just leading it, unlike {@link parseSleepSecs} \u2014 or null\n * when none reaches SLEEP_FLOOR_SECS. Used by the PreToolUse Bash gate\n * (block-sleep-poll.ts), which must catch a sleep buried after `&&`, inside a\n * `do ... done` loop, or in a subshell, not only one that leads the command.\n */\nexport function longestSleepSecs(command: string): number | null {\n  let longest: number | null = null;\n  for (const m of command.matchAll(SLEEP_SEGMENT_RE)) {\n    const secs = Number(m[1]) * sleepUnitMultiplier(m[2]);\n    if (!Number.isFinite(secs) || secs < SLEEP_FLOOR_SECS) continue;\n    if (longest === null || secs > longest) longest = secs;\n  }\n  return longest;\n}\n\nconst LOOP_RE = /\\b(for|while|until)\\b([^;\\n]*)[;\\n]\\s*do\\b([\\s\\S]*?)\\bdone\\b/g;\n\nfunction sleepSum(text: string): number {\n  let sum = 0;\n  for (const m of text.matchAll(SLEEP_SEGMENT_RE)) {\n    const secs = Number(m[1]) * sleepUnitMultiplier(m[2]);\n    if (Number.isFinite(secs)) sum += secs;\n  }\n  return sum;\n}\n\n/**\n * `while ... read ...; do ...; done < file`: a bounded loop over the file's lines. Its length is\n * unknown here, so the per-step sleep stands alone (the gate's intent is \"no single wait > 60s\").\n */\nfunction readsFromFile(header: string, afterDone: string): boolean {\n  return /\\bread\\b/.test(header) && /^\\s*<(?!<)/.test(afterDone);\n}\n\n/** Iterations of a `for x in <words>` header, or null when not provably small. */\nfunction forIterations(header: string): number | null {\n  const words = header.replace(/^\\s*\\w+\\s+in\\s+/, \"\").trim();\n  if (words === header.trim() || /[$`*?]/.test(words)) return null;\n  const range = /^\\{(-?\\d+)\\.\\.(-?\\d+)\\}$/.exec(words);\n  if (range) return Math.abs(Number(range[2]) - Number(range[1])) + 1;\n  return words ? words.split(/\\s+/).length : 0;\n}\n\n/**\n * Total sleep of one Bash command: every `sleep N` segment counted once, plus\n * loop repetition \u2014 `for x in a b c` multiplies its body by the word count;\n * `while`/`until` and any unbounded `for` count as at least SLEEP_FLOOR_SECS,\n * except a `kill -0` job-wait (the sanctioned wait-for-a-job loop). Used by\n * the PreToolUse gate so `sleep 58; sleep 58` is caught as the 116 s it is.\n */\n// ponytail: loops are not nested-aware (body ends at the first `done`); upgrade to a real shell parse if that bites.\nexport function totalSleepSecs(command: string): number {\n  let total = sleepSum(command);\n  for (const m of command.matchAll(LOOP_RE)) {\n    const body = sleepSum(m[3]);\n    if (body === 0) continue;\n    if (m[1] === \"for\") {\n      const n = forIterations(m[2]);\n      total += n === null ? Math.max(SLEEP_FLOOR_SECS, body) : body * (n - 1);\n    } else if (!/\\bkill\\s+-0\\b/.test(m[2]) && !readsFromFile(m[2], command.slice(m.index + m[0].length))) {\n      total += SLEEP_FLOOR_SECS;\n    }\n  }\n  return total;\n}\n\n/** One-line description of a tool call, e.g. \"Bash: npm test\". */\nexport function describeTool(name: string, inp: unknown): string {\n  if (typeof inp !== \"object\" || inp === null) return name;\n  const i = inp as Record<string, unknown>;\n  const get = (k: string) => (typeof i[k] === \"string\" ? (i[k] as string) : \"\");\n  if (name === \"Bash\") return `Bash: ${shortText(get(\"command\"), 70)}`;\n  if (name === \"Read\" || name === \"Edit\" || name === \"Write\" || name === \"MultiEdit\") {\n    const file = get(\"file_path\").split(\"/\").pop() ?? \"\";\n    return `${name}: ${file}`;\n  }\n  if (name === \"Grep\" || name === \"Glob\") return `${name}: ${shortText(get(\"pattern\"), 50)}`;\n  if (name === \"WebSearch\") return `${name}: ${shortText(get(\"query\"), 50)}`;\n  if (name === \"WebFetch\") return `${name}: ${shortText(get(\"url\"), 60)}`;\n  return name;\n}\n", "/**\n * Start gate: runs starting together on one provider all prefill the same\n * prefix (system prompt, tool definitions, shared spec text) at once, so none\n * hits another's cache fill. A run waits for its immediate predecessor on the\n * same provider to receive its first model response (status.turns >= 1) or\n * for a limit learned from that provider's recent first-response times, then\n * starts. Queued runs release one at a time: run k waits for run k-1 only.\n */\nimport { readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { alive, isLive, loadStatuses, type WorkerStatus } from \"./status.js\";\n\nexport interface StaggerConfig {\n  enabled: boolean;\n  /** Limit without history, per provider kind (seconds). */\n  localDefaultSec: number;\n  remoteDefaultSec: number;\n  /** Clamp of the learned limit (seconds). */\n  minSec: number;\n  maxSec: number;\n}\n\nexport const DEFAULT_STAGGER: StaggerConfig = {\n  enabled: true,\n  localDefaultSec: 20,\n  remoteDefaultSec: 8,\n  minSec: 3,\n  maxSec: 120,\n};\n\nexport const WARMUP_WINDOW = 20;\nconst POLL_STEPS_MS = [250, 500, 1000, 2000];\n\n/** Poll interval after `n` checks: 250, 500, 1000, 2000, then 2000 ms. */\nexport function backoffMs(n: number): number {\n  return POLL_STEPS_MS[Math.min(n, POLL_STEPS_MS.length - 1)];\n}\n\n// --- per-provider first-response history (spawn \u2192 first assistant message) ---\n\nconst historyPath = (logDir: string) => join(logDir, \"warmup-history.json\");\n\nexport function loadWarmupHistory(logDir: string): Record<string, number[]> {\n  try {\n    const raw = JSON.parse(readFileSync(historyPath(logDir), \"utf8\")) as Record<string, unknown>;\n    const out: Record<string, number[]> = {};\n    for (const [k, v] of Object.entries(raw)) {\n      if (Array.isArray(v)) out[k] = v.filter((n): n is number => typeof n === \"number\" && n >= 0);\n    }\n    return out;\n  } catch {\n    return {};\n  }\n}\n\n/** Append one first-response time (seconds), keeping the last WARMUP_WINDOW. */\nexport function recordWarmup(logDir: string, provider: string, secs: number): void {\n  try {\n    const all = loadWarmupHistory(logDir);\n    all[provider] = [...(all[provider] ?? []), Math.round(secs * 10) / 10].slice(-WARMUP_WINDOW);\n    const tmp = `${historyPath(logDir)}.${process.pid}.tmp`;\n    writeFileSync(tmp, JSON.stringify(all));\n    renameSync(tmp, historyPath(logDir));\n  } catch {\n    /* history is advisory */\n  }\n}\n\n/** clamp(p75 of recent first-response times x 1.5, min, max); the per-kind default without history. */\nexport function warmupLimitSec(history: number[] | undefined, local: boolean, cfg: StaggerConfig): number {\n  if (!history || history.length === 0) return local ? cfg.localDefaultSec : cfg.remoteDefaultSec;\n  const sorted = [...history].sort((a, b) => a - b);\n  const p75 = sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * 0.75) - 1)];\n  return Math.min(cfg.maxSec, Math.max(cfg.minSec, p75 * 1.5));\n}\n\n// --- the gate ---\n\nconst isActive = (s: WorkerStatus) => isLive(s, (w) => alive(w.pid));\n\n/**\n * The immediate predecessor: the latest-started live run on this provider\n * before `self`, if it has no model response yet and started within the limit.\n * (A predecessor that already answered, or is gone, means no wait.)\n */\nexport function findPredecessor(\n  statuses: WorkerStatus[],\n  self: { id: string; provider: string },\n  limitSec: number,\n  now: number = Date.now(),\n): WorkerStatus | null {\n  let prev: WorkerStatus | null = null;\n  for (const s of statuses) {\n    if (s.id >= self.id || s.provider !== self.provider || s.origin === \"chat\" || !isActive(s)) continue;\n    if (!prev || s.id > prev.id) prev = s;\n  }\n  if (!prev || prev.turns > 0) return null;\n  const started = Date.parse(prev.started.replace(\" \", \"T\"));\n  if (Number.isFinite(started) && now - started > limitSec * 1000) return null;\n  return prev;\n}\n\nexport type StaggerRelease = \"answered\" | \"limit\" | \"predecessor-ended\";\n\nexport interface WaitInfo {\n  predecessor: string;\n  provider: string;\n  elapsedSec: number;\n  limitSec: number;\n  line: string;\n}\n\nexport interface StaggerOpts {\n  logDir: string;\n  self: { id: string; provider: string };\n  local: boolean;\n  config: StaggerConfig;\n  disabled?: boolean;\n  /** One-off lines: the wait notice and the release summary. */\n  log?: (line: string) => void;\n  /** Called at the start and after every poll while waiting; null when released. */\n  onWait?: (info: WaitInfo | null) => void;\n  now?: () => number;\n  sleep?: (ms: number) => Promise<void>;\n  load?: (logDir: string) => WorkerStatus[];\n  history?: number[];\n}\n\nexport interface StaggerResult {\n  predecessor: string;\n  waitedSec: number;\n  limitSec: number;\n  release: StaggerRelease;\n}\n\nconst RELEASE_TEXT: Record<StaggerRelease, string> = {\n  answered: \"predecessor answered\",\n  limit: \"limit reached\",\n  \"predecessor-ended\": \"predecessor ended without a response\",\n};\n\n/** Resolves once the predecessor has answered, ended, or the limit expired; null when there was nothing to wait for. */\nexport async function staggerGate(o: StaggerOpts): Promise<StaggerResult | null> {\n  if (o.disabled || !o.config.enabled) return null;\n  const load = o.load ?? loadStatuses;\n  const now = o.now ?? Date.now;\n  const sleep = o.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));\n  const history = o.history ?? loadWarmupHistory(o.logDir)[o.self.provider];\n  const limitSec = warmupLimitSec(history, o.local, o.config);\n  if (limitSec <= 0) return null;\n  const pred = findPredecessor(load(o.logDir), o.self, limitSec, now());\n  if (!pred) return null;\n  const limitLabel = Math.round(limitSec);\n  o.log?.(`waiting for ${pred.id} to warm the ${o.self.provider} cache (max ${limitLabel}s)`);\n  const t0 = now();\n  const info = (): WaitInfo => {\n    const elapsedSec = Math.floor((now() - t0) / 1000);\n    return {\n      predecessor: pred.id,\n      provider: o.self.provider,\n      elapsedSec,\n      limitSec: limitLabel,\n      line: `waiting ${elapsedSec}s/${limitLabel}s for ${pred.id} to warm ${o.self.provider} cache`,\n    };\n  };\n  o.onWait?.(info());\n  let release: StaggerRelease = \"limit\";\n  for (let n = 0; now() - t0 < limitSec * 1000; n++) {\n    await sleep(backoffMs(n));\n    const cur = load(o.logDir).find((s) => s.id === pred.id);\n    if (cur && cur.turns > 0) {\n      release = \"answered\";\n      break;\n    }\n    if (!cur || !isActive(cur)) {\n      release = \"predecessor-ended\";\n      break;\n    }\n    o.onWait?.(info());\n  }\n  const waitedSec = Math.round((now() - t0) / 100) / 10;\n  o.onWait?.(null);\n  o.log?.(`waited ${waitedSec}s for ${pred.id} (${RELEASE_TEXT[release]})`);\n  return { predecessor: pred.id, waitedSec, limitSec: limitLabel, release };\n}\n\n", "/**\n * Capacity gate: a provider can only serve so many runs at once (a local\n * vllm-mlx server with --max-num-seqs 3 queues the rest inside the server,\n * where nobody sees it). Runs beyond `maxConcurrent` wait here instead, in\n * FIFO order, with a status line saying why. Queue = one file per waiting run\n * (name = run id, content = pid) in <logDir>/queue/<provider>/; entries whose\n * pid is gone are dropped. The waiting state is \"queued\" (status.ts): the\n * gate writes it through onWait while held, and the caller flips it back to\n * \"running\" (and rewrites `started`) once the slot is granted.\n *\n * Grant order is FIFO by id unless a pull-forward file\n * (<logDir>/queue/<provider>.order, written atomically by `pai worker queue\n * front|up`, the cockpit or the MCP tool) puts named ids first: the file\n * lists ids in grant order, live entries not in it keep their id order at the\n * back. Every waiting loop re-reads it with each poll, so a move takes effect\n * on the next check. Running workers are never interrupted \u2014 only the order\n * of the waiting ones changes.\n */\nimport { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { backoffMs } from \"./stagger.js\";\nimport { isLocalProvider } from \"./config.js\";\nimport { alive, isLive, loadStatus, loadStatuses, ownsPid, reconcileStatuses, type WorkerStatus } from \"./status.js\";\n\nexport interface CapacityConfig {\n  enabled: boolean;\n}\n\nexport const DEFAULT_CAPACITY: CapacityConfig = { enabled: true };\n\n/**\n * Loopback default. The server exposes no capacity: GET /v1/models, /health\n * and /v1/cache/stats on a vllm-mlx server report model names and cache\n * counters only, not max-num-seqs.\n */\nexport const LOCAL_DEFAULT_CAP = 1;\n\n/** Cap shown/used by queue and plan runners for a provider with no cap at all (cloud, unset). */\nexport const UNCAPPED_QUEUE_CAP = 3;\n\n/**\n * Provider cap, the model's default concurrency: explicit `maxConcurrent`\n * (N for a parallel-capable server), else LOCAL_DEFAULT_CAP for loopback (one\n * local engine serves one request at a time), else unlimited (undefined).\n */\nexport function resolveCap(maxConcurrent: number | undefined, local: boolean): number | undefined {\n  return maxConcurrent ?? (local ? LOCAL_DEFAULT_CAP : undefined);\n}\n\n/** resolveCap with a number always: unlimited becomes UNCAPPED_QUEUE_CAP (queue/plan runners). */\nexport function effectiveCap(maxConcurrent: number | undefined, local: boolean): number {\n  return resolveCap(maxConcurrent, local) ?? UNCAPPED_QUEUE_CAP;\n}\n\n/** Cap of a named provider from the config, the same rule the gate applies. */\nexport function providerCap(config: { providers: Record<string, { maxConcurrent?: number; baseUrl: string }> }, provider: string): number | undefined {\n  const p = config.providers[provider];\n  if (!p) return undefined;\n  return resolveCap(p.maxConcurrent, isLocalProvider(p.baseUrl));\n}\n\n/** Longest silence of the log while queued. */\nexport const HEARTBEAT_MS = 60_000;\n\n/** Queue key of a session's `max_parallel` gate (a provider name never contains \"~\"). */\nexport const sessionScopeKey = (name: string): string => `session~${name}`;\n\nconst queueDir = (logDir: string, provider: string) => join(logDir, \"queue\", provider.replace(/[^\\w.~-]/g, \"_\"));\n\ninterface QueueEntry {\n  id: string;\n  pid: number;\n  front: boolean;\n  /** Explicit position (moveUp), within the entry's group; unset = after the ranked ones, by name. */\n  rank?: number;\n  /** Epoch ms the run started waiting; written once at enqueue, kept across every reorder. */\n  since?: number;\n}\n\nconst entryText = (e: QueueEntry): string =>\n  `${e.pid}${e.front ? \" front\" : \"\"}${e.rank === undefined ? \"\" : ` ${e.rank}`}${e.since === undefined ? \"\" : ` @${e.since}`}`;\n\nfunction readEntry(dir: string, id: string): QueueEntry {\n  const [p, ...marks] = readFileSync(join(dir, id), \"utf8\").trim().split(/\\s+/);\n  const rank = marks.map(Number).find((n) => Number.isInteger(n));\n  const at = marks.find((m) => m.startsWith(\"@\"));\n  const since = at ? Number(at.slice(1)) : NaN;\n  return {\n    id,\n    pid: Number(p),\n    front: marks.includes(\"front\"),\n    ...(rank === undefined ? {} : { rank }),\n    ...(Number.isFinite(since) ? { since } : {}),\n  };\n}\n\n/** Live entries in waiting order: \"front\" runs first, then by rank, then oldest (by name); dead entries are removed. */\nfunction liveEntries(logDir: string, provider: string, isAlive: (pid: number) => boolean): QueueEntry[] {\n  const dir = queueDir(logDir, provider);\n  let names: string[];\n  try {\n    names = readdirSync(dir).filter((n) => !n.endsWith(\".tmp\"));\n  } catch {\n    return [];\n  }\n  const out: QueueEntry[] = [];\n  for (const id of names.sort()) {\n    const e = readEntry(dir, id);\n    if (Number.isInteger(e.pid) && e.pid > 0 && isAlive(e.pid)) out.push(e);\n    else rmSync(join(dir, id), { force: true });\n  }\n  const group = (e: QueueEntry) => (e.front ? 0 : 1);\n  // stable sort: ties keep the by-name order\n  return out.sort((a, b) => group(a) - group(b) || (a.rank ?? Infinity) - (b.rank ?? Infinity));\n}\n\n/**\n * Live queue entries of a provider in waiting order (see liveEntries). An\n * entry file holds \"<pid>\", \"<pid> front\" and, after moveUp, \"<pid> [front] <rank>\".\n * This is the single ordering mechanism: `--now`, `pai worker queue front|up`\n * and the cockpit queue panel all go through capacityGate's priority, setFront\n * and moveUp.\n */\nexport function liveQueue(logDir: string, provider: string, isAlive: (pid: number) => boolean = alive): string[] {\n  return liveEntries(logDir, provider, isAlive).map((e) => e.id);\n}\n\n/** Put a waiting run ahead of the provider's non-front runs (\"front\"). False when it is not waiting. */\nexport function setFront(logDir: string, provider: string, id: string, isAlive: (pid: number) => boolean = alive): boolean {\n  const e = liveEntries(logDir, provider, isAlive).find((x) => x.id === id);\n  if (!e) return false;\n  if (!e.front) writeFileSync(join(queueDir(logDir, provider), id), entryText({ ...e, front: true, rank: undefined }));\n  return true;\n}\n\n/** Move a waiting run one place up. False when it is not waiting or already first. */\nexport function moveUp(logDir: string, provider: string, id: string, isAlive: (pid: number) => boolean = alive): boolean {\n  const order = liveEntries(logDir, provider, isAlive);\n  const i = order.findIndex((x) => x.id === id);\n  if (i <= 0) return false;\n  // the run takes the place (and group) of the one it passes\n  [order[i - 1], order[i]] = [{ ...order[i], front: order[i - 1].front }, order[i - 1]];\n  order.forEach((e, rank) => writeFileSync(join(queueDir(logDir, provider), e.id), entryText({ ...e, rank })));\n  return true;\n}\n\n/**\n * Move a waiting run to a 1-based `position` in one step (drag and drop). The run\n * takes the group (front or not) of the neighbour it lands next to, so the\n * order stays one list. False when it is not waiting.\n */\nexport function moveTo(logDir: string, provider: string, id: string, position: number, isAlive: (pid: number) => boolean = alive): boolean {\n  const order = liveEntries(logDir, provider, isAlive);\n  const from = order.findIndex((x) => x.id === id);\n  if (from < 0) return false;\n  const [e] = order.splice(from, 1);\n  const to = Math.max(0, Math.min(order.length, Math.floor(position) - 1));\n  const near = order[to - 1] ?? order[to];\n  order.splice(to, 0, { ...e, front: near ? near.front : e.front });\n  order.forEach((x, rank) => writeFileSync(join(queueDir(logDir, provider), x.id), entryText({ ...x, rank })));\n  return true;\n}\n\n/**\n * setFront / moveUp for callers that report the result (CLI, cockpit, MCP):\n * returns the new waiting order; throws when `id` is not waiting on the provider.\n */\nexport function reorderQueue(logDir: string, provider: string, id: string, how: \"front\" | \"up\"): string[] {\n  if (!liveQueue(logDir, provider).includes(id)) throw new Error(`worker ${id} is not queued on provider ${provider}`);\n  if (how === \"front\") setFront(logDir, provider, id);\n  else moveUp(logDir, provider, id);\n  return liveQueue(logDir, provider);\n}\n\n/** moveTo for callers that report the result: the new waiting order; throws when `id` is not waiting. */\nexport function reorderQueueTo(logDir: string, provider: string, id: string, position: number): string[] {\n  if (!liveQueue(logDir, provider).includes(id)) throw new Error(`worker ${id} is not queued on provider ${provider}`);\n  moveTo(logDir, provider, id, position);\n  return liveQueue(logDir, provider);\n}\n\nexport interface QueueRow {\n  id: string;\n  provider: string;\n  /** 1 = next in line. */\n  position: number;\n  /** Waiting runs on this provider (M). */\n  queued: number;\n  /** Provider cap (C); undefined = unlimited. */\n  cap?: number;\n  /** Status of the waiting run, when its file is readable. */\n  status?: WorkerStatus;\n  /** Epoch ms the run started waiting (not the last reorder); undefined for an entry written before this field existed. */\n  since?: number;\n}\n\n/**\n * The waiting workers of every provider, in grant order \u2014 the one queue\n * reader behind `pai worker ps`' QUEUED section, /cockpit/queue and the MCP\n * worker_queue list. Dead entries are dropped; a provider with no cap (or a\n * cap the config no longer names) still lists its waiters, cap undefined.\n */\nexport function queueRows(logDir: string, caps: Record<string, number | undefined> = {}): QueueRow[] {\n  let providers: string[];\n  try {\n    providers = readdirSync(join(logDir, \"queue\")).sort();\n  } catch {\n    return [];\n  }\n  const rows: QueueRow[] = [];\n  for (const prov of providers) {\n    const queue = liveEntries(logDir, prov, alive);\n    queue.forEach((e, i) => {\n      rows.push({\n        id: e.id,\n        provider: prov,\n        position: i + 1,\n        queued: queue.length,\n        cap: caps[prov],\n        status: loadStatus(logDir, e.id) ?? undefined,\n        ...(e.since === undefined ? {} : { since: e.since }),\n      });\n    });\n  }\n  return rows;\n}\n\nexport interface CapacityInfo {\n  provider: string;\n  cap: number;\n  /** Runs on the provider: those holding a slot plus those queued. */\n  total: number;\n  /** 1 = next in line. */\n  position: number;\n  elapsedSec: number;\n  line: string;\n  /** Waiting runs on the queue (M of \"position N of M\"). */\n  queued: number;\n  /** Label of a run holding a slot, when one is known. */\n  behind?: string;\n  /** Epoch ms this run started waiting. */\n  sinceMs: number;\n}\n\n/** The pane / footer line of a waiting run: \"queued on p: position 2 of 3, behind <label> \u2014 waiting since 18:12\". */\nexport function queuedText(q: { provider: string; position: number; queued: number; behind?: string; sinceMs: number }): string {\n  const d = new Date(q.sinceMs);\n  const hhmm = `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n  return `queued on ${q.provider}: position ${q.position} of ${q.queued}${q.behind ? `, behind ${q.behind}` : \"\"} \u2014 waiting since ${hhmm}`;\n}\n\nexport interface CapacityOpts {\n  logDir: string;\n  self: { id: string; provider: string; pid?: number };\n  /**\n   * Gate on something other than the provider: a session's `max_parallel`.\n   * `key` names the queue (<logDir>/queue/<key>), `label` the status line,\n   * `holds` picks the running runs that count against the cap.\n   */\n  scope?: { key: string; label: string; holds: (s: WorkerStatus) => boolean };\n  /** Resolved cap; undefined = unlimited. */\n  cap: number | undefined;\n  config: CapacityConfig;\n  disabled?: boolean;\n  /** \"front\": wait ahead of the provider's other queued runs (--now). */\n  priority?: \"front\";\n  log?: (line: string) => void;\n  /** Called at the start and after every poll while queued; null once the slot is granted (the caller flips queued -> running then). */\n  onWait?: (info: CapacityInfo | null) => void;\n  now?: () => number;\n  sleep?: (ms: number) => Promise<void>;\n  load?: (logDir: string) => WorkerStatus[];\n  isAlive?: (pid: number) => boolean;\n}\n\nexport interface CapacityResult {\n  waitedSec: number;\n  cap: number;\n}\n\n/** Resolves once fewer than `cap` other runs hold a slot on the provider and this run heads the queue; null when it never had to wait. */\nexport async function capacityGate(o: CapacityOpts): Promise<CapacityResult | null> {\n  if (o.disabled || !o.config.enabled || o.cap === undefined || o.cap <= 0) return null;\n  const { logDir, self, cap } = o;\n  const load = o.load ?? ((dir: string) => (reconcileStatuses(dir, new Date(), 0), loadStatuses(dir)));\n  const now = o.now ?? Date.now;\n  const isAlive = o.isAlive ?? alive;\n  const sleep = o.sleep ?? ((ms: number) => new Promise<void>((r) => setTimeout(r, ms)));\n  const queueKey = o.scope?.key ?? self.provider;\n  const label = o.scope?.label ?? self.provider;\n  const holds = o.scope?.holds ?? ((s: WorkerStatus) => s.provider === self.provider);\n  const dir = queueDir(logDir, queueKey);\n  mkdirSync(dir, { recursive: true });\n  const entry = join(dir, self.id);\n  const t0 = now();\n  writeFileSync(entry, `${self.pid ?? process.pid}${o.priority === \"front\" ? \" front\" : \"\"} @${t0}`);\n\n  // Runs holding a slot: live and running on this provider \u2014 a \"queued\" status\n  // is a waiter, never a holder, whatever its queue entry says.\n  const check = () => {\n    const queue = liveQueue(logDir, queueKey, isAlive);\n    const owns = o.isAlive ? (w: { pid: number }) => isAlive(w.pid) : ownsPid;\n    const holders = load(logDir).filter(\n      (s) => s.id !== self.id && holds(s) && s.state === \"running\" && isLive(s, owns),\n    );\n    const position = queue.indexOf(self.id) + 1;\n    return { holding: holders.length, behind: holders[0]?.label, position: position || queue.length + 1, queued: queue.length };\n  };\n\n  let c = check();\n  if (c.position === 1 && c.holding < cap) {\n    rmSync(entry, { force: true });\n    return null;\n  }\n  const info = (): CapacityInfo => {\n    const total = c.holding + c.queued;\n    return {\n      provider: label,\n      cap,\n      total,\n      position: c.position,\n      elapsedSec: Math.floor((now() - t0) / 1000),\n      queued: c.queued,\n      ...(c.behind ? { behind: c.behind } : {}),\n      sinceMs: t0,\n      line: `queued: ${total} on ${label}, cap ${cap} (position ${c.position})`,\n    };\n  };\n  o.log?.(info().line);\n  let lastBeat = t0;\n  try {\n    o.onWait?.(info());\n    for (let n = 0; ; n++) {\n      await sleep(backoffMs(n));\n      c = check();\n      if (c.position === 1 && c.holding < cap) break;\n      o.onWait?.(info());\n      // a quiet log looks hung to wait-on's stall rule\n      if (now() - lastBeat >= HEARTBEAT_MS) {\n        lastBeat = now();\n        o.log?.(`queued: still waiting, position ${c.position}, ${info().elapsedSec}s`);\n      }\n    }\n  } finally {\n    rmSync(entry, { force: true });\n  }\n  const waitedSec = Math.round((now() - t0) / 100) / 10;\n  // The slot is ours: the caller flips the status back to \"running\" (and\n  // rewrites `started`) before we report the release.\n  o.onWait?.(null);\n  o.log?.(`waited ${waitedSec}s for a ${label} slot`);\n  return { waitedSec, cap };\n}\n", "/**\n * config.ts \u2014 `builds.slots` and `builds.testSlots` of the main PAI config:\n * how many builds (default 1) and how many simulator/device test runs\n * (default 2) run at once machine-wide. PAI_BUILD_SLOTS / PAI_TEST_SLOTS\n * override them for one process.\n */\n\nimport { readMainConfigRaw } from \"../daemon/config.js\";\n\nexport const DEFAULT_BUILD_SLOTS = 1;\nexport const DEFAULT_TEST_SLOTS = 2;\n\nconst ok = (v: unknown): number | null => {\n  const n = Number(v);\n  return Number.isInteger(n) && n >= 1 ? n : null;\n};\n\nfunction slotsOf(key: \"slots\" | \"testSlots\", envVar: string, dflt: number, env: NodeJS.ProcessEnv, read: () => Record<string, unknown>): number {\n  const fromEnv = ok(env[envVar]);\n  if (fromEnv) return fromEnv;\n  try {\n    const b = read().builds as Record<string, unknown> | undefined;\n    return ok(b?.[key]) ?? dflt;\n  } catch {\n    return dflt;\n  }\n}\n\nexport function buildSlots(env: NodeJS.ProcessEnv = process.env, read: () => Record<string, unknown> = readMainConfigRaw): number {\n  return slotsOf(\"slots\", \"PAI_BUILD_SLOTS\", DEFAULT_BUILD_SLOTS, env, read);\n}\n\nexport function testSlots(env: NodeJS.ProcessEnv = process.env, read: () => Record<string, unknown> = readMainConfigRaw): number {\n  return slotsOf(\"testSlots\", \"PAI_TEST_SLOTS\", DEFAULT_TEST_SLOTS, env, read);\n}\n\n/** Slots of one pool. */\nexport function slotsFor(kind: \"build\" | \"test\", env: NodeJS.ProcessEnv = process.env, read: () => Record<string, unknown> = readMainConfigRaw): number {\n  return kind === \"test\" ? testSlots(env, read) : buildSlots(env, read);\n}\n\nexport function poolSlots(env: NodeJS.ProcessEnv = process.env, read: () => Record<string, unknown> = readMainConfigRaw): { build: number; test: number } {\n  return { build: buildSlots(env, read), test: testSlots(env, read) };\n}\n", "/**\n * queue.ts \u2014 the machine-wide build queue as plain files.\n *\n * One ticket file per build in <dir>/tickets, written under a short mkdir\n * mutex. A ticket is \"waiting\" or \"running\"; the running ones hold the slots\n * (config `builds.slots`, default 1; test-kind tickets have their own pool,\n * `builds.testSlots`), the waiting ones start in `seq` order within their\n * pool (FIFO; `next` gives one the lowest seq). A ticket whose gate process is\n * gone is pruned by whoever looks next, and the build tree it left behind is\n * killed, so a SIGKILLed caller cannot keep the lock.\n */\n\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, rmdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { paiHomeDir } from \"../config/pai-home.js\";\nimport { DEFAULT_TEST_SLOTS } from \"./config.js\";\n\n/** Which pool a ticket draws from: compiling (\"build\") or simulator/device test runs (\"test\"). */\nexport type BuildKind = \"build\" | \"test\";\n\nexport const ticketKind = (t: Pick<BuildTicket, \"kind\">): BuildKind => t.kind ?? \"build\";\n\nexport interface BuildTicket {\n  id: string;\n  /** Pool of the ticket; absent means \"build\". */\n  kind?: BuildKind;\n  /** Simulators already booted when a test job started (so only the ones it added get shut down). */\n  bootedBefore?: string[];\n  /** The gate process (`pai build run|lock`) that owns this ticket. */\n  pid: number;\n  label: string;\n  /** Build key: jobs with the same key run one at a time (one warm tree per target). */\n  key?: string;\n  /** Who asked: \"worker <id>\" or the session/project name. */\n  session: string;\n  /** Worker id when started by a worker (so killing the worker frees the lock). */\n  worker?: string;\n  cmd: string;\n  mode: \"delta\" | \"full\";\n  seq: number;\n  state: \"waiting\" | \"running\";\n  enqueuedAt: number;\n  startedAt?: number;\n  /** Paths (cwd, derived data, ...) that name this job's compilers in `ps`. */\n  paths?: string[];\n  /** Process group of the running build. */\n  pgid?: number;\n  log?: string;\n}\n\nexport interface QueueDeps {\n  isAlive: (pid: number) => boolean;\n  kill: (pid: number, sig: NodeJS.Signals) => void;\n  now: () => number;\n}\n\nconst defaultDeps = (): QueueDeps => ({\n  isAlive: (pid) => {\n    try {\n      process.kill(pid, 0);\n      return true;\n    } catch (e) {\n      return (e as NodeJS.ErrnoException).code === \"EPERM\";\n    }\n  },\n  kill: (pid, sig) => {\n    try {\n      process.kill(pid, sig);\n    } catch {\n      /* already gone */\n    }\n  },\n  now: () => Date.now(),\n});\n\n/** Where builds live: PAI_BUILD_DIR, else PAI_HOME/logs/builds. */\nexport function buildsDir(env: NodeJS.ProcessEnv = process.env): string {\n  if (env.PAI_BUILD_DIR) return env.PAI_BUILD_DIR;\n  return join(env.PAI_HOME || paiHomeDir(), \"logs\", \"builds\");\n}\n\nconst ticketsDir = (dir: string): string => join(dir, \"tickets\");\nconst ticketFile = (dir: string, id: string): string => join(ticketsDir(dir), `${id}.json`);\nexport const buildLogPath = (dir: string, id: string): string => join(dir, `${id}.log`);\n\nasync function withMutex<T>(dir: string, fn: () => T): Promise<T> {\n  mkdirSync(dir, { recursive: true });\n  const m = join(dir, \"mutex\");\n  const t0 = Date.now();\n  for (;;) {\n    try {\n      mkdirSync(m);\n      break;\n    } catch {\n      try {\n        if (Date.now() - statSync(m).mtimeMs > 5000) rmdirSync(m); // holder died inside the critical section\n      } catch {\n        /* raced */\n      }\n      if (Date.now() - t0 > 15000) throw new Error(\"build queue mutex busy\");\n      await new Promise((r) => setTimeout(r, 10 + Math.random() * 20));\n    }\n  }\n  try {\n    return fn();\n  } finally {\n    try {\n      rmdirSync(m);\n    } catch {\n      /* gone */\n    }\n  }\n}\n\nfunction readTickets(dir: string): BuildTicket[] {\n  const d = ticketsDir(dir);\n  if (!existsSync(d)) return [];\n  const out: BuildTicket[] = [];\n  for (const f of readdirSync(d)) {\n    if (!f.endsWith(\".json\")) continue;\n    try {\n      out.push(JSON.parse(readFileSync(join(d, f), \"utf8\")) as BuildTicket);\n    } catch {\n      /* half-written or foreign */\n    }\n  }\n  return out;\n}\n\nfunction writeTicket(dir: string, t: BuildTicket): void {\n  mkdirSync(ticketsDir(dir), { recursive: true });\n  const f = ticketFile(dir, t.id);\n  writeFileSync(`${f}.tmp`, JSON.stringify(t), \"utf8\");\n  renameSync(`${f}.tmp`, f);\n}\n\n/** Running tickets first (oldest start first), then waiting ones in start order. */\nexport function ordered(tickets: BuildTicket[]): BuildTicket[] {\n  const run = tickets.filter((t) => t.state === \"running\").sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0));\n  const wait = tickets.filter((t) => t.state === \"waiting\").sort((a, b) => a.seq - b.seq);\n  return [...run, ...wait];\n}\n\n/** Kill the build tree of a ticket (group first, the gate itself after). */\nexport function killTicket(t: BuildTicket, deps: QueueDeps, withGate: boolean): void {\n  if (t.pgid && t.pgid > 1) {\n    deps.kill(-t.pgid, \"SIGTERM\");\n    setTimeout(() => deps.kill(-(t.pgid as number), \"SIGKILL\"), 3000).unref();\n  }\n  if (withGate && t.pid > 1) deps.kill(t.pid, \"SIGTERM\");\n}\n\nfunction prune(dir: string, deps: QueueDeps): void {\n  for (const t of readTickets(dir)) {\n    if (deps.isAlive(t.pid)) continue;\n    killTicket(t, deps, false);\n    rmSync(ticketFile(dir, t.id), { force: true });\n  }\n}\n\nexport async function listBuilds(dir: string, d: Partial<QueueDeps> = {}): Promise<BuildTicket[]> {\n  const deps = { ...defaultDeps(), ...d };\n  if (!existsSync(ticketsDir(dir))) return [];\n  return withMutex(dir, () => {\n    prune(dir, deps);\n    return ordered(readTickets(dir));\n  });\n}\n\nexport async function enqueue(\n  dir: string,\n  info: Pick<BuildTicket, \"pid\" | \"label\" | \"session\" | \"cmd\" | \"mode\"> & { worker?: string; kind?: BuildKind; key?: string },\n  d: Partial<QueueDeps> = {}\n): Promise<BuildTicket> {\n  const deps = { ...defaultDeps(), ...d };\n  return withMutex(dir, () => {\n    prune(dir, deps);\n    const all = readTickets(dir);\n    const now = deps.now();\n    const seq = Math.max(now, ...all.map((t) => t.seq + 1));\n    const id = `b-${new Date(now).toISOString().replace(/[-:T]/g, \"\").slice(0, 14)}-${info.pid}${Math.floor(Math.random() * 36 ** 3).toString(36)}`;\n    const t: BuildTicket = { ...info, id, seq, state: \"waiting\", enqueuedAt: now, log: buildLogPath(dir, id) };\n    writeTicket(dir, t);\n    return t;\n  });\n}\n\n/** Start `id` when a slot is free and it is among the first waiters. */\nexport async function tryStart(dir: string, id: string, slots: number, d: Partial<QueueDeps> = {}): Promise<boolean> {\n  const deps = { ...defaultDeps(), ...d };\n  return withMutex(dir, () => {\n    prune(dir, deps);\n    const all = readTickets(dir);\n    const me = all.find((t) => t.id === id);\n    if (!me) throw new Error(`build ${id} is not queued any more`);\n    if (me.state === \"running\") return true;\n    const kind = ticketKind(me);\n    const pool = all.filter((t) => ticketKind(t) === kind);\n    const free = slots - pool.filter((t) => t.state === \"running\").length;\n    if (free <= 0) return false;\n    // a waiter whose key is running (any pool) or already claimed by an earlier waiter does not take a slot\n    const taken = new Set(all.filter((t) => t.state === \"running\" && t.key).map((t) => t.key as string));\n    const first: BuildTicket[] = [];\n    for (const t of pool.filter((x) => x.state === \"waiting\").sort((a, b) => a.seq - b.seq)) {\n      if (first.length >= free) break;\n      if (t.key) {\n        if (taken.has(t.key)) continue;\n        taken.add(t.key);\n      }\n      first.push(t);\n    }\n    if (!first.some((t) => t.id === id)) return false;\n    writeTicket(dir, { ...me, state: \"running\", startedAt: deps.now() });\n    return true;\n  });\n}\n\nexport async function patchTicket(dir: string, id: string, patch: Partial<BuildTicket>): Promise<void> {\n  await withMutex(dir, () => {\n    const t = readTickets(dir).find((x) => x.id === id);\n    if (t) writeTicket(dir, { ...t, ...patch });\n  });\n}\n\nexport async function removeTicket(dir: string, id: string): Promise<void> {\n  if (!existsSync(ticketFile(dir, id))) return;\n  await withMutex(dir, () => rmSync(ticketFile(dir, id), { force: true }));\n}\n\nexport async function getTicket(dir: string, id: string): Promise<BuildTicket | null> {\n  return withMutex(dir, () => readTickets(dir).find((t) => t.id === id) ?? null);\n}\n\n/** Give a waiting build the next start. Returns the new waiting order. */\nexport async function buildNext(dir: string, idOrLabel: string, d: Partial<QueueDeps> = {}): Promise<string[]> {\n  const deps = { ...defaultDeps(), ...d };\n  return withMutex(dir, () => {\n    prune(dir, deps);\n    const all = readTickets(dir);\n    const t = all.find((x) => x.id === idOrLabel) ?? all.find((x) => x.label === idOrLabel && x.state === \"waiting\");\n    if (!t) throw new Error(`no queued build \"${idOrLabel}\"`);\n    if (t.state !== \"waiting\") throw new Error(`build ${t.id} is already running`);\n    const lowest = Math.min(...all.filter((x) => x.state === \"waiting\").map((x) => x.seq));\n    writeTicket(dir, { ...t, seq: lowest - 1 });\n    return ordered(readTickets(dir)).filter((x) => x.state === \"waiting\").map((x) => x.id);\n  });\n}\n\n/** A worker was killed or loop-stopped: free its builds and kill their trees. Returns the ticket ids freed. */\nexport async function releaseBuildsOf(dir: string, worker: string, d: Partial<QueueDeps> = {}): Promise<string[]> {\n  const deps = { ...defaultDeps(), ...d };\n  if (!existsSync(ticketsDir(dir))) return [];\n  return withMutex(dir, () => {\n    const mine = readTickets(dir).filter((t) => t.worker === worker);\n    for (const t of mine) {\n      killTicket(t, deps, true);\n      rmSync(ticketFile(dir, t.id), { force: true });\n    }\n    return mine.map((t) => t.id);\n  });\n}\n\nconst mins = (ms: number): string => {\n  const s = Math.max(0, Math.floor(ms / 1000));\n  return s < 90 ? `${s}s` : `${Math.floor(s / 60)}m`;\n};\n\n/** Why a waiting job is held back by its key: \"same key as <job>\" (the running or earlier-queued holder), else null. */\nexport function waitReason(t: BuildTicket, tickets: BuildTicket[]): string | null {\n  if (!t.key) return null;\n  const holder =\n    tickets.find((x) => x.id !== t.id && x.key === t.key && x.state === \"running\") ??\n    tickets.filter((x) => x.id !== t.id && x.key === t.key && x.state === \"waiting\" && x.seq < t.seq).sort((a, b) => a.seq - b.seq)[0];\n  return holder ? `same key as ${holder.label}` : null;\n}\n\n/** Slot counts of both pools. */\nexport interface PoolSlots {\n  build: number;\n  test: number;\n}\n\n/** The text `pai build queue` and `pai worker ps` print: one block per pool. */\nexport function renderBuildQueue(tickets: BuildTicket[], slots: number | PoolSlots, testSlots?: number, now = Date.now()): string {\n  const pools: PoolSlots = typeof slots === \"number\" ? { build: slots, test: testSlots ?? DEFAULT_TEST_SLOTS } : slots;\n  const plural = (n: number): string => `${n} slot${n === 1 ? \"\" : \"s\"}`;\n  const block = (kind: BuildKind, name: string, n: number): string => {\n    const head = `${name} (${plural(n)})`;\n    const mine = tickets.filter((t) => ticketKind(t) === kind);\n    if (!mine.length) return `${head}: nothing running or waiting`;\n    const lines = [head];\n    let pos = 0;\n    for (const t of mine) {\n      const mode = t.mode === \"full\" ? \" [full]\" : \"\";\n      const key = t.key ? ` [${t.key}]` : \"\";\n      if (t.state === \"running\") lines.push(`  running  ${t.label}${mode}${key} \u2014 ${t.session}, ${mins(now - (t.startedAt ?? t.enqueuedAt))}  ${t.id}`);\n      else {\n        const why = waitReason(t, tickets);\n        lines.push(`  ${++pos}. waiting ${t.label}${mode}${key} \u2014 ${t.session}, ${mins(now - t.enqueuedAt)}${why ? `, ${why}` : \"\"}  ${t.id}`);\n      }\n    }\n    return lines.join(\"\\n\");\n  };\n  return [block(\"build\", \"builds\", pools.build), block(\"test\", \"tests\", pools.test)].join(\"\\n\");\n}\n\n/** Read-only, lock-free view for `pai worker ps` and the cockpit: live tickets only, nothing pruned. */\nexport function listBuildsSync(dir: string, isAlive: (pid: number) => boolean = defaultDeps().isAlive): BuildTicket[] {\n  return ordered(readTickets(dir).filter((t) => isAlive(t.pid)));\n}\n", "/**\n * ledger.ts \u2014 the routing ledger, one append-only log for every worker event.\n *\n * Line shapes (whitespace-aligned so `tail` reads as a table):\n *\n *   2026-09-17 12:00:00 WORKER-START id=<id> provider=<p> mode=headless model=<m> cwd=<cwd> label=<label>\n *   2026-09-17 12:01:00 WORKER-END   id=<id> provider=<p> mode=headless model=<m> rc=0 secs=60 turns=3 tools=8 label=<label>\n *   2026-09-17 12:00:20 WORKER-REROUTE from=<a> to=<b> reason=quota\n *   2026-09-17 12:00:00 DENIED-ANTHROPIC-AGENT cwd=<cwd> desc=<desc>\n *   2026-09-17 12:00:00 ALLOWED-ANTHROPIC-AGENT cwd=<cwd> desc=<desc>\n *\n * `pai worker log` (and glm-log before it) counts these; keep the tags stable.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\n\nexport function ledgerStamp(d: Date = new Date()): string {\n  const p = (n: number) => String(n).padStart(2, \"0\");\n  return (\n    `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ` +\n    `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`\n  );\n}\n\nexport interface LedgerLine {\n  stamp: string;\n  event: string;\n  fields: Record<string, string>;\n}\n\n/** Parse `2026-09-17 12:00:00 TAG key=value \u2026` into its parts. */\nexport function parseLedgerLine(line: string): LedgerLine | null {\n  const m = line.match(/^(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) (\\S+)(?: (.*))?$/);\n  if (!m) return null;\n  const fields: Record<string, string> = {};\n  // values are whitespace-collapsed by the writers, so spaces safely delimit\n  for (const part of (m[3] ?? \"\").split(\" \")) {\n    if (!part) continue;\n    const eq = part.indexOf(\"=\");\n    if (eq <= 0) continue;\n    fields[part.slice(0, eq)] = part.slice(eq + 1);\n  }\n  return { stamp: m[1], event: m[2], fields };\n}\n\nexport function parseLedger(text: string): LedgerLine[] {\n  const out: LedgerLine[] = [];\n  for (const line of text.split(\"\\n\")) {\n    if (!line.trim()) continue;\n    const parsed = parseLedgerLine(line);\n    if (parsed) out.push(parsed);\n  }\n  return out;\n}\n\n/** Append one ledger line. `kv` order is the caller's; values are flattened. */\nexport function appendLedger(\n  path: string,\n  event: string,\n  kv: Record<string, string | number | null | undefined>,\n  now: Date = new Date()\n): void {\n  const parts: string[] = [];\n  for (const [k, v] of Object.entries(kv)) {\n    if (v === undefined || v === null) continue;\n    parts.push(`${k}=${String(v).replace(/\\s+/g, \" \")}`);\n  }\n  const dir = dirname(path);\n  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n  const tail = parts.length ? \" \" + parts.join(\" \") : \"\";\n  appendFileSync(path, `${ledgerStamp(now)} ${event}${tail}\\n`, \"utf8\");\n}\n\nexport interface LedgerSummary {\n  scope: string;\n  started: number;\n  endedOk: number;\n  endedFailed: number;\n  denied: number;\n  allowed: number;\n  reroutes: number;\n  lastLines: string[];\n}\n\n/** The counts `pai worker log` prints, over today's lines or the whole file. */\nexport function ledgerSummary(\n  path: string,\n  scope: \"today\" | \"all\",\n  lastN = 15,\n  now: Date = new Date()\n): LedgerSummary | null {\n  if (!existsSync(path)) return null;\n  const text = readFileSync(path, \"utf8\");\n  const today = ledgerStamp(now).slice(0, 10);\n  const lines = text.split(\"\\n\").filter((l) => l.trim());\n  const scoped =\n    scope === \"all\" ? lines : lines.filter((l) => l.startsWith(today));\n  const parsed = scoped.map(parseLedgerLine).filter((x): x is LedgerLine => x !== null);\n  const count = (ev: string) => parsed.filter((p) => p.event === ev).length;\n  const ends = parsed.filter((p) => p.event === \"WORKER-END\");\n  const ok = ends.filter((p) => p.fields.rc === \"0\").length;\n  return {\n    scope: scope === \"all\" ? \"all time\" : `today ${today}`,\n    started: count(\"WORKER-START\"),\n    endedOk: ok,\n    endedFailed: ends.length - ok,\n    denied: count(\"DENIED-ANTHROPIC-AGENT\"),\n    allowed: count(\"ALLOWED-ANTHROPIC-AGENT\"),\n    reroutes: count(\"WORKER-REROUTE\"),\n    lastLines: lines.slice(-lastN),\n  };\n}\n", "/**\n * run-lock.ts \u2014 one plan or queue run at a time. The run writes a lock file\n * with its pid and start time and removes it on exit; a lock whose pid is dead\n * is taken over.\n */\n\nimport { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { pidAlive } from \"./status.js\";\n\nexport interface RunLock {\n  pid: number;\n  startedAt: number;\n}\n\nexport const planLockPath = (planPath: string): string => `${planPath}.lock`;\nexport const queueLockPath = (queueStatePath: string): string => `${queueStatePath}.lock`;\n\nconst hhmm = (ms: number): string => {\n  const d = new Date(ms);\n  return `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n};\nexport const sinceText = (l: RunLock): string => `since ${hhmm(l.startedAt)} (pid ${l.pid})`;\n\nexport class RunLockedError extends Error {\n  constructor(readonly what: string, readonly lock: RunLock) {\n    super(`a run of ${what} is already in progress since ${hhmm(lock.startedAt)}`);\n  }\n}\n\n/** The lock of a live run, or null (no file, unreadable, or a dead pid). */\nexport function readRunLock(path: string, isAlive: (pid: number) => boolean = (p) => pidAlive(p)): RunLock | null {\n  if (!existsSync(path)) return null;\n  try {\n    const l = JSON.parse(readFileSync(path, \"utf8\")) as RunLock;\n    return Number.isInteger(l.pid) && l.pid > 0 && typeof l.startedAt === \"number\" && isAlive(l.pid) ? l : null;\n  } catch {\n    return null;\n  }\n}\n\nexport interface LockDeps {\n  pid?: number;\n  now?: () => number;\n  isAlive?: (pid: number) => boolean;\n  /** Runs on SIGTERM/SIGINT before the lock is released and the process exits (stop workers, write the run-end). */\n  onSignal?: (sig: NodeJS.Signals) => Promise<void>;\n  /** Exit hook; tests replace process.exit. */\n  exit?: (code: number) => void;\n}\n\n/** Run `fn` holding the lock at `path`; refuses when a live run holds it. `what` names the run in the message. */\nexport async function withRunLock<T>(path: string | undefined, what: string, fn: () => Promise<T>, deps: LockDeps = {}): Promise<T> {\n  if (!path) return fn();\n  const pid = deps.pid ?? process.pid;\n  const held = readRunLock(path, deps.isAlive);\n  if (held && held.pid !== pid) throw new RunLockedError(what, held);\n  mkdirSync(dirname(path), { recursive: true });\n  writeFileSync(path, JSON.stringify({ pid, startedAt: (deps.now ?? Date.now)() } satisfies RunLock), \"utf8\");\n  const release = (): void => {\n    try {\n      const l = JSON.parse(readFileSync(path, \"utf8\")) as RunLock;\n      if (l.pid === pid) unlinkSync(path);\n    } catch {\n      /* already gone */\n    }\n  };\n  let signalled = false;\n  const onSignal = (sig: NodeJS.Signals): void => {\n    if (signalled) return;\n    signalled = true;\n    void (async () => {\n      try {\n        await deps.onSignal?.(sig);\n      } catch {\n        /* the exit must happen anyway */\n      }\n      release();\n      (deps.exit ?? process.exit)(128 + (sig === \"SIGTERM\" ? 15 : 2));\n    })();\n  };\n  process.on(\"SIGTERM\", onSignal);\n  process.on(\"SIGINT\", onSignal);\n  try {\n    return await fn();\n  } finally {\n    process.off(\"SIGTERM\", onSignal);\n    process.off(\"SIGINT\", onSignal);\n    release();\n  }\n}\n", "/**\n * plan-status.ts \u2014 the live status entry of a plan run (kind \"plan\").\n *\n * The runner's own work between two workers (merge, gate, unit check,\n * bookkeeping) has no worker to show it. This entry carries the current phase\n * and its start time so `pai worker ps`, the cockpit and plan_status can; the\n * live log beside it feeds the plan pane (`pai worker follow plan-<job>`).\n * Kept apart from the worker `.status` files: nothing that walks workers has\n * to know about it.\n */\n\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { nowStamp, pidAlive } from \"./status.js\";\n\nexport interface PlanRunStatus {\n  kind: \"plan\";\n  /** `plan-<slug of job>`: also the id the plan pane follows. */\n  id: string;\n  job: string;\n  pid: number;\n  started: string;\n  updated: string;\n  state: \"running\" | \"done\" | \"failed\" | \"stopped\" | \"lost\";\n  phase: string;\n  /** Epoch ms the current phase began. */\n  phaseSince: number;\n  parts: { merged: number; total: number };\n  /** Ids of the parts whose worker runs now. */\n  running: string[];\n  /** Worker id per part id, for every worker the run started: the run's workers (each in its own pane). */\n  workers?: Record<string, string>;\n  /** True once the runner's own pane is open (its workers each open theirs). */\n  hasPane?: boolean;\n  /** Tests the plan gate counted last time: the denominator of \"tests 214/2600\". */\n  testTotal?: number;\n  /** Local model idle ms between workers and overlapped part starts so far. */\n  idleMs?: number;\n  overlaps?: number;\n}\n\nexport const planRunId = (job: string): string => `plan-${job.replace(/[^A-Za-z0-9._-]+/g, \"-\").replace(/^-+|-+$/g, \"\") || \"job\"}`;\nexport const planStatusFile = (logDir: string, id: string): string => join(logDir, `${id}.planstatus`);\nexport const planLiveFile = (logDir: string, id: string): string => join(logDir, `${id}.planlog`);\n\nexport function writePlanStatus(logDir: string, s: PlanRunStatus): void {\n  s.updated = nowStamp();\n  mkdirSync(logDir, { recursive: true });\n  const path = planStatusFile(logDir, s.id);\n  const tmp = `${path}.${process.pid}.tmp`;\n  writeFileSync(tmp, JSON.stringify(s), \"utf8\");\n  renameSync(tmp, path);\n}\n\nexport function loadPlanStatus(logDir: string, id: string): PlanRunStatus | null {\n  try {\n    const s = JSON.parse(readFileSync(planStatusFile(logDir, id), \"utf8\")) as PlanRunStatus;\n    return s.state === \"running\" && !pidAlive(s.pid) ? { ...s, state: \"lost\" } : s;\n  } catch {\n    return null;\n  }\n}\n\nexport function loadPlanStatuses(logDir: string): PlanRunStatus[] {\n  if (!existsSync(logDir)) return [];\n  return readdirSync(logDir)\n    .filter((n) => n.endsWith(\".planstatus\"))\n    .sort()\n    .map((n) => loadPlanStatus(logDir, n.slice(0, -\".planstatus\".length)))\n    .filter((s): s is PlanRunStatus => !!s);\n}\n\nexport function fmtElapsed(ms: number): string {\n  const s = Math.max(0, Math.round(ms / 1000));\n  return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m${String(s % 60).padStart(2, \"0\")}s`;\n}\n\n/** \"local model idle 1m02s of 7m24s (14%), 2 overlapped starts\". */\nexport function idleLine(idleMs: number, wallMs: number, overlaps: number): string {\n  const pct = wallMs > 0 ? Math.round((idleMs / wallMs) * 100) : 0;\n  return `local model idle ${fmtElapsed(idleMs)} of ${fmtElapsed(wallMs)} (${pct}%), ${overlaps} overlapped ${overlaps === 1 ? \"start\" : \"starts\"}`;\n}\n\n/** ps lines of the running plans: one per plan, phase and how long it has lasted. */\nexport function renderPlanLines(statuses: PlanRunStatus[], now = Date.now()): string[] {\n  return statuses\n    .filter((s) => s.state === \"running\")\n    .map((s) => `plan ${s.job}  ${s.parts.merged}/${s.parts.total} merged  ${s.phase} (${fmtElapsed(now - s.phaseSince)})${s.running.length ? `  workers: ${s.running.join(\", \")}` : \"\"}${s.idleMs !== undefined ? `  ${idleLine(s.idleMs, now - Date.parse(s.started.replace(\" \", \"T\")), s.overlaps ?? 0)}` : \"\"}`);\n}\n\n/** Cockpit rows of the running plans. */\nexport function planRows(statuses: PlanRunStatus[], now = Date.now()): Array<{ id: string; job: string; phase: string; phaseSec: number; ageSec: number; merged: number; total: number; running: string[] }> {\n  return statuses\n    .filter((s) => s.state === \"running\")\n    .map((s) => ({\n      id: s.id,\n      job: s.job,\n      phase: s.phase,\n      phaseSec: Math.max(0, Math.round((now - s.phaseSince) / 1000)),\n      ageSec: Math.max(0, Math.round((now - Date.parse(s.started.replace(\" \", \"T\"))) / 1000)) || 0,\n      merged: s.parts.merged,\n      total: s.parts.total,\n      running: s.running,\n    }));\n}\n\n/** The phase a gate command is shown as: \"tsc\", \"build\", \"tests\", else its first words. */\nexport function gatePhaseName(cmd: string): string {\n  if (/\\btsc\\b/.test(cmd)) return \"tsc\";\n  if (/\\b(tsdown|build)\\b/.test(cmd)) return \"build\";\n  if (/\\b(vitest|jest|test)\\b/.test(cmd)) return \"tests\";\n  return cmd.trim().split(/\\s+/).slice(0, 3).join(\" \").slice(0, 30);\n}\n\n/** Owns the status entry and the live log of one run; every change goes to both. */\nexport class PlanTracker {\n  private status: PlanRunStatus;\n  private lastWrite = 0;\n\n  constructor(private logDir: string, job: string, total: number, merged: number, private now: () => number = Date.now) {\n    const id = planRunId(job);\n    const prev = loadPlanStatus(logDir, id);\n    this.status = {\n      kind: \"plan\",\n      id,\n      job,\n      pid: process.pid,\n      started: nowStamp(new Date(now())),\n      updated: \"\",\n      state: \"running\",\n      phase: \"starting\",\n      phaseSince: now(),\n      parts: { merged, total },\n      running: [],\n      idleMs: 0,\n      overlaps: 0,\n      ...(prev?.testTotal ? { testTotal: prev.testTotal } : {}),\n    };\n    mkdirSync(logDir, { recursive: true });\n    writeFileSync(planLiveFile(logDir, id), \"\", \"utf8\");\n    this.log(`plan ${job} started: ${merged}/${total} parts already merged`);\n    this.save();\n  }\n\n  get id(): string {\n    return this.status.id;\n  }\n\n  get testTotal(): number | undefined {\n    return this.status.testTotal;\n  }\n\n  /** One timestamped line in the live log. */\n  log(line: string): void {\n    const t = new Date(this.now()).toTimeString().slice(0, 8);\n    try {\n      appendFileSync(planLiveFile(this.logDir, this.status.id), line.split(\"\\n\").map((l) => `${t} ${l}`).join(\"\\n\") + \"\\n\", \"utf8\");\n    } catch {\n      /* the log is a view: never fail the run over it */\n    }\n  }\n\n  private save(): void {\n    try {\n      writePlanStatus(this.logDir, this.status);\n    } catch {\n      /* same */\n    }\n  }\n\n  /** A new phase: written to the status entry and the live log. */\n  phase(text: string): void {\n    if (text === this.status.phase) return;\n    this.status.phase = text;\n    this.status.phaseSince = this.now();\n    this.lastWrite = this.now();\n    this.log(`phase: ${text}`);\n    this.save();\n  }\n\n  /** A progress refinement of the current phase (e.g. test counts): status only, at most once a second. */\n  progress(text: string): void {\n    if (text === this.status.phase || this.now() - this.lastWrite < 1000) return;\n    this.status.phase = text;\n    this.lastWrite = this.now();\n    this.save();\n  }\n\n  setRunning(ids: string[]): void {\n    this.status.running = ids;\n    this.save();\n  }\n\n  /** True once the runner's own pane is open (its workers each open theirs). */\n  get hasPane(): boolean {\n    return this.status.hasPane === true;\n  }\n\n  set hasPane(v: boolean) {\n    this.status.hasPane = v;\n    this.save();\n  }\n\n  setWorker(part: string, wid: string): void {\n    this.status.workers = { ...this.status.workers, [part]: wid };\n    this.save();\n  }\n\n  merged(n: number): void {\n    this.status.parts.merged = n;\n    this.save();\n  }\n\n  /** model-idle accounting from the run log: added to the status entry. */\n  addIdle(ms: number): void {\n    this.status.idleMs = (this.status.idleMs ?? 0) + ms;\n    this.save();\n  }\n\n  overlapped(): void {\n    this.status.overlaps = (this.status.overlaps ?? 0) + 1;\n    this.status.idleMs ??= 0;\n    this.save();\n  }\n\n  setTestTotal(n: number): void {\n    if (n > 0) this.status.testTotal = n;\n  }\n\n  end(state: \"done\" | \"failed\" | \"stopped\"): void {\n    this.status.state = state;\n    this.status.phase = state;\n    this.status.phaseSince = this.now();\n    this.status.running = [];\n    this.log(`plan ${this.status.job} ${state}`);\n    this.save();\n  }\n}\n\n/** The last `n` lines of a plan's live log. */\nexport function planLogTail(logDir: string, id: string, n = 40): string {\n  try {\n    return readFileSync(planLiveFile(logDir, id), \"utf8\").trimEnd().split(\"\\n\").slice(-n).join(\"\\n\");\n  } catch {\n    return `no live log for ${id}`;\n  }\n}\n\n/** True once the plan's runner has ended, been killed or vanished: its status is not \"running\" any more. */\nexport function planRunEnded(logDir: string, id: string): boolean {\n  const s = loadPlanStatus(logDir, id);\n  return !s || s.state !== \"running\";\n}\n\n/** Stream the live log into `write` until `stop()` is true; the file's whole content first, then what is appended. */\nexport async function followPlanLog(logDir: string, id: string, write: (s: string) => void, stop: () => boolean = () => false, pollMs = 500): Promise<void> {\n  let at = 0;\n  for (;;) {\n    const done = stop(); // asked before the read, so the last lines are still shown\n    try {\n      const text = readFileSync(planLiveFile(logDir, id), \"utf8\");\n      if (text.length > at) {\n        write(text.slice(at));\n        at = text.length;\n      }\n    } catch {\n      /* not written yet */\n    }\n    if (done) return;\n    await new Promise((r) => setTimeout(r, pollMs));\n  }\n}\n", "/**\n * plan.ts \u2014 the plan file of `pai plan run`: parse, validate, compose specs.\n *\n * A plan is YAML (see docs/plan-runner.md). Pure functions only; reading the\n * file and the spec files is the caller's job except `loadPlan`, which does\n * both and resolves relative paths against the plan's directory.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\n\nexport interface PlanPart {\n  id: string;\n  unit: string;\n  /** Spec text, preamble(s) already prepended. */\n  spec: string;\n  files: string[];\n  proof?: string;\n  dependsOn: string[];\n  risky: boolean;\n  /** Orchestrator-marked: may start its worker while the previous part's worker has ended but its gate/merge still runs. */\n  overlap?: boolean;\n}\n\nexport interface PlanUnit {\n  id: string;\n  unitCheck?: string;\n  parts: PlanPart[];\n}\n\nexport interface Plan {\n  job: string;\n  repo: string;\n  gate: string;\n  provider: string;\n  /** Explicit max_parallel; undefined = the provider's maxConcurrent. */\n  maxParallel?: number;\n  units: PlanUnit[];\n  /** Non-fatal findings of parsePlan, shown by `pai plan validate`. */\n  warnings: string[];\n}\n\n/** JSON-schema-like description of the plan file; `pai plan validate` enforces it. */\nexport const PLAN_SCHEMA = {\n  type: \"object\",\n  required: [\"job\", \"repo\", \"gate\", \"provider\", \"units\"],\n  properties: {\n    job: \"string \u2014 job name, the prefix of every worker label\",\n    repo: \"string \u2014 path of the git checkout the workers change (relative to the plan file)\",\n    gate: \"string \u2014 shell command run on the merged tree; a failure reverts the merge\",\n    provider: \"string \u2014 local provider the parts run on (--class implement)\",\n    max_parallel: \"integer >= 1 \u2014 parts running at once (default: the provider's maxConcurrent, else 3)\",\n    preamble: \"string \u2014 text or file path prepended to every part spec\",\n    units: {\n      type: \"array\",\n      minItems: 1,\n      items: {\n        required: [\"id\", \"parts\"],\n        properties: {\n          id: \"string \u2014 unit name\",\n          preamble: \"string \u2014 text or file path prepended to this unit's part specs (after the plan preamble)\",\n          unit_check: \"string \u2014 command run after the unit's last part merged; failure = needs-decision\",\n          parts: {\n            type: \"array\",\n            minItems: 1,\n            items: {\n              required: [\"id\", \"spec\"],\n              properties: {\n                id: \"string \u2014 unique in the whole plan\",\n                spec: \"string \u2014 task text, or a path to a file holding it\",\n                files: \"string[] \u2014 paths the part touches; parts sharing a file never run together\",\n                proof: \"string \u2014 command the worker must run and show passing\",\n                depends_on: \"string[] \u2014 part ids (any unit) that must be merged first\",\n                risky: \"boolean \u2014 documented intent for review; shown in the schedule\",\n                overlap: \"boolean \u2014 default false; the part is independent of the part before it, so its worker may start as soon as that part's worker has ended, while its gate and merge still run (its merge waits for it). Rejected when it depends on that part\",\n              },\n            },\n          },\n        },\n      },\n    },\n  },\n} as const;\n\nexport class PlanError extends Error {\n  constructor(readonly problems: string[]) {\n    super(problems.join(\"\\n\"));\n  }\n}\n\ntype Obj = Record<string, unknown>;\nconst isObj = (v: unknown): v is Obj => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** `text` as given, or the content of the file it names (single line naming an existing file). */\nfunction textOrFile(value: string, baseDir: string): string {\n  const v = value.trim();\n  if (v && !v.includes(\"\\n\")) {\n    const p = isAbsolute(v) ? v : resolve(baseDir, v);\n    if (existsSync(p)) {\n      try {\n        return readFileSync(p, \"utf8\").trim();\n      } catch {\n        /* a directory or unreadable file: keep the text */\n      }\n    }\n  }\n  return v;\n}\n\n/** A spec value that looks like a path (no spaces, has an extension or slash) but names no file. */\nfunction missingFile(value: string, baseDir: string): boolean {\n  const v = value.trim();\n  if (!v || /\\s/.test(v)) return false;\n  if (!/[\\\\/]|\\.(md|txt|spec)$/.test(v)) return false;\n  return !existsSync(isAbsolute(v) ? v : resolve(baseDir, v));\n}\n\nfunction strList(v: unknown, where: string, problems: string[]): string[] {\n  if (v === undefined || v === null) return [];\n  if (!Array.isArray(v) || v.some((x) => typeof x !== \"string\" || !x)) {\n    problems.push(`${where}: must be a list of non-empty strings`);\n    return [];\n  }\n  return v as string[];\n}\n\n/** Parse and validate plan text. Throws PlanError listing every problem. */\nexport function parsePlan(text: string, baseDir: string): Plan {\n  const problems: string[] = [];\n  let raw: unknown;\n  try {\n    raw = parseYaml(text);\n  } catch (e) {\n    throw new PlanError([`not valid YAML: ${e instanceof Error ? e.message : String(e)}`]);\n  }\n  if (!isObj(raw)) throw new PlanError([\"the plan must be a YAML mapping\"]);\n  const reqStr = (o: Obj, k: string, where: string): string => {\n    const v = o[k];\n    if (typeof v !== \"string\" || !v.trim()) {\n      problems.push(`${where}${k}: required, a non-empty string`);\n      return \"\";\n    }\n    return v.trim();\n  };\n  const job = reqStr(raw, \"job\", \"\");\n  const repo = reqStr(raw, \"repo\", \"\");\n  const gate = reqStr(raw, \"gate\", \"\");\n  const provider = reqStr(raw, \"provider\", \"\");\n  let maxParallel: number | undefined;\n  if (raw.max_parallel !== undefined) {\n    if (typeof raw.max_parallel !== \"number\" || !Number.isInteger(raw.max_parallel) || raw.max_parallel < 1) {\n      problems.push(\"max_parallel: must be an integer >= 1\");\n    } else maxParallel = raw.max_parallel;\n  }\n  const planPre = typeof raw.preamble === \"string\" ? raw.preamble : undefined;\n  if (raw.preamble !== undefined && planPre === undefined) problems.push(\"preamble: must be a string\");\n  if (planPre && missingFile(planPre, baseDir)) problems.push(`preamble: file not found: ${planPre}`);\n\n  const units: PlanUnit[] = [];\n  const seen = new Map<string, string>();\n  if (!Array.isArray(raw.units) || raw.units.length === 0) problems.push(\"units: required, a non-empty list\");\n  else {\n    raw.units.forEach((u, ui) => {\n      const uw = `units[${ui}].`;\n      if (!isObj(u)) return void problems.push(`units[${ui}]: must be a mapping`);\n      const uid = reqStr(u, \"id\", uw);\n      const unitPre = typeof u.preamble === \"string\" ? u.preamble : undefined;\n      if (u.preamble !== undefined && unitPre === undefined) problems.push(`${uw}preamble: must be a string`);\n      if (unitPre && missingFile(unitPre, baseDir)) problems.push(`${uw}preamble: file not found: ${unitPre}`);\n      if (u.unit_check !== undefined && (typeof u.unit_check !== \"string\" || !u.unit_check.trim())) problems.push(`${uw}unit_check: must be a non-empty string`);\n      const parts: PlanPart[] = [];\n      if (!Array.isArray(u.parts) || u.parts.length === 0) problems.push(`${uw}parts: required, a non-empty list`);\n      else {\n        u.parts.forEach((p, pi) => {\n          const pw = `${uw}parts[${pi}].`;\n          if (!isObj(p)) return void problems.push(`${uw}parts[${pi}]: must be a mapping`);\n          const id = reqStr(p, \"id\", pw);\n          const spec = reqStr(p, \"spec\", pw);\n          if (spec && missingFile(spec, baseDir)) problems.push(`${pw}spec: file not found: ${spec}`);\n          if (id) {\n            if (seen.has(id)) problems.push(`${pw}id: duplicate part id \"${id}\" (also in unit ${seen.get(id)})`);\n            else seen.set(id, uid);\n          }\n          if (p.proof !== undefined && typeof p.proof !== \"string\") problems.push(`${pw}proof: must be a string`);\n          if (p.overlap !== undefined && typeof p.overlap !== \"boolean\") problems.push(`${pw}overlap: must be a boolean`);\n          if (p.risky !== undefined && typeof p.risky !== \"boolean\") problems.push(`${pw}risky: must be a boolean`);\n          const pieces = [planPre, unitPre, spec].filter((x): x is string => !!x).map((x) => textOrFile(x, baseDir));\n          parts.push({\n            id,\n            unit: uid,\n            spec: pieces.join(\"\\n\\n\"),\n            files: strList(p.files, `${pw}files`, problems),\n            ...(typeof p.proof === \"string\" && p.proof.trim() ? { proof: p.proof.trim() } : {}),\n            dependsOn: strList(p.depends_on, `${pw}depends_on`, problems),\n            risky: p.risky === true,\n            ...(p.overlap === true ? { overlap: true } : {}),\n          });\n        });\n      }\n      units.push({ id: uid, ...(typeof u.unit_check === \"string\" && u.unit_check.trim() ? { unitCheck: u.unit_check.trim() } : {}), parts });\n    });\n  }\n  const unitIds = units.map((u) => u.id).filter(Boolean);\n  unitIds.forEach((id, i) => {\n    if (unitIds.indexOf(id) !== i) problems.push(`units: duplicate unit id \"${id}\"`);\n  });\n  const all = units.flatMap((u) => u.parts);\n  for (const p of all) {\n    for (const d of p.dependsOn) {\n      if (d === p.id) problems.push(`part ${p.id}: depends on itself`);\n      else if (!seen.has(d)) problems.push(`part ${p.id}: depends_on unknown part \"${d}\"`);\n    }\n  }\n  const byId = new Map(all.map((p) => [p.id, p]));\n  all.forEach((p, i) => {\n    if (!p.overlap) return;\n    const prev = all[i - 1];\n    if (!prev) return void problems.push(`part ${p.id}: overlap: true on the first part of the plan; there is no earlier part to overlap`);\n    const via = dependsVia(p, prev.id, byId);\n    if (via) problems.push(`part ${p.id}: overlap: true but it depends on ${prev.id}, the part before it that is still being gated when it would start${via.length > 1 ? ` (via ${via.slice(0, -1).join(\" -> \")})` : \"\"}; remove overlap or the dependency`);\n  });\n  const cycle = findCycle(all);\n  if (cycle) problems.push(`dependency cycle: ${cycle.join(\" -> \")}`);\n  if (problems.length) throw new PlanError([...new Set(problems)]);\n  const repoAbs = isAbsolute(repo) ? repo : resolve(baseDir, repo);\n  const warnings: string[] = [];\n  for (const p of all) {\n    if (mentionsRepoPath(p.spec, repoAbs)) warnings.push(`part ${p.id}: spec names an absolute path into the repo (${repoAbs}); it is rewritten to a relative path, workers must stay in their worktree`);\n    if (p.files.some((f) => mentionsRepoPath(f, repoAbs))) warnings.push(`part ${p.id}: files lists an absolute path into the repo; use paths relative to the repo`);\n    if (p.proof && mentionsRepoPath(p.proof, repoAbs)) warnings.push(`part ${p.id}: proof names an absolute path into the repo; it is rewritten to a relative path`);\n    p.files = p.files.map((f) => stripRepoPaths(f, repoAbs));\n  }\n  return { job, repo: repoAbs, gate, provider, ...(maxParallel ? { maxParallel } : {}), units, warnings };\n}\n\n/** The dependency path from `part` to `target` (ending in target), or null when `part` does not depend on it, directly or transitively. */\nfunction dependsVia(part: PlanPart, target: string, byId: Map<string, PlanPart>, seen = new Set<string>()): string[] | null {\n  for (const d of part.dependsOn) {\n    if (d === target) return [d];\n    if (seen.has(d)) continue;\n    seen.add(d);\n    const dep = byId.get(d);\n    const sub = dep ? dependsVia(dep, target, byId, seen) : null;\n    if (sub) return [d, ...sub];\n  }\n  return null;\n}\n\nfunction findCycle(parts: PlanPart[]): string[] | null {\n  const byId = new Map(parts.map((p) => [p.id, p]));\n  const state = new Map<string, 1 | 2>();\n  const stack: string[] = [];\n  const visit = (id: string): string[] | null => {\n    if (state.get(id) === 2) return null;\n    if (state.get(id) === 1) return [...stack.slice(stack.indexOf(id)), id];\n    state.set(id, 1);\n    stack.push(id);\n    for (const d of byId.get(id)?.dependsOn ?? []) {\n      if (!byId.has(d)) continue;\n      const c = visit(d);\n      if (c) return c;\n    }\n    stack.pop();\n    state.set(id, 2);\n    return null;\n  };\n  for (const p of parts) {\n    const c = visit(p.id);\n    if (c) return c;\n  }\n  return null;\n}\n\n/** Read and validate the plan file at `path`. */\nexport function loadPlan(path: string): Plan {\n  let text: string;\n  try {\n    text = readFileSync(path, \"utf8\");\n  } catch (e) {\n    throw new PlanError([`cannot read ${path}: ${e instanceof Error ? e.message : String(e)}`]);\n  }\n  return parsePlan(text, dirname(resolve(path)));\n}\n\n/** Where the run log of a plan lives. */\nexport const runLogPath = (planPath: string): string => `${planPath}.run.jsonl`;\n\n/** Replace absolute paths into the plan's repo by relative ones. */\nexport function stripRepoPaths(text: string, repo: string): string {\n  const r = repo.replace(/\\/+$/, \"\");\n  if (!r) return text;\n  const esc = r.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n  return text.replace(new RegExp(`${esc}/`, \"g\"), \"\").replace(new RegExp(`${esc}(?![\\\\w.-])`, \"g\"), \".\");\n}\n\n/** Whether `text` names an absolute path inside `repo`. */\nexport function mentionsRepoPath(text: string, repo: string): boolean {\n  const r = repo.replace(/\\/+$/, \"\");\n  return !!r && r !== \"/\" && stripRepoPaths(text, r) !== text;\n}\n\n/** Fixed footer of every composed spec: keeps the worker in its worktree and makes it commit. */\nexport function specFooter(part: PlanPart, job: string): string {\n  return [\n    \"---\",\n    \"Rules for this task:\",\n    \"- Work only with RELATIVE paths inside the current working directory. It is your own git worktree; never use an absolute path to the repository, or you edit the main checkout instead.\",\n    `- Git add the files you changed, then commit on the current branch before you report, with the message \"${job}: ${part.unit} ${part.id}\". An empty commit is not a commit; uncommitted work is lost.`,\n    \"- Report with an R message.\",\n  ].join(\"\\n\");\n}\n\n/** The worker task: the part spec, its file list and proof, the fixed footer, and (on a retry) the failure tail. */\nexport function composeSpec(part: PlanPart, failure: string | undefined, job: string, repo?: string): string {\n  const clean = (t: string) => (repo ? stripRepoPaths(t, repo) : t);\n  const lines = [clean(part.spec)];\n  if (part.files.length) lines.push(\"\", `Files this part touches: ${part.files.map(clean).join(\", \")}`);\n  if (part.proof) lines.push(\"\", `Proof required: run \\`${clean(part.proof)}\\` and show it passing.`);\n  if (failure) lines.push(\"\", \"The previous attempt failed. Its failure output (tail):\", \"```\", failure.trim(), \"```\", \"Fix the cause and try again.\");\n  lines.push(\"\", specFooter(part, job));\n  return lines.join(\"\\n\");\n}\n\n/** Dirty paths (from `git status --porcelain`) that a part of the plan touches. */\nexport function dirtyConflicts(plan: Plan, dirty: string[]): string[] {\n  const files = plan.units.flatMap((u) => u.parts.flatMap((p) => p.files));\n  return dirty.filter((d) => filesOverlap([d], files));\n}\n\n/** Two file lists collide when they share a path or one names a directory containing the other. */\nexport function filesOverlap(a: string[], b: string[]): boolean {\n  const norm = (f: string) => f.replace(/^\\.\\//, \"\").replace(/\\/+$/, \"\");\n  for (const x of a.map(norm))\n    for (const y of b.map(norm)) if (x === y || x.startsWith(y + \"/\") || y.startsWith(x + \"/\")) return true;\n  return false;\n}\n", "/**\n * plan-runner.ts \u2014 the scheduler behind `pai plan run`. No model involved.\n *\n * Keeps up to N parts running, starts a part once its dependencies are merged\n * and none of its files is in use by a running part, merges each finished part\n * with the plan gate, and walks the failure ladder: one local retry with the\n * failure tail, then needs-decision (or one Sonnet worker with --escalate).\n * Everything that touches workers, git or the clock goes through RunnerDeps.\n */\n\nimport { withRunLock, type LockDeps } from \"./run-lock.js\";\nimport { appendFileSync, existsSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { gatePhaseName } from \"./plan-status.js\";\nimport { composeSpec, filesOverlap, type Plan, type PlanPart } from \"./plan.js\";\n\nexport interface WorkerOutcome {\n  id: string;\n  /** Worker ended with a valid report whose result is \"+\". */\n  ok: boolean;\n  /** Failure text (report why, open items, or the run's last output) when !ok. */\n  failure?: string;\n  tokens?: number | null;\n  /** The part must not be retried (e.g. the worker had no worktree); it goes straight to needs-decision. */\n  fatal?: boolean;\n  /** `pai worker run` refused or failed before any worker existed (pane/gate/config): an environment error, never retried. */\n  spawnRefused?: boolean;\n  /** The loop guard stopped the worker: a failed attempt (retry ladder), never a pass. */\n  loopStopped?: boolean;\n  /** The worker's worktree, when it has one. */\n  worktreeDir?: string;\n  /** The worker's final message (its R report). */\n  report?: string;\n}\n\nexport interface StartRequest {\n  part: PlanPart;\n  label: string;\n  spec: string;\n  provider: string;\n  /** \"local\" runs on plan.provider; \"sonnet\" is the escalation. */\n  tier: \"local\" | \"sonnet\";\n  attempt: number;\n  /** Model for a local-tier worker (`pai worker run --model`); the sonnet tier names its own. */\n  model?: string;\n  /** \"front\": the worker goes ahead of the provider's waiting runs (`--now`). */\n  priority?: \"front\";\n}\n\nexport interface MergeOutcome {\n  ok: boolean;\n  commit?: string;\n  /** HEAD before the merge; a merge that leaves HEAD here changed nothing. */\n  before?: string;\n  /** The worker branch carried no commit, so nothing was merged. */\n  noCommit?: boolean;\n  output: string;\n}\n\n/** What the gate reports while `worker merge --verify` runs it, one command at a time. */\nexport type GateEvent =\n  | { ev: \"gate-start\"; commands: string[] }\n  | { ev: \"cmd-start\"; i: number; cmd: string }\n  | { ev: \"cmd-end\"; i: number; cmd: string; ms: number; ok: boolean; tail?: string }\n  | { ev: \"gate-end\"; ok: boolean; ms: number }\n  /** Refinement of the current phase, e.g. \"gate: tests 214/2600\". */\n  | { ev: \"progress\"; text: string };\n\nexport interface RunnerDeps {\n  startWorker(req: StartRequest): Promise<WorkerOutcome>;\n  merge(workerId: string, gate: string, onGate?: (e: GateEvent) => void): Promise<MergeOutcome>;\n  unitCheck(cmd: string): Promise<{ ok: boolean; output: string }>;\n  now(): number;\n  /** The provider a plan/queue value names: `local` becomes the running local server, any other name is returned unchanged. */\n  resolveLocal(provider: string): Promise<string>;\n  log(entry: RunLogEntry): void;\n  /** The runner's current phase (\"merging p1\", \"gate: tsc\"), shown by ps, the cockpit and plan_status. */\n  phase?(text: string, refine?: boolean): void;\n  /** Parts merged so far / workers running now, for the status entry. */\n  progress?(merged: number, running: string[]): void;\n  /** A line for the plan pane's live log. */\n  note?(line: string): void;\n  /** Stop the workers this runner has started (SIGTERM/SIGINT path); resolves once they are told to end. */\n  stopActive?(): Promise<void>;\n}\n\nexport interface RunLogEntry {\n  ts?: string;\n  event: \"run-start\" | \"run-end\" | \"unit-done\" | \"part-start\" | \"part-end\" | \"merge\" | \"gate-start\" | \"gate-end\" | \"escalate\" | \"needs-decision\" | \"unit-check\" | \"worker-start\" | \"worker-end\" | \"model-idle\" | \"overlap-continue\";\n  part?: string;\n  unit?: string;\n  worker?: string;\n  attempt?: number;\n  tier?: string;\n  ok?: boolean;\n  /** \"no-commit\" when the worker branch held no commit; the merge counts as not done. */\n  status?: string;\n  gate?: string;\n  commit?: string;\n  tokens?: number | null;\n  note?: string;\n  /** gate-start: the gate's commands; gate-end: each command's duration. */\n  commands?: Array<{ cmd: string; ms?: number; ok?: boolean }>;\n  /** gate-end: whole gate duration in ms; worker-end: the worker's run; model-idle: the gap. */\n  ms?: number;\n  /** worker-start/worker-end/model-idle: the provider the worker runs on. */\n  provider?: string;\n  /** model-idle: what the runner did meanwhile (gate, merge, unit-check, waiting-for-dependency); part-end: \"loop\". */\n  reason?: string;\n  /** part-start: the part started while an earlier part was still being gated. */\n  overlap?: boolean;\n  /** run-end: local model idle time, wall time and overlapped starts of the run. */\n  idleMs?: number;\n  wallMs?: number;\n  overlaps?: number;\n}\n\nexport type PartState = \"waiting\" | \"running\" | \"merged\" | \"needs-decision\";\n\nexport interface RunSummary {\n  merged: string[];\n  needsDecision: string[];\n  /** Waiting parts that can never start because a dependency needs a decision. */\n  blocked: string[];\n  unitFailures: string[];\n  /** Parts are waiting, nothing runs and nothing can start: `blockedOn` lists the needs-decision parts and failed units. */\n  stuck: boolean;\n  blockedOn: string[];\n  escalations: number;\n  /** The refusal that stopped scheduling (repeated identically), printed once. */\n  spawnRefusal?: string;\n  /** The run ended on a stop request (marker or signal); `interrupted` lists parts that were in flight. */\n  stopped?: boolean;\n  interrupted?: string[];\n}\n\nexport interface RunOptions {\n  parallel: number;\n  escalate?: boolean;\n  /** Model every local-tier worker of the run gets (default: the provider's). */\n  model?: string;\n  /** \"front\": every worker of the run goes ahead of the provider's waiting runs. */\n  priority?: \"front\";\n  /** Part ids already merged (from the log). */\n  merged?: ReadonlySet<string>;\n  /** Unit ids whose unit_check already passed. */\n  unitsChecked?: ReadonlySet<string>;\n  /** Lock file held for the duration of the run (one run per plan). */\n  lockPath?: string;\n  /** Stop marker (`pai plan stop`): checked before each new part; the run ends \"stopped\" once in-flight parts finish. */\n  stopPath?: string;\n  /** Lock/signal hooks, replaced by tests. */\n  lockDeps?: LockDeps;\n}\n\nexport const planStopPath = (planPath: string): string => `${planPath}.stop`;\n\nexport function requestPlanStop(planPath: string): void {\n  writeFileSync(planStopPath(planPath), new Date().toISOString(), \"utf8\");\n}\n\nconst TAIL_LINES = 40;\nexport const tail = (text: string, n = TAIL_LINES): string => text.trim().split(\"\\n\").slice(-n).join(\"\\n\");\n\n/** Parts startable now: waiting, dependencies merged, no file clash with `busy` or each other, up to `slots`. */\nexport function pickStartable(\n  parts: PlanPart[],\n  state: ReadonlyMap<string, PartState>,\n  busy: PlanPart[],\n  slots: number,\n): PlanPart[] {\n  const picked: PlanPart[] = [];\n  const active = [...busy];\n  for (const p of parts) {\n    if (picked.length >= slots) break;\n    if (state.get(p.id) !== \"waiting\") continue;\n    if (!p.dependsOn.every((d) => state.get(d) === \"merged\")) continue;\n    if (active.some((a) => filesOverlap(a.files, p.files))) continue;\n    picked.push(p);\n    active.push(p);\n  }\n  return picked;\n}\n\nexport function runPlan(plan: Plan, opts: RunOptions, deps: RunnerDeps): Promise<RunSummary> {\n  const running = new Map<string, PlanPart>();\n  let aborted = false;\n  // once a signal arrived the killed workers' failures must not be logged, retried or merged: the parts restart on the next run\n  const guarded: RunnerDeps = {\n    ...deps,\n    log: (e) => {\n      if (!aborted) deps.log(e);\n    },\n    startWorker: (req) => (aborted ? new Promise<WorkerOutcome>(() => undefined) : deps.startWorker(req).then((o) => (aborted ? new Promise<WorkerOutcome>(() => undefined) : o))),\n  };\n  const onSignal = async (): Promise<void> => {\n    aborted = true;\n    const interrupted = [...running.keys()];\n    await deps.stopActive?.();\n    deps.log({ event: \"run-end\", ok: false, status: \"stopped\", note: `interrupted=${interrupted.join(\",\") || \"none\"}` });\n  };\n  return withRunLock(opts.lockPath, `plan ${plan.job}`, () => runPlanLocked(plan, opts, guarded, running), { ...opts.lockDeps, onSignal });\n}\n\nasync function runPlanLocked(plan: Plan, opts: RunOptions, deps: RunnerDeps, active: Map<string, PlanPart>): Promise<RunSummary> {\n  // `provider: local` in a plan means resolveLocalProvider() at run start \u2014\n  // the loopback server that answers, never a provider picked by name.\n  plan = { ...plan, provider: await deps.resolveLocal(plan.provider) };\n  const parts = plan.units.flatMap((u) => u.parts);\n  const state = new Map<string, PartState>(parts.map((p) => [p.id, opts.merged?.has(p.id) ? \"merged\" : \"waiting\"]));\n  const checked = new Set(opts.unitsChecked ?? []);\n  const unitFailures: string[] = [];\n  const failedParts = new Set<string>();\n  let escalations = 0;\n  const refusals = new Map<string, number>();\n  let haltedBy: string | undefined;\n  const running = new Map<string, { part: PlanPart; done: Promise<void> }>();\n  let stopping = false;\n  const stopRequested = (): boolean => !!opts.stopPath && existsSync(opts.stopPath);\n  if (opts.stopPath) {\n    try {\n      unlinkSync(opts.stopPath); // a marker left from before this run is stale\n    } catch {\n      /* none */\n    }\n  }\n  // merges and unit checks touch the checkout: one at a time\n  let lock: Promise<unknown> = Promise.resolve();\n  const exclusive = <T>(fn: () => Promise<T>): Promise<T> => {\n    const r = lock.then(fn, fn);\n    lock = r.catch(() => undefined);\n    return r;\n  };\n\n  // one worker at a time per provider slot count: `parallel` workers on a provider, never more\n  const cap = opts.parallel;\n  const activeWorkers = new Map<string, number>();\n  const waiters: Array<{ provider: string; grant: () => void }> = [];\n  const lastEnd = new Map<string, number>();\n  const causes = new Map<string, string[]>();\n  let idleTotal = 0;\n  let overlaps = 0;\n  const startedAt = deps.now();\n  let wake: () => void = () => undefined;\n  // parts whose worker ended but whose gate, merge or unit check is still going\n  const postWorker = new Set<string>();\n  // overlapped part -> the parts it overlapped (merge waits for them)\n  const overlapOn = new Map<string, string[]>();\n  const noteCause = (c: string): void => {\n    for (const [prov, list] of causes) if ((activeWorkers.get(prov) ?? 0) === 0 && !list.includes(c)) list.push(c);\n  };\n  const workerBegin = (p: PlanPart, provider: string, tier: string, attempt: number): void => {\n    const t = deps.now();\n    if ((activeWorkers.get(provider) ?? 0) === 0 && lastEnd.has(provider)) {\n      const ms = t - lastEnd.get(provider)!;\n      const list = causes.get(provider) ?? [];\n      const why = list.includes(\"gate\") ? list.filter((c) => c !== \"merge\") : list;\n      if (tier === \"local\") idleTotal += ms;\n      deps.log({ event: \"model-idle\", part: p.id, unit: p.unit, provider, tier, ms, reason: why.join(\"+\") || \"waiting-for-dependency\" });\n    }\n    causes.set(provider, []);\n    activeWorkers.set(provider, (activeWorkers.get(provider) ?? 0) + 1);\n    postWorker.delete(p.id);\n    deps.log({ event: \"worker-start\", part: p.id, unit: p.unit, provider, tier, attempt });\n  };\n  const acquire = (p: PlanPart, provider: string, tier: string, attempt: number): Promise<void> => {\n    if ((activeWorkers.get(provider) ?? 0) < cap) {\n      workerBegin(p, provider, tier, attempt);\n      return Promise.resolve();\n    }\n    return new Promise((resolve) => waiters.push({ provider, grant: () => (workerBegin(p, provider, tier, attempt), resolve()) }));\n  };\n  const release = (p: PlanPart, provider: string): void => {\n    activeWorkers.set(provider, (activeWorkers.get(provider) ?? 1) - 1);\n    lastEnd.set(provider, deps.now());\n    causes.set(provider, []);\n    postWorker.add(p.id);\n    const i = waiters.findIndex((w) => w.provider === provider);\n    if (i >= 0 && (activeWorkers.get(provider) ?? 0) < cap) waiters.splice(i, 1)[0].grant();\n    wake();\n  };\n\n  const progress = (): void => deps.progress?.([...state.values()].filter((v) => v === \"merged\").length, [...running.keys()]);\n\n  const label = (p: PlanPart) => `${plan.job}: ${p.unit} ${p.id}`;\n\n  const checkUnit = async (unitId: string): Promise<void> => {\n    const unit = plan.units.find((u) => u.id === unitId)!;\n    if (checked.has(unitId) || !unit.parts.every((p) => state.get(p.id) === \"merged\")) return;\n    checked.add(unitId);\n    if (!unit.unitCheck) {\n      deps.log({ event: \"unit-done\", unit: unitId });\n      return;\n    }\n    const r = await exclusive(() => {\n      noteCause(\"unit-check\");\n      deps.phase?.(`unit check ${unitId}`);\n      return deps.unitCheck(unit.unitCheck!);\n    });\n    deps.log({ event: \"unit-check\", unit: unitId, ok: r.ok, note: r.ok ? undefined : tail(r.output, 10) });\n    if (r.ok) deps.log({ event: \"unit-done\", unit: unitId });\n    if (!r.ok) {\n      unitFailures.push(unitId);\n      // parts that depend on a unit whose check failed wait for a decision\n      for (const up of unit.parts) failedParts.add(up.id);\n      deps.log({ event: \"needs-decision\", unit: unitId, note: `unit_check failed: ${tail(r.output, 10)}` });\n    }\n  };\n\n  const attemptPart = async (p: PlanPart): Promise<void> => {\n    const ladder: Array<{ tier: \"local\" | \"sonnet\"; provider: string }> = [\n      { tier: \"local\", provider: plan.provider },\n      { tier: \"local\", provider: plan.provider },\n      ...(opts.escalate ? [{ tier: \"sonnet\" as const, provider: \"anthropic\" }] : []),\n    ];\n    let failure: string | undefined;\n    for (let i = 0; i < ladder.length; i++) {\n      const { tier, provider } = ladder[i];\n      if (tier === \"sonnet\") {\n        escalations++;\n        deps.log({ event: \"escalate\", part: p.id, unit: p.unit, attempt: i + 1, tier, note: tail(failure ?? \"\", 10) });\n      }\n      deps.log({ event: \"part-start\", part: p.id, unit: p.unit, attempt: i + 1, tier, ...(overlapOn.has(p.id) && i === 0 ? { overlap: true } : {}) });\n      deps.phase?.(`starting ${p.id}`);\n      const granted = acquire(p, provider, tier, i + 1);\n      const wstart = deps.now();\n      await granted;\n      let out: WorkerOutcome;\n      try {\n        out = await deps.startWorker({ part: p, label: label(p), spec: composeSpec(p, failure, plan.job, plan.repo), provider, tier, attempt: i + 1, ...(tier === \"local\" && opts.model ? { model: opts.model } : {}), ...(opts.priority ? { priority: opts.priority } : {}) });\n      } finally {\n        release(p, provider);\n      }\n      deps.log({ event: \"worker-end\", part: p.id, unit: p.unit, worker: out.id, provider, tier, attempt: i + 1, ok: out.ok, ms: deps.now() - wstart });\n      deps.note?.(`${p.id} worker ${out.id || \"-\"} ${out.ok ? \"finished ok\" : \"failed\"}${out.ok ? \"\" : `: ${tail(out.failure ?? \"\", 4)}`}`);\n      deps.log({ event: \"part-end\", part: p.id, unit: p.unit, worker: out.id, attempt: i + 1, tier, ok: out.ok, ...(out.loopStopped ? { reason: \"loop\" } : {}), tokens: out.tokens ?? null, note: out.ok ? undefined : tail(out.failure ?? \"\", 10) });\n      if (!out.ok && out.spawnRefused) {\n        failure = out.failure || \"worker spawn refused\";\n        const n = (refusals.get(failure) ?? 0) + 1;\n        refusals.set(failure, n);\n        if (n >= 2) haltedBy = failure;\n        break;\n      }\n      if (!out.ok && out.fatal) {\n        failure = out.failure || \"the worker could not be isolated\";\n        break;\n      }\n      if (!out.ok) {\n        failure = out.failure || \"the worker ended without a valid passing report\";\n        continue;\n      }\n      const gateObserver = (): ((e: GateEvent) => void) => {\n        let cmds: Array<{ cmd: string; ms?: number; ok?: boolean }> = [];\n        return (e) => {\n          if (e.ev === \"gate-start\") {\n            cmds = e.commands.map((cmd) => ({ cmd }));\n            noteCause(\"gate\");\n            deps.log({ event: \"gate-start\", part: p.id, unit: p.unit, worker: out.id, gate: plan.gate, commands: cmds });\n          } else if (e.ev === \"cmd-start\") {\n            deps.phase?.(`gate: ${gatePhaseName(e.cmd)}`);\n            deps.note?.(`gate command ${e.i + 1}/${cmds.length || \"?\"}: ${e.cmd}`);\n          } else if (e.ev === \"cmd-end\") {\n            cmds[e.i] = { cmd: e.cmd, ms: e.ms, ok: e.ok };\n            deps.note?.(`gate command ${e.i + 1} ${e.ok ? \"ok\" : \"FAILED\"} in ${(e.ms / 1000).toFixed(1)}s${e.tail ? `\\n${e.tail}` : \"\"}`);\n          } else if (e.ev === \"gate-end\") {\n            deps.log({ event: \"gate-end\", part: p.id, unit: p.unit, worker: out.id, ok: e.ok, ms: e.ms, commands: cmds });\n          } else deps.phase?.(e.text, true);\n        };\n      };\n      // an overlapped part merges only after the parts it overlapped are through (merged, or out with a decision)\n      const gated = (overlapOn.get(p.id) ?? []).map((id) => running.get(id)?.done).filter((d): d is Promise<void> => !!d);\n      if (gated.length) await Promise.all(gated);\n      let m = await exclusive(() => {\n        noteCause(\"merge\");\n        deps.phase?.(`merging ${p.id}`);\n        return deps.merge(out.id, plan.gate, gateObserver());\n      });\n      // a \"merge\" that moved nothing is not a merge: the worker never committed\n      if (m.ok && (m.noCommit || !m.commit || m.commit === m.before)) m = { ...m, ok: false, noCommit: true };\n      deps.log({ event: \"merge\", part: p.id, unit: p.unit, worker: out.id, attempt: i + 1, ok: m.ok, ...(m.noCommit ? { status: \"no-commit\" } : {}), gate: plan.gate, ...(m.noCommit ? {} : { commit: m.commit }), note: m.ok ? undefined : tail(m.output, 10) });\n      deps.note?.(`${p.id} ${m.ok ? \"merged\" : \"merge failed\"}${m.commit ? ` ${m.commit.slice(0, 7)}` : \"\"}${m.ok ? \"\" : `\\n${tail(m.output, 8)}`}`);\n      if (m.ok) {\n        state.set(p.id, \"merged\");\n        progress();\n        await checkUnit(p.unit);\n        return;\n      }\n      failure = m.noCommit\n        ? `no-commit: the worker branch has no commit, nothing was merged. Git add the files you changed, then commit on the current branch (message \"${label(p)}\") and edit files only by relative path inside your worktree.${m.output.trim() ? `\\n${tail(m.output, 10)}` : \"\"}`\n        : `gate failed (${plan.gate}):\\n${tail(m.output)}`;\n    }\n    state.set(p.id, \"needs-decision\");\n    deps.log({ event: \"needs-decision\", part: p.id, unit: p.unit, note: tail(failure ?? \"\", 10) });\n    for (const [q, over] of overlapOn) {\n      if (over.includes(p.id) && running.has(q)) deps.log({ event: \"overlap-continue\", part: q, unit: p.unit, note: `${p.id} failed its gate; ${q} is independent of it and keeps running` });\n    }\n  };\n\n  const idleFields = (): Pick<RunLogEntry, \"idleMs\" | \"wallMs\" | \"overlaps\"> => ({ idleMs: idleTotal, wallMs: deps.now() - startedAt, overlaps });\n  deps.log({ event: \"run-start\", note: `parallel=${opts.parallel}${opts.escalate ? \" escalate=sonnet\" : \"\"}` });\n  // a resumed run may have units complete but unchecked (killed between merge and check)\n  for (const u of plan.units) await checkUnit(u.id);\n  for (;;) {\n    if (!stopping && stopRequested()) stopping = true;\n    const launch = (p: PlanPart): void => {\n      state.set(p.id, \"running\");\n      // started while earlier parts are past their worker (gate, merge, unit check): an overlap\n      const over = [...postWorker].filter((id) => running.has(id));\n      if (p.overlap && over.length) {\n        overlapOn.set(p.id, over);\n        overlaps++;\n      }\n      const done = attemptPart(p).finally(() => {\n        running.delete(p.id);\n        active.delete(p.id);\n        postWorker.delete(p.id);\n        overlapOn.delete(p.id);\n        progress();\n      });\n      running.set(p.id, { part: p, done });\n      active.set(p.id, p);\n      progress();\n    };\n    if (!haltedBy && !stopping) {\n      const eligible = parts.filter((p) => !p.dependsOn.some((d) => failedParts.has(d)));\n      for (const p of pickStartable(eligible, state, [...running.values()].map((r) => r.part), opts.parallel - running.size)) launch(p);\n      // an orchestrator-marked part, once every part before it has started, may start once the workers of the running parts have ended, though their gate is still going\n      const workers = [...activeWorkers.values()].reduce((a, b) => a + b, 0);\n      const free = opts.parallel - workers;\n      if (free > 0) {\n        for (const p of pickStartable(eligible.filter((q) => q.overlap && parts.slice(0, parts.indexOf(q)).every((e) => state.get(e.id) !== \"waiting\")), state, [...running.values()].map((r) => r.part), free)) launch(p);\n      }\n    }\n    if (running.size === 0) break;\n    const woken = new Promise<void>((r) => (wake = r));\n    await Promise.race([woken, ...[...running.values()].map((r) => r.done)]);\n  }\n  if (stopping && opts.stopPath) {\n    try {\n      unlinkSync(opts.stopPath);\n    } catch {\n      /* already gone */\n    }\n  }\n  const waiting = parts.filter((p) => state.get(p.id) === \"waiting\").map((p) => p.id);\n  const summary: RunSummary = {\n    merged: parts.filter((p) => state.get(p.id) === \"merged\").map((p) => p.id),\n    needsDecision: parts.filter((p) => state.get(p.id) === \"needs-decision\").map((p) => p.id),\n    blocked: waiting,\n    unitFailures,\n    stuck: !haltedBy && !stopping && waiting.length > 0,\n    blockedOn: [...parts.filter((p) => state.get(p.id) === \"needs-decision\").map((p) => p.id), ...unitFailures.map((u) => `unit ${u}`)],\n    escalations,\n    ...(haltedBy ? { spawnRefusal: haltedBy } : {}),\n    ...(stopping ? { stopped: true } : {}),\n  };\n  if (stopping) {\n    deps.log({ event: \"run-end\", ok: false, status: \"stopped\", note: `stop requested; merged=${summary.merged.length} waiting=${waiting.length}`, ...idleFields() });\n    return summary;\n  }\n  deps.log({ event: \"run-end\", ok: !summary.needsDecision.length && !waiting.length && !unitFailures.length, ...(summary.stuck ? { status: \"blocked\" } : {}), note: `merged=${summary.merged.length} needs-decision=${summary.needsDecision.length} blocked=${waiting.length}`, ...idleFields() });\n  return summary;\n}\n\n/** Exit code of `pai plan run`: 0 all merged, 4 stopped by `pai plan stop`, 1 failures with everything else done, 3 blocked (parts left that cannot start). */\nexport function runExitCode(s: RunSummary): number {\n  if (s.stopped) return 4;\n  if (s.stuck) return 3;\n  return s.needsDecision.length || s.blocked.length || s.unitFailures.length ? 1 : 0;\n}\n\nexport function blockedMessage(s: RunSummary, planPath: string): string {\n  return `blocked: ${s.blocked.length} parts wait on needs-decision items: ${s.blockedOn.join(\", \") || \"none\"}; fix and re-run \\`pai plan run ${planPath}\\` (merged parts are skipped)`;\n}\n\n/** The schedule a run would execute, assuming every part succeeds: waves of at most `parallel` parts. */\nexport function dryRunSchedule(plan: Plan, opts: RunOptions): PlanPart[][] {\n  const parts = plan.units.flatMap((u) => u.parts);\n  const state = new Map<string, PartState>(parts.map((p) => [p.id, opts.merged?.has(p.id) ? \"merged\" : \"waiting\"]));\n  const waves: PlanPart[][] = [];\n  for (;;) {\n    const wave = pickStartable(parts, state, [], opts.parallel);\n    if (!wave.length) break;\n    waves.push(wave);\n    for (const p of wave) state.set(p.id, \"merged\");\n  }\n  return waves;\n}\n\nexport function renderSchedule(plan: Plan, opts: RunOptions): string {\n  const waves = dryRunSchedule(plan, opts);\n  const lines = [`job ${plan.job}: ${plan.units.flatMap((u) => u.parts).length} parts, provider ${plan.provider}, ${opts.parallel} at a time, gate: ${plan.gate}`];\n  waves.forEach((w, i) => {\n    lines.push(`step ${i + 1}:`);\n    for (const p of w) lines.push(`  ${p.unit} ${p.id}${p.risky ? \" [risky]\" : \"\"}${p.dependsOn.length ? `  after ${p.dependsOn.join(\", \")}` : \"\"}${p.files.length ? `  files ${p.files.join(\", \")}` : \"\"}`);\n  });\n  const left = plan.units.flatMap((u) => u.parts).filter((p) => !opts.merged?.has(p.id) && !waves.flat().includes(p));\n  if (left.length) lines.push(`never scheduled: ${left.map((p) => p.id).join(\", \")}`);\n  for (const u of plan.units) if (u.unitCheck && !opts.unitsChecked?.has(u.id)) lines.push(`after unit ${u.id}: ${u.unitCheck}`);\n  if (opts.merged?.size) lines.push(`skipped (already merged): ${[...opts.merged].join(\", \")}`);\n  return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// run log\n\nexport function readRunLog(path: string): RunLogEntry[] {\n  if (!existsSync(path)) return [];\n  const out: RunLogEntry[] = [];\n  for (const line of readFileSync(path, \"utf8\").split(\"\\n\")) {\n    if (!line.startsWith(\"{\")) continue;\n    try {\n      out.push(JSON.parse(line) as RunLogEntry);\n    } catch {\n      /* torn line */\n    }\n  }\n  return out;\n}\n\nexport function appendRunLog(path: string, entry: RunLogEntry, now: () => number = Date.now): void {\n  appendFileSync(path, JSON.stringify({ ts: new Date(now()).toISOString(), ...entry }) + \"\\n\", \"utf8\");\n}\n\n/** What a resumed run skips: merged parts and units whose check passed. */\nexport function resumeState(log: RunLogEntry[]): { merged: Set<string>; unitsChecked: Set<string> } {\n  const merged = new Set<string>();\n  const unitsChecked = new Set<string>();\n  for (const e of log) {\n    if (e.event === \"merge\" && e.ok && e.part) merged.add(e.part);\n    if (e.event === \"unit-check\" && e.ok && e.unit) unitsChecked.add(e.unit);\n  }\n  return { merged, unitsChecked };\n}\n\nexport interface PlanStatus {\n  done: string[];\n  running: string[];\n  waiting: string[];\n  needsDecision: string[];\n  tokens: number;\n  wallMs: number;\n  /** Local model idle time between workers (sum of model-idle events) and parts that started overlapped. */\n  idleMs: number;\n  overlaps: number;\n}\n\n/** Summarise the log against the plan's parts. `now` closes an unfinished run segment. */\nexport function planStatus(plan: Plan, log: RunLogEntry[], now: number): PlanStatus {\n  const last = new Map<string, RunLogEntry>();\n  let tokens = 0;\n  let idleMs = 0;\n  let overlaps = 0;\n  let wallMs = 0;\n  let segStart: number | null = null;\n  let lastTs = 0;\n  for (const e of log) {\n    const t = e.ts ? Date.parse(e.ts) : 0;\n    lastTs = t || lastTs;\n    if (e.event === \"run-start\") segStart = t;\n    if (e.event === \"run-end\" && segStart !== null) {\n      wallMs += t - segStart;\n      segStart = null;\n    }\n    if (e.part && [\"part-start\", \"part-end\", \"merge\", \"needs-decision\"].includes(e.event)) last.set(e.part, e);\n    if (e.event === \"model-idle\" && e.tier === \"local\" && typeof e.ms === \"number\") idleMs += e.ms;\n    if (e.event === \"part-start\" && e.overlap) overlaps++;\n    if (e.event === \"part-end\" && typeof e.tokens === \"number\") tokens += e.tokens;\n  }\n  // an open segment is a run in progress when the log is fresh, else a run that died: count up to its last entry\n  if (segStart !== null) wallMs += (now - lastTs < 120_000 ? now : lastTs) - segStart;\n  const s: PlanStatus = { done: [], running: [], waiting: [], needsDecision: [], tokens, wallMs, idleMs, overlaps };\n  const runActive = segStart !== null && now - lastTs < 120_000;\n  for (const p of plan.units.flatMap((u) => u.parts)) {\n    const e = last.get(p.id);\n    if (e?.event === \"merge\" && e.ok) s.done.push(p.id);\n    else if (e?.event === \"needs-decision\") s.needsDecision.push(p.id);\n    else if (e?.event === \"part-start\" && runActive) s.running.push(p.id);\n    else s.waiting.push(p.id);\n  }\n  return s;\n}\n\n/** \"local model idle 1m02s of 7m24s (14%), 2 overlapped starts\". */\nexport function idleText(idleMs: number, wallMs: number, overlaps: number): string {\n  const f = (ms: number): string => {\n    const s = Math.round(ms / 1000);\n    return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m${String(s % 60).padStart(2, \"0\")}s`;\n  };\n  const pct = wallMs > 0 ? Math.round((idleMs / wallMs) * 100) : 0;\n  return `local model idle ${f(idleMs)} of ${f(wallMs)} (${pct}%), ${overlaps} overlapped ${overlaps === 1 ? \"start\" : \"starts\"}`;\n}\n\nexport function renderStatus(plan: Plan, st: PlanStatus): string {\n  const fmt = (ids: string[]) => (ids.length ? `${ids.length} (${ids.join(\", \")})` : \"0\");\n  const secs = Math.round(st.wallMs / 1000);\n  return [\n    `job ${plan.job}`,\n    `done           ${fmt(st.done)}`,\n    `running        ${fmt(st.running)}`,\n    `waiting        ${fmt(st.waiting)}`,\n    `needs-decision ${fmt(st.needsDecision)}`,\n    `wall so far    ${Math.floor(secs / 60)}m${String(secs % 60).padStart(2, \"0\")}s, tokens ${st.tokens}`,\n    idleText(st.idleMs, st.wallMs, st.overlaps),\n  ].join(\"\\n\");\n}\n", "/**\n * run-events.ts \u2014 report plan and queue runs back into the conversation.\n *\n * The operator never polls: a run reports the moments that need them to the\n * session that started it (AIBroker send_to_session, so the session wakes and\n * can talk to the operator) and, for blocked/finished, to PAILot. A run\n * started with --detach is registered under <logDir>/runs and keeps going\n * after the starting session ends. Everything outside the process (AIBroker,\n * clock, spawning) goes through injected functions; tests never send.\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, openSync, readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { idleText, type RunLogEntry } from \"./plan-runner.js\";\nimport type { QueueItem } from \"./queue.js\";\n\n/** The session that started a run: where its events go. */\nexport interface RunOrigin {\n  claudeSession?: string;\n  /** iTerm2 session uuid (AIBroker target). */\n  iterm?: string;\n  /** PAI / AIBroker session name. */\n  name?: string;\n}\n\nexport interface Messenger {\n  /** Type a message into the originating session. */\n  toSession(origin: RunOrigin, text: string): Promise<boolean>;\n  /** Short plain sentence to the operator's phone. */\n  toPailot(text: string): Promise<boolean>;\n}\n\nexport const PLAN_GROUP_THROTTLE_MS = 15 * 60_000;\nexport const QUEUE_READY_BATCH_MS = 10 * 60_000;\n\n/** The origin of the calling session, from its environment. */\nexport function originFromEnv(env: NodeJS.ProcessEnv = process.env): RunOrigin {\n  if (env.PAI_RUN_ORIGIN) {\n    try {\n      return JSON.parse(env.PAI_RUN_ORIGIN) as RunOrigin;\n    } catch {\n      /* fall through to the live environment */\n    }\n  }\n  const o: RunOrigin = {};\n  if (env.CLAUDE_CODE_SESSION_ID) o.claudeSession = env.CLAUDE_CODE_SESSION_ID;\n  const iterm = env.ITERM_SESSION_ID?.split(\":\").pop();\n  if (iterm) o.iterm = iterm;\n  const name = env.PAI_WORKER_PARENT ?? env.PAI_SESSION_NAME ?? env.AIBROKER_SESSION_NAME ?? env.AIBROKER_SESSION;\n  if (name) o.name = name;\n  return o;\n}\n\n/** Real messenger over AIBroker's socket; unreachable AIBroker is logged and ignored. */\nexport function aibrokerMessenger(logDir: string, warn: (s: string) => void = (s) => process.stderr.write(s + \"\\n\")): Messenger {\n  return {\n    async toSession(origin, text) {\n      try {\n        const { sendToSession } = await import(\"../cli/lib/aibroker-client.js\");\n        let target = origin.iterm;\n        if (!target && origin.claudeSession) {\n          const { itermForClaudeSession } = await import(\"./scope.js\");\n          target = itermForClaudeSession(logDir, origin.claudeSession) ?? undefined;\n        }\n        target = target ?? origin.name;\n        if (!target) {\n          warn(\"run report not delivered: the run has no originating session\");\n          return false;\n        }\n        const r = await sendToSession(target, text);\n        if (!r.ok && !r.timedOut) warn(`run report not delivered to the session: ${r.error}`);\n        return r.ok || r.timedOut === true;\n      } catch (e) {\n        warn(`run report not delivered to the session: ${String(e)}`);\n        return false;\n      }\n    },\n    async toPailot(text) {\n      try {\n        const { callAiBroker } = await import(\"../cli/lib/aibroker-client.js\");\n        await callAiBroker(\"pailot_send\", { text }, 8_000);\n        return true;\n      } catch (e) {\n        warn(`run report not delivered to PAILot: ${String(e)}`);\n        return false;\n      }\n    },\n  };\n}\n\nconst short = (s: string | undefined, n = 160): string => (s ?? \"\").replace(/\\s+/g, \" \").trim().slice(0, n);\nconst mins = (ms: number): string => {\n  const m = Math.round(ms / 60_000);\n  return m < 1 ? `${Math.round(ms / 1000)}s` : `${m} min`;\n};\nconst fmtTok = (n: number): string => (n >= 1000 ? `${Math.round(n / 100) / 10}k` : String(n));\n\n/** AG2-style kind line plus a plain sentence, one line (it is typed into a terminal). */\nexport function eventLine(id: string, kind: string, fields: Record<string, string | number>, sentence: string): string {\n  const kv = Object.entries(fields).map(([k, v]) => `${k}=${String(v).replace(/[|\\n]/g, \" \")}`);\n  return `S i=${id} z=${kind}${kv.length ? \" | \" + kv.join(\" | \") : \"\"} \u2014 ${sentence}`;\n}\n\nabstract class Reporter {\n  private chain: Promise<unknown> = Promise.resolve();\n  constructor(protected readonly msg: Messenger, protected readonly origin: RunOrigin, protected readonly now: () => number) {}\n  protected send(text: string, pailot?: string): void {\n    this.chain = this.chain.then(async () => {\n      await this.msg.toSession(this.origin, text).catch(() => false);\n      if (pailot) await this.msg.toPailot(pailot).catch(() => false);\n    });\n  }\n  /** Wait for every queued message; call before the process exits. */\n  async flush(): Promise<void> {\n    await this.chain;\n  }\n}\n\n/** Turns the plan runner's log entries into messages. Feed it every entry via `onLog`. */\nexport class PlanReporter extends Reporter {\n  private readonly start: number;\n  private localTokens = 0;\n  private cloudTokens = 0;\n  private idleMs = 0;\n  private overlaps = 0;\n  private escalations = 0;\n  private merged = 0;\n  private readonly nd: string[] = [];\n  private lastGroup = -Infinity;\n  private pendingGroups: string[] = [];\n\n  constructor(private readonly job: string, private readonly totalParts: number, msg: Messenger, origin: RunOrigin, now: () => number = Date.now) {\n    super(msg, origin, now);\n    this.start = now();\n  }\n\n  private get id(): string {\n    return `plan:${this.job}`;\n  }\n\n  onLog(e: RunLogEntry): void {\n    if (e.event === \"part-end\" && typeof e.tokens === \"number\") {\n      if (e.tier === \"sonnet\") this.cloudTokens += e.tokens;\n      else this.localTokens += e.tokens;\n    }\n    if (e.event === \"model-idle\" && e.tier === \"local\" && typeof e.ms === \"number\") this.idleMs += e.ms;\n    if (e.event === \"part-start\" && e.overlap) this.overlaps++;\n    if (e.event === \"escalate\") this.escalations++;\n    if (e.event === \"merge\" && e.ok) this.merged++;\n    if (e.event === \"needs-decision\" && e.part) {\n      this.nd.push(e.part);\n      this.send(\n        eventLine(this.id, \"needs-decision\", { part: e.part }, `Part ${e.part} of ${this.job} failed twice and needs your decision: ${short(e.note)}`),\n      );\n    }\n    if (e.event === \"unit-check\" && e.ok === false) {\n      this.send(eventLine(this.id, \"unit-check-failed\", { unit: e.unit ?? \"?\" }, `The check after unit ${e.unit} of ${this.job} failed: ${short(e.note)}`));\n    }\n    if (e.event === \"unit-done\" && e.unit) this.unitDone(e.unit);\n    if (e.event === \"run-end\") this.runEnd(e);\n  }\n\n  private unitDone(unit: string): void {\n    this.pendingGroups.push(unit);\n    const t = this.now();\n    if (t - this.lastGroup < PLAN_GROUP_THROTTLE_MS) return;\n    this.lastGroup = t;\n    const units = this.pendingGroups.splice(0);\n    this.send(eventLine(this.id, \"unit-done\", { units: units.join(\",\") }, `${this.job}: ${units.length === 1 ? \"unit\" : \"units\"} ${units.join(\", \")} finished; ${this.merged} of ${this.totalParts} parts merged so far.`));\n  }\n\n  private runEnd(e: RunLogEntry): void {\n    const outcome = e.ok ? \"merged\" : e.status === \"blocked\" ? \"blocked\" : \"failed\";\n    const wallMs = this.now() - this.start;\n    const wall = mins(wallMs);\n    const idle = idleText(this.idleMs, wallMs, this.overlaps);\n    const stats = { outcome, wall, merged: `${this.merged}/${this.totalParts}`, escalations: this.escalations, local_tokens: this.localTokens, cloud_tokens: this.cloudTokens, idle: idle.replace(/^local model idle /, \"\") };\n    const what =\n      outcome === \"merged\"\n        ? `${this.job} is done: all ${this.totalParts} parts merged in ${wall}.`\n        : outcome === \"blocked\"\n          ? `${this.job} is blocked after ${wall}: ${this.merged} of ${this.totalParts} parts merged, the rest wait on ${this.nd.join(\", \") || \"a failed check\"}. It needs your decision.`\n          : `${this.job} ended with failures after ${wall}: ${this.merged} of ${this.totalParts} parts merged${this.nd.length ? `, ${this.nd.join(\", \")} need your decision` : \"\"}.`;\n    const extra = ` Escalations ${this.escalations}, ${fmtTok(this.localTokens)} local tokens, ${fmtTok(this.cloudTokens)} cloud tokens; ${idle}.`;\n    this.send(eventLine(this.id, \"run-\" + outcome, stats, what + extra), what);\n  }\n}\n\n/** Queue events: needs-decision at once, ready items batched. */\nexport class QueueReporter extends Reporter {\n  private readyBatch: QueueItem[] = [];\n  private lastReady = -Infinity;\n\n  constructor(private readonly project: string, msg: Messenger, origin: RunOrigin, now: () => number = Date.now) {\n    super(msg, origin, now);\n  }\n\n  private get id(): string {\n    return `queue:${this.project}`;\n  }\n\n  onReady(item: QueueItem): void {\n    this.readyBatch.push(item);\n    if (this.now() - this.lastReady >= QUEUE_READY_BATCH_MS) this.sendReady();\n  }\n\n  onNeedsDecision(item: QueueItem): void {\n    this.send(eventLine(this.id, \"needs-decision\", { item: item.id }, `Queue item ${item.id} (\"${short(item.text, 80)}\") failed twice and needs your decision: ${short(item.why)}`));\n  }\n\n  private sendReady(): void {\n    const items = this.readyBatch.splice(0);\n    if (!items.length) return;\n    this.lastReady = this.now();\n    const list = items.map((i) => `${i.id} \"${short(i.text, 60)}\"`).join(\", \");\n    this.send(eventLine(this.id, \"ready\", { items: items.map((i) => i.id).join(\",\") }, `${items.length === 1 ? \"One queue item is\" : `${items.length} queue items are`} ready for review: ${list}.`));\n  }\n\n  /** Send what the throttle held back; call once the run is over. */\n  async finish(): Promise<void> {\n    this.sendReady();\n    await this.flush();\n  }\n}\n\n// ---------------------------------------------------------------------------\n// detach: registry of runs that outlive their session\n\nexport interface RunRecord {\n  id: string;\n  kind: \"plan\" | \"queue\";\n  pid: number;\n  argv: string[];\n  cwd: string;\n  origin: RunOrigin;\n  started: string;\n  output: string;\n}\n\nexport const runsDir = (logDir: string): string => join(logDir, \"runs\");\nexport const runRecordPath = (logDir: string, id: string): string => join(runsDir(logDir), `${id}.json`);\n\nexport interface DetachDeps {\n  /** Start `bin args` detached with stdout/stderr to `outFd`; returns the pid. */\n  spawnDetached(bin: string, args: string[], opts: { cwd: string; env: NodeJS.ProcessEnv; outPath: string }): number;\n  now(): number;\n}\n\nexport const realDetachDeps: DetachDeps = {\n  spawnDetached(bin, args, { cwd, env, outPath }) {\n    const fd = openSync(outPath, \"a\");\n    const child = spawn(bin, args, { cwd, env, detached: true, stdio: [\"ignore\", fd, fd] });\n    child.unref();\n    return child.pid ?? 0;\n  },\n  now: Date.now,\n};\n\n/**\n * Hand a run to a detached process and register it. `argv` is the pai\n * command line of the run without --detach; the origin travels in the env so\n * the child reports to the session that started it.\n */\nexport function detachRun(\n  logDir: string,\n  kind: \"plan\" | \"queue\",\n  label: string,\n  bin: string,\n  argv: string[],\n  cwd: string,\n  origin: RunOrigin,\n  deps: DetachDeps = realDetachDeps,\n): RunRecord {\n  mkdirSync(runsDir(logDir), { recursive: true });\n  const stamp = new Date(deps.now()).toISOString().replace(/[-:T]/g, \"\").slice(0, 14);\n  const id = `${kind}-${label.replace(/[^\\w.-]/g, \"_\")}-${stamp}`;\n  const output = join(runsDir(logDir), `${id}.out`);\n  const pid = deps.spawnDetached(bin, argv, { cwd, env: { ...process.env, PAI_RUN_ORIGIN: JSON.stringify(origin), PAI_RUN_ID: id }, outPath: output });\n  const rec: RunRecord = { id, kind, pid, argv: [bin, ...argv], cwd, origin, started: new Date(deps.now()).toISOString(), output };\n  writeFileSync(runRecordPath(logDir, id), JSON.stringify(rec, null, 2) + \"\\n\", \"utf8\");\n  return rec;\n}\n\nexport function listRuns(logDir: string): RunRecord[] {\n  const dir = runsDir(logDir);\n  if (!existsSync(dir)) return [];\n  const out: RunRecord[] = [];\n  for (const f of readdirSync(dir)) {\n    if (!f.endsWith(\".json\")) continue;\n    try {\n      out.push(JSON.parse(readFileSync(join(dir, f), \"utf8\")) as RunRecord);\n    } catch {\n      /* torn record */\n    }\n  }\n  return out;\n}\n\n/** The detached child calls this on a clean end, so the sweep does not report it as dead. */\nexport function markRunDone(logDir: string, id: string, now: () => number = Date.now): void {\n  try {\n    writeFileSync(`${runRecordPath(logDir, id)}.done`, new Date(now()).toISOString(), \"utf8\");\n  } catch {\n    /* the registry is a courtesy; the run's own result stands */\n  }\n}\n\n/**\n * Daemon tick: a registered run whose process is gone without a done marker\n * died silently; tell its session (and PAILot) once.\n */\nexport async function sweepRuns(logDir: string, msg: Messenger, isAlive: (pid: number) => boolean, now: () => number = Date.now): Promise<string[]> {\n  const told: string[] = [];\n  for (const r of listRuns(logDir)) {\n    const marker = `${runRecordPath(logDir, r.id)}.done`;\n    if (existsSync(marker) || isAlive(r.pid)) continue;\n    markRunDone(logDir, r.id, now);\n    const what = `The detached ${r.kind} run ${r.id} stopped without finishing; see ${r.output}.`;\n    await msg.toSession(r.origin, eventLine(r.id, \"run-died\", { output: r.output }, what)).catch(() => false);\n    await msg.toPailot(what).catch(() => false);\n    told.push(r.id);\n  }\n  return told;\n}\n", "/**\n * local-status.ts \u2014 the one authoritative answer to \"which local model is\n * running?\" (incident 2026-10-19: a session curled ports 8010/8011/8020, found\n * nothing, and went looking for how to start a model server while the running\n * one was never considered). The PAI daemon computes the answer every 30 s and\n * caches it in <logDir>/local-status.json; everything else only reads it:\n * `pai worker local`, the `local_status` MCP tool, resolveLocalProvider, the\n * prompt hint and the Bash guard. Readers fall back to a direct probe only\n * when the cache is missing or stale (daemon unreachable).\n */\n\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\nexport const LOCAL_STATUS_REFRESH_MS = 30_000;\n/** A cache older than this means the daemon is not refreshing it. */\nexport const LOCAL_STATUS_MAX_AGE_MS = 90_000;\n\nexport interface LocalStatus {\n  /** ISO time the daemon computed this. */\n  at: string;\n  running: { provider: string; model: string; why: string } | null;\n  /** Enabled local providers with nothing listening. */\n  down: string[];\n  /** Local providers switched off in workers.yaml. */\n  disabled: string[];\n}\n\nexport const localStatusPath = (logDir: string): string => join(logDir, \"local-status.json\");\n\nexport const NO_SERVER_HINT = \"do not start model servers\";\n\nexport function localStatusLine(st: LocalStatus): string {\n  if (st.running) {\n    return `local: ${st.running.provider} (${st.running.model}) running \u2014 use \\`pai worker run --local\\``;\n  }\n  return `local: none running \u2014 local work is not available; ${NO_SERVER_HINT}`;\n}\n\nexport function writeLocalStatus(logDir: string, st: LocalStatus): void {\n  const path = localStatusPath(logDir);\n  mkdirSync(dirname(path), { recursive: true });\n  const tmp = `${path}.${process.pid}.tmp`;\n  writeFileSync(tmp, JSON.stringify(st) + \"\\n\", \"utf8\");\n  renameSync(tmp, path);\n}\n\n/** The daemon's cached answer; null when absent, damaged or older than maxAgeMs. */\nexport function readLocalStatus(logDir: string, now = Date.now(), maxAgeMs = LOCAL_STATUS_MAX_AGE_MS): LocalStatus | null {\n  const path = localStatusPath(logDir);\n  if (!existsSync(path)) return null;\n  try {\n    const st = JSON.parse(readFileSync(path, \"utf8\")) as LocalStatus;\n    const at = Date.parse(st.at);\n    if (Number.isNaN(at) || now - at > maxAgeMs || !Array.isArray(st.down) || !Array.isArray(st.disabled)) return null;\n    return st;\n  } catch {\n    return null;\n  }\n}\n\n/** Text + JSON of the answer for the CLI and the MCP tool. */\nexport function localStatusReport(st: LocalStatus): { text: string; json: LocalStatus } {\n  const extra =\n    (st.down.length ? ` down: ${st.down.join(\", \")}.` : \"\") + (st.disabled.length ? ` disabled: ${st.disabled.join(\", \")}.` : \"\");\n  return { text: localStatusLine(st) + (extra ? `\\n${extra.trim()}` : \"\"), json: st };\n}\n", "/**\n * local-idle.ts \u2014 the local model must not sit idle while workers build.\n * Incident 2026-10-20: both workers of the local engine were inside build-gate jobs, the GPU read 0 %,\n * two slots were free and nothing was queued. The daemon samples every 30 s (GPU utilisation, what each\n * running local worker is doing, free slots, queued local work); when the GPU stays under the threshold\n * for `minutes`, every running local worker is blocked in a build/test (or none runs), a slot is free and\n * nothing local is queued, it nudges each owning session ONCE (then backs off) and writes LOCAL-IDLE to the\n * ledger. The latest sample is persisted for `pai worker ps` and the cockpit. Read-only towards the engine.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { listBuildsSync, ticketKind, buildsDir, type BuildTicket } from \"../builds/queue.js\";\nimport { providerCap, queueRows } from \"./capacity.js\";\nimport { isLocalProvider, type WorkersConfig } from \"./config.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { ledgerPath } from \"./paths.js\";\nimport { eventLine, type Messenger } from \"./run-events.js\";\nimport { readLocalStatus, type LocalStatus } from \"./local-status.js\";\nimport { isChatPane, isLive, loadStatusesCached, ownsPidCached, type WorkerStatus } from \"./status.js\";\n\nexport interface LocalIdleConfig {\n  enabled: boolean;\n  /** GPU \"Device Utilization %\" under which the engine counts as idle. */\n  gpuBelowPct: number;\n  /** Minutes the GPU must stay idle before the nudge. */\n  minutes: number;\n  /** Minutes before the same provider may nudge again. */\n  backoffMinutes: number;\n}\n\nexport const DEFAULT_LOCAL_IDLE: LocalIdleConfig = { enabled: true, gpuBelowPct: 10, minutes: 2, backoffMinutes: 10 };\n\nexport type Activity = \"build\" | \"test\" | \"generating\";\n\nexport interface LocalWorker {\n  id: string;\n  activity: Activity;\n  /** Where a nudge goes: the owning session. */\n  iterm?: string;\n  name?: string;\n}\n\nexport interface LocalProviderSample {\n  provider: string;\n  cap: number;\n  workers: LocalWorker[];\n  queued: number;\n}\n\nexport interface LocalSample {\n  at: number;\n  /** null: not measurable (other platform, ioreg failed). */\n  gpuPct: number | null;\n  providers: LocalProviderSample[];\n}\n\nexport interface LocalIdleState {\n  /** Epoch ms since which the GPU has read idle without a break; null = not idle. */\n  idleSince: number | null;\n  /** provider -> epoch ms of the last nudge. */\n  lastNudge: Record<string, number>;\n}\n\nexport interface Nudge {\n  provider: string;\n  free: number;\n  workers: LocalWorker[];\n  /** One entry per owning session. */\n  targets: Array<{ iterm?: string; name?: string; ids: string[] }>;\n}\n\n/** \"Device Utilization %\"=N out of `ioreg -r -d 1 -c IOAccelerator`; null when absent. */\nexport function parseGpuUtilization(out: string): number | null {\n  const m = /\"Device Utilization %\"\\s*=\\s*(\\d+)/.exec(out);\n  return m ? Number(m[1]) : null;\n}\n\nexport function readGpuUtilization(): Promise<number | null> {\n  if (process.platform !== \"darwin\") return Promise.resolve(null);\n  return new Promise((resolve) => {\n    execFile(\"ioreg\", [\"-r\", \"-d\", \"1\", \"-c\", \"IOAccelerator\"], { timeout: 5000 }, (err, stdout) => resolve(err ? null : parseGpuUtilization(stdout)));\n  });\n}\n\nconst BUILD_LAST = /\\b(build\\.sh|install\\.sh|xcodebuild|gradlew?|cargo build|npm run build|make)\\b/;\nconst TEST_LAST = /\\b(vitest|jest|pytest|npm (run )?test|bun test|cargo test|go test)\\b/;\n\n/** What a running worker is doing: a build-gate ticket of its own, else the shape of its last tool, else generating. */\nexport function activityOf(s: Pick<WorkerStatus, \"id\" | \"last\">, tickets: BuildTicket[]): Activity {\n  const t = tickets.find((x) => x.worker === s.id);\n  if (t) return ticketKind(t) === \"test\" ? \"test\" : \"build\";\n  if (TEST_LAST.test(s.last ?? \"\")) return \"test\";\n  if (BUILD_LAST.test(s.last ?? \"\")) return \"build\";\n  return \"generating\";\n}\n\n/** The sample from disk: statuses, build tickets and the provider queue. */\nexport async function takeSample(logDir: string, workers: WorkersConfig, now = Date.now(), gpu: () => Promise<number | null> = readGpuUtilization, status: LocalStatus | null = readLocalStatus(logDir, now)): Promise<LocalSample> {\n  let tickets: BuildTicket[] = [];\n  try {\n    tickets = listBuildsSync(buildsDir());\n  } catch {\n    /* no build dir yet */\n  }\n  const statuses = loadStatusesCached(logDir).filter((s) => !isChatPane(s) && isLive(s, ownsPidCached) && s.state === \"running\");\n  const queue = queueRows(logDir);\n  const providers: LocalProviderSample[] = [];\n  for (const [name, p] of Object.entries(workers.providers)) {\n    if (!isLocalProvider(p.baseUrl)) continue;\n    const cap = providerCap(workers, name);\n    if (cap === undefined) continue;\n    const mine = statuses.filter((s) => s.provider === name);\n    const queued = queue.filter((q) => q.provider === name).length;\n    // only a provider that is enabled and served (the daemon's local-status names it) or has work is worth a line\n    const served = status?.running?.provider === name;\n    if (!(p.enabled && served) && !mine.length && !queued) continue;\n    providers.push({\n      provider: name,\n      cap,\n      workers: mine.map((s) => ({ id: s.id, activity: activityOf(s, tickets), iterm: s.term || undefined, name: s.session?.name })),\n      queued,\n    });\n  }\n  return { at: now, gpuPct: await gpu(), providers };\n}\n\n/** One step of the rule; pure. Returns the next state and the nudges due now. */\nexport function evaluate(state: LocalIdleState, sample: LocalSample, cfg: LocalIdleConfig, muted: ReadonlySet<string> = new Set()): { state: LocalIdleState; nudges: Nudge[] } {\n  const idle = sample.gpuPct !== null && sample.gpuPct < cfg.gpuBelowPct;\n  const next: LocalIdleState = { idleSince: idle ? (state.idleSince ?? sample.at) : null, lastNudge: { ...state.lastNudge } };\n  const nudges: Nudge[] = [];\n  if (!cfg.enabled || next.idleSince === null || sample.at - next.idleSince < cfg.minutes * 60_000) return { state: next, nudges };\n  for (const p of sample.providers) {\n    const free = p.cap - p.workers.length;\n    if (free <= 0 || p.queued > 0 || p.workers.some((w) => w.activity === \"generating\")) continue;\n    if (sample.at - (next.lastNudge[p.provider] ?? -Infinity) < cfg.backoffMinutes * 60_000) continue;\n    const targets = new Map<string, { iterm?: string; name?: string; ids: string[] }>();\n    for (const w of p.workers) {\n      if ((w.name && muted.has(w.name)) || (w.iterm && muted.has(w.iterm))) continue;\n      const key = w.iterm ?? w.name;\n      if (!key) continue;\n      const t = targets.get(key) ?? { iterm: w.iterm, name: w.name, ids: [] };\n      t.ids.push(w.id);\n      targets.set(key, t);\n    }\n    next.lastNudge[p.provider] = sample.at; // the idle spell is logged once per backoff even with nobody to tell\n    nudges.push({ provider: p.provider, free, workers: p.workers, targets: [...targets.values()] });\n  }\n  return { state: next, nudges };\n}\n\nexport const nudgeText = (n: Nudge, ids: string[], minutes: number): string =>\n  eventLine(\n    ids[0] ?? n.provider,\n    \"local-idle\",\n    { provider: n.provider, free: n.free },\n    `GPU idle (under ${DEFAULT_LOCAL_IDLE.gpuBelowPct}% busy) for ${minutes} min: your local workers ${ids.join(\", \")} are in builds; ${n.provider} has ${n.free} free slot${n.free === 1 ? \"\" : \"s\"}: start more independent local tasks`\n  );\n\nexport interface LocalIdleFile {\n  at: string;\n  gpuPct: number | null;\n  idleSince: number | null;\n  lastNudge: Record<string, number>;\n  providers: Array<{ provider: string; cap: number; running: number; free: number; queued: number; activity: Record<string, number> }>;\n}\n\nconst stateFile = (logDir: string): string => join(logDir, \"local-idle.json\");\nconst muteFile = (logDir: string): string => join(logDir, \"local-idle-muted.json\");\n\nexport function readLocalIdle(logDir: string): LocalIdleFile | null {\n  try {\n    return JSON.parse(readFileSync(stateFile(logDir), \"utf8\")) as LocalIdleFile;\n  } catch {\n    return null;\n  }\n}\n\nexport function readMuted(logDir: string): string[] {\n  try {\n    const v = JSON.parse(readFileSync(muteFile(logDir), \"utf8\"));\n    return Array.isArray(v) ? v.filter((x): x is string => typeof x === \"string\") : [];\n  } catch {\n    return [];\n  }\n}\n\n/** A session mutes the nudge by its AIBroker name or iTerm id; returns the muted list. */\nexport function setMuted(logDir: string, session: string, muted: boolean): string[] {\n  const set = new Set(readMuted(logDir));\n  if (muted) set.add(session);\n  else set.delete(session);\n  mkdirSync(logDir, { recursive: true });\n  writeFileSync(muteFile(logDir), JSON.stringify([...set]), \"utf8\");\n  return [...set];\n}\n\n/** The `ps` / cockpit line: \"local model: GPU 63% busy \u00B7 gyro-m 2/4 running, 2 free\"; \"\" with no reading. */\nexport function localIdleLine(f: LocalIdleFile | null, now = Date.now()): string {\n  if (!f || now - Date.parse(f.at) > 120_000) return \"\";\n  const gpu = f.gpuPct === null ? \"GPU ?\" : `GPU ${f.gpuPct}% ${f.gpuPct < DEFAULT_LOCAL_IDLE.gpuBelowPct ? \"idle\" : \"busy\"}`;\n  const provs = f.providers.map((p) => `${p.provider} ${p.running}/${p.cap} running, ${p.free} free${p.queued ? `, ${p.queued} queued` : \"\"}`);\n  return `local model: ${[gpu, ...provs].join(\" \u00B7 \")}`;\n}\n\nexport interface LocalIdleDeps {\n  sample?: () => Promise<LocalSample>;\n  now?: () => number;\n}\n\nconst daemonState = { current: { idleSince: null, lastNudge: {} } as LocalIdleState };\n\n/** One daemon tick: sample, persist, nudge the owning sessions once per backoff, ledger LOCAL-IDLE. */\nexport async function localIdleTick(logDir: string, workers: WorkersConfig, msg: Messenger, deps: LocalIdleDeps = {}, state: { current: LocalIdleState } = daemonState): Promise<Nudge[]> {\n  const cfg = workers.localIdle;\n  if (!cfg.enabled) return [];\n  const now = (deps.now ?? Date.now)();\n  const sample = await (deps.sample ?? (() => takeSample(logDir, workers, now)))();\n  const { state: next, nudges } = evaluate(state.current, sample, cfg, new Set(readMuted(logDir)));\n  state.current = next;\n  const file: LocalIdleFile = {\n    at: new Date(sample.at).toISOString(),\n    gpuPct: sample.gpuPct,\n    idleSince: next.idleSince,\n    lastNudge: next.lastNudge,\n    providers: sample.providers.map((p) => {\n      const activity: Record<string, number> = {};\n      for (const w of p.workers) activity[w.activity] = (activity[w.activity] ?? 0) + 1;\n      return { provider: p.provider, cap: p.cap, running: p.workers.length, free: Math.max(0, p.cap - p.workers.length), queued: p.queued, activity };\n    }),\n  };\n  try {\n    mkdirSync(logDir, { recursive: true });\n    writeFileSync(stateFile(logDir), JSON.stringify(file), \"utf8\");\n  } catch {\n    /* a state file is an extra: never fail the tick over it */\n  }\n  for (const n of nudges) {\n    appendLedger(ledgerPath(logDir), \"LOCAL-IDLE\", { provider: n.provider, free: n.free, workers: n.workers.map((w) => `${w.id}:${w.activity}`).join(\",\") || \"-\" }, new Date(sample.at));\n    for (const t of n.targets) await msg.toSession({ iterm: t.iterm, name: t.name }, nudgeText(n, t.ids, cfg.minutes)).catch(() => false);\n  }\n  return nudges;\n}\n", "/**\n * model-window.ts \u2014 facts derivable from a bare model id.\n *\n * Shared by the worker stack (provider registry) and the session hooks\n * (context-fill). Zero imports on purpose: this leaf is bundled into hooks\n * (scripts/build-hooks.mjs) and must never drag config or path machinery\n * into a hook process.\n */\n\n/** Last-resort window when nothing on hand reports one and the model id\n *  carries no window information. */\nexport const DEFAULT_CONTEXT_WINDOW = 200_000;\n\n/** Strip a bracketed variant suffix: \"glm-5.3[1m]\" \u2192 \"glm-5.3\". The suffix\n *  selects a variant of the same base model (a context-window tier, a\n *  quantization), so the prefix is the id's family. */\nexport function stripModelVariant(model: string): string {\n  const stripped = model.replace(/\\[[^\\]]*\\]\\s*$/, \"\").trim();\n  return stripped || model;\n}\n\n/**\n * The context window a model id itself declares, or null when the id carries\n * no window information. Only the bracketed variant suffix is read\n * (\"[1m]\" \u2192 1,000,000); a bare id says nothing and stays null so callers can\n * fall back to whatever they measured themselves.\n */\nexport function contextWindowFromModelId(model: string | null | undefined): number | null {\n  if (typeof model !== \"string\" || model === \"\") return null;\n  const m = /\\[(\\d+)m\\]$/i.exec(model.trim());\n  if (!m) return null;\n  const millions = Number(m[1]);\n  if (!Number.isFinite(millions) || millions < 1) return null;\n  return millions * 1_000_000;\n}\n\n/**\n * A window we are certain of from the id alone: the `[Nm]` variant suffix, or\n * a plain `claude-*` id (200k). Anything else (local or third-party models)\n * is null \u2014 the caller shows no window rather than a guess.\n */\nexport function certainContextWindow(model: string | null | undefined): number | null {\n  const declared = contextWindowFromModelId(model);\n  if (declared) return declared;\n  return typeof model === \"string\" && /^claude-/i.test(model.trim()) ? DEFAULT_CONTEXT_WINDOW : null;\n}\n", "/**\n * workers-config.ts \u2014 workers.yaml: providers, model roles, class routing,\n * mcp_sets.\n *\n * config.ts's readWorkersSection/writeWorkersSection are the only callers \u2014\n * every other module keeps talking to the WorkersConfig shape from config.ts\n * and never touches this file. That is deliberate: adding a provider used to\n * mean editing JSON inside an unrelated config blob; this module makes\n * providers/classes/mcp_sets/active a single human-editable file, documented\n * in docs/workers-config.md, while everything downstream is unchanged.\n *\n * Writes go through the `yaml` package's Document API and only touch the\n * entries that actually changed, so hand-written comments elsewhere in the\n * file \u2014 including a comment directly above an untouched provider \u2014 survive\n * byte-for-byte across `add`, `use`, `disable`, and every other mutation.\n */\n\nimport { existsSync, readFileSync, writeFileSync, chmodSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Document, Scalar, parseDocument, type Node } from \"yaml\";\nimport { readJsonStrict, writeJsonAtomic } from \"../config/json-store.js\";\nimport { writeYamlFileAtomic, YamlStoreError } from \"../config/yaml-store.js\";\nimport { paiHomePath, resolvePaiFile, migratePaiFile, type MigrateFileResult } from \"../config/pai-home.js\";\nimport {\n  ANTHROPIC_NATIVE,\n  NATIVE_ANTHROPIC_MODELS,\n  WorkersConfigError,\n  expandHome,\n  isModelCapability,\n  parseCapabilitiesValue,\n  parseClassesValue,\n  parseMcpSetsValue,\n  parseModelsBlock,\n  parseProvider,\n  parseWorkersConfig,\n  type ClassTarget,\n  type WorkerProvider,\n} from \"./config.js\";\n\nexport const WORKERS_YAML_FILENAME = \"workers.yaml\";\n\n/**\n * Where workers.yaml lives since 2026-09-19: under the PAI_HOME namespace\n * dir (~/.pai by default) \u2014 this file is per-user state that can\n * carry API keys and must never be committed, same as the rest of PAI_HOME.\n */\nexport function defaultWorkersYamlPath(): string {\n  return paiHomePath(WORKERS_YAML_FILENAME);\n}\n\n/** Briefly the canonical location between 2026-09-19's two migrations \u2014\n *  read during the transition, never written to again. */\nfunction oldWorkersYamlPath(): string {\n  return join(homedir(), \".claude\", WORKERS_YAML_FILENAME);\n}\n\n/** Where workers.yaml lived before 2026-09-19 \u2014 read during the transition,\n *  never written to (see `pai worker config migrate`). */\nfunction legacyWorkersYamlPath(): string {\n  return join(homedir(), \".config\", \"pai\", WORKERS_YAML_FILENAME);\n}\n\n/**\n * The path any read/write of workers.yaml actually uses: PAI_WORKERS_YAML\n * (tests, power users) first, else the new PAI_HOME location if it exists,\n * else the most recent old location that is actually on disk (printing a\n * one-time notice), else the new location (the target a first write creates).\n */\nexport function workersYamlPath(): string {\n  const override = process.env.PAI_WORKERS_YAML;\n  if (override) return override;\n  return resolvePaiFile(\n    defaultWorkersYamlPath(),\n    [oldWorkersYamlPath(), legacyWorkersYamlPath()],\n    \"pai worker config migrate\"\n  );\n}\n\n/** Where a fresh write (init, migrate, relocate's destination) always\n *  targets \u2014 the new location, or PAI_WORKERS_YAML for test isolation.\n *  Never an old path: nothing is ever written there again. */\nfunction writeTargetWorkersYamlPath(): string {\n  return process.env.PAI_WORKERS_YAML ?? defaultWorkersYamlPath();\n}\n\n/**\n * One-line notice for `pai worker config check` and `pai worker providers`\n * when workers.yaml is still sitting at an old location. Null once it has\n * moved (or PAI_WORKERS_YAML is set \u2014 that always wins, nothing to migrate).\n */\nexport function workersYamlLegacyNotice(): string | null {\n  if (process.env.PAI_WORKERS_YAML) return null;\n  if (existsSync(defaultWorkersYamlPath())) return null;\n  const found = [oldWorkersYamlPath(), legacyWorkersYamlPath()].find((p) => existsSync(p));\n  if (!found) return null;\n  return `workers.yaml is still at the old location (${found}) \u2014 run \\`pai worker config migrate\\` to move it to ${defaultWorkersYamlPath()}`;\n}\n\nexport interface WorkersYamlData {\n  active: string | null;\n  providers: Record<string, WorkerProvider>;\n  classes: Record<string, ClassTarget>;\n  capabilities: Record<string, string[]>;\n  mcpSets: Record<string, string[]>;\n  nativeModels: WorkerProvider[\"models\"];\n}\n\n// ---------------------------------------------------------------------------\n// Friendly (on-disk) provider shape \u2194 internal WorkerProvider\n// ---------------------------------------------------------------------------\n\n/** snake_case on-disk keys \u2192 the camelCase raw shape parseProvider expects. */\nfunction yamlProviderToRaw(y: Record<string, unknown>): Record<string, unknown> {\n  const raw: Record<string, unknown> = {};\n  if (y.enabled !== undefined) raw.enabled = y.enabled;\n  if (y.url !== undefined) raw.baseUrl = y.url;\n  if (y.key_file !== undefined) raw.keyFile = y.key_file;\n  if (y.key !== undefined) raw.key = y.key;\n  if (y.tier !== undefined) raw.costTier = y.tier;\n  if (y.models !== undefined) raw.models = y.models;\n  if (y.protocol !== undefined) raw.protocol = y.protocol;\n  if (y.engine !== undefined) raw.engine = y.engine;\n  if (y.upstream_url !== undefined) raw.upstreamUrl = y.upstream_url;\n  if (y.env !== undefined) raw.env = y.env;\n  if (y.note !== undefined) raw.note = y.note;\n  if (y.router !== undefined) raw.router = y.router;\n  if (y.contract !== undefined) raw.contract = y.contract;\n  if (y.quota_probe !== undefined) raw.quotaProbe = y.quota_probe;\n  if (y.quota_skip_at !== undefined) raw.quotaSkipAt = y.quota_skip_at;\n  if (y.worker_model !== undefined) raw.workerModel = y.worker_model;\n  if (y.context_window !== undefined) raw.contextWindow = y.context_window;\n  if (y.thinking !== undefined) raw.thinking = y.thinking;\n  if (y.maxConcurrent !== undefined) raw.maxConcurrent = y.maxConcurrent;\n  else if (y.max_concurrent !== undefined) raw.maxConcurrent = y.max_concurrent;\n  if (y.tags !== undefined) raw.tags = y.tags;\n  if (y.usage !== undefined) raw.usage = y.usage;\n  if (y.model_tiers !== undefined) raw.modelTiers = y.model_tiers;\n  return raw;\n}\n\n/** Internal WorkerProvider \u2192 the friendly on-disk plain object for writing. */\nfunction providerToYamlPlain(p: WorkerProvider): Record<string, unknown> {\n  const y: Record<string, unknown> = {};\n  if (!p.enabled) y.enabled = false;\n  if (p.baseUrl) y.url = p.baseUrl;\n  if (p.keyFile) y.key_file = p.keyFile;\n  if (p.key) y.key = p.key;\n  if (p.costTier !== undefined) y.tier = p.costTier;\n  y.models = { ...p.models };\n  if (p.protocol && p.protocol !== \"anthropic\") y.protocol = p.protocol;\n  if (p.engine && p.engine !== \"claude\") y.engine = p.engine;\n  if (p.upstreamUrl) y.upstream_url = p.upstreamUrl;\n  if (p.env && Object.keys(p.env).length) y.env = { ...p.env };\n  if (p.note) y.note = p.note;\n  if (p.router === false) y.router = false;\n  if (p.contract) y.contract = p.contract;\n  if (p.quotaProbe) y.quota_probe = p.quotaProbe;\n  if (p.quotaSkipAt !== undefined) y.quota_skip_at = p.quotaSkipAt;\n  if (p.workerModel) y.worker_model = p.workerModel;\n  if (p.contextWindow !== undefined) y.context_window = p.contextWindow;\n  if (p.thinking !== undefined) y.thinking = p.thinking ? \"on\" : \"off\";\n  if (p.maxConcurrent !== undefined) y.maxConcurrent = p.maxConcurrent;\n  if (p.tags?.length) y.tags = [...p.tags];\n  if (p.usage) y.usage = p.usage;\n  if (p.modelTiers) y.model_tiers = { ...p.modelTiers };\n  return y;\n}\n\n/** The builtin `anthropic` block only ever carries `builtin: true` + `models`. */\nfunction parseBuiltinAnthropic(raw: unknown, pathPrefix: string): WorkerProvider[\"models\"] {\n  if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n    throw new WorkersConfigError(`${pathPrefix}: must be an object`);\n  }\n  const p = raw as Record<string, unknown>;\n  if (p.builtin !== true) {\n    throw new WorkersConfigError(\n      `${pathPrefix}: \"${ANTHROPIC_NATIVE}\" is reserved for the built-in Claude Code login \u2014 ` +\n        `set \"builtin: true\" (optionally with a \"models\" override) or rename this provider`\n    );\n  }\n  const disallowed = [\"url\", \"key_file\", \"key\", \"engine\", \"protocol\", \"upstream_url\"].filter(\n    (k) => p[k] !== undefined\n  );\n  if (disallowed.length) {\n    throw new WorkersConfigError(\n      `${pathPrefix}: a builtin provider cannot set ${disallowed.join(\", \")} \u2014 it runs on Claude Code's own login`\n    );\n  }\n  if (p.models === undefined) return { ...NATIVE_ANTHROPIC_MODELS };\n  return { ...NATIVE_ANTHROPIC_MODELS, ...parseModelsBlock(`${pathPrefix}.models`, p.models) };\n}\n\n// ---------------------------------------------------------------------------\n// Line-numbered errors\n// ---------------------------------------------------------------------------\n\nfunction lineOf(doc: Document, path: (string | number)[]): number | null {\n  try {\n    const node = doc.getIn(path, true) as Node | undefined;\n    const range = node && typeof node === \"object\" && \"range\" in node ? node.range : undefined;\n    if (!range) return null;\n    const text = String(doc);\n    return text.slice(0, range[0]).split(\"\\n\").length;\n  } catch {\n    return null;\n  }\n}\n\nfunction withLine(doc: Document, path: (string | number)[], yamlPath: string, e: unknown): never {\n  const msg = e instanceof Error ? e.message : String(e);\n  const line = lineOf(doc, path);\n  throw new WorkersConfigError(line ? `${yamlPath}:${line}: ${msg}` : `${yamlPath}: ${msg}`);\n}\n\n// ---------------------------------------------------------------------------\n// Load + validate\n// ---------------------------------------------------------------------------\n\n/**\n * Parse and fully validate a workers.yaml Document. Throws WorkersConfigError\n * naming `<path>:<line>` for anything wrong \u2014 an unknown provider referenced\n * by a class is a load error here, not a spawn-time surprise.\n */\nexport function parseWorkersYamlDocument(doc: Document, yamlPath: string): WorkersYamlData {\n  const root = doc.toJS() ?? {};\n  if (typeof root !== \"object\" || Array.isArray(root)) {\n    throw new WorkersConfigError(`${yamlPath}: top level must be a mapping`);\n  }\n  const r = root as Record<string, unknown>;\n\n  const providers: Record<string, WorkerProvider> = {};\n  let nativeModels: WorkerProvider[\"models\"] = { ...NATIVE_ANTHROPIC_MODELS };\n  const providersRaw = r.providers;\n  if (providersRaw !== undefined) {\n    if (typeof providersRaw !== \"object\" || providersRaw === null || Array.isArray(providersRaw)) {\n      withLine(doc, [\"providers\"], yamlPath, new WorkersConfigError(\"providers: must be a mapping of name \u2192 provider\"));\n    }\n    for (const [name, raw] of Object.entries(providersRaw as Record<string, unknown>)) {\n      try {\n        if (name === ANTHROPIC_NATIVE) {\n          nativeModels = parseBuiltinAnthropic(raw, `providers.${name}`);\n        } else {\n          providers[name] = parseProvider(name, yamlProviderToRaw(raw as Record<string, unknown>));\n        }\n      } catch (e) {\n        withLine(doc, [\"providers\", name], yamlPath, e);\n      }\n    }\n  }\n\n  let classes: Record<string, ClassTarget> = {};\n  if (r.classes !== undefined) {\n    try {\n      classes = parseClassesValue(r.classes, \"classes\");\n    } catch (e) {\n      withLine(doc, [\"classes\"], yamlPath, e);\n    }\n    // Cross-validate: every class must name a known provider (or \"anthropic\")\n    // and, if it names a role, a capability that provider actually declares.\n    // Unlike the JSON path (where a provider can be removed out from under a\n    // class and the error surfaces at spawn time), workers.yaml catches this\n    // at load time.\n    for (const [cls, target] of Object.entries(classes)) {\n      const provider = typeof target === \"string\" ? target.split(\"/\")[0] : target.provider;\n      const alias = typeof target === \"string\" ? target.split(\"/\")[1] : undefined;\n      if (provider !== undefined) {\n        const native = provider === ANTHROPIC_NATIVE;\n        if (!native && !providers[provider]) {\n          withLine(\n            doc,\n            [\"classes\", cls],\n            yamlPath,\n            new WorkersConfigError(\n              `classes.${cls}: no provider named \"${provider}\" (configured: ${Object.keys(providers).join(\", \") || \"(none)\"})`\n            )\n          );\n        }\n        if (alias !== undefined) {\n          if (!isModelCapability(alias)) {\n            withLine(\n              doc,\n              [\"classes\", cls],\n              yamlPath,\n              new WorkersConfigError(\n                `classes.${cls}: \"${alias}\" is not a valid capability name (must match ^[a-z][a-z0-9-]*$)`\n              )\n            );\n          } else if (!native && providers[provider] && alias !== \"default\" && !providers[provider].models[alias]) {\n            // the native provider has no configurable model slots \u2014 any\n            // alias resolves through resolveModelCapability's own fallback\n            withLine(\n              doc,\n              [\"classes\", cls],\n              yamlPath,\n              new WorkersConfigError(`classes.${cls}: provider \"${provider}\" has no \"${alias}\" model configured`)\n            );\n          }\n        }\n      }\n    }\n  }\n\n  let capabilities: Record<string, string[]> = {};\n  if (r.capabilities !== undefined) {\n    try {\n      capabilities = parseCapabilitiesValue(r.capabilities, \"capabilities\");\n    } catch (e) {\n      withLine(doc, [\"capabilities\"], yamlPath, e);\n    }\n  }\n\n  let mcpSets: Record<string, string[]> = {};\n  if (r.mcp_sets !== undefined) {\n    try {\n      mcpSets = parseMcpSetsValue(r.mcp_sets, \"mcp_sets\");\n    } catch (e) {\n      withLine(doc, [\"mcp_sets\"], yamlPath, e);\n    }\n  } else {\n    mcpSets = parseMcpSetsValue(undefined, \"mcp_sets\");\n  }\n\n  mergeVerifyOf(r, (m) => withLine(doc, [\"merge\", \"verify\"], yamlPath, new WorkersConfigError(m)));\n\n  const active = r.active === undefined || r.active === null ? null : String(r.active);\n  if (active !== null && active !== ANTHROPIC_NATIVE && active !== \"auto\" && !providers[active]) {\n    withLine(\n      doc,\n      [\"active\"],\n      yamlPath,\n      new WorkersConfigError(\n        `active: no provider named \"${active}\" (configured: ${Object.keys(providers).join(\", \") || \"(none)\"})`\n      )\n    );\n  }\n\n  return { active, providers, classes, capabilities, mcpSets, nativeModels };\n}\n\n/** The `merge.verify` command of a parsed workers.yaml root (undefined when unset); `fail` is called with the message when the key is malformed. */\nfunction mergeVerifyOf(root: Record<string, unknown>, fail: (msg: string) => never): string | undefined {\n  const m = root.merge;\n  if (m === undefined || m === null) return undefined;\n  if (typeof m !== \"object\" || Array.isArray(m)) return fail(\"merge: must be a mapping\");\n  const v = (m as Record<string, unknown>).verify;\n  if (v === undefined || v === null) return undefined;\n  if (typeof v !== \"string\" || !v.trim()) return fail(\"merge.verify: must be a non-empty string (a shell command)\");\n  return v;\n}\n\n/** The default `pai worker merge` gate from workers.yaml `merge.verify`; undefined when the file or key is absent. */\nexport function readMergeVerify(yamlPath: string = workersYamlPath()): string | undefined {\n  const loaded = readWorkersYaml(yamlPath);\n  if (!loaded) return undefined;\n  return mergeVerifyOf((loaded.doc.toJS() ?? {}) as Record<string, unknown>, (m) => {\n    throw new WorkersConfigError(`${yamlPath}: ${m}`);\n  });\n}\n\n/** Read + parse workers.yaml. Returns null if the file does not exist. */\nexport function readWorkersYaml(yamlPath: string): { doc: Document; data: WorkersYamlData } | null {\n  if (!existsSync(yamlPath)) return null;\n  let text: string;\n  try {\n    text = readFileSync(yamlPath, \"utf8\");\n  } catch (e) {\n    throw new WorkersConfigError(\n      `Could not read ${yamlPath}: ${e instanceof Error ? e.message : String(e)}`\n    );\n  }\n  const doc = parseDocument(text);\n  if (doc.errors.length) {\n    throw new WorkersConfigError(`${yamlPath}: ${doc.errors[0].message}`);\n  }\n  const data = parseWorkersYamlDocument(doc, yamlPath);\n  return { doc, data };\n}\n\n// ---------------------------------------------------------------------------\n// Write (diff-based, comment-preserving)\n// ---------------------------------------------------------------------------\n\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n  return JSON.stringify(a) === JSON.stringify(b);\n}\n\n/**\n * A `key:` value as an explicit double-quoted YAML scalar. A key is an\n * opaque token, not YAML-authored text \u2014 plain-scalar auto-styling could\n * read one back as a number or boolean (an all-digit token, \"true\", \"no\", \u2026)\n * if it were ever left unquoted, so every write forces the quoted form\n * regardless of what the plain style would otherwise pick.\n */\nfunction quotedKeyNode(value: string): Scalar {\n  const s = new Scalar(value);\n  s.type = Scalar.QUOTE_DOUBLE;\n  return s;\n}\n\n/**\n * Apply the changes between `before` and `after` onto `doc`, touching only\n * the providers/classes/mcp_sets/active entries that actually differ. This is\n * what makes comment preservation possible: an untouched provider's node\n * (and any comment above it) is never revisited.\n */\nfunction syncWorkersYamlDocument(doc: Document, before: WorkersYamlData, after: WorkersYamlData): void {\n  // providers: add/update changed, remove gone (never touch the builtin anthropic block)\n  for (const [name, p] of Object.entries(after.providers)) {\n    const beforeP = before.providers[name];\n    const changed = !beforeP || !shallowEqual(providerToYamlPlain(beforeP), providerToYamlPlain(p));\n    if (changed) {\n      // a fresh (or wholesale-replaced) provider entry is set in one shot as\n      // a plain object \u2014 the yaml lib only turns it into real Map/Scalar\n      // nodes lazily at stringify time, so a follow-up setIn/deleteIn into\n      // the same not-yet-a-collection value would fail. Embedding the `key`\n      // as an actual Scalar node here rides along: stringify leaves already-\n      // Node values alone (see stringifyPair.js), quoting only that field.\n      const y: Record<string, unknown> = providerToYamlPlain(p);\n      if (typeof y.key === \"string\") y.key = quotedKeyNode(y.key);\n      doc.setIn([\"providers\", name], y);\n    }\n  }\n  for (const name of Object.keys(before.providers)) {\n    if (!(name in after.providers)) doc.deleteIn([\"providers\", name]);\n  }\n\n  // builtin anthropic models: only the capabilities that changed; the entry\n  // is created (with `builtin: true`) when the file has none yet\n  if (!shallowEqual(before.nativeModels, after.nativeModels)) {\n    if (!doc.hasIn([\"providers\", ANTHROPIC_NATIVE])) {\n      doc.setIn([\"providers\", ANTHROPIC_NATIVE], { builtin: true, models: { ...after.nativeModels } });\n    } else {\n      for (const [cap, id] of Object.entries(after.nativeModels)) {\n        if (before.nativeModels[cap] !== id) doc.setIn([\"providers\", ANTHROPIC_NATIVE, \"models\", cap], id);\n      }\n    }\n  }\n\n  // classes: same add/update/remove diff\n  for (const [cls, target] of Object.entries(after.classes)) {\n    if (!shallowEqual(before.classes[cls], target)) {\n      doc.setIn([\"classes\", cls], target);\n    }\n  }\n  for (const cls of Object.keys(before.classes)) {\n    if (!(cls in after.classes)) doc.deleteIn([\"classes\", cls]);\n  }\n\n  // capabilities: same add/update/remove diff\n  for (const [cap, prefs] of Object.entries(after.capabilities)) {\n    if (!shallowEqual(before.capabilities[cap], prefs)) {\n      doc.setIn([\"capabilities\", cap], [...prefs]);\n    }\n  }\n  for (const cap of Object.keys(before.capabilities)) {\n    if (!(cap in after.capabilities)) doc.deleteIn([\"capabilities\", cap]);\n  }\n\n  // mcp_sets: same add/update/remove diff\n  for (const [set, servers] of Object.entries(after.mcpSets)) {\n    if (!shallowEqual(before.mcpSets[set], servers)) {\n      doc.setIn([\"mcp_sets\", set], [...servers]);\n    }\n  }\n  for (const set of Object.keys(before.mcpSets)) {\n    if (!(set in after.mcpSets)) doc.deleteIn([\"mcp_sets\", set]);\n  }\n\n  // active\n  if (before.active !== after.active) {\n    if (after.active === null) doc.deleteIn([\"active\"]);\n    else doc.setIn([\"active\"], after.active);\n  }\n}\n\n/**\n * Write `after` into workers.yaml, preserving comments on everything that did\n * not change. Re-reads the file fresh (so the diff is against what is really\n * on disk, not a stale in-memory copy) and re-validates the result before\n * committing; on failure the previous bytes are left untouched.\n */\nexport function writeWorkersYaml(yamlPath: string, after: WorkersYamlData): void {\n  const existing = readWorkersYaml(yamlPath);\n  const doc = existing ? existing.doc : new Document({});\n  const before: WorkersYamlData = existing\n    ? existing.data\n    : {\n        active: null,\n        providers: {},\n        classes: {},\n        capabilities: {},\n        mcpSets: {},\n        nativeModels: { ...NATIVE_ANTHROPIC_MODELS },\n      };\n  syncWorkersYamlDocument(doc, before, after);\n  writeWorkersYamlText(yamlPath, String(doc));\n}\n\n/** Atomic write with a .bak-pai backup and a re-validate-or-restore guard.\n *  The file-I/O tail (backup, temp write at 0600, rename, chmod) is shared\n *  with every other PAI YAML file via writeYamlFileAtomic; only the\n *  workers.yaml-specific validation (parseWorkersYamlDocument) lives here. */\nexport function writeWorkersYamlText(yamlPath: string, text: string): void {\n  // A worker never writes the live file (2026-10-18: a worker's inline-keys run replaced every key_file).\n  if (process.env.PAI_WORKER === \"1\" && resolve(yamlPath) === resolve(defaultWorkersYamlPath())) {\n    throw new WorkersConfigError(\n      `refusing to write ${yamlPath}: workers never write live PAI config; set HOME=$(mktemp -d) or PAI_WORKERS_YAML=<copy> for state-changing checks`\n    );\n  }\n  // Validate before committing anything to disk.\n  const probe = parseDocument(text);\n  if (probe.errors.length) {\n    throw new WorkersConfigError(`refusing to write ${yamlPath}: ${probe.errors[0].message} (previous file left unchanged)`);\n  }\n  try {\n    parseWorkersYamlDocument(probe, yamlPath);\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    throw new WorkersConfigError(`refusing to write ${yamlPath}: ${msg} (previous file left unchanged)`);\n  }\n\n  try {\n    writeYamlFileAtomic(yamlPath, text, { label: yamlPath });\n  } catch (e) {\n    throw new WorkersConfigError(e instanceof YamlStoreError ? e.message : String(e instanceof Error ? e.message : e));\n  }\n}\n\n// ---------------------------------------------------------------------------\n// Starter template, init, migrate\n// ---------------------------------------------------------------------------\n\n/** The exact commented starter shipped as examples/workers.yaml and by `init`. */\nexport function starterWorkersYamlText(): string {\n  return `# PAI worker configuration.\n# Providers PAI can run workers on, the model each role uses, and which\n# provider every --class goes to. Edit by hand; \\`pai worker providers\\` shows\n# the effective result. Comments are preserved when PAI writes this file.\n#\n# Per-user state, kept 0600: this file can hold API keys (\\`key:\\`, below).\n# Never commit it or share it. \\`key_file: <path>\\` keeps a secret in its own\n# 0600 file instead, if you'd rather not put it here.\n#\n# Per provider, \\`worker_model: default | fast\\` is the model a worker gets when\n# the request names none (default: default). Set \\`fast\\` on an expensive\n# provider; an explicit --model, a provider/role class target or where/model\n# still wins.\n\nactive: anthropic          # provider for \\`pai worker run\\` without --provider or --class\n\nproviders:\n  anthropic:\n    builtin: true          # Claude Code's own login: no url, no key file\n    models:\n      default: ${NATIVE_ANTHROPIC_MODELS.default}\n      fast: ${NATIVE_ANTHROPIC_MODELS.fast}\n  glm:\n    url: https://api.z.ai/api/anthropic\n    key: \"<your-api-key>\"   # or key_file: <path to a 0600 file>\n    tier: 3\n    models:\n      default: glm-5.3[1m]\n      fast: glm-5.3-flash\n      image: example-paint\n  kimi:\n    url: https://api.kimi.ai/coding/\n    key: \"<your-api-key>\"   # or key_file: <path to a 0600 file>\n    tier: 3\n    models:\n      default: k3[1m]\n      fast: kimi-for-coding[1m]\n\n# An \\`engine: image\\` provider does not spawn Claude \u2014 \\`pai worker run\n# --capability image\\` POSTs straight to its OpenAI-compatible images API and\n# writes the PNG it gets back. Uncomment and point it at a real provider\n# (still nested under providers:, above) to enable \\`--capability image\\`:\n#   pictures:\n#     engine: image\n#     url: https://api.example.com/v1\n#     key: \"<your-api-key>\"\n#     models:\n#       default: example-image-model\n#       image: example-image-model\n\n# A class names a provider, or provider/role to pick a non-default model.\n# Workers exist to parallelise and to save cost: the default is Sonnet,\n# Haiku for the mechanical classes, and nothing here inherits the\n# orchestrating session's model.\nclasses:\n  implement: anthropic\n  draft: anthropic\n  research: anthropic\n  complex: anthropic\n  plan: anthropic\n  review: anthropic\n  spotcheck: anthropic/fast\n  simple: anthropic/fast\n  image: glm/image\n\n# Cross-provider capability preference: which provider serves a --capability\n# request, in order, when more than one declares it. Unlisted capabilities\n# fall back to the active provider, then any provider that declares them.\n# capabilities:\n#   image: [pictures, glm]\n#   fast: [anthropic]\n\nmcp_sets:\n  desktop: [clickr]\n`;\n}\n\n/**\n * Build a workers.yaml document from real (migrated) data, keeping the same\n * header/section comments as the starter. Used by `pai worker config\n * migrate` \u2014 `init` writes the literal starter instead, since it has no data\n * to carry over.\n */\nexport function buildWorkersYamlText(data: WorkersYamlData): string {\n  const lines: string[] = [];\n  lines.push(\"# PAI worker configuration.\");\n  lines.push(\"# Providers PAI can run workers on, the model each role uses, and which\");\n  lines.push(\"# provider every --class goes to. Edit by hand; `pai worker providers` shows\");\n  lines.push(\"# the effective result. Comments are preserved when PAI writes this file.\");\n  lines.push(\"# Per provider, `worker_model: default | fast` is the model a worker gets when\");\n  lines.push(\"# the request names none (default: default); an explicit model still wins.\");\n  lines.push(\"\");\n  lines.push(\n    `active: ${data.active ?? \"null\"}` +\n      \"          # provider for `pai worker run` without --provider or --class\"\n  );\n  lines.push(\"\");\n  lines.push(\"providers:\");\n  lines.push(\"  anthropic:\");\n  lines.push(\"    builtin: true          # Claude Code's own login: no url, no key file\");\n  lines.push(\"    models:\");\n  lines.push(`      default: ${data.nativeModels.default}`);\n  if (data.nativeModels.fast) lines.push(`      fast: ${data.nativeModels.fast}`);\n  if (data.nativeModels.image) lines.push(`      image: ${data.nativeModels.image}`);\n  for (const [name, p] of Object.entries(data.providers)) {\n    const y = providerToYamlPlain(p);\n    lines.push(`  ${name}:`);\n    for (const [k, v] of Object.entries(y)) {\n      if (k === \"models\") continue;\n      // a key is an opaque token, not YAML-authored text \u2014 always quoted so\n      // it never round-trips as a number or boolean (see quotedKeyNode).\n      lines.push(k === \"key\" ? `    key: ${JSON.stringify(String(v))}` : `    ${k}: ${yamlScalar(v)}`);\n    }\n    lines.push(\"    models:\");\n    for (const [k, v] of Object.entries(p.models)) lines.push(`      ${k}: ${yamlScalar(v)}`);\n  }\n  lines.push(\"\");\n  lines.push(\"# A class names a provider, or provider/role to pick a non-default model.\");\n  lines.push(\"# Workers exist to parallelise and to save cost: the default is Sonnet,\");\n  lines.push(\"# Haiku for the mechanical classes, and nothing here inherits the\");\n  lines.push(\"# orchestrating session's model.\");\n  const classEntries = Object.entries(data.classes);\n  lines.push(classEntries.length ? \"classes:\" : \"classes: {}\");\n  for (const [cls, target] of classEntries) {\n    lines.push(`  ${cls}: ${typeof target === \"string\" ? target : yamlScalar(target)}`);\n  }\n  lines.push(\"\");\n  const capEntries = Object.entries(data.capabilities);\n  if (capEntries.length) {\n    lines.push(\"capabilities:\");\n    for (const [cap, prefs] of capEntries) lines.push(`  ${cap}: [${prefs.join(\", \")}]`);\n    lines.push(\"\");\n  }\n  const mcpEntries = Object.entries(data.mcpSets);\n  lines.push(mcpEntries.length ? \"mcp_sets:\" : \"mcp_sets: {}\");\n  for (const [set, servers] of mcpEntries) {\n    lines.push(`  ${set}: [${servers.join(\", \")}]`);\n  }\n  lines.push(\"\");\n  return lines.join(\"\\n\");\n}\n\n/** Minimal scalar formatter for buildWorkersYamlText's flat key: value lines. */\nfunction yamlScalar(v: unknown): string {\n  if (typeof v === \"string\") {\n    // quote only when the plain form would be ambiguous YAML\n    return /^[\\w./~\\[\\]-]+$/.test(v) ? v : JSON.stringify(v);\n  }\n  if (Array.isArray(v)) return `[${v.map(yamlScalar).join(\", \")}]`;\n  if (v && typeof v === \"object\") return JSON.stringify(v);\n  return String(v);\n}\n\n// ---------------------------------------------------------------------------\n// Migration from the JSON `workers` section\n// ---------------------------------------------------------------------------\n\nexport interface MigrateResult {\n  yamlPath: string;\n  yamlText: string;\n  /** Path the pre-migration JSON `workers` section was backed up to; null on a dry run. */\n  backupPath: string | null;\n  dryRun: boolean;\n}\n\n/**\n * `pai worker config migrate`: reads the JSON `workers` section, writes\n * workers.yaml with the starter's comments, backs the JSON section up to\n * `workers.json.migrated-<date>` next to it, and strips providers/classes\n * (and the legacy `roles`)/mcpSets/active from the JSON. Idempotent: refuses\n * when workers.yaml already exists unless `force`. `dryRun` computes and\n * returns the would-be YAML text without touching either file.\n */\nexport function migrateWorkersToYaml(\n  jsonPath: string,\n  opts: { force?: boolean; dryRun?: boolean } = {}\n): MigrateResult {\n  const existing = workersYamlPath();\n  if (existsSync(existing) && !opts.force) {\n    throw new WorkersConfigError(\n      `${existing} already exists \u2014 refusing to overwrite without --force`\n    );\n  }\n  const yamlPath = writeTargetWorkersYamlPath();\n  const raw = readJsonStrict(jsonPath, jsonPath);\n  const workers = parseWorkersConfig(raw.workers);\n  const data: WorkersYamlData = {\n    active: workers.active,\n    providers: workers.providers,\n    classes: workers.classes,\n    capabilities: workers.capabilities,\n    mcpSets: workers.mcpSets,\n    nativeModels: workers.nativeModels,\n  };\n  const yamlText = buildWorkersYamlText(data);\n\n  if (opts.dryRun) {\n    return { yamlPath, yamlText, backupPath: null, dryRun: true };\n  }\n\n  writeWorkersYamlText(yamlPath, yamlText);\n\n  const stamp = new Date().toISOString().slice(0, 10);\n  const backupPath = join(dirname(jsonPath), `workers.json.migrated-${stamp}`);\n  writeFileSync(backupPath, JSON.stringify(raw.workers ?? {}, null, 2) + \"\\n\", \"utf8\");\n\n  const priorWorkers = (raw.workers as Record<string, unknown>) ?? {};\n  const { providers: _p, classes: _c, roles: _r, mcpSets: _m, active: _a, ...rest } = priorWorkers;\n  raw.workers = rest;\n  writeJsonAtomic(jsonPath, raw, { label: jsonPath });\n\n  return { yamlPath, yamlText, backupPath, dryRun: false };\n}\n\n/**\n * `pai worker config init`: write the literal commented starter (no data to\n * carry over \u2014 that is what `migrate` is for). Refuses if workers.yaml\n * already exists.\n */\nexport function initWorkersYaml(): string {\n  const existing = workersYamlPath();\n  if (existsSync(existing)) {\n    throw new WorkersConfigError(\n      `${existing} already exists \u2014 edit it directly, or run \\`pai worker config migrate --force\\` to regenerate it from the JSON config`\n    );\n  }\n  const yamlPath = writeTargetWorkersYamlPath();\n  writeWorkersYamlText(yamlPath, starterWorkersYamlText());\n  return yamlPath;\n}\n\n// ---------------------------------------------------------------------------\n// Relocating from an old location\n// ---------------------------------------------------------------------------\n\nexport type RelocateResult = MigrateFileResult;\n\n/** True when an old-location workers.yaml exists and nothing is at the new one yet. */\nexport function needsWorkersYamlRelocation(): boolean {\n  return (\n    !process.env.PAI_WORKERS_YAML &&\n    !existsSync(defaultWorkersYamlPath()) &&\n    [oldWorkersYamlPath(), legacyWorkersYamlPath()].some((p) => existsSync(p))\n  );\n}\n\n/**\n * `pai worker config migrate` when workers.yaml is still at an old location\n * (~/.claude/workers.yaml or, older still, ~/.config/pai/workers.yaml): a\n * byte-for-byte copy to the new path (comments, keys, everything \u2014 this only\n * changes where the file lives), verified, then the old file is renamed\n * aside as workers.yaml.migrated-<YYYYMMDD> (never deleted). No JSON\n * involved, and no secrets are read or transformed beyond copying bytes.\n */\nexport function relocateWorkersYaml(opts: { force?: boolean; dryRun?: boolean } = {}): RelocateResult {\n  const toPath = writeTargetWorkersYamlPath();\n  const result = migratePaiFile(toPath, [oldWorkersYamlPath(), legacyWorkersYamlPath()], opts);\n  if (!result.dryRun && result.fromPath) chmodSync(toPath, 0o600);\n  return result;\n}\n\n// ---------------------------------------------------------------------------\n// Inlining key_file contents as `key:`\n// ---------------------------------------------------------------------------\n\nexport interface InlineKeysResult {\n  yamlPath: string;\n  inlined: { provider: string; keyFilePath: string }[];\n  dryRun: boolean;\n}\n\n/**\n * `pai worker config inline-keys`: for every provider with a `key_file` and\n * no `key`, read the file, write its trimmed contents as a quoted `key:`,\n * and remove `key_file`. The key file itself is left on disk \u2014 this only\n * changes what workers.yaml reads from, never deletes a credential the\n * operator might still want. `dryRun` computes the plan (provider names and\n * key file paths only \u2014 never the key values) without writing anything.\n */\nexport function inlineWorkersYamlKeys(opts: { dryRun?: boolean } = {}): InlineKeysResult {\n  const yamlPath = workersYamlPath();\n  const existing = readWorkersYaml(yamlPath);\n  if (!existing) {\n    throw new WorkersConfigError(`${yamlPath} does not exist \u2014 run \\`pai worker config init\\` first`);\n  }\n  const { doc, data } = existing;\n  const inlined: { provider: string; keyFilePath: string }[] = [];\n  for (const [name, p] of Object.entries(data.providers)) {\n    if (p.key || !p.keyFile) continue;\n    const keyPath = expandHome(p.keyFile);\n    let content: string;\n    try {\n      content = readFileSync(keyPath, \"utf8\");\n    } catch (e) {\n      throw new WorkersConfigError(\n        `providers.${name}.key_file: could not read ${keyPath}: ${e instanceof Error ? e.message : String(e)}`\n      );\n    }\n    const token = content.trim();\n    if (!token) throw new WorkersConfigError(`providers.${name}.key_file: ${keyPath} is empty`);\n    inlined.push({ provider: name, keyFilePath: p.keyFile });\n    if (!opts.dryRun) {\n      doc.setIn([\"providers\", name, \"key\"], quotedKeyNode(token));\n      doc.deleteIn([\"providers\", name, \"key_file\"]);\n    }\n  }\n  if (!opts.dryRun && inlined.length) {\n    writeWorkersYamlText(yamlPath, String(doc));\n  }\n  return { yamlPath, inlined, dryRun: !!opts.dryRun };\n}\n", "/**\n * config.ts \u2014 the `workers` section of the PAI config file (CONFIG_FILE,\n * see src/daemon/config.ts's paiConfigFilePath \u2014 ~/.pai/config.yaml\n * today, config.json until `pai config yaml` runs).\n *\n * Everything that knows about worker providers reads this module: the CLI\n * (`pai worker \u2026`), the MCP tools (worker_*), and the Agent-routing hook.\n * Keep it free of CLI/MCP imports so all three layers stay thin over it.\n *\n * Keys are never stored here \u2014 only paths to 0600 files. A provider without a\n * keyFile is a local server and gets the placeholder token \"local\".\n *\n * `protocol: \"openai\"` providers run through the PAI proxy (src/workers/proxy):\n * `upstreamUrl` is their Chat Completions base, and `run` points\n * ANTHROPIC_BASE_URL at the local proxy with the provider name in the path.\n * `engine: \"codex\"` providers run through the Codex CLI instead of Claude\n * Code.\n */\n\nimport { DEFAULT_CAPACITY, type CapacityConfig } from \"./capacity.js\";\nimport { DEFAULT_LOCAL_IDLE, type LocalIdleConfig } from \"./local-idle.js\";\nimport { DEFAULT_STAGGER, type StaggerConfig } from \"./stagger.js\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { CONFIG_FILE, expandHome, readMainConfigRaw, writeMainConfigRaw } from \"../daemon/config.js\";\nimport { paiHomePath } from \"../config/pai-home.js\";\nimport { contextWindowFromModelId, DEFAULT_CONTEXT_WINDOW } from \"../utils/model-window.js\";\nimport { readWorkersYaml, workersYamlPath, writeWorkersYaml, type WorkersYamlData } from \"./workers-config.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Wire protocol a provider speaks: \"anthropic\" natively, \"openai\" through the\n * PAI proxy (which translates the Anthropic Messages API to Chat Completions).\n */\nexport type WorkerProtocol = \"anthropic\" | \"openai\";\n\n/**\n * Runner executable behind a provider: Claude Code, the Codex CLI, or the\n * \"image\" engine (src/workers/engines/image.ts \u2014 an OpenAI-compatible\n * images API, not a claude/codex spawn).\n */\nexport type WorkerEngine = \"claude\" | \"codex\" | \"image\";\n\n/** One quota window the statusline renders for a provider (e.g. \"5h\", \"7d\"). */\nexport interface UsageWindow {\n  /** Window label shown in the statusline, e.g. \"5h\". */\n  name: string;\n  /** jq expression against the usage response yielding a percent 0\u2013100. */\n  percent: string;\n  /** jq expression against the usage response yielding the reset time. */\n  resetAt?: string;\n  /** How resetAt is interpreted: epoch ms (default), epoch s, or ISO string. */\n  resetUnit?: \"ms\" | \"s\" | \"iso\";\n}\n\n/**\n * How the statusline fetches and renders a provider's plan quota. The block\n * names a JSON GET endpoint plus per-window jq expressions, so a new provider\n * is config, not code in statusline-command.sh.\n */\nexport interface ProviderUsage {\n  /** GET endpoint returning the usage JSON. */\n  url: string;\n  /**\n   * Auth header template, default \"Authorization: Bearer\"; the key from\n   * keyFile is appended after a space. A value without a space (e.g.\n   * \"x-api-key\") is sent as \"<authHeader>: <key>\".\n   */\n  authHeader?: string;\n  /** Display label for the usage segments, default = provider name. */\n  label?: string;\n  windows: UsageWindow[];\n  /** Statusline cache TTL in seconds, default 60. */\n  ttlSeconds?: number;\n}\n\nexport interface WorkerProvider {\n  enabled: boolean;\n  /** Wire protocol the baseUrl speaks. */\n  protocol: WorkerProtocol;\n  /** Anthropic-compatible Messages API base. */\n  baseUrl: string;\n  /** 0600 file holding the auth token; null for local servers (\"local\"). */\n  keyFile: string | null;\n  /**\n   * The auth token itself, inline in workers.yaml. Wins over keyFile when\n   * both are set (`pai worker config check` notices this). Keeping the key\n   * out of a separate file is the operator's explicit call \u2014 the file this\n   * lives in must be 0600 (writeWorkersYamlText enforces that) and is never\n   * committed. See resolveProviderKey for the one place this is read.\n   */\n  key?: string;\n  /** Model id per capability; see MODEL_CAPABILITIES. Only default is required. */\n  models: { default: string } & Partial<Record<ModelCapability, string>>;\n  /**\n   * Model id \u2192 tier alias, overriding the built-in mapping (models.fast \u2192\n   * haiku tier, models.default \u2192 sonnet tier). Lets a provider pin an extra\n   * model id to a tier, e.g. a heavyweight default to the opus tier.\n   */\n  modelTiers?: Record<string, ModelTier>;\n  /**\n   * Model a worker gets on this provider when the request names none:\n   * \"default\" (the default, unset) or \"fast\". Set \"fast\" on an expensive\n   * provider. An explicit model (`--model`, a `provider/role` class target,\n   * where/model) still wins. workers.yaml `worker_model`.\n   */\n  workerModel?: \"default\" | \"fast\";\n  /** Extra environment variables for runs through this provider (string values). */\n  env: Record<string, string>;\n  note?: string;\n  /**\n   * `false` keeps a loopback provider on its direct URL; unset routes it\n   * through the local router (shared prefix-cache fixes, usage accounting).\n   */\n  router?: boolean;\n  /** Contract addendum file for this provider; default <PAI_HOME>/contracts/<name>.md. */\n  contract?: string;\n  /**\n   * OpenAI Chat Completions base (e.g. \"https://api.openai.com/v1\"). Required\n   * for protocol \"openai\"; read by the PAI proxy, never by the runner.\n   */\n  upstreamUrl?: string;\n  /** Runner for this provider; \"codex\" goes through the Codex CLI. */\n  engine?: WorkerEngine;\n  /** Optional shell command printing 0\u2013100 (percent of quota used). */\n  quotaProbe?: string;\n  /** Auto-routing skips the provider at or above this percentage. Default 95. */\n  quotaSkipAt?: number;\n  /**\n   * Context window of the provider's model, for the context meter. No\n   * default: the meter only shows a window the init event announced (or\n   * this explicit value, for engines without one, e.g. codex).\n   */\n  contextWindow?: number;\n  /**\n   * Extended thinking for runs through this provider. Unset = off for a\n   * non-native provider: the worker settings then carry\n   * CLAUDE_CODE_DISABLE_THINKING and an unset effort level, so the request has\n   * neither `thinking` nor `output_config` (a local server's own reasoning\n   * default then applies). `true` leaves Claude Code's defaults alone.\n   */\n  thinking?: boolean;\n  /** Most runs this provider serves at once; more queue (capacity.ts). Default 3 on loopback, unlimited otherwise. */\n  maxConcurrent?: number;\n  /** Cost tier 1 (cheapest) \u2026 5 (most expensive); classes cap it via maxCostTier. */\n  costTier?: number;\n  /** Capability tags; classes filter auto-routing via requireTags. */\n  tags?: ProviderTag[];\n  /** Statusline plan-quota block; see ProviderUsage. Absent = \"usage n/a\". */\n  usage?: ProviderUsage;\n  /**\n   * True only for the synthetic built-in \"anthropic\" provider (see\n   * ANTHROPIC_NATIVE): plain Claude Code, on its own OAuth/Max-plan login \u2014\n   * no baseUrl, no key file, no config entry. Never set by parseProvider.\n   */\n  native?: boolean;\n}\n\n/**\n * Reserved provider name for plain Anthropic: Claude Code's own OAuth/Max-plan\n * login, no base URL override and no API key. It never needs (or accepts) a\n * `providers.anthropic` config entry \u2014 resolveTarget/mustExist synthesize it\n * on demand via `nativeAnthropicProvider()`.\n */\nexport const ANTHROPIC_NATIVE = \"anthropic\";\n\n/**\n * Models the built-in provider runs on. This is the only place a model id is\n * named for it: run.ts resolves the class capability against this table like\n * it does for any configured provider and passes `--model` explicitly.\n *\n * Workers exist to parallelise and to save cost, so they must never inherit\n * the orchestrator's model. A bare headless `claude` takes the interactive\n * session default, and one probe came up on the most expensive tier because\n * the chat session had been switched to it. Sonnet is the working tier;\n * haiku serves the fast classes (spotcheck, simple). These are the Claude\n * Code CLI's own tier aliases, not pinned ids: the CLI resolves each to its\n * newest model on every run, so the tier tracks a release without a code\n * change here (adoptInitModel in run.ts swaps the alias for the init event's\n * resolved id once the run announces one).\n */\nexport const NATIVE_ANTHROPIC_MODELS: WorkerProvider[\"models\"] = {\n  default: \"sonnet\",\n  fast: \"haiku\",\n};\n\n/**\n * The synthetic provider object for ANTHROPIC_NATIVE. `baseUrl` is empty and\n * there is no key file, so buildRunEnv strips every provider env override;\n * the models come from NATIVE_ANTHROPIC_MODELS so a run always names its\n * model on the command line.\n */\nexport function nativeAnthropicProvider(\n  models: WorkerProvider[\"models\"] = NATIVE_ANTHROPIC_MODELS\n): WorkerProvider {\n  return {\n    enabled: true,\n    protocol: \"anthropic\",\n    baseUrl: \"\",\n    keyFile: null,\n    models: { ...models },\n    env: {},\n    native: true,\n  };\n}\n\n/**\n * Provider by name, synthesizing ANTHROPIC_NATIVE when it is asked for. Reads\n * `config.nativeModels` (workers.yaml's documented `providers.anthropic.models`\n * override, if any) so a config that pins a different default/fast id for the\n * built-in provider is honored everywhere a provider is resolved.\n */\nexport function getProviderOrNative(\n  config: { providers: Record<string, WorkerProvider>; nativeModels?: WorkerProvider[\"models\"] },\n  name: string\n): WorkerProvider | undefined {\n  if (name === ANTHROPIC_NATIVE) return nativeAnthropicProvider(config.nativeModels);\n  return config.providers[name];\n}\n\nexport interface WorkersPaneConfig {\n  enabled: boolean;\n  /** Font size (points) of the follow-pane profile's font. */\n  fontSize: number;\n  /** Seconds a pane lingers after its worker goes quiet. */\n  autoExitSecs: number;\n}\n\nexport interface WorkersRoutingConfig {\n  /** Provider names in preference order; first usable one wins. */\n  order: string[];\n  /** Minutes a provider sits in cooldown after a quota/rate failure. */\n  cooldownMinutes: number;\n  /** Reroute a failed-before-first-tool run to the next provider. */\n  retryOnQuota: boolean;\n  /**\n   * Auto-routing policy: \"budget\" (default) walks the priority list but skips\n   * every provider whose live budget probe reads at or above its wall\n   * (routing.budgetWalls) \u2014 a run waits a full provider out on the next one\n   * with room, and the full one re-enters when its window resets. \"order\"\n   * walks routing.order verbatim, quota probes aside.\n   */\n  policy: \"budget\" | \"order\";\n  /**\n   * Provider names in operator preference order for policy \"budget\". Empty\n   * falls back to routing.order, else DEFAULT_BUDGET_PRIORITY.\n   */\n  priority: string[];\n  /**\n   * Per-provider usage percent at or above which budget routing treats the\n   * provider as full (default DEFAULT_BUDGET_WALL). From the provider's\n   * `usage` block (anthropic: the OAuth usage endpoint). A number applies to\n   * every window; a map by window name ({\"5h\": 95, \"7d\": 70}) walls each\n   * window on its own, a window with no entry using the provider's number\n   * (map form has none) and then DEFAULT_BUDGET_WALLS. A provider is full\n   * when ANY window it reports is at or above that window's wall.\n   */\n  budgetWalls: Record<string, number | Record<string, number>>;\n}\n\n/** workers.supervision: pointer = type \"N worker alerts pending\" lines into session panes; default off. */\nexport interface WorkersSupervisionConfig {\n  pointer: boolean;\n  /**\n   * Which alerts reach the orchestrating session. \"harness\" (default): harness-class\n   * tool errors, loop detections and stalls; the rest is recorded and summarised once\n   * when the unit ends. \"all\": every first-occurrence tool error, thrashing and sleeping too.\n   */\n  push: \"harness\" | \"all\";\n  /** Minutes without a tool call before a running unit counts as stalled (default 10). */\n  stallMinutes: number;\n}\n\n/** Sub-worker caps: how deep the worker tree may grow, how wide per parent. */\nexport interface WorkersTreeConfig {\n  /** Maximum nesting depth of sub-workers (top-level = 0). */\n  maxDepth: number;\n  /** Maximum concurrently running children per parent. */\n  maxChildren: number;\n}\n\n/**\n * The worker session pool (workers.pool): long-lived headless claude slots\n * that successive small runs talk to instead of each spawning a fresh\n * claude, so the provider prompt cache carries over between tasks\n * (src/workers/pool.ts). Default OFF \u2014 the operator turns it on with\n * `pai worker pool on`.\n */\nexport interface WorkersPoolConfig {\n  /** Pool routing on: eligible runs go through the pool host. */\n  enabled: boolean;\n  /** Maximum slot processes the host keeps, across all (provider, model) keys. */\n  maxSlots: number;\n  /** Idle seconds before a slot is killed (0 = never). */\n  idleSecs: number;\n  /** Loopback port the pool host listens on. */\n  port: number;\n  /** Native anthropic (Claude Code's own login) runs may use pooled slots; worktree runs never do. */\n  native: boolean;\n}\n\n/** The tier aliases the CLI and daemon tables understand \u2014 class proxies, not\n *  Anthropic model names: any provider's model maps onto one of these. */\nexport type ModelTier = \"haiku\" | \"sonnet\" | \"opus\";\n\nconst MODEL_TIERS: readonly ModelTier[] = [\"haiku\", \"sonnet\", \"opus\"];\n\nexport const DEFAULT_COST_TIER = 3;\n\n/** Tags a provider may carry; classes filter auto-routing on them. */\nexport const PROVIDER_TAGS = [\n  \"code\",\n  \"vision\",\n  \"image-gen\",\n  \"long-context\",\n  \"fast\",\n  \"reasoning\",\n] as const;\n\nexport type ProviderTag = (typeof PROVIDER_TAGS)[number];\n\n/** The standard task classes; `workers.classes` maps each to a target. */\nexport const WORKER_CLASSES = [\n  \"draft\",\n  \"plan\",\n  \"implement\",\n  \"review\",\n  \"research\",\n  \"spotcheck\",\n  \"simple\",\n  \"complex\",\n  \"image\",\n] as const;\n\nexport type WorkerClassName = (typeof WORKER_CLASSES)[number];\n\n/**\n * The well-known model capabilities \u2014 documented, and what the starter\n * workers.yaml and `pai worker model`'s listing show by name. The set is\n * open, though: a provider's `models` block may carry any capability name\n * matching CAPABILITY_NAME_RE (see isModelCapability), e.g. \"vision\" or\n * \"longcontext\" for a provider that serves them. \"default\" is the required\n * catch-all; the others name what a model is *for* \u2014 \"fast\" the cheap tier\n * (spotcheck, haiku-tier spawns), \"image\" the image class/capability.\n * Everything resolves through resolveModelCapability, falling back to\n * default; cross-provider preference for a capability is `capabilities:` in\n * workers.yaml (see resolveCapability).\n */\nexport const MODEL_CAPABILITIES = [\"default\", \"fast\", \"image\"] as const;\n\nexport type ModelCapability = string;\n\nexport const CAPABILITY_NAME_RE = /^[a-z][a-z0-9-]*$/;\n\n/** Is this string a syntactically valid capability name (open set)? */\nexport function isModelCapability(v: string): v is ModelCapability {\n  return CAPABILITY_NAME_RE.test(v);\n}\n\n/**\n * Which model capability a class runs on when its target names no alias: the\n * image class uses the image model, the cheap classes (spotcheck, simple) the\n * fast model, every other class the provider default. A provider without a\n * fast model falls back to its default (resolveModelCapability).\n */\nconst CLASS_MODEL_CAPABILITY: Record<WorkerClassName, ModelCapability> = {\n  draft: \"default\",\n  plan: \"default\",\n  implement: \"default\",\n  review: \"default\",\n  research: \"default\",\n  spotcheck: \"fast\",\n  simple: \"fast\",\n  complex: \"default\",\n  image: \"image\",\n};\n\n/** Capability for a run's class (default when unset or not a standard class). */\nexport function classModelCapability(className?: string): ModelCapability {\n  return (className && CLASS_MODEL_CAPABILITY[className as WorkerClassName]) || \"default\";\n}\n\n/**\n * A class target: \"<provider>\", \"<provider>/<modelAlias>\", or an object. The\n * object either pins a `provider` (plus optional `mcp` allowlist) or only\n * constrains auto-routing (`maxCostTier`, `requireTags`, per-class `order`).\n */\nexport type ClassTarget =\n  | string\n  | {\n      provider?: string;\n      mcp?: string[];\n      /** Auto-routing may only use providers at or below this cost tier. */\n      maxCostTier?: number;\n      /** Auto-routing may only use providers carrying all these tags. */\n      requireTags?: string[];\n      /** Provider order for this class; defaults to routing.order. */\n      order?: string[];\n    };\n\n/**\n * State of the machine-wide Claude Code fallback (`pai worker fallback on`):\n * every new Claude Code process runs on `provider` until `fallback off`.\n * `saved` holds what ~/.claude/settings.json carried before the switch so\n * `off` can restore it exactly.\n */\nexport interface WorkersFallback {\n  /** Provider every new Claude Code process is pointed at. */\n  provider: string;\n  /**\n   * settings.json before the switch. `env` records the previous value of\n   * every env key fallback touched (null = the key was absent), `model` the\n   * previous top-level model pin (null = none), `envExisted` whether an env\n   * block existed at all.\n   */\n  saved: {\n    env: Record<string, string | null>;\n    model: string | null;\n    envExisted: boolean;\n  };\n  /** ISO stamp of the switch (shown by `fallback status`). */\n  on: string;\n}\n\nexport interface WorkersConfig {\n  enabled: boolean;\n  /** Provider name, or \"auto\" for routing.order resolution. */\n  active: string | null;\n  providers: Record<string, WorkerProvider>;\n  /** Class \u2192 target; see ClassTarget. (Reads the legacy `roles` key.) */\n  classes: Record<string, ClassTarget>;\n  /** MCP set name \u2192 server names; `--mcp <set>` and class `mcp` expand these. */\n  mcpSets: Record<string, string[]>;\n  /**\n   * Capability \u2192 provider names in preference order (workers.yaml\n   * `capabilities:`), e.g. `{ image: [\"pictures\", \"glm\"] }`. Cross-provider,\n   * unlike a provider's own `models` table: this is what lets `--capability\n   * image` find the one provider configured to serve it. See resolveCapability.\n   */\n  capabilities: Record<string, string[]>;\n  pane: WorkersPaneConfig;\n  logDir: string;\n  routing: WorkersRoutingConfig;\n  /** Sub-worker caps (workers.tree). */\n  tree: WorkersTreeConfig;\n  /** Worker supervision options (workers.supervision). */\n  supervision: WorkersSupervisionConfig;\n  /** Worker session pool (workers.pool); default off. */\n  pool: WorkersPoolConfig;\n  /**\n   * Daemon cache-keepalive cadence in seconds: how often a trivial\n   * single-turn heartbeat worker re-arms the provider prompt cache\n   * (src/workers/keepalive.ts). 0 = off.\n   */\n  cacheKeepaliveSecs: number;\n  /** Default time limit of every run in minutes (workers.defaultMaxMinutes); 0 = none. */\n  defaultMaxMinutes: number;\n  /** Start gate for same-provider runs (workers.stagger); see stagger.ts. */\n  stagger: StaggerConfig;\n  /** Per-provider concurrency cap (workers.capacity); see capacity.ts. */\n  capacity: CapacityConfig;\n  /** Local-model idle watcher (workers.localIdle); see local-idle.ts. */\n  localIdle: LocalIdleConfig;\n  /** Runs strip browser tools unless --browser is passed (workers.noBrowserByDefault). */\n  noBrowserByDefault: boolean;\n  /** Hourly gc archives finished workers' worktrees older than this many hours (workers.gcOlderThanHours). */\n  gcOlderThanHours: number;\n  /** Size cap in GB of the worktrees dir; over it, gc archives the oldest finished ones (workers.worktreesMaxGb). */\n  worktreesMaxGb: number;\n  /** Daemon reaper for orphaned worker processes (workers.reaper, default on). */\n  reaper: boolean;\n  /**\n   * Run native-Anthropic workers through `caveman claude` (workers.caveman).\n   * Off pins them to api.anthropic.com even when the user settings route\n   * every claude through a proxy; see claudeCommand in run-env.ts.\n   */\n  caveman: boolean;\n  /** Machine-wide Claude Code fallback; null = off. */\n  fallback: WorkersFallback | null;\n  /**\n   * Model ids for the built-in `anthropic` provider, when workers.yaml\n   * documents an override under `providers.anthropic.models`. Defaults to\n   * NATIVE_ANTHROPIC_MODELS.\n   */\n  nativeModels: WorkerProvider[\"models\"];\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_GC_OLDER_THAN_HOURS = 6;\nexport const DEFAULT_WORKTREES_MAX_GB = 20;\n\nexport const DEFAULT_LOG_DIR =\"~/.claude/logs/workers\";\n\nexport const DEFAULT_PANE: WorkersPaneConfig = {\n  enabled: true,\n  fontSize: 13,\n  autoExitSecs: 5,\n};\n\n/**\n * Budget routing's default preference order (policy \"budget\" with neither\n * routing.priority nor routing.order set): Anthropic while it has budget,\n * then the cloud relays. Providers that are not configured are simply\n * skipped, so this degrades to plain Anthropic on a fresh install.\n */\nexport const DEFAULT_BUDGET_PRIORITY = [\"anthropic\", \"glm\", \"kimi\"];\n\n/**\n * Budget walls when routing.budgetWalls names none for a provider: Anthropic\n * Max-plan 5h, the z.ai 5h window, Kimi \u2014 percent used at which budget\n * routing treats the provider as full.\n */\nexport const DEFAULT_BUDGET_WALLS: Record<string, number> = {\n  anthropic: 95,\n  glm: 90,\n  kimi: 85,\n};\n\n/** Budget wall when neither routing.budgetWalls nor DEFAULT_BUDGET_WALLS knows a provider. */\nexport const DEFAULT_BUDGET_WALL = 95;\n\nexport const DEFAULT_ROUTING: WorkersRoutingConfig = {\n  order: [],\n  cooldownMinutes: 30,\n  retryOnQuota: true,\n  policy: \"budget\",\n  priority: [],\n  budgetWalls: {},\n};\n\nexport const DEFAULT_TREE: WorkersTreeConfig = {\n  maxDepth: 2,\n  maxChildren: 4,\n};\n\n/**\n * Session-pool defaults when the config names none. OFF: the pool changes\n * where a task's turns run (a shared, warm claude session), which deserves\n * an explicit operator call. idleSecs 900 = 15 min \u2014 the provider prompt\n * cache the pool exists to carry is gone by then anyway.\n */\nexport const DEFAULT_POOL: WorkersPoolConfig = {\n  enabled: false,\n  maxSlots: 3,\n  idleSecs: 900,\n  port: 8798,\n  native: true,\n};\n\n/**\n * Cache-keepalive cadence when the config names none. Measured 2026-09-18\n * (pair probes through the real worker spawn path, numbers in\n * docs/cache-keepalive.md): the implicit provider cache only serves a fresh\n * worker warm within roughly the first minute (positive back-to-back, gone\n * at 2 min), and it covers only ~2.6k of the ~18k-token prefix. Holding it\n * needs a beat every <2 min \u2014 a continuous bill for a small saving \u2014 so the\n * default is OFF and arming it is the operator's explicit call (set e.g.\n * \"cacheKeepaliveSecs\": 60).\n */\nexport const DEFAULT_CACHE_KEEPALIVE_SECS = 0;\nexport const DEFAULT_MAX_MINUTES = 60;\n\n/**\n * The default MCP sets every config starts from. `desktop` names the clickr\n * server so `--mcp desktop` hands a worker the machine controls (read-only\n * tools always; actuating ones after the operator hands the controls over,\n * see `pai worker controls`). A user's mcpSets section is merged over this,\n * so `desktop: []` removes the set deliberately.\n */\nexport const DEFAULT_MCP_SETS: Record<string, string[]> = {\n  desktop: [\"clickr\"],\n};\n\nexport function defaultWorkersConfig(): WorkersConfig {\n  return {\n    enabled: false,\n    active: null,\n    providers: {},\n    classes: {},\n    mcpSets: { ...DEFAULT_MCP_SETS },\n    capabilities: {},\n    pane: { ...DEFAULT_PANE },\n    logDir: DEFAULT_LOG_DIR,\n    routing: { ...DEFAULT_ROUTING, order: [] },\n    tree: { ...DEFAULT_TREE },\n    supervision: { pointer: false, push: \"harness\", stallMinutes: 10 },\n    pool: { ...DEFAULT_POOL },\n    cacheKeepaliveSecs: DEFAULT_CACHE_KEEPALIVE_SECS,\n    defaultMaxMinutes: DEFAULT_MAX_MINUTES,\n    stagger: { ...DEFAULT_STAGGER },\n    capacity: { ...DEFAULT_CAPACITY },\n    localIdle: { ...DEFAULT_LOCAL_IDLE },\n    noBrowserByDefault: false,\n    gcOlderThanHours: DEFAULT_GC_OLDER_THAN_HOURS,\n    worktreesMaxGb: DEFAULT_WORKTREES_MAX_GB,\n    reaper: true,\n    caveman: false,\n    fallback: null,\n    nativeModels: { ...NATIVE_ANTHROPIC_MODELS },\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nexport class WorkersConfigError extends Error {}\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\nfunction bad(path: string, why: string): never {\n  throw new WorkersConfigError(`workers${path}: ${why}`);\n}\n\nfunction str(v: unknown): string {\n  return typeof v === \"string\" ? v : \"\";\n}\n\n/**\n * Parse a `models` block (capability \u2192 model id, \"default\" required). Shared\n * by parseProvider and the YAML loader's builtin-anthropic override, which\n * has no other provider fields to validate.\n */\nexport function parseModelsBlock(pathPrefix: string, modelsRaw: unknown): WorkerProvider[\"models\"] {\n  const raw = modelsRaw === undefined ? {} : modelsRaw;\n  if (typeof raw !== \"object\" || raw === null) bad(pathPrefix, \"must be an object\");\n  const m = raw as Record<string, unknown>;\n  const defaultModel = str(m.default);\n  if (!defaultModel) bad(`${pathPrefix}.default`, \"is required\");\n  const models: WorkerProvider[\"models\"] = { default: defaultModel };\n  for (const key of Object.keys(m)) {\n    if (key === \"default\") continue;\n    if (!isModelCapability(key)) {\n      bad(\n        `${pathPrefix}.${key}`,\n        `\"${key}\" is not a valid capability name (must match ^[a-z][a-z0-9-]*$; well-known: ${MODEL_CAPABILITIES.join(\", \")}, but any name in that shape is accepted)`\n      );\n    }\n    const id = str(m[key]);\n    if (!id) bad(`${pathPrefix}.${key}`, \"must be a non-empty model id\");\n    models[key] = id;\n  }\n  return models;\n}\n\n/** Parse and validate one provider entry (shared by JSON and YAML loading). */\nexport function parseProvider(name: string, raw: unknown): WorkerProvider {\n  if (name === ANTHROPIC_NATIVE) {\n    bad(\n      `.providers.${name}`,\n      `\"${ANTHROPIC_NATIVE}\" is reserved for plain Anthropic (Claude Code's own OAuth/Max-plan login) and cannot be configured here \u2014 remove this entry; use --provider ${ANTHROPIC_NATIVE} or \"pai worker providers use ${ANTHROPIC_NATIVE}\" instead`\n    );\n  }\n  if (typeof raw !== \"object\" || raw === null) bad(`.providers.${name}`, \"must be an object\");\n  const p = raw as Record<string, unknown>;\n\n  const protocol = p.protocol === undefined ? \"anthropic\" : str(p.protocol);\n  if (protocol !== \"anthropic\" && protocol !== \"openai\") {\n    bad(`.providers.${name}.protocol`, `\"${str(p.protocol)}\" is neither \"anthropic\" nor \"openai\"`);\n  }\n  const engine = p.engine === undefined ? \"claude\" : str(p.engine);\n  if (engine !== \"claude\" && engine !== \"codex\" && engine !== \"image\") {\n    bad(`.providers.${name}.engine`, `\"${str(p.engine)}\" is none of \"claude\", \"codex\", \"image\"`);\n  }\n\n  const baseUrl = str(p.baseUrl);\n  // openai providers reach the model through the PAI proxy; their baseUrl is\n  // the proxy URL, filled in by the runner \u2014 only anthropic needs one here.\n  if (p.router !== undefined && typeof p.router !== \"boolean\") {\n    bad(`.providers.${name}.router`, \"must be true or false\");\n  }\n  if (p.contract !== undefined && typeof p.contract !== \"string\") {\n    bad(`.providers.${name}.contract`, \"must be a string path\");\n  }\n  if (!baseUrl && protocol !== \"openai\") bad(`.providers.${name}.baseUrl`, \"is required\");\n\n  const keyFile =\n    p.keyFile === undefined || p.keyFile === null || str(p.keyFile) === \"\"\n      ? null\n      : str(p.keyFile);\n  const key = str(p.key) || undefined;\n\n  const models = parseModelsBlock(`.providers.${name}.models`, p.models);\n\n  let modelTiers: Record<string, ModelTier> | undefined;\n  if (p.modelTiers !== undefined) {\n    if (typeof p.modelTiers !== \"object\" || p.modelTiers === null || Array.isArray(p.modelTiers)) {\n      bad(`.providers.${name}.modelTiers`, \"must be an object of model id \u2192 haiku | sonnet | opus\");\n    }\n    modelTiers = {};\n    for (const [id, tier] of Object.entries(p.modelTiers as Record<string, unknown>)) {\n      if (typeof tier !== \"string\" || !MODEL_TIERS.includes(tier as ModelTier)) {\n        bad(\n          `.providers.${name}.modelTiers.${id}`,\n          `must be \"haiku\", \"sonnet\" or \"opus\" (got ${JSON.stringify(tier)})`\n        );\n      }\n      modelTiers[id] = tier as ModelTier;\n    }\n  }\n\n  const env: Record<string, string> = {};\n  if (p.env !== undefined) {\n    if (typeof p.env !== \"object\" || p.env === null || Array.isArray(p.env)) {\n      bad(`.providers.${name}.env`, \"must be an object of string values\");\n    }\n    for (const [k, v] of Object.entries(p.env as Record<string, unknown>)) {\n      if (typeof v !== \"string\") bad(`.providers.${name}.env.${k}`, \"must be a string\");\n      env[k] = v;\n    }\n  }\n\n  if (p.workerModel !== undefined && p.workerModel !== \"default\" && p.workerModel !== \"fast\") {\n    bad(`.providers.${name}.worker_model`, 'must be \"default\" or \"fast\"');\n  }\n  const quotaSkipAt = p.quotaSkipAt === undefined ? undefined : p.quotaSkipAt;\n  if (quotaSkipAt !== undefined) {\n    if (typeof quotaSkipAt !== \"number\" || quotaSkipAt < 0 || quotaSkipAt > 100) {\n      bad(`.providers.${name}.quotaSkipAt`, \"must be a number between 0 and 100\");\n    }\n  }\n\n  let thinking: boolean | undefined;\n  if (p.thinking !== undefined) {\n    if (p.thinking !== \"on\" && p.thinking !== \"off\" && typeof p.thinking !== \"boolean\") {\n      bad(`.providers.${name}.thinking`, 'must be \"on\" or \"off\"');\n    }\n    thinking = p.thinking === \"on\" || p.thinking === true;\n  }\n\n  const contextWindow = p.contextWindow === undefined ? undefined : p.contextWindow;\n  if (contextWindow !== undefined) {\n    if (typeof contextWindow !== \"number\" || contextWindow <= 0) {\n      bad(`.providers.${name}.contextWindow`, \"must be a positive number of tokens\");\n    }\n  }\n\n  if (p.maxConcurrent !== undefined) {\n    if (typeof p.maxConcurrent !== \"number\" || !Number.isInteger(p.maxConcurrent) || p.maxConcurrent < 1) {\n      bad(`.providers.${name}.maxConcurrent`, \"must be a positive integer\");\n    }\n  }\n\n  const upstreamUrl = str(p.upstreamUrl);\n  if (protocol === \"openai\" && !upstreamUrl) {\n    bad(`.providers.${name}.upstreamUrl`, `is required for protocol \"openai\" (the Chat Completions base, e.g. \"https://api.openai.com/v1\")`);\n  }\n\n  const costTier = p.costTier === undefined ? undefined : p.costTier;\n  if (costTier !== undefined) {\n    if (typeof costTier !== \"number\" || !Number.isInteger(costTier) || costTier < 1 || costTier > 5) {\n      bad(`.providers.${name}.costTier`, \"must be an integer 1 (cheapest) \u2026 5 (most expensive)\");\n    }\n  }\n\n  let tags: ProviderTag[] | undefined;\n  if (p.tags !== undefined) {\n    if (!Array.isArray(p.tags) || p.tags.some((x) => typeof x !== \"string\")) {\n      bad(`.providers.${name}.tags`, `must be an array of tags from: ${PROVIDER_TAGS.join(\", \")}`);\n    }\n    for (const t of p.tags as string[]) {\n      if (!(PROVIDER_TAGS as readonly string[]).includes(t)) {\n        bad(`.providers.${name}.tags`, `\"${t}\" is not a tag (from: ${PROVIDER_TAGS.join(\", \")})`);\n      }\n    }\n    tags = p.tags as ProviderTag[];\n  }\n\n  let usage: ProviderUsage | undefined;\n  if (p.usage !== undefined) {\n    if (typeof p.usage !== \"object\" || p.usage === null || Array.isArray(p.usage)) {\n      bad(`.providers.${name}.usage`, \"must be an object\");\n    }\n    const u = p.usage as Record<string, unknown>;\n    const usageUrl = str(u.url);\n    if (!usageUrl) bad(`.providers.${name}.usage.url`, \"is required\");\n    if (!Array.isArray(u.windows) || u.windows.length === 0) {\n      bad(`.providers.${name}.usage.windows`, \"must be a non-empty array\");\n    }\n    const windows: UsageWindow[] = [];\n    for (const [i, wRaw] of (u.windows as unknown[]).entries()) {\n      const wpath = `.providers.${name}.usage.windows[${i}]`;\n      if (typeof wRaw !== \"object\" || wRaw === null || Array.isArray(wRaw)) {\n        bad(wpath, \"must be an object\");\n      }\n      const w = wRaw as Record<string, unknown>;\n      const wname = str(w.name);\n      if (!wname) bad(`${wpath}.name`, \"is required\");\n      const percent = str(w.percent);\n      if (!percent) bad(`${wpath}.percent`, \"is required (a jq expression yielding 0\u2013100)\");\n      const resetUnit = w.resetUnit === undefined ? undefined : str(w.resetUnit);\n      if (resetUnit !== undefined && resetUnit !== \"ms\" && resetUnit !== \"s\" && resetUnit !== \"iso\") {\n        bad(`${wpath}.resetUnit`, `must be \"ms\", \"s\" or \"iso\"`);\n      }\n      windows.push({\n        name: wname,\n        percent,\n        ...(str(w.resetAt) ? { resetAt: str(w.resetAt) } : {}),\n        ...(resetUnit ? { resetUnit } : {}),\n      });\n    }\n    const ttlSeconds = u.ttlSeconds === undefined ? undefined : u.ttlSeconds;\n    if (ttlSeconds !== undefined && (typeof ttlSeconds !== \"number\" || ttlSeconds <= 0)) {\n      bad(`.providers.${name}.usage.ttlSeconds`, \"must be a positive number of seconds\");\n    }\n    usage = {\n      url: usageUrl,\n      ...(str(u.authHeader) ? { authHeader: str(u.authHeader) } : {}),\n      ...(str(u.label) ? { label: str(u.label) } : {}),\n      windows,\n      ...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n    };\n  }\n\n  return {\n    enabled: p.enabled === undefined ? true : p.enabled === true,\n    protocol,\n    baseUrl,\n    keyFile,\n    ...(key ? { key } : {}),\n    models,\n    ...(modelTiers ? { modelTiers } : {}),\n    env,\n    ...(str(p.note) ? { note: str(p.note) } : {}),\n    ...(p.router === false ? { router: false } : {}),\n    ...(str(p.contract) ? { contract: str(p.contract) } : {}),\n    ...(upstreamUrl ? { upstreamUrl } : {}),\n    ...(engine !== \"claude\" ? { engine } : {}),\n    ...(str(p.quotaProbe) ? { quotaProbe: str(p.quotaProbe) } : {}),\n    ...(p.workerModel === \"fast\" ? { workerModel: \"fast\" as const } : {}),\n    ...(quotaSkipAt !== undefined ? { quotaSkipAt } : {}),\n    ...(contextWindow !== undefined ? { contextWindow } : {}),\n    ...(thinking !== undefined ? { thinking } : {}),\n    ...(p.maxConcurrent !== undefined ? { maxConcurrent: p.maxConcurrent as number } : {}),\n    ...(costTier !== undefined ? { costTier } : {}),\n    ...(tags ? { tags } : {}),\n    ...(usage ? { usage } : {}),\n  };\n}\n\n/**\n * Parse a `classes` (or legacy `roles`) value shared by JSON and YAML\n * loading. `badPathPrefix` names the field in error messages, e.g. \".classes\".\n */\nexport function parseClassesValue(\n  classesRaw: unknown,\n  badPathPrefix: string\n): Record<string, ClassTarget> {\n  const classes: Record<string, ClassTarget> = {};\n  if (classesRaw === undefined) return classes;\n  if (typeof classesRaw !== \"object\" || classesRaw === null || Array.isArray(classesRaw)) {\n    bad(badPathPrefix, \"must be an object of class \u2192 provider[/alias] or {provider, mcp, maxCostTier, requireTags, order}\");\n  }\n  for (const [cls, target] of Object.entries(classesRaw as Record<string, unknown>)) {\n    if (typeof target === \"object\" && target !== null && !Array.isArray(target)) {\n      const o = target as Record<string, unknown>;\n      const provider = str(o.provider);\n      if (o.provider !== undefined && (!provider || provider.includes(\" \"))) {\n        bad(`${badPathPrefix}.${cls}.provider`, `invalid provider \"${provider}\"`);\n      }\n      let mcp: string[] | undefined;\n      if (o.mcp !== undefined) {\n        if (!Array.isArray(o.mcp) || o.mcp.some((x) => typeof x !== \"string\")) {\n          bad(`${badPathPrefix}.${cls}.mcp`, \"must be an array of MCP server or set names\");\n        }\n        mcp = o.mcp as string[];\n      }\n      let maxCostTier: number | undefined;\n      if (o.maxCostTier !== undefined) {\n        if (\n          typeof o.maxCostTier !== \"number\" ||\n          !Number.isInteger(o.maxCostTier) ||\n          o.maxCostTier < 1 ||\n          o.maxCostTier > 5\n        ) {\n          bad(`${badPathPrefix}.${cls}.maxCostTier`, \"must be an integer 1 \u2026 5\");\n        }\n        maxCostTier = o.maxCostTier;\n      }\n      let requireTags: string[] | undefined;\n      if (o.requireTags !== undefined) {\n        if (!Array.isArray(o.requireTags) || o.requireTags.some((x) => typeof x !== \"string\")) {\n          bad(`${badPathPrefix}.${cls}.requireTags`, `must be an array of tags from: ${PROVIDER_TAGS.join(\", \")}`);\n        }\n        for (const t of o.requireTags as string[]) {\n          if (!(PROVIDER_TAGS as readonly string[]).includes(t)) {\n            bad(`${badPathPrefix}.${cls}.requireTags`, `\"${t}\" is not a tag (from: ${PROVIDER_TAGS.join(\", \")})`);\n          }\n        }\n        requireTags = o.requireTags as string[];\n      }\n      let order: string[] | undefined;\n      if (o.order !== undefined) {\n        if (!Array.isArray(o.order) || o.order.some((x) => typeof x !== \"string\")) {\n          bad(`${badPathPrefix}.${cls}.order`, \"must be an array of provider names\");\n        }\n        order = o.order as string[];\n      }\n      const obj: ClassTarget = {\n        ...(provider ? { provider } : {}),\n        ...(mcp ? { mcp } : {}),\n        ...(maxCostTier !== undefined ? { maxCostTier } : {}),\n        ...(requireTags ? { requireTags } : {}),\n        ...(order ? { order } : {}),\n      };\n      classes[cls] = Object.keys(obj).length ? obj : {};\n    } else {\n      const t = str(target);\n      if (!t || t.includes(\" \")) bad(`${badPathPrefix}.${cls}`, `invalid target \"${t}\"`);\n      classes[cls] = t;\n    }\n  }\n  return classes;\n}\n\n/**\n * Parse an `mcpSets` (or `mcp_sets`) value shared by JSON and YAML loading.\n * Always merged over DEFAULT_MCP_SETS, so an omitted `desktop` key keeps the\n * built-in clickr set (see DEFAULT_MCP_SETS).\n */\nexport function parseMcpSetsValue(raw: unknown, badPathPrefix: string): Record<string, string[]> {\n  const mcpSets: Record<string, string[]> = { ...DEFAULT_MCP_SETS };\n  if (raw === undefined) return mcpSets;\n  if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n    bad(badPathPrefix, \"must be an object of set name \u2192 [server names]\");\n  }\n  for (const [setName, servers] of Object.entries(raw as Record<string, unknown>)) {\n    if (!Array.isArray(servers) || servers.some((x) => typeof x !== \"string\")) {\n      bad(`${badPathPrefix}.${setName}`, \"must be an array of MCP server names\");\n    }\n    mcpSets[setName] = servers as string[];\n  }\n  return mcpSets;\n}\n\n/**\n * Parse a `capabilities` value shared by JSON and YAML loading: capability\n * name (open set, see isModelCapability) \u2192 provider names in preference\n * order. Provider names are not cross-checked here \u2014 a name that stops\n * existing is simply skipped by resolveCapability at resolve time, the same\n * way routing.order tolerates a removed provider.\n */\nexport function parseCapabilitiesValue(raw: unknown, badPathPrefix: string): Record<string, string[]> {\n  const capabilities: Record<string, string[]> = {};\n  if (raw === undefined) return capabilities;\n  if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n    bad(badPathPrefix, \"must be an object of capability name \u2192 [provider names]\");\n  }\n  for (const [name, order] of Object.entries(raw as Record<string, unknown>)) {\n    if (!isModelCapability(name)) {\n      bad(`${badPathPrefix}.${name}`, `\"${name}\" is not a valid capability name (must match ^[a-z][a-z0-9-]*$)`);\n    }\n    if (!Array.isArray(order) || order.length === 0 || order.some((x) => typeof x !== \"string\" || !x)) {\n      bad(`${badPathPrefix}.${name}`, \"must be a non-empty array of provider names\");\n    }\n    capabilities[name] = order as string[];\n  }\n  return capabilities;\n}\n\n/**\n * Parse and validate a raw `workers` value. Missing section \u2192 defaults.\n * Unknown-but-typed garbage \u2192 WorkersConfigError naming the offending field.\n */\nexport function parseWorkersConfig(raw: unknown): WorkersConfig {\n  const d = defaultWorkersConfig();\n  if (raw === undefined || raw === null) return d;\n  if (typeof raw !== \"object\" || Array.isArray(raw)) {\n    bad(\"\", \"section must be an object\");\n  }\n  const w = raw as Record<string, unknown>;\n\n  const providers: Record<string, WorkerProvider> = {};\n  if (w.providers !== undefined) {\n    if (typeof w.providers !== \"object\" || w.providers === null || Array.isArray(w.providers)) {\n      bad(\".providers\", \"must be an object keyed by provider name\");\n    }\n    for (const [name, p] of Object.entries(w.providers)) {\n      providers[name] = parseProvider(name, p);\n    }\n  }\n\n  // classes is canonical; a config that still carries the pre-classes `roles`\n  // key is migrated by reading it here \u2014 the next write stores only `classes`.\n  const classesRaw = w.classes !== undefined ? w.classes : w.roles;\n  const classes = parseClassesValue(classesRaw, w.classes !== undefined ? \".classes\" : \".roles\");\n\n  const mcpSets = parseMcpSetsValue(w.mcpSets, \".mcpSets\");\n  const capabilities = parseCapabilitiesValue(w.capabilities, \".capabilities\");\n\n  let pane = { ...DEFAULT_PANE };\n  if (w.pane !== undefined) {\n    if (typeof w.pane !== \"object\" || w.pane === null) bad(\".pane\", \"must be an object\");\n    const pc = w.pane as Record<string, unknown>;\n    if (pc.enabled !== undefined && typeof pc.enabled !== \"boolean\") bad(\".pane.enabled\", \"must be boolean\");\n    if (pc.fontSize !== undefined && (typeof pc.fontSize !== \"number\" || pc.fontSize <= 0)) {\n      bad(\".pane.fontSize\", \"must be a positive number of points\");\n    }\n    if (pc.autoExitSecs !== undefined && typeof pc.autoExitSecs !== \"number\") {\n      bad(\".pane.autoExitSecs\", \"must be a number\");\n    }\n    // legacy fontScale (a relative scale, superseded by fontSize) is tolerated and ignored\n    pane = {\n      enabled: pc.enabled === undefined ? DEFAULT_PANE.enabled : pc.enabled === true,\n      fontSize: pc.fontSize === undefined ? DEFAULT_PANE.fontSize : pc.fontSize,\n      autoExitSecs: pc.autoExitSecs === undefined ? DEFAULT_PANE.autoExitSecs : pc.autoExitSecs,\n    };\n  }\n\n  let routing = { ...DEFAULT_ROUTING, order: [] as string[] };\n  if (w.routing !== undefined) {\n    if (typeof w.routing !== \"object\" || w.routing === null) bad(\".routing\", \"must be an object\");\n    const r = w.routing as Record<string, unknown>;\n    if (r.order !== undefined) {\n      if (!Array.isArray(r.order) || r.order.some((x) => typeof x !== \"string\")) {\n        bad(\".routing.order\", \"must be an array of provider names\");\n      }\n      routing.order = r.order as string[];\n    }\n    if (r.cooldownMinutes !== undefined && typeof r.cooldownMinutes !== \"number\") {\n      bad(\".routing.cooldownMinutes\", \"must be a number\");\n    }\n    if (r.retryOnQuota !== undefined && typeof r.retryOnQuota !== \"boolean\") {\n      bad(\".routing.retryOnQuota\", \"must be boolean\");\n    }\n    if (r.policy !== undefined && r.policy !== \"budget\" && r.policy !== \"order\") {\n      bad(\".routing.policy\", 'must be \"budget\" or \"order\"');\n    }\n    if (r.priority !== undefined) {\n      if (!Array.isArray(r.priority) || r.priority.some((x) => typeof x !== \"string\")) {\n        bad(\".routing.priority\", \"must be an array of provider names\");\n      }\n    }\n    if (r.budgetWalls !== undefined) {\n      if (typeof r.budgetWalls !== \"object\" || r.budgetWalls === null || Array.isArray(r.budgetWalls)) {\n        bad(\".routing.budgetWalls\", \"must be an object of provider name \u2192 percent or window \u2192 percent\");\n      }\n      const okPct = (v: unknown) => typeof v === \"number\" && Number.isFinite(v) && v >= 0 && v <= 100;\n      for (const [k, v] of Object.entries(r.budgetWalls as Record<string, unknown>)) {\n        if (typeof v === \"object\" && v !== null && !Array.isArray(v)) {\n          for (const [w, pct] of Object.entries(v as Record<string, unknown>)) {\n            if (!okPct(pct)) bad(`.routing.budgetWalls.${k}.${w}`, \"must be a percent 0\u2013100\");\n          }\n        } else if (!okPct(v)) {\n          bad(`.routing.budgetWalls.${k}`, \"must be a percent 0\u2013100 or a map of window name \u2192 percent\");\n        }\n      }\n    }\n    routing = {\n      order: routing.order,\n      cooldownMinutes: r.cooldownMinutes === undefined ? DEFAULT_ROUTING.cooldownMinutes : r.cooldownMinutes,\n      retryOnQuota: r.retryOnQuota === undefined ? DEFAULT_ROUTING.retryOnQuota : r.retryOnQuota === true,\n      policy: (r.policy as WorkersRoutingConfig[\"policy\"]) ?? DEFAULT_ROUTING.policy,\n      priority: r.priority === undefined ? [] : [...(r.priority as string[])],\n      budgetWalls:\n        r.budgetWalls === undefined ? {} : ({ ...(r.budgetWalls as WorkersRoutingConfig[\"budgetWalls\"]) }),\n    };\n  }\n\n  let supervision: WorkersSupervisionConfig = { pointer: false, push: \"harness\", stallMinutes: 10 };\n  if (w.supervision !== undefined) {\n    if (typeof w.supervision !== \"object\" || w.supervision === null || Array.isArray(w.supervision)) {\n      bad(\".supervision\", \"must be an object\");\n    }\n    const sv = w.supervision as Record<string, unknown>;\n    if (sv.pointer !== undefined && typeof sv.pointer !== \"boolean\") bad(\".supervision.pointer\", \"must be a boolean\");\n    if (sv.push !== undefined && sv.push !== \"harness\" && sv.push !== \"all\") bad(\".supervision.push\", 'must be \"harness\" or \"all\"');\n    if (sv.stallMinutes !== undefined && !(typeof sv.stallMinutes === \"number\" && Number.isFinite(sv.stallMinutes) && sv.stallMinutes > 0)) {\n      bad(\".supervision.stallMinutes\", \"must be a positive number\");\n    }\n    supervision = {\n      pointer: sv.pointer === true,\n      push: sv.push === \"all\" ? \"all\" : \"harness\",\n      stallMinutes: typeof sv.stallMinutes === \"number\" ? sv.stallMinutes : 10,\n    };\n  }\n\n  let tree = { ...DEFAULT_TREE };\n  if (w.tree !== undefined) {\n    if (typeof w.tree !== \"object\" || w.tree === null || Array.isArray(w.tree)) {\n      bad(\".tree\", \"must be an object\");\n    }\n    const t = w.tree as Record<string, unknown>;\n    if (t.maxDepth !== undefined) {\n      if (typeof t.maxDepth !== \"number\" || !Number.isInteger(t.maxDepth) || t.maxDepth < 0) {\n        bad(\".tree.maxDepth\", \"must be a non-negative integer\");\n      }\n    }\n    if (t.maxChildren !== undefined) {\n      if (typeof t.maxChildren !== \"number\" || !Number.isInteger(t.maxChildren) || t.maxChildren < 1) {\n        bad(\".tree.maxChildren\", \"must be a positive integer\");\n      }\n    }\n    tree = {\n      maxDepth: t.maxDepth === undefined ? DEFAULT_TREE.maxDepth : t.maxDepth,\n      maxChildren: t.maxChildren === undefined ? DEFAULT_TREE.maxChildren : t.maxChildren,\n    };\n  }\n\n  let pool = { ...DEFAULT_POOL };\n  if (w.pool !== undefined) {\n    if (typeof w.pool !== \"object\" || w.pool === null || Array.isArray(w.pool)) {\n      bad(\".pool\", \"must be an object\");\n    }\n    const pc = w.pool as Record<string, unknown>;\n    if (pc.enabled !== undefined && typeof pc.enabled !== \"boolean\") bad(\".pool.enabled\", \"must be boolean\");\n    if (pc.native !== undefined && typeof pc.native !== \"boolean\") bad(\".pool.native\", \"must be boolean\");\n    if (pc.maxSlots !== undefined) {\n      if (typeof pc.maxSlots !== \"number\" || !Number.isInteger(pc.maxSlots) || pc.maxSlots < 1 || pc.maxSlots > 8) {\n        bad(\".pool.maxSlots\", \"must be an integer 1 \u2026 8\");\n      }\n    }\n    if (pc.idleSecs !== undefined) {\n      if (typeof pc.idleSecs !== \"number\" || !Number.isInteger(pc.idleSecs) || pc.idleSecs < 0) {\n        bad(\".pool.idleSecs\", \"must be a non-negative integer (seconds, 0 = never)\");\n      }\n    }\n    if (pc.port !== undefined) {\n      if (typeof pc.port !== \"number\" || !Number.isInteger(pc.port) || pc.port <= 0 || pc.port > 65535) {\n        bad(\".pool.port\", \"must be a TCP port (1 \u2026 65535)\");\n      }\n    }\n    pool = {\n      enabled: pc.enabled === undefined ? DEFAULT_POOL.enabled : pc.enabled === true,\n      maxSlots: pc.maxSlots === undefined ? DEFAULT_POOL.maxSlots : pc.maxSlots,\n      idleSecs: pc.idleSecs === undefined ? DEFAULT_POOL.idleSecs : pc.idleSecs,\n      port: pc.port === undefined ? DEFAULT_POOL.port : pc.port,\n      native: pc.native === undefined ? DEFAULT_POOL.native : pc.native === true,\n    };\n  }\n\n  let cacheKeepaliveSecs = DEFAULT_CACHE_KEEPALIVE_SECS;\n  if (w.cacheKeepaliveSecs !== undefined) {\n    if (\n      typeof w.cacheKeepaliveSecs !== \"number\" ||\n      !Number.isInteger(w.cacheKeepaliveSecs) ||\n      w.cacheKeepaliveSecs < 0\n    ) {\n      bad(\".cacheKeepaliveSecs\", \"must be a non-negative integer (seconds, 0 = off)\");\n    }\n    cacheKeepaliveSecs = w.cacheKeepaliveSecs;\n  }\n\n  let defaultMaxMinutes = DEFAULT_MAX_MINUTES;\n  if (w.defaultMaxMinutes !== undefined) {\n    if (typeof w.defaultMaxMinutes !== \"number\" || !Number.isInteger(w.defaultMaxMinutes) || w.defaultMaxMinutes < 0) {\n      bad(\".defaultMaxMinutes\", \"must be a non-negative integer (minutes, 0 = no limit)\");\n    }\n    defaultMaxMinutes = w.defaultMaxMinutes;\n  }\n  const stagger = { ...DEFAULT_STAGGER };\n  if (w.stagger !== undefined) {\n    if (typeof w.stagger !== \"object\" || w.stagger === null || Array.isArray(w.stagger)) {\n      bad(\".stagger\", \"must be an object\");\n    }\n    const t = w.stagger as Record<string, unknown>;\n    if (t.enabled !== undefined) {\n      if (typeof t.enabled !== \"boolean\") bad(\".stagger.enabled\", \"must be true or false\");\n      stagger.enabled = t.enabled as boolean;\n    }\n    for (const k of [\"localDefaultSec\", \"remoteDefaultSec\", \"minSec\", \"maxSec\"] as const) {\n      if (t[k] === undefined) continue;\n      if (typeof t[k] !== \"number\" || !Number.isFinite(t[k]) || (t[k] as number) < 0) {\n        bad(`.stagger.${k}`, \"must be a non-negative number of seconds\");\n      }\n      stagger[k] = t[k] as number;\n    }\n  }\n  const capacity = { ...DEFAULT_CAPACITY };\n  if (w.capacity !== undefined) {\n    if (typeof w.capacity !== \"object\" || w.capacity === null || Array.isArray(w.capacity)) {\n      bad(\".capacity\", \"must be an object\");\n    }\n    const t = w.capacity as Record<string, unknown>;\n    if (t.enabled !== undefined) {\n      if (typeof t.enabled !== \"boolean\") bad(\".capacity.enabled\", \"must be true or false\");\n      capacity.enabled = t.enabled as boolean;\n    }\n  }\n  const localIdle = { ...DEFAULT_LOCAL_IDLE };\n  if (w.localIdle !== undefined) {\n    if (typeof w.localIdle !== \"object\" || w.localIdle === null || Array.isArray(w.localIdle)) {\n      bad(\".localIdle\", \"must be an object\");\n    }\n    const t = w.localIdle as Record<string, unknown>;\n    if (t.enabled !== undefined) {\n      if (typeof t.enabled !== \"boolean\") bad(\".localIdle.enabled\", \"must be true or false\");\n      localIdle.enabled = t.enabled as boolean;\n    }\n    for (const k of [\"gpuBelowPct\", \"minutes\", \"backoffMinutes\"] as const) {\n      if (t[k] === undefined) continue;\n      if (typeof t[k] !== \"number\" || !Number.isFinite(t[k]) || (t[k] as number) <= 0) bad(`.localIdle.${k}`, \"must be a positive number\");\n      localIdle[k] = t[k] as number;\n    }\n  }\n  if (w.noBrowserByDefault !== undefined && typeof w.noBrowserByDefault !== \"boolean\") {\n    bad(\".noBrowserByDefault\", \"must be true or false\");\n  }\n  const noBrowserByDefault = w.noBrowserByDefault === true;\n  const positive = (key: string, v: unknown, dflt: number): number => {\n    if (v === undefined) return dflt;\n    if (typeof v !== \"number\" || !Number.isFinite(v) || v <= 0) bad(`.${key}`, \"must be a positive number\");\n    return v as number;\n  };\n  const gcOlderThanHours = positive(\"gcOlderThanHours\", w.gcOlderThanHours, DEFAULT_GC_OLDER_THAN_HOURS);\n  const worktreesMaxGb = positive(\"worktreesMaxGb\", w.worktreesMaxGb, DEFAULT_WORKTREES_MAX_GB);\n  if (w.reaper !== undefined && typeof w.reaper !== \"boolean\") {\n    bad(\".reaper\", \"must be true or false\");\n  }\n  const reaper = w.reaper !== false;\n\n  if (w.caveman !== undefined && typeof w.caveman !== \"boolean\") {\n    bad(\".caveman\", \"must be true or false\");\n  }\n  const caveman = w.caveman === true;\n\n  const active = w.active === undefined || w.active === null ? null : str(w.active);\n  if (active !== null && active !== \"auto\" && !(active in providers)) {\n    // Tolerated at parse time (a provider may have been removed while active\n    // still names it) but every consumer resolves it to a clear error.\n  }\n\n  let fallback: WorkersFallback | null = null;\n  if (w.fallback !== undefined && w.fallback !== null) {\n    if (typeof w.fallback !== \"object\" || Array.isArray(w.fallback)) {\n      bad(\".fallback\", \"must be an object (written by `pai worker fallback on`)\");\n    }\n    const f = w.fallback as Record<string, unknown>;\n    const provider = str(f.provider);\n    if (!provider) bad(\".fallback.provider\", \"is required\");\n    const savedRaw = f.saved;\n    if (typeof savedRaw !== \"object\" || savedRaw === null || Array.isArray(savedRaw)) {\n      bad(\".fallback.saved\", \"must be an object\");\n    }\n    const s = savedRaw as Record<string, unknown>;\n    if (typeof s.env !== \"object\" || s.env === null || Array.isArray(s.env)) {\n      bad(\".fallback.saved.env\", \"must be an object of env key \u2192 previous value or null\");\n    }\n    const env: Record<string, string | null> = {};\n    for (const [k, v] of Object.entries(s.env as Record<string, unknown>)) {\n      env[k] = v === null || typeof v === \"string\" ? (v as string | null) : null;\n    }\n    if (s.model !== null && s.model !== undefined && typeof s.model !== \"string\") {\n      bad(\".fallback.saved.model\", \"must be a string or null\");\n    }\n    fallback = {\n      provider,\n      saved: {\n        env,\n        model: typeof s.model === \"string\" ? s.model : null,\n        envExisted: s.envExisted === true,\n      },\n      on: str(f.on) || new Date(0).toISOString(),\n    };\n  }\n\n  return {\n    enabled: w.enabled === undefined ? d.enabled : w.enabled === true,\n    active,\n    providers,\n    classes,\n    mcpSets,\n    capabilities,\n    pane,\n    logDir: str(w.logDir) || d.logDir,\n    routing,\n    tree,\n    supervision,\n    pool,\n    cacheKeepaliveSecs,\n    defaultMaxMinutes,\n    stagger,\n    capacity,\n    localIdle,\n    noBrowserByDefault,\n    gcOlderThanHours,\n    worktreesMaxGb,\n    reaper,\n    caveman,\n    fallback,\n    nativeModels: { ...NATIVE_ANTHROPIC_MODELS },\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Read / write\n// ---------------------------------------------------------------------------\n\nfunction workersYamlDataOf(workers: WorkersConfig): WorkersYamlData {\n  return {\n    active: workers.active,\n    providers: workers.providers,\n    classes: workers.classes,\n    mcpSets: workers.mcpSets,\n    capabilities: workers.capabilities,\n    nativeModels: workers.nativeModels,\n  };\n}\n\n/**\n * Read the whole config file and return (raw, workers) \u2014 the raw record so\n * callers can rewrite it preserving every other section, the parsed+validated\n * workers section. Unreadable config throws, missing is fine. `path`\n * overrides the config location (tests, CLAUDE_SETTINGS_PATH-style dry runs);\n * default CONFIG_FILE (see paiConfigFilePath). Reads via readMainConfigRaw,\n * so a config.yaml next to `path` is preferred over JSON, same as every\n * other main-config reader.\n *\n * Providers, classes, mcpSets and active load from workers.yaml (next to\n * `path`) when it exists; otherwise they fall back to the JSON `workers`\n * section, and then to built-in defaults \u2014 nothing breaks before migration\n * (`pai worker config migrate`). Everything else (pane, routing, tree,\n * cacheKeepaliveSecs, fallback, enabled, logDir) always comes from the main\n * config (JSON or YAML).\n */\nexport function readWorkersSection(path: string = CONFIG_FILE): {\n  raw: Record<string, unknown>;\n  workers: WorkersConfig;\n} {\n  const raw = readMainConfigRaw(path);\n  const workers = parseWorkersConfig(raw.workers);\n  const yaml = readWorkersYaml(workersYamlPath());\n  if (yaml) {\n    workers.active = yaml.data.active;\n    workers.providers = yaml.data.providers;\n    workers.classes = yaml.data.classes;\n    workers.mcpSets = yaml.data.mcpSets;\n    workers.capabilities = yaml.data.capabilities;\n    workers.nativeModels = yaml.data.nativeModels;\n  }\n  return { raw, workers };\n}\n\n/**\n * Write the workers section back, atomically. When workers.yaml exists, its\n * providers/classes/mcpSets/active are synced there (comment-preserving,\n * only the entries that changed are touched) and stripped from the JSON\n * `workers` section; everything else still writes to JSON as before.\n */\nexport function writeWorkersSection(\n  raw: Record<string, unknown>,\n  workers: WorkersConfig,\n  path: string = CONFIG_FILE\n): void {\n  const yamlPath = workersYamlPath();\n  const usingYaml = existsSync(yamlPath);\n  if (usingYaml) {\n    writeWorkersYaml(yamlPath, workersYamlDataOf(workers));\n  }\n  const jsonWorkers = usingYaml\n    ? {\n        enabled: workers.enabled,\n        pane: workers.pane,\n        logDir: workers.logDir,\n        routing: workers.routing,\n        tree: workers.tree,\n        supervision: workers.supervision,\n        pool: workers.pool,\n        cacheKeepaliveSecs: workers.cacheKeepaliveSecs,\n        defaultMaxMinutes: workers.defaultMaxMinutes,\n        stagger: workers.stagger,\n        capacity: workers.capacity,\n        localIdle: workers.localIdle,\n        noBrowserByDefault: workers.noBrowserByDefault,\n        gcOlderThanHours: workers.gcOlderThanHours,\n        worktreesMaxGb: workers.worktreesMaxGb,\n        reaper: workers.reaper,\n        caveman: workers.caveman,\n        ...(workers.fallback ? { fallback: workers.fallback } : {}),\n      }\n    : workers.fallback\n      ? workers\n      : { ...workers, fallback: undefined };\n  raw.workers = jsonWorkers;\n  writeMainConfigRaw(raw, path);\n}\n\n/** Expand a leading ~ (config values are written with `~` to stay portable). */\nexport { expandHome };\n\n// ---------------------------------------------------------------------------\n// Runnability checks (proxy / codex providers included)\n// ---------------------------------------------------------------------------\n\nexport function assertProviderRunnable(name: string, p: WorkerProvider): void {\n  if (p.protocol === \"openai\" && !p.upstreamUrl) {\n    throw new WorkersConfigError(\n      `provider \"${name}\" uses protocol \"openai\" but has no upstreamUrl \u2014 ` +\n        `set its Chat Completions base (e.g. \"https://api.openai.com/v1\") ` +\n        `so the PAI proxy knows where to translate to.`\n    );\n  }\n}\n\n/** Where a provider's key file lives, or null. Shared by run + test + add. */\nexport function providerKeyPath(p: WorkerProvider): string | null {\n  return p.keyFile ? expandHome(p.keyFile) : null;\n}\n\n/**\n * The provider's auth token: inline `key` wins when set, else `key_file`\n * read and trimmed, else null (native/local providers use the \"local\"\n * placeholder). The one place a provider's key value is resolved \u2014 every\n * spawn path (buildRunEnv, the codex runner, the openai proxy, the\n * machine-wide fallback) reads through this instead of its own\n * readFileSync, so inline keys work everywhere a key file already did.\n */\nexport function resolveProviderKey(p: WorkerProvider): string | null {\n  if (p.key) return p.key;\n  const keyPath = providerKeyPath(p);\n  if (!keyPath) return null;\n  let content: string;\n  try {\n    content = readFileSync(keyPath, \"utf8\");\n  } catch {\n    throw new WorkersConfigError(`key file not readable: ${keyPath}`);\n  }\n  const token = content.trim();\n  if (!token) throw new WorkersConfigError(`key file is empty: ${keyPath}`);\n  return token;\n}\n\n/** `key: ****` + last 4 chars \u2014 never the full value. Used everywhere a\n *  provider's key would otherwise be echoed (`providers`, `config check`). */\nexport function maskKey(key: string): string {\n  return `****${key.slice(-4)}`;\n}\n\nexport { DEFAULT_CONTEXT_WINDOW } from \"../utils/model-window.js\";\n\n/** Cost tier of a provider for class filtering (unset = 3, the middle). */\nexport function providerCostTier(p: WorkerProvider): number {\n  return p.costTier ?? DEFAULT_COST_TIER;\n}\n\n/**\n * The model id for a capability: the provider's preference for it, else its\n * default model \u2014 the one resolution rule every consumer (image class, fast\n * tier spawns, env pins) goes through.\n */\nexport function resolveModelCapability(p: WorkerProvider, capability: ModelCapability): string {\n  return p.models[capability] ?? p.models.default;\n}\n\n/** Runner behind a provider, defaulting to the Claude Code harness. */\nfunction providerEngine(p: WorkerProvider): WorkerEngine {\n  return p.engine ?? \"claude\";\n}\n\n/** Does this provider name a model for the capability (\"default\" always counts)? */\nfunction providerDeclaresCapability(p: WorkerProvider, capability: string): boolean {\n  return capability === \"default\" || p.models[capability] !== undefined;\n}\n\n/** A provider usable for this capability: declares it, enabled, and passes assertProviderRunnable. */\nfunction providerUsableForCapability(p: WorkerProvider | undefined, capability: string): p is WorkerProvider {\n  if (!p || !p.enabled || !providerDeclaresCapability(p, capability)) return false;\n  try {\n    assertProviderRunnable(\"\", p);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nexport interface ResolvedCapability {\n  /** Provider name the capability resolved to (\"anthropic\" for the built-in). */\n  provider: string;\n  model: string;\n  engine: WorkerEngine;\n  /**\n   * True when nothing declared this capability anywhere and the result is\n   * just the active provider's default model \u2014 a caller should treat this as\n   * \"no real image/vision/\u2026 provider is configured\", not a genuine match.\n   */\n  fellBack: boolean;\n}\n\n/**\n * Cross-provider capability resolution (`workers.yaml`'s `capabilities:`\n * map): which provider+model serves a named capability (e.g. \"image\"),\n * independent of any one provider's own `models` table.\n *\n * Resolution order:\n *   1. `capabilities.<name>` (explicit preference list) \u2014 first provider\n *      that declares the capability, is enabled, and passes\n *      assertProviderRunnable.\n *   2. No preference list: the active provider, if it declares the\n *      capability.\n *   3. Still nothing: any enabled provider that declares the capability\n *      (stable order \u2014 provider names sorted, \"anthropic\" included).\n *   4. Nothing anywhere: the active provider's default model, `fellBack: true`.\n */\nexport function resolveCapability(\n  workers: Pick<WorkersConfig, \"providers\" | \"active\" | \"capabilities\" | \"nativeModels\">,\n  capability: string\n): ResolvedCapability {\n  const order = workers.capabilities[capability];\n  if (order?.length) {\n    for (const name of order) {\n      const p = getProviderOrNative(workers, name);\n      if (providerUsableForCapability(p, capability)) {\n        return { provider: name, model: resolveModelCapability(p, capability), engine: providerEngine(p), fellBack: false };\n      }\n    }\n    throw new WorkersConfigError(\n      `no configured provider for capability \"${capability}\" is usable right now ` +\n        `(checked, in order: ${order.join(\", \")}) \u2014 set one with: pai worker capability ${capability} <provider>`\n    );\n  }\n\n  const activeName = workers.active && workers.active !== \"auto\" ? workers.active : null;\n  if (activeName) {\n    const p = getProviderOrNative(workers, activeName);\n    if (providerUsableForCapability(p, capability)) {\n      return { provider: activeName, model: resolveModelCapability(p, capability), engine: providerEngine(p), fellBack: false };\n    }\n  }\n\n  const candidates: Record<string, WorkerProvider> = {\n    [ANTHROPIC_NATIVE]: nativeAnthropicProvider(workers.nativeModels),\n    ...workers.providers,\n  };\n  for (const name of Object.keys(candidates).sort()) {\n    const p = candidates[name];\n    if (providerUsableForCapability(p, capability)) {\n      return { provider: name, model: resolveModelCapability(p, capability), engine: providerEngine(p), fellBack: false };\n    }\n  }\n\n  const fallbackName = activeName ?? ANTHROPIC_NATIVE;\n  const fallback = getProviderOrNative(workers, fallbackName) ?? nativeAnthropicProvider(workers.nativeModels);\n  return {\n    provider: fallbackName,\n    model: fallback.models.default,\n    engine: providerEngine(fallback),\n    fellBack: true,\n  };\n}\n\n/**\n * Context window used by the meter when the run reports none: an explicit\n * `contextWindow` first, then whatever the default model's id declares\n * (\"[1m]\" \u2192 1,000,000), then the last-resort default.\n */\nexport function providerContextWindow(p: WorkerProvider): number {\n  return (\n    p.contextWindow ??\n    contextWindowFromModelId(p.models.default) ??\n    DEFAULT_CONTEXT_WINDOW\n  );\n}\n\n/** Directory under which inline keys (MCP `key` field) are stored, 0600. */\nexport function keysDir(): string {\n  return paiHomePath(\"keys\");\n}\n", "/**\n * worker-id.ts \u2014 \"which job am I\", also inside a pooled slot.\n *\n * A pool slot serves many jobs, so it has no static PAI_WORKER_ID. The host\n * gives it a stable PAI_POOL_SLOT=<name> and records the job it hands the slot\n * in <logDir>/slots/<name>.current; this resolver reads that back.\n */\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { readWorkersSection } from \"./config.js\";\nimport { slotCurrentPath, workersLogDir } from \"./paths.js\";\nimport { isTerminal, loadStatus } from \"./status.js\";\n\nexport const POOL_SLOT_ENV = \"PAI_POOL_SLOT\";\n\n/** PAI_WORKER_ID, else the job the pool slot named by PAI_POOL_SLOT serves, else undefined. */\nexport function currentWorkerId(env: NodeJS.ProcessEnv = process.env, logDir?: string): string | undefined {\n  if (env.PAI_WORKER_ID) return env.PAI_WORKER_ID;\n  const slot = env[POOL_SLOT_ENV];\n  if (!slot) return undefined;\n  try {\n    const dir = logDir ?? workersLogDir(readWorkersSection().workers);\n    const path = slotCurrentPath(dir, slot);\n    if (!existsSync(path)) return undefined;\n    const id = readFileSync(path, \"utf8\").trim();\n    if (!id) return undefined;\n    // a file left by a crashed host points at a job that is over (or never existed)\n    const st = loadStatus(dir, id);\n    return !st || isTerminal(st.state) ? undefined : id;\n  } catch {\n    return undefined;\n  }\n}\n\nexport function setSlotCurrent(logDir: string, slot: string, id: string): void {\n  const path = slotCurrentPath(logDir, slot);\n  mkdirSync(dirname(path), { recursive: true });\n  writeFileSync(path, id + \"\\n\", \"utf8\");\n}\n\nexport function clearSlotCurrent(logDir: string, slot: string): void {\n  rmSync(slotCurrentPath(logDir, slot), { force: true });\n}\n", "/**\n * scope.ts \u2014 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 \u2192 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 \u2014 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 \u2014\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 { currentWorkerId } from \"./worker-id.js\";\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 \u2014 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 \u2014\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  // keyed by the term asked about, not the caller's env: siblings of one orchestrator share a scope\n  return tabKey(term) || 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 \"?\" \u2014 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 \u2014 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 \u2014 the payload's session_id and\n * the tab's ITERM_SESSION_ID \u2014 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  /** Working directory the entry was recorded for; absent on legacy entries, whose map key is the cwd. */\n  cwd?: string;\n  /**\n   * The tab's ITERM_SESSION_ID, when the status line could see one \u2014 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 \u2014 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  // one entry per (cwd, session): two sessions in the same directory must not overwrite each other\n  const key = `${cwd}\\u0000${session}`;\n  const prev = map[key];\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 [k, e] of Object.entries(map)) {\n    // a legacy cwd-keyed entry for this cwd is superseded by the per-session key\n    if (k === cwd && e.session === session) continue;\n    if (now - e.ts < SESSION_MAP_PRUNE_MS) pruned[k] = e;\n  }\n  pruned[key] = { session, ts: now, cwd, ...(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 = currentWorkerId(env, logDir);\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 map = JSON.parse(readFileSync(path, \"utf8\")) as Record<string, SessionMapEntry>;\n    const fresh = Object.entries(map)\n      .filter(([k, e]) => e.session && (e.cwd ?? k) === cwd && now - e.ts < SPAWNER_SESSION_TTL_MS)\n      .map(([, e]) => e);\n    const sessions = new Set(fresh.map((e) => e.session));\n    if (sessions.size === 1) return fresh[0].session;\n    if (sessions.size > 1) {\n      // several sessions share this directory: only the caller's own pane tells them apart\n      const uuid = itermUuid(env.ITERM_SESSION_ID ?? \"\");\n      const own = uuid ? fresh.filter((e) => e.term && itermUuid(e.term) === uuid) : [];\n      const ownSessions = new Set(own.map((e) => e.session));\n      if (ownSessions.size === 1) return own[0].session;\n      return null; // ambiguous: attribute to nobody rather than to a neighbour\n    }\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 \u2014 worker \u2192 orchestrator there, orchestrator \u2192\n * terminal here \u2014 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/**\n * Does the terminal this process runs in already carry `name` in the AIBroker\n * registry (keyed by this ITERM_SESSION_ID's UUID)? Only then is /Name skipped:\n * a new claude process usually runs in a new terminal session, whatever the\n * transcript or the old tab was called.\n */\nexport function terminalHasName(\n  name: string,\n  term: string = process.env.ITERM_SESSION_ID ?? \"\",\n  registryPath: string = AIBROKER_REGISTRY\n): boolean {\n  const uuid = itermUuid(term);\n  if (!uuid || !name) return false;\n  try {\n    const names = JSON.parse(readFileSync(registryPath, \"utf8\")) as Record<string, unknown>;\n    const have = names[uuid];\n    return typeof have === \"string\" && have.trim() === name.trim();\n  } catch {\n    return false;\n  }\n}\n", "/**\n * supervision-banner.ts \u2014 SessionStart text telling a session it is supervised.\n *\n * Tool descriptions are forgotten at compaction; the AIBroker watcher entry in\n * managers.json is the ground truth, so the harness re-injects it on startup\n * and after every compact. Silent (null) whenever there is no live entry.\n */\n\nimport { readFileSync } from \"fs\";\nimport { homedir } from \"os\";\nimport { join } from \"path\";\nimport { itermUuid } from \"../../../workers/scope.js\";\nimport { resolveHostSession } from \"../../../workers/pane.js\";\n\ninterface Manager {\n  sessionId?: string;\n  objective?: string;\n  paused?: boolean;\n  shift?: { until?: number };\n}\n\nexport const MANAGERS_PATH = join(homedir(), \".aibroker\", \"managers.json\");\n\n/** The calling session's iTerm uuid: ITERM_SESSION_ID when set, else the pane host resolver (background-daemon sessions). */\nexport async function callerItermId(\n  env: NodeJS.ProcessEnv = process.env,\n  resolve: () => Promise<{ uid: string }> = () => resolveHostSession(env),\n): Promise<string> {\n  const fromEnv = itermUuid(env.ITERM_SESSION_ID ?? \"\");\n  if (fromEnv) return fromEnv;\n  try {\n    return (await resolve()).uid;\n  } catch {\n    return \"\";\n  }\n}\n\nexport function supervisionBanner(itermId: string, managersPath: string = MANAGERS_PATH, now: number = Date.now()): string | null {\n  if (!itermId) return null;\n  try {\n    const all = JSON.parse(readFileSync(managersPath, \"utf8\")) as Record<string, Manager>;\n    const m = Object.values(all).find((e) => e && typeof e.sessionId === \"string\" && itermUuid(e.sessionId) === itermId);\n    const until = m?.shift?.until;\n    if (!m || m.paused || typeof until !== \"number\" || until <= now) return null;\n    const d = new Date(until);\n    const hhmm = `${String(d.getHours()).padStart(2, \"0\")}:${String(d.getMinutes()).padStart(2, \"0\")}`;\n    const objective = (m.objective ?? \"\").slice(0, 120);\n    return [\n      `SUPERVISION ACTIVE until ${hhmm} (this survives compaction \u2014 it is your ground truth, not memory). Objective: ${objective}.`,\n      \"To re-arm NOW after a lapse: run `aibroker arm-self` \u2014 no arguments, it reads this watcher and forces the arm; safe mid-turn.\",\n      \"NEVER ask the operator to type /goal or arm anything for you.\",\n    ].join(\"\\n\");\n  } catch {\n    return null;\n  }\n}\n\nexport async function supervisionBannerForSession(env: NodeJS.ProcessEnv = process.env): Promise<string | null> {\n  return supervisionBanner(await callerItermId(env));\n}\n", "/**\n * pane.ts \u2014 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 \u2014 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 \u2014 iTerm creates the new session already\n * running it \u2014 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 \u2014 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 \u2014\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 \u2014\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 { ledgerPath, panesDir, statusPath } from \"./paths.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { paiHomePath } from \"../config/pai-home.js\";\nimport { itermUuid, scopeKey } from \"./scope.js\";\nimport { psTable, psTableOrNull } from \"./ps-table.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    function arr(f) { try { return f() || []; } catch (e) { return []; } }\n    var ws = arr(function () { return app.windows(); });\n    for (var i = 0; i < ws.length; i++) {\n        if (!ws[i]) continue;\n        var ts = arr(function () { return ws[i].tabs(); });\n        for (var j = 0; j < ts.length; j++) {\n            if (!ts[j]) continue;\n            var ss = arr(function () { return ts[j].sessions(); });\n            for (var k = 0; k < ss.length; k++) {\n                try { if (ss[k] && ss[k].id() === id) return { w: ws[i], t: ts[j], s: ss[k], all: ss }; } catch (e) {}\n            }\n        }\n    }\n    return null;\n}`;\n\n// A split or new tab makes the new session the active one. Capture what was\n// active before and put it back afterwards, so typing focus stays with the\n// operator's session. Only restores a selection inside the app's own window:\n// never activates iTerm, raises a window or touches the frontmost app.\nconst CAPTURE_ACTIVE = `var prevTab = null, prevSess = null;\n    try { prevTab = f.w.currentTab(); prevSess = prevTab.currentSession(); } catch (e) {}`;\nconst RESTORE_ACTIVE = `try {\n        if (prevTab && f.w.currentTab().id() !== prevTab.id()) prevTab.select();\n        if (prevSess && prevTab.currentSession().id() !== prevSess.id()) prevSess.select();\n    } catch (e) {}`;\n\n// Smallest height a follow pane may be split down to (the visible-pane gate\n// refuses anything under 6 rows).\nconst MIN_ROWS = 6;\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// \"id<TAB>columns<TAB>rows\" of every session in the launching session's tab.\nexport const TAB_SIZES_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.id() + \"\\\\t\" + o.columns() + \"\\\\t\" + o.rows(); }).join(\"\\\\n\");\n}`;\n\nexport interface TabPane {\n  id: string;\n  cols: number;\n  rows: number;\n}\nexport interface SplitChoice {\n  id: string;\n  dir: \"h\" | \"v\";\n  cols: number;\n  rows: number;\n}\n\nconst MIN_COLS = 40;\n\n/** Smaller half of a split: \"h\" stacks top/bottom, \"v\" puts the halves side by side (one cell for the divider). */\nfunction halves(p: TabPane, dir: \"h\" | \"v\"): { cols: number; rows: number } {\n  return dir === \"h\" ? { cols: p.cols, rows: Math.floor((p.rows - 1) / 2) } : { cols: Math.floor((p.cols - 1) / 2), rows: p.rows };\n}\nconst fits = (h: { cols: number; rows: number }) => h.cols >= MIN_COLS && h.rows >= MIN_ROWS;\n\n/**\n * Pick the pane to split and the direction from the tab's real sizes: the pane\n * with the most cells whose result keeps >= 40x6, never the host's own pane\n * while another fits. Side by side only if both halves keep >= 80 columns or\n * the pane is wider than 3.5x its height in cells (a cell is ~2x taller than wide), else stacked; the other direction is\n * the fallback when the preferred one leaves too small a pane. null = nothing fits.\n */\nexport function pickSplit(panes: TabPane[], hostId: string): (SplitChoice & { small: boolean }) | null {\n  const options: Array<SplitChoice & { host: boolean; cells: number; small: boolean }> = [];\n  for (const p of panes) {\n    const wide = Math.floor((p.cols - 1) / 2) >= 80 || p.cols > 3.5 * p.rows;\n    const order: Array<\"h\" | \"v\"> = wide ? [\"v\", \"h\"] : [\"h\", \"v\"];\n    const dir = order.find((d) => fits(halves(p, d)));\n    if (dir) options.push({ id: p.id, dir, ...halves(p, dir), host: p.id === hostId, cells: p.cols * p.rows, small: false });\n  }\n  const pool = options.some((o) => !o.host) ? options.filter((o) => !o.host) : options;\n  pool.sort((a, b) => b.cells - a.cells);\n  if (pool[0]) return pool[0];\n  // nothing reaches the minimum: a small pane never stops a start, so take the split whose smaller half is largest\n  let best: (SplitChoice & { small: boolean; score: number }) | null = null;\n  for (const p of panes) {\n    for (const d of [\"h\", \"v\"] as const) {\n      const h = halves(p, d);\n      const score = Math.min(h.cols / MIN_COLS, h.rows / MIN_ROWS) - (p.id === hostId ? 0.001 : 0);\n      if (h.cols > 0 && h.rows > 0 && (!best || score > best.score)) best = { id: p.id, dir: d, ...h, small: true, score };\n    }\n  }\n  return best ? { id: best.id, dir: best.dir, cols: best.cols, rows: best.rows, small: true } : null;\n}\n\n// One pane per worker: split the pane pickSplit chose (argv[4]) side by side\n// (\"v\") or stacked (\"h\", argv[5]). 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}\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    ${CAPTURE_ACTIVE}\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    // the pane and direction were chosen in TypeScript from the tab's real sizes (pickSplit)\n    var splitS = f.all[sessIDs.indexOf(argv[4])] || f.s;\n    var stack = argv[5] === \"h\";\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 \u2014 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 (!stack) {\n        newS = profileName === \"\"\n            ? splitS.splitVerticallyWithDefaultProfile({ command: followCmd })\n            : splitS.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    ${RESTORE_ACTIVE}\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 \u2014 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    ${CAPTURE_ACTIVE}\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    ${RESTORE_ACTIVE}\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. */\nexport { osascript as osascriptJxa };\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 psTable(format);\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 \u2014 `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 \u2014 \"Invalid object in plist\n * for JSON format\" \u2014 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 \u2014 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 \u2192 \"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 \u2014 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 \u2014 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}) \u2014 ${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  /** iTerm uuid of the session that started the process: its tab is where the pane must be */\n  host?: 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\nexport function 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, ...(e.host ? { host: e.host } : {}) }, 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    function arr(f) { try { return f() || []; } catch (e) { return []; } }\n    var out = \"\";\n    arr(function () { return app.windows(); }).forEach(function (w) {\n        if (!w) return;\n        arr(function () { return w.tabs(); }).forEach(function (t) {\n            if (!t) return;\n            arr(function () { return t.sessions(); }).forEach(function (s) {\n                if (!s) return;\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 twice. */\nexport async function listItermSessions(\n  run: (script: string, args: string[]) => Promise<{ stdout: string }> = osascript\n): Promise<ItermSession[]> {\n  let p: { stdout: string };\n  try {\n    p = await run(LIST_SESSIONS_SCRIPT, []);\n  } catch {\n    await new Promise((r) => setTimeout(r, 300)); // a window may be mid-close; one retry\n    p = await run(LIST_SESSIONS_SCRIPT, []);\n  }\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 \u2014\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 \u2026; \u2026` 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 \u2014 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; rejects when the listing fails. */\n  listSessions(): Promise<ItermSession[]>;\n  /** Pids of `claude attach <id>` client processes for a Claude session (short or full id). */\n  attachClients?(cid: string): number[];\n  /** ITERM_SESSION_ID in a process's environment, \"\" when unreadable or unset. */\n  itermIdOf?(pid: number): string;\n  /** iTerm session uid the AIBroker registry records for a Claude session id, \"\" when none. */\n  registryHost?(cid: string): string;\n  /** iTerm session uid the statusline recorded for a Claude session id (short or full) in session-claude-map.json, \"\" when none. */\n  mapHost?(cid: string): string;\n}\n\n/** Entries older than this are dropped by the statusline writer and ignored here. */\nexport const SESSION_MAP_MAX_AGE_MS = 7 * 24 * 3600_000;\n\n/** The iTerm uid recorded for a Claude session id (prefix match for short ids), newest entry wins, \"\" when none or stale. */\nexport function lookupSessionMap(mapPath: string, cid: string, now: number = Date.now()): string {\n  try {\n    const m = JSON.parse(readFileSync(mapPath, \"utf8\")) as Record<string, { uid?: unknown; ts?: unknown }>;\n    const hits = Object.entries(m)\n      .filter(([k, v]) => (k === cid || (cid.length >= 8 && k.startsWith(cid))) && typeof v?.uid === \"string\" && typeof v.ts === \"number\" && now - v.ts < SESSION_MAP_MAX_AGE_MS)\n      .sort((a, b) => (b[1].ts as number) - (a[1].ts as number));\n    return (hits[0]?.[1].uid as string | undefined) ?? \"\";\n  } catch {\n    return \"\";\n  }\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    return listItermSessions(); // rejects on failure: resolveHostSession reports why\n  },\n  attachClients(cid) {\n    const short = cid.slice(0, 8);\n    const re = new RegExp(`\\\\battach\\\\s+(${short}|${cid})\\\\b`, \"i\");\n    let out = \"\";\n    try {\n      out = psTable(\"pid=,command=\");\n    } catch {\n      return [];\n    }\n    return out.split(\"\\n\").flatMap((l) => {\n      const r = /^\\s*(\\d+)\\s+(.*)$/.exec(l);\n      return r && re.test(r[2]) && !/--bg-pty-host/.test(r[2]) ? [Number(r[1])] : [];\n    });\n  },\n  itermIdOf(pid) {\n    try {\n      const out = execFileSync(\"ps\", [\"eww\", \"-o\", \"command=\", \"-p\", String(pid)], { encoding: \"utf8\", env: { ...process.env, LC_ALL: \"C\" } });\n      return /\\bITERM_SESSION_ID=(\\S+)/.exec(out)?.[1] ?? \"\";\n    } catch {\n      return \"\";\n    }\n  },\n  mapHost(cid) {\n    return lookupSessionMap(paiHomePath(\"session-claude-map.json\"), cid);\n  },\n  registryHost(cid) {\n    try {\n      const dir = join(homedir(), \".aibroker\");\n      const entries = (JSON.parse(readFileSync(join(dir, \"session-restore.json\"), \"utf8\")) as { entries?: Record<string, unknown>[] }).entries ?? [];\n      const hit = entries.find((e) => [e.claudeSessionId, e.sessionId].some((v) => typeof v === \"string\" && (v === cid || v.startsWith(cid))));\n      if (!hit || typeof hit.name !== \"string\") return \"\";\n      const names = JSON.parse(readFileSync(join(dir, \"session-names.json\"), \"utf8\")) as Record<string, unknown>;\n      return Object.keys(names).find((k) => names[k] === hit.name) ?? \"\";\n    } catch {\n      return \"\";\n    }\n  },\n};\n\n/** True when the process chain from `start` passes a `--bg-pty-host` process: a session under the Claude background daemon. */\nexport function underBackgroundDaemon(deps: HostDeps = realHostDeps, start: number = process.pid): boolean {\n  const cmds = deps.commandsOf(ancestorPids(deps, start));\n  return [...cmds.values()].some((c) => /--bg-pty-host\\b/.test(c));\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 \u2026/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 *  0. map: the statusline-recorded session-claude-map.json entry for the Claude session id, when live\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 \u2014 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  let sessions: ItermSession[] = [];\n  let listFail = \"\";\n  try {\n    sessions = await deps.listSessions();\n  } catch (e) {\n    listFail = `session listing failed: ${(e as Error).message?.trim().split(\"\\n\")[0] ?? e}`;\n  }\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[] = listFail ? [listFail] : [];\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 cid = env.CLAUDE_CODE_SESSION_ID || ancestors.map((p) => claudeIdOf(cmds.get(p) ?? \"\")).find(Boolean);\n  if (cid) {\n    const mapped = deps.mapHost?.(cid) ?? \"\";\n    const mapLive = mapped && sessions.find((s) => s.id === mapped);\n    if (mapLive) return found(mapLive, \"map\");\n    notes.push(mapped ? `map: entry for ${cid.slice(0, 8)} names ${mapped}, not a live session` : `map: no entry for ${cid.slice(0, 8)}`);\n  }\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  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    for (const pid of deps.attachClients?.(cid) ?? []) {\n      const uid = itermUuid(deps.itermIdOf?.(pid) ?? \"\");\n      const live = uid && sessions.find((s) => s.id === uid);\n      if (live) return found(live, `attach client ${pid}`);\n    }\n    notes.push(\"attach client: no attach process with a live ITERM_SESSION_ID\");\n    const reg = deps.registryHost?.(cid) ?? \"\";\n    const regLive = reg && sessions.find((s) => s.id === reg);\n    if (regLive) return found(regLive, \"registry\");\n    notes.push(\"registry: no AIBroker entry for this Claude session\");\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/**\n * Worker state straight from its status file (pane.ts cannot import status.ts: it imports us).\n * Ended = any state but \"running\"/\"queued\", or a live state whose runner pid is gone (a ghost\n * that died before it could write its end state). A missing/unreadable file or a status without\n * a pid is never ended.\n */\nexport function workerEnded(logDir: string, wid: string): boolean {\n  if (wid.startsWith(\"plan-\") || wid.startsWith(\"pane-\")) return planEnded(logDir, wid);\n  try {\n    const st = JSON.parse(readFileSync(statusPath(logDir, wid), \"utf8\")) as { state?: string; pid?: number };\n    if (!st.state) return false;\n    if (st.state !== \"running\" && st.state !== \"queued\") return true;\n    if (typeof st.pid !== \"number\" || st.pid <= 0) return false;\n    try {\n      process.kill(st.pid, 0);\n      return false;\n    } catch (e) {\n      return (e as NodeJS.ErrnoException).code !== \"EPERM\";\n    }\n  } catch {\n    return false;\n  }\n}\n\n/** A plan pane is over once its run wrote an end state or its runner pid is gone. */\nfunction planEnded(logDir: string, wid: string): boolean {\n  try {\n    const st = JSON.parse(readFileSync(join(logDir, wid.startsWith(\"pane-\") ? `${wid}.panestatus` : `${wid}.planstatus`), \"utf8\")) as { state?: string; pid?: number };\n    if (!st.state) return false;\n    if (st.state !== \"running\") return true;\n    if (typeof st.pid !== \"number\" || st.pid <= 0) return false;\n    try {\n      process.kill(st.pid, 0);\n      return false;\n    } catch (e) {\n      return (e as NodeJS.ErrnoException).code !== \"EPERM\";\n    }\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. */\nexport async 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 sizes = (await osascript(TAB_SIZES_SCRIPT, [uid])).stdout.trim();\n  const panes: TabPane[] = sizes.includes(\"\\t\")\n    ? sizes.split(\"\\n\").map((l) => {\n        const [id, c, r] = l.split(\"\\t\");\n        return { id, cols: Number(c), rows: Number(r) };\n      })\n    : [];\n  const choice = panes.length ? pickSplit(panes, uid) : null;\n  if (choice?.small) {\n    process.stderr.write(`[pai worker] pane: no pane of the tab splits to ${MIN_COLS}x${MIN_ROWS}; opening a small ${choice.cols}x${choice.rows} pane\\n`);\n  }\n  const p = await osascript(WORKER_SPLIT_SCRIPT, [uid, cands, cmd, profile, choice?.id ?? \"\", choice?.dir ?? \"v\"]);\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    host: itermUuid(term) || undefined,\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 \u2014 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(\n  wid: string,\n  fontSize: number,\n  term: string,\n  env: NodeJS.ProcessEnv = process.env,\n  platform: string = process.platform\n): Promise<string> {\n  const lines = [workerPaneOpen(wid) ? `pane for ${wid} open` : `no pane for ${wid}`];\n  if (paneBackend(env, 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 \u2014 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 \u2014 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 ?? paiHomePath(\"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    var where = argv[2] || \"split\";\n    if (where !== \"window\" && 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 (where === \"split\" && ids(h.all).indexOf(argv[1]) < 0) return JSON.stringify({ fail: \"pane is not in the host's tab\" });\n    var tabs = p.w.tabs();\n    var tabIdx = 0;\n    for (var i = 0; i < tabs.length; i++) if (ids(tabs[i].sessions()).indexOf(argv[1]) >= 0) tabIdx = i + 1;\n    var selected = ids(p.w.currentTab().sessions()).indexOf(argv[1]) >= 0;\n    var b = p.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: p.w.id(), bounds: [b.x, b.y, b.width, b.height], tab: tabIdx + \"/\" + tabs.length,\n        selectedTab: selected, onScreen: onScreen, hidden: hidden, miniaturized: p.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\n// Host session's tab selection and size, read before anything is opened.\n// JSON on stdout.\nconst HOST_TAB_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 selected = h.w.currentTab().sessions().map(function (o) { return o.id(); }).indexOf(argv[0]) >= 0;\n    return JSON.stringify({ selectedTab: selected, rows: h.s.rows(), cols: h.s.columns() });\n}`;\n\n/** True unless the host session's tab is known not to be its window's selected tab. */\nasync function hostTabSelected(hostUid: string): Promise<boolean> {\n  try {\n    const p = await osascript(HOST_TAB_SCRIPT, [hostUid]);\n    return (JSON.parse(p.stdout.trim()) as { selectedTab?: boolean }).selectedTab !== false;\n  } catch {\n    return true; // unreadable: split, and let verifyPane judge the result\n  }\n}\n\n/** Where a follow pane sits: always a split of the host's tab. */\nexport type PanePlacement = \"split\";\n\nconst placements = new Map<string, PanePlacement>();\n\n/** The placement openVerifiedPane used for a worker in this process, for the status record. */\nexport function panePlacement(wid: string): PanePlacement | undefined {\n  return placements.get(wid);\n}\n\nconst MIN_PANE_ROWS = 6;\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  const table = psTableOrNull(\"tty=,command=\");\n  if (table === null) {\n    // ps failed: the follow command is unknown, never a reason to refuse a start\n    console.error(`PANE-UNVERIFIED: process table unreadable, \\`worker follow ${wid}\\` not confirmed on ${short}`);\n    return true;\n  }\n  return table\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  placement: PanePlacement = \"split\"\n): Promise<string> {\n  let last = \"\";\n  for (let i = 0; i < tries; i++) {\n    const p = await run(VERIFY_PANE_SCRIPT, [hostUid, paneUid, placement]);\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    // a small pane never stops a start: it is marked SMALL, logged, and audited as SMALL\n    const small = rows < MIN_PANE_ROWS || cols < MIN_PANE_COLS;\n    if (running(String(r.tty), wid)) return small ? `${where} SMALL (< ${MIN_PANE_COLS}x${MIN_PANE_ROWS})` : 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/** Test seams of openVerifiedPane: the real iTerm-touching steps. */\nexport interface PaneSeam {\n  split?: typeof openPaneForWorker;\n  verify?: typeof verifyPane;\n  hostSelected?: (hostUid: string) => Promise<boolean>;\n  daemon?: () => boolean;\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  ops: PaneOps = realPaneOps,\n  seam: PaneSeam = {}\n): Promise<string> {\n  const split = seam.split ?? openPaneForWorker;\n  const verify = seam.verify ?? verifyPane;\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 && !hostUid) {\n    const daemon = (seam.daemon ?? (() => underBackgroundDaemon()))();\n    throw new Error(\n      `neither TMUX nor ITERM_SESSION_ID is set: this process does not run in a terminal pane the operator sees${daemon ? \" (runs under the Claude background daemon; no attached terminal found)\" : \"\"}`\n    );\n  }\n  if (envUid && envUid !== hostUid) {\n    throw new Error(`ITERM_SESSION_ID names ${envUid}, which is not a live iTerm2 session${hostUid ? ` (host found: ${hostUid})` : \"\"}`);\n  }\n  // Every pane is a split of the host's own tab: never a window, never another tab. A host tab that is not\n  // the selected one still gets its split (the operator finds it there); only the visibility proof is skipped.\n  const hostShown = await (seam.hostSelected ?? hostTabSelected)(hostUid);\n  let r = await split(logDir, config, wid, term);\n  if (!r.session) throw new Error(`pane verification failed: ${r.message}`);\n  if (!hostShown) {\n    placements.set(wid, \"split\");\n    process.stderr.write(\"[pai worker] pane: split in the host tab (not the selected one)\\n\");\n    return `host tab not selected (iTerm session ${r.session})`;\n  }\n  try {\n    const where = await verify(hostUid, r.session, wid);\n    placements.set(wid, \"split\");\n    if (/ SMALL /.test(where)) {\n      appendLedger(ledgerPath(logDir), \"PANE-SMALL\", { id: wid, session: r.session, where });\n      process.stderr.write(`[pai worker] pane: small pane for ${wid}: ${where}\\n`);\n    }\n    return `${where} (iTerm session ${r.session})`;\n  } catch (e) {\n    await closePaneForWorker(logDir, wid, ops).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 * The one way PAI reads the whole process table. A table with a few dozen\n * Xcode swift-frontend processes (23k-99k char command lines) is over 1 MB,\n * Node's default maxBuffer, so a bare execFileSync(\"ps\", ...) died with ENOBUFS.\n * Here the buffer is large and every line is cut to MAX_LINE chars after reading\n * (the identifying words of a command come first).\n */\nimport { execFileSync } from \"node:child_process\";\n\nexport const PS_MAX_BUFFER = 64 * 1024 * 1024;\nexport const PS_MAX_LINE = 1024;\n\nexport type PsRunner = (args: string[]) => string;\n\nconst realRunner: PsRunner = (args) =>\n  execFileSync(\"ps\", args, { encoding: \"utf8\", env: { ...process.env, LC_ALL: \"C\" }, maxBuffer: PS_MAX_BUFFER, timeout: 15_000 });\n\n/** Cut each line of `text` to `max` chars. */\nexport function truncateLines(text: string, max = PS_MAX_LINE): string {\n  return text\n    .split(\"\\n\")\n    .map((l) => (l.length > max ? l.slice(0, max) : l))\n    .join(\"\\n\");\n}\n\n/** `ps -axo <format>` with a large buffer and truncated lines; throws when ps fails. */\nexport function psTable(format: string, run: PsRunner = realRunner, maxLine = PS_MAX_LINE): string {\n  return truncateLines(run([\"-axo\", format]), maxLine);\n}\n\n/** psTable that retries, then returns null (\"unknown\") instead of throwing. */\nexport function psTableOrNull(format: string, run: PsRunner = realRunner, tries = 3, maxLine = PS_MAX_LINE): string | null {\n  for (let i = 0; i < tries; i++) {\n    try {\n      return psTable(format, run, maxLine);\n    } catch {\n      /* retry */\n    }\n  }\n  return null;\n}\n", "/**\n * Worker-session detection.\n *\n * A disposable headless worker (a `claude -p` run started by an orchestrating\n * session, possibly against a different model provider with a different\n * context window) shares the project directory and the hook configuration\n * with the real session that spawned it. Left alone, the hooks treat it as a\n * session in its own right: they inject project context into it, create and\n * rename a numbered session note for it, autosave it, and enqueue a\n * model-written handover for it. Its compactions also land in the project's\n * transcript folder, where they are indistinguishable from a real session's\n * and drag the measured compaction trigger down (observed 2026-09-17: two\n * workers compacting at ~151k tokens pulled a project's trigger from ~784k\n * to ~151k, and the real session's handover fired at ~50k tokens).\n *\n * The launcher marks such sessions with `PAI_WORKER=1`. Every hook that does\n * per-session bookkeeping returns immediately when this predicate is true.\n * Deliberately NOT guarded: the security validator (a worker's shell\n * commands must still be checked) and observability capture.\n */\nexport function isWorkerSession(env: NodeJS.ProcessEnv = process.env): boolean {\n  return env.PAI_WORKER === \"1\";\n}\n", "#!/usr/bin/env node\n\n/**\n * supervision-banner.ts \u2014 SessionStart hook (matchers: startup, compact)\n *\n * Injects \"SUPERVISION ACTIVE ...\" when an AIBroker watcher for this session\n * is live. See ../lib/supervision-banner.ts. Never blocks: always exits 0.\n */\n\nimport { supervisionBannerForSession } from \"../lib/supervision-banner.js\";\nimport { isWorkerSession } from \"../lib/worker-session.js\";\n\nasync function main() {\n  if (isWorkerSession()) return;\n  const banner = await supervisionBannerForSession();\n  if (banner) console.log(banner);\n  process.exit(0);\n}\n\nmain().catch(() => {\n  process.exit(0);\n});\n"],
  "mappings": "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",
  "names": ["init_config", "res", "res", "anchors", "doc", "Scalar", "Scalar", "node", "i", "end", "Scalar", "Scalar", "Scalar", "value", "stringify", "comment", "str", "Scalar", "YAMLMap", "YAMLMap", "map", "Scalar", "seq", "Scalar", "Scalar", "Scalar", "require_schema", "Scalar", "Scalar", "Scalar", "pairs", "YAMLMap", "omap", "require_bool", "Scalar", "require_float", "Scalar", "require_int", "n", "YAMLMap", "set", "res", "require_schema", "tags", "Document", "res", "YAMLMap", "YAMLMap", "Scalar", "YAMLMap", "Scalar", "value", "end", "Scalar", "Scalar", "tag", "Document", "Document", "i", "ch", "start", "Document", "lineCounter", "parseDocument", "parse", "Document", "Scalar", "YAMLMap", "init_types", "import_yaml", "homedir", "join", "dirname", "init_types", "import_yaml", "init_config", "init_config", "init_config", "import_yaml", "existsSync", "mkdirSync", "readdirSync", "readFileSync", "init_config", "import_yaml", "init_config", "init_config", "init_config", "homedir", "join", "readFileSync", "homedir", "join", "spawn", "execFileSync", "existsSync", "mkdirSync", "readdirSync", "readFileSync", "renameSync", "writeFileSync", "homedir", "dirname", "join", "spawn", "readFileSync", "execFileSync", "join", "homedir", "why", "join", "homedir", "readFileSync"]
}
