github-router 0.3.316 → 0.3.318

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist/{aic-ledger-BS47yb_C.js → aic-ledger-DY03LbBM.js} +2 -2
  2. package/dist/{aic-ledger-BS47yb_C.js.map → aic-ledger-DY03LbBM.js.map} +1 -1
  3. package/dist/{attribution-settings-jcsSeVur.js → attribution-settings-CyyOsjPF.js} +39 -28
  4. package/dist/attribution-settings-CyyOsjPF.js.map +1 -0
  5. package/dist/{auth-JfxL9q7z.js → auth-Iun7ftd8.js} +3 -3
  6. package/dist/{auth-JfxL9q7z.js.map → auth-Iun7ftd8.js.map} +1 -1
  7. package/dist/browser-ext/manifest.json +1 -1
  8. package/dist/{check-usage-DXUEQzt_.js → check-usage-1c-0z3iy.js} +4 -4
  9. package/dist/{check-usage-DXUEQzt_.js.map → check-usage-1c-0z3iy.js.map} +1 -1
  10. package/dist/{claude-CJ4Nd2_3.js → claude-UhOOquJW.js} +92 -28
  11. package/dist/claude-UhOOquJW.js.map +1 -0
  12. package/dist/{codex-CwuLqNZF.js → codex-CyuVJFDZ.js} +5 -5
  13. package/dist/{codex-CwuLqNZF.js.map → codex-CyuVJFDZ.js.map} +1 -1
  14. package/dist/{copilot-discount-IbXR5A28.js → copilot-discount-C3jx9Q-o.js} +2 -2
  15. package/dist/{copilot-discount-IbXR5A28.js.map → copilot-discount-C3jx9Q-o.js.map} +1 -1
  16. package/dist/{debug-CA7pg8V9.js → debug-CDyXUPcC.js} +2 -2
  17. package/dist/{debug-CA7pg8V9.js.map → debug-CDyXUPcC.js.map} +1 -1
  18. package/dist/engine-D5GNXylB.js +2 -0
  19. package/dist/{gate-discovery-z04ywOzB.js → gate-discovery-B17pvgE0.js} +5 -5
  20. package/dist/{gate-discovery-z04ywOzB.js.map → gate-discovery-B17pvgE0.js.map} +1 -1
  21. package/dist/{get-copilot-usage-BMD_cJNT.js → get-copilot-usage-BWCAzmSc.js} +2 -2
  22. package/dist/{get-copilot-usage-BMD_cJNT.js.map → get-copilot-usage-BWCAzmSc.js.map} +1 -1
  23. package/dist/hooks.mjs +124 -117
  24. package/dist/hooks.sha256 +1 -1
  25. package/dist/{internal-aic-status-BN0xRLSF.js → internal-aic-status-BiblmImt.js} +13 -6
  26. package/dist/{internal-aic-status-BN0xRLSF.js.map → internal-aic-status-BiblmImt.js.map} +1 -1
  27. package/dist/{internal-artifact-open-B3stUu74.js → internal-artifact-open-CA1trJ15.js} +2 -2
  28. package/dist/{internal-artifact-open-B3stUu74.js.map → internal-artifact-open-CA1trJ15.js.map} +1 -1
  29. package/dist/{internal-fast-dispatch-guard-CAwYj_Yz.js → internal-fast-dispatch-guard-CZ4BZuAG.js} +2 -2
  30. package/dist/{internal-fast-dispatch-guard-CAwYj_Yz.js.map → internal-fast-dispatch-guard-CZ4BZuAG.js.map} +1 -1
  31. package/dist/{internal-fast-dispatch-guard-2QjUlhqO.js → internal-fast-dispatch-guard-D5kozMpo.js} +1 -1
  32. package/dist/{internal-first-mate-guard-VgnHBp0s.js → internal-first-mate-guard-B_or0eN3.js} +1 -1
  33. package/dist/{internal-first-mate-guard-B0rbLb0L.js → internal-first-mate-guard-CiFH6X1H.js} +3 -3
  34. package/dist/{internal-first-mate-guard-B0rbLb0L.js.map → internal-first-mate-guard-CiFH6X1H.js.map} +1 -1
  35. package/dist/{internal-max-dispatch-guard-Bz7a2Ntf.js → internal-max-dispatch-guard-BGOHRYRt.js} +1 -1
  36. package/dist/{internal-max-dispatch-guard-BNqQ2wMc.js → internal-max-dispatch-guard-CnBZxo5l.js} +2 -2
  37. package/dist/{internal-max-dispatch-guard-BNqQ2wMc.js.map → internal-max-dispatch-guard-CnBZxo5l.js.map} +1 -1
  38. package/dist/{internal-plan-review-QUvNZmMR.js → internal-plan-review-C73snnD8.js} +3 -3
  39. package/dist/{internal-plan-review-QUvNZmMR.js.map → internal-plan-review-C73snnD8.js.map} +1 -1
  40. package/dist/{internal-prompt-submit-fj5ccft_.js → internal-prompt-submit-dov4UvBo.js} +4 -4
  41. package/dist/{internal-prompt-submit-fj5ccft_.js.map → internal-prompt-submit-dov4UvBo.js.map} +1 -1
  42. package/dist/{internal-session-bind-0Hmak8PR.js → internal-session-bind-C3hrb9rP.js} +2 -2
  43. package/dist/{internal-session-bind-0Hmak8PR.js.map → internal-session-bind-C3hrb9rP.js.map} +1 -1
  44. package/dist/{internal-stop-hook-UN1JHtjj.js → internal-stop-hook-DZ6CCP4C.js} +5 -5
  45. package/dist/{internal-stop-hook-UN1JHtjj.js.map → internal-stop-hook-DZ6CCP4C.js.map} +1 -1
  46. package/dist/{internal-stop-review-8v2llYDB.js → internal-stop-review-MBVYtrYS.js} +2 -2
  47. package/dist/{internal-stop-review-8v2llYDB.js.map → internal-stop-review-MBVYtrYS.js.map} +1 -1
  48. package/dist/{internal-worker-guard-BI4Pl0Ew.js → internal-worker-guard-dxGawAY3.js} +2 -2
  49. package/dist/{internal-worker-guard-BI4Pl0Ew.js.map → internal-worker-guard-dxGawAY3.js.map} +1 -1
  50. package/dist/{internal-workspace-header-kg9FoNXK.js → internal-workspace-header-_aEXOz5q.js} +2 -2
  51. package/dist/{internal-workspace-header-kg9FoNXK.js.map → internal-workspace-header-_aEXOz5q.js.map} +1 -1
  52. package/dist/{lifecycle-IwotwedG.js → lifecycle-CJB2BwSJ.js} +2 -2
  53. package/dist/{lifecycle-IwotwedG.js.map → lifecycle-CJB2BwSJ.js.map} +1 -1
  54. package/dist/lifecycle-D0KsoSqz.js +2 -0
  55. package/dist/lifecycle-DimEguJO.js +2 -0
  56. package/dist/{lifecycle-Cx1wVrLb.js → lifecycle-_BE0SnNs.js} +2 -2
  57. package/dist/{lifecycle-Cx1wVrLb.js.map → lifecycle-_BE0SnNs.js.map} +1 -1
  58. package/dist/main.js +20 -20
  59. package/dist/{mcp-workspace-header-BYgWioLH.js → mcp-workspace-header-mXRLE8kg.js} +2 -2
  60. package/dist/{mcp-workspace-header-BYgWioLH.js.map → mcp-workspace-header-mXRLE8kg.js.map} +1 -1
  61. package/dist/{models-CVzyU5mJ.js → models-CSFIVxAH.js} +3 -3
  62. package/dist/{models-CVzyU5mJ.js.map → models-CSFIVxAH.js.map} +1 -1
  63. package/dist/{orchestration-Dc3mO0G3.js → orchestration-B8kypXcZ.js} +2 -2
  64. package/dist/{orchestration-Dc3mO0G3.js.map → orchestration-B8kypXcZ.js.map} +1 -1
  65. package/dist/{paths-CMxlQEfQ.js → paths-CHBAj_t9.js} +36 -4
  66. package/dist/{paths-CMxlQEfQ.js.map → paths-CHBAj_t9.js.map} +1 -1
  67. package/dist/paths-eHoOwFzZ.js +2 -0
  68. package/dist/{peer-mcp-personas-CQ-0Piyr.js → peer-mcp-personas-CRMVuMVF.js} +37 -18
  69. package/dist/{peer-mcp-personas-CQ-0Piyr.js.map → peer-mcp-personas-CRMVuMVF.js.map} +1 -1
  70. package/dist/{plan-review-hook-BIMwWkh6.js → plan-review-hook-BMmw7wxR.js} +3 -3
  71. package/dist/{plan-review-hook-BIMwWkh6.js.map → plan-review-hook-BMmw7wxR.js.map} +1 -1
  72. package/dist/{prompt-submit-hook-BcFpjHXg.js → prompt-submit-hook-CGgT5m4Q.js} +3 -3
  73. package/dist/{prompt-submit-hook-BcFpjHXg.js.map → prompt-submit-hook-CGgT5m4Q.js.map} +1 -1
  74. package/dist/{provision-_R7ZMygj.js → provision-Bn8-NDp7.js} +4 -4
  75. package/dist/{provision-_R7ZMygj.js.map → provision-Bn8-NDp7.js.map} +1 -1
  76. package/dist/{self-invocation-cRl4RqfW.js → self-invocation-DahN1gw9.js} +2 -2
  77. package/dist/{self-invocation-cRl4RqfW.js.map → self-invocation-DahN1gw9.js.map} +1 -1
  78. package/dist/{serve-DU66K5kv.js → serve-B1yhiPls.js} +12 -12
  79. package/dist/{serve-DU66K5kv.js.map → serve-B1yhiPls.js.map} +1 -1
  80. package/dist/{server-setup-iLG8LrfL.js → server-setup-BOJCIEQi.js} +177 -49
  81. package/dist/server-setup-BOJCIEQi.js.map +1 -0
  82. package/dist/{start-DQTcm0o_.js → start-DxGJKVd_.js} +3 -3
  83. package/dist/{start-DQTcm0o_.js.map → start-DxGJKVd_.js.map} +1 -1
  84. package/dist/{stop-gate-hook-BUCxa5Kc.js → stop-gate-hook-D7N759TG.js} +72 -4
  85. package/dist/{stop-gate-hook-BUCxa5Kc.js.map → stop-gate-hook-D7N759TG.js.map} +1 -1
  86. package/dist/{stop-gate-policy--bpyxxXU.js → stop-gate-policy-C0gt04R0.js} +2 -2
  87. package/dist/{stop-gate-policy--bpyxxXU.js.map → stop-gate-policy-C0gt04R0.js.map} +1 -1
  88. package/dist/{token-C1gthnbV.js → token-86uk6y4P.js} +2 -2
  89. package/dist/{token-C1gthnbV.js.map → token-86uk6y4P.js.map} +1 -1
  90. package/dist/{worker-dispatch-DMIdrlsF.js → worker-dispatch-B-OA7tvU.js} +2 -2
  91. package/dist/{worker-dispatch-DMIdrlsF.js.map → worker-dispatch-B-OA7tvU.js.map} +1 -1
  92. package/package.json +1 -1
  93. package/dist/attribution-settings-jcsSeVur.js.map +0 -1
  94. package/dist/claude-CJ4Nd2_3.js.map +0 -1
  95. package/dist/engine-DwrxceOS.js +0 -2
  96. package/dist/lifecycle-BpqAyaAj.js +0 -2
  97. package/dist/lifecycle-CL2WiyeQ.js +0 -2
  98. package/dist/paths-BXXCVFvA.js +0 -2
  99. package/dist/server-setup-iLG8LrfL.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-Cx1wVrLb.js","names":[],"sources":["../src/lib/colbert/lifecycle.ts"],"sourcesContent":["/**\n * ColBERT sidecar lifecycle: in-memory PID ledger for the short-lived\n * `colgrep` children this proxy spawns, signal-handler tree-kill on\n * exit, and a boot-time metadata reclassification sweep.\n *\n * Because colgrep is CLI-per-invocation (no daemon), the lifecycle\n * problem is **process tracking + cancellation + boot/exit sweep**, NOT\n * keep-alive. Modeled on `worker-agent/lifecycle.ts` (PID ledger + boot\n * sweep + per-proxy-run instance UUID) and `exec.ts`'s tree-kill.\n *\n * Three cooperating layers (none sufficient alone):\n * 1. Per-call cleanup — the runner's `finally` force-kills the child\n * it spawned (handled in runner.ts).\n * 2. Session-end signal sweep (this file) — SIGINT/SIGTERM/exit kill\n * every still-tracked child of THIS run.\n * 3. Boot-time sweep (`sweepStaleColbertMetaAtBoot`) — reclassifies\n * `.gh-router-meta/*.json` entries whose `buildPid` is dead from\n * `building` → `failed`. It NEVER issues a kill to a PID from a\n * prior boot (a reused PID may belong to an unrelated process);\n * only the in-memory ledger (this run's spawns) is ever killed.\n */\n\nimport { spawn } from \"node:child_process\"\nimport { randomUUID } from \"node:crypto\"\nimport fs from \"node:fs/promises\"\nimport path from \"node:path\"\nimport process from \"node:process\"\n\nimport consola from \"consola\"\n\nimport { killManagedTree } from \"../exec\"\nimport { PATHS } from \"../paths\"\n\n// ---------------------------------------------------------------------\n// Per-launch instance UUID (mirrors worker-agent/lifecycle.ts)\n// ---------------------------------------------------------------------\n\nlet _instanceUuid: string | null = null\n\n/**\n * Stable UUID4 generated once per proxy process. Written into the\n * sidecar metadata `ownerInstanceId` so the boot sweep can tell \"this\n * proxy's still-live build\" from \"a stranded `building` entry from a\n * prior proxy whose PID got recycled\" (Docker PID-1 across restarts).\n */\nexport function getColbertInstanceUuid(): string {\n if (_instanceUuid === null) _instanceUuid = randomUUID()\n return _instanceUuid\n}\n\n/** Test-only: reset the cached UUID. */\nexport function __resetColbertInstanceUuidForTests(): void {\n _instanceUuid = null\n}\n\n// ---------------------------------------------------------------------\n// In-memory PID ledger of THIS run's live colgrep children\n// ---------------------------------------------------------------------\n\ntype TrackedChild = ReturnType<typeof spawn>\n\nconst _liveChildren = new Set<TrackedChild>()\n\n/**\n * Register a freshly-spawned colgrep child so the exit sweep can reap\n * it. The runner also removes it on natural close via `untrackChild`.\n */\nexport function trackChild(child: TrackedChild): void {\n _liveChildren.add(child)\n child.once(\"close\", () => _liveChildren.delete(child))\n child.once(\"error\", () => _liveChildren.delete(child))\n}\n\n/** Remove a child from the ledger (e.g. after a clean per-call kill). */\nexport function untrackChild(child: TrackedChild): void {\n _liveChildren.delete(child)\n}\n\n/** Count of live tracked children (test/diagnostic). */\nexport function liveChildCount(): number {\n return _liveChildren.size\n}\n\n/**\n * Synchronous best-effort tree-kill of every tracked child. Called from\n * the signal/exit handlers. After killing, the set is cleared so a\n * second call is a no-op.\n */\nexport function sweepLiveChildren(): void {\n const isWin = process.platform === \"win32\"\n for (const child of _liveChildren) {\n try {\n killManagedTree(child, isWin)\n } catch {\n // already gone\n }\n }\n _liveChildren.clear()\n}\n\n// ---------------------------------------------------------------------\n// Signal handlers (mirror worker-agent/lifecycle.ts re-raise pattern)\n// ---------------------------------------------------------------------\n\nlet _registered = false\nlet _exitHandler: (() => void) | null = null\nlet _sigintHandler: (() => void) | null = null\nlet _sigtermHandler: (() => void) | null = null\n\n/**\n * Wire SIGINT/SIGTERM/exit handlers that tree-kill every tracked\n * colgrep child. Idempotent — subsequent calls are a no-op (we never\n * leak listeners). The signal handlers re-raise after sweeping so Node's\n * default terminate-on-signal behavior is restored (otherwise attaching\n * a listener cancels the default and Ctrl-C would clean but not exit).\n */\nexport function registerColbertExitHandlers(): void {\n if (_registered) return\n _registered = true\n _exitHandler = () => sweepLiveChildren()\n _sigintHandler = () => {\n sweepLiveChildren()\n if (_sigintHandler) process.off(\"SIGINT\", _sigintHandler)\n process.kill(process.pid, \"SIGINT\")\n }\n _sigtermHandler = () => {\n sweepLiveChildren()\n if (_sigtermHandler) process.off(\"SIGTERM\", _sigtermHandler)\n process.kill(process.pid, \"SIGTERM\")\n }\n process.on(\"SIGINT\", _sigintHandler)\n process.on(\"SIGTERM\", _sigtermHandler)\n process.on(\"exit\", _exitHandler)\n}\n\n/** Test-only: unregister handlers + reset module state. */\nexport function __unregisterColbertExitHandlersForTests(): void {\n if (_sigintHandler) {\n process.off(\"SIGINT\", _sigintHandler)\n _sigintHandler = null\n }\n if (_sigtermHandler) {\n process.off(\"SIGTERM\", _sigtermHandler)\n _sigtermHandler = null\n }\n if (_exitHandler) {\n process.off(\"exit\", _exitHandler)\n _exitHandler = null\n }\n _registered = false\n _liveChildren.clear()\n}\n\n// ---------------------------------------------------------------------\n// Boot-time metadata reclassification sweep\n// ---------------------------------------------------------------------\n\n/**\n * True iff `pid` names a live process. `process.kill(pid, 0)` probes\n * existence without signalling; `EPERM` means the process exists but is\n * owned by another user (still alive). Exported so the per-query freshness\n * verdict can mirror the boot sweep's liveness check.\n */\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false\n try {\n process.kill(pid, 0)\n return true\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EPERM\") return true\n return false\n }\n}\n\n/**\n * Boot-time sweep. Walks `.gh-router-meta/*.json`; any entry stuck in\n * `status:\"building\"` whose `buildPid` is DEAD is a crashed-build\n * escapee → reset to `status:\"failed\"` so the next search re-kicks a\n * build instead of routing to a never-finishing one.\n *\n * It NEVER kills anything: a live PID matching a stale `buildPid` from a\n * prior boot may be a recycled PID belonging to an unrelated process, so\n * the boot sweep only RECLASSIFIES metadata. The in-memory ledger (this\n * run's spawns) is the only thing the SIGINT/SIGTERM handler ever kills.\n *\n * Best-effort; never throws (wrapped by the caller in `ensurePaths`).\n */\nexport async function sweepStaleColbertMetaAtBoot(): Promise<void> {\n const metaDir = PATHS.COLBERT_META_DIR\n let names: Array<string>\n try {\n names = await fs.readdir(metaDir)\n } catch {\n return // no meta dir yet — nothing to sweep\n }\n for (const name of names) {\n if (!name.endsWith(\".json\")) continue\n const file = path.join(metaDir, name)\n let meta: Record<string, unknown>\n try {\n meta = JSON.parse(await fs.readFile(file, \"utf8\")) as Record<string, unknown>\n } catch {\n continue // corrupt — leave it; index-store re-derives on next access\n }\n if (meta.status !== \"building\") continue\n const buildPid = typeof meta.buildPid === \"number\" ? meta.buildPid : 0\n if (buildPid > 0 && isPidAlive(buildPid)) {\n // A live PID — could be ours (this run re-kicked) or a recycled\n // unrelated PID. Either way: never kill from the boot sweep. Leave\n // the entry; the runner's own ownership check governs.\n continue\n }\n // Dead build PID → reclassify to failed (atomic temp+rename). Stamp the\n // crash class so the per-query self-heal treats it as transient (re-kick)\n // rather than operator-actionable.\n meta.status = \"failed\"\n meta.failureClass = \"crashed\"\n const tmp = `${file}.${process.pid}.tmp`\n try {\n await fs.writeFile(tmp, JSON.stringify(meta, null, 2))\n await fs.rename(tmp, file)\n } catch {\n await fs.rm(tmp, { force: true }).catch(() => {})\n }\n }\n await sweepColbertStore()\n}\n\n/** An orphan stub younger than this may be a build that just started. */\nconst ORPHAN_GRACE_MS = 60 * 60 * 1000\n\n/**\n * Reclaim disk in the colgrep store, ordered so it cannot destroy good data.\n *\n * This runs AFTER the identity fix in this same release, which matters: the\n * scanner that decides \"unreachable\" is the one that was wrong. Under the old\n * comparison a perfectly healthy index looked unreachable, and a sweep written\n * against that scanner would have deleted it. Everything below therefore\n * leans on evidence that does not depend on our path matching at all.\n *\n * Three rules, in increasing order of caution:\n *\n * 1. ORPHAN STUB — no `project.json` AND an empty-or-absent `index/` AND\n * older than the grace window. All three required. A dir with a\n * `project.json` is real data and is never touched by this rule; a dir\n * with shards is never touched even without one. Deleted outright,\n * because there is provably nothing in it.\n *\n * 2. DEAD META — a sidecar whose `workspace` no longer exists on disk. Pure\n * bookkeeping, never index data. (48 such entries had accumulated on the\n * machine where this was diagnosed; nothing ever reaped them.)\n *\n * 3. QUARANTINE, NEVER DIRECT DELETE, for anything holding real bytes. A\n * dir with a `project.json` is left alone entirely, however unreachable\n * it looks. Reclaiming those would mean deleting real index data on our\n * own inference about what colgrep would use — and this incident is the\n * proof that such inference can be wrong. If it is ever added it must\n * rename first and delete only after a retention window, so the whole\n * period stays reversible.\n *\n * Skips any dir with a live lock owner or an in-flight init, and logs a\n * one-line count so the accumulation is observable rather than silent.\n */\nexport async function sweepColbertStore(): Promise<void> {\n const indicesDir = PATHS.COLBERT_INDICES_DIR\n let names: Array<string>\n try {\n names = await fs.readdir(indicesDir)\n } catch {\n return\n }\n\n const now = Date.now()\n let orphans = 0\n let deadMeta = 0\n\n for (const name of names) {\n if (name === \".gh-router-meta\") continue\n // Quarantined husks are owned by the corrupt-repair path, which deletes\n // them out of band. Never race it.\n if (name.includes(\".corrupt-\")) continue\n const dir = path.join(indicesDir, name)\n try {\n const st = await fs.stat(dir)\n if (!st.isDirectory()) continue\n // A live build claims its dir with a `.lock`; another proxy may own it.\n if (await exists(path.join(dir, \".lock\"))) continue\n // Real index data — leave it. Reclaiming this needs quarantine-first\n // semantics, not a boot-time `rm`.\n if (await exists(path.join(dir, \"project.json\"))) continue\n // Anything with shards is real work in progress, even mid-build.\n let indexEntries: Array<string> = []\n try {\n indexEntries = await fs.readdir(path.join(dir, \"index\"))\n } catch {\n /* absent index/ is the orphan shape */\n }\n if (indexEntries.length > 0) continue\n if (now - st.mtimeMs < ORPHAN_GRACE_MS) continue\n await fs.rm(dir, { recursive: true, force: true })\n orphans++\n } catch {\n // Vanished mid-sweep or unreadable — skip.\n }\n }\n\n // Dead sidecars: the workspace they describe is gone.\n try {\n const metaDir = PATHS.COLBERT_META_DIR\n for (const f of await fs.readdir(metaDir)) {\n if (!f.endsWith(\".json\")) continue\n const file = path.join(metaDir, f)\n try {\n const meta = JSON.parse(await fs.readFile(file, \"utf8\")) as {\n workspace?: string\n status?: string\n }\n const ws = meta.workspace\n if (typeof ws !== \"string\" || ws.length === 0) continue\n // Never reap a live build's bookkeeping.\n if (meta.status === \"building\") continue\n // Nor an entry this very sweep just reclassified. The reclassification\n // pass above rewrites `building` + dead-PID to `failed` so the runner\n // can self-heal; deleting it in the same pass would erase that state\n // before anything could act on it. Only reap entries that have been\n // settled for a while.\n const stMeta = await fs.stat(file).catch(() => null)\n if (stMeta && now - stMeta.mtimeMs < ORPHAN_GRACE_MS) continue\n if (await exists(ws)) continue\n await fs.rm(file, { force: true })\n deadMeta++\n } catch {\n // Corrupt/unreadable — leave it; index-store re-derives on next access.\n }\n }\n } catch {\n // No meta dir yet.\n }\n\n if (orphans > 0 || deadMeta > 0) {\n consola.debug(\n `colbert: store sweep reclaimed ${orphans} orphan index dir(s), ${deadMeta} dead metadata entr(ies)`,\n )\n }\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await fs.stat(p)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle-_BE0SnNs.js","names":[],"sources":["../src/lib/colbert/lifecycle.ts"],"sourcesContent":["/**\n * ColBERT sidecar lifecycle: in-memory PID ledger for the short-lived\n * `colgrep` children this proxy spawns, signal-handler tree-kill on\n * exit, and a boot-time metadata reclassification sweep.\n *\n * Because colgrep is CLI-per-invocation (no daemon), the lifecycle\n * problem is **process tracking + cancellation + boot/exit sweep**, NOT\n * keep-alive. Modeled on `worker-agent/lifecycle.ts` (PID ledger + boot\n * sweep + per-proxy-run instance UUID) and `exec.ts`'s tree-kill.\n *\n * Three cooperating layers (none sufficient alone):\n * 1. Per-call cleanup — the runner's `finally` force-kills the child\n * it spawned (handled in runner.ts).\n * 2. Session-end signal sweep (this file) — SIGINT/SIGTERM/exit kill\n * every still-tracked child of THIS run.\n * 3. Boot-time sweep (`sweepStaleColbertMetaAtBoot`) — reclassifies\n * `.gh-router-meta/*.json` entries whose `buildPid` is dead from\n * `building` → `failed`. It NEVER issues a kill to a PID from a\n * prior boot (a reused PID may belong to an unrelated process);\n * only the in-memory ledger (this run's spawns) is ever killed.\n */\n\nimport { spawn } from \"node:child_process\"\nimport { randomUUID } from \"node:crypto\"\nimport fs from \"node:fs/promises\"\nimport path from \"node:path\"\nimport process from \"node:process\"\n\nimport consola from \"consola\"\n\nimport { killManagedTree } from \"../exec\"\nimport { PATHS } from \"../paths\"\n\n// ---------------------------------------------------------------------\n// Per-launch instance UUID (mirrors worker-agent/lifecycle.ts)\n// ---------------------------------------------------------------------\n\nlet _instanceUuid: string | null = null\n\n/**\n * Stable UUID4 generated once per proxy process. Written into the\n * sidecar metadata `ownerInstanceId` so the boot sweep can tell \"this\n * proxy's still-live build\" from \"a stranded `building` entry from a\n * prior proxy whose PID got recycled\" (Docker PID-1 across restarts).\n */\nexport function getColbertInstanceUuid(): string {\n if (_instanceUuid === null) _instanceUuid = randomUUID()\n return _instanceUuid\n}\n\n/** Test-only: reset the cached UUID. */\nexport function __resetColbertInstanceUuidForTests(): void {\n _instanceUuid = null\n}\n\n// ---------------------------------------------------------------------\n// In-memory PID ledger of THIS run's live colgrep children\n// ---------------------------------------------------------------------\n\ntype TrackedChild = ReturnType<typeof spawn>\n\nconst _liveChildren = new Set<TrackedChild>()\n\n/**\n * Register a freshly-spawned colgrep child so the exit sweep can reap\n * it. The runner also removes it on natural close via `untrackChild`.\n */\nexport function trackChild(child: TrackedChild): void {\n _liveChildren.add(child)\n child.once(\"close\", () => _liveChildren.delete(child))\n child.once(\"error\", () => _liveChildren.delete(child))\n}\n\n/** Remove a child from the ledger (e.g. after a clean per-call kill). */\nexport function untrackChild(child: TrackedChild): void {\n _liveChildren.delete(child)\n}\n\n/** Count of live tracked children (test/diagnostic). */\nexport function liveChildCount(): number {\n return _liveChildren.size\n}\n\n/**\n * Synchronous best-effort tree-kill of every tracked child. Called from\n * the signal/exit handlers. After killing, the set is cleared so a\n * second call is a no-op.\n */\nexport function sweepLiveChildren(): void {\n const isWin = process.platform === \"win32\"\n for (const child of _liveChildren) {\n try {\n killManagedTree(child, isWin)\n } catch {\n // already gone\n }\n }\n _liveChildren.clear()\n}\n\n// ---------------------------------------------------------------------\n// Signal handlers (mirror worker-agent/lifecycle.ts re-raise pattern)\n// ---------------------------------------------------------------------\n\nlet _registered = false\nlet _exitHandler: (() => void) | null = null\nlet _sigintHandler: (() => void) | null = null\nlet _sigtermHandler: (() => void) | null = null\n\n/**\n * Wire SIGINT/SIGTERM/exit handlers that tree-kill every tracked\n * colgrep child. Idempotent — subsequent calls are a no-op (we never\n * leak listeners). The signal handlers re-raise after sweeping so Node's\n * default terminate-on-signal behavior is restored (otherwise attaching\n * a listener cancels the default and Ctrl-C would clean but not exit).\n */\nexport function registerColbertExitHandlers(): void {\n if (_registered) return\n _registered = true\n _exitHandler = () => sweepLiveChildren()\n _sigintHandler = () => {\n sweepLiveChildren()\n if (_sigintHandler) process.off(\"SIGINT\", _sigintHandler)\n process.kill(process.pid, \"SIGINT\")\n }\n _sigtermHandler = () => {\n sweepLiveChildren()\n if (_sigtermHandler) process.off(\"SIGTERM\", _sigtermHandler)\n process.kill(process.pid, \"SIGTERM\")\n }\n process.on(\"SIGINT\", _sigintHandler)\n process.on(\"SIGTERM\", _sigtermHandler)\n process.on(\"exit\", _exitHandler)\n}\n\n/** Test-only: unregister handlers + reset module state. */\nexport function __unregisterColbertExitHandlersForTests(): void {\n if (_sigintHandler) {\n process.off(\"SIGINT\", _sigintHandler)\n _sigintHandler = null\n }\n if (_sigtermHandler) {\n process.off(\"SIGTERM\", _sigtermHandler)\n _sigtermHandler = null\n }\n if (_exitHandler) {\n process.off(\"exit\", _exitHandler)\n _exitHandler = null\n }\n _registered = false\n _liveChildren.clear()\n}\n\n// ---------------------------------------------------------------------\n// Boot-time metadata reclassification sweep\n// ---------------------------------------------------------------------\n\n/**\n * True iff `pid` names a live process. `process.kill(pid, 0)` probes\n * existence without signalling; `EPERM` means the process exists but is\n * owned by another user (still alive). Exported so the per-query freshness\n * verdict can mirror the boot sweep's liveness check.\n */\nexport function isPidAlive(pid: number): boolean {\n if (!Number.isInteger(pid) || pid <= 0) return false\n try {\n process.kill(pid, 0)\n return true\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"EPERM\") return true\n return false\n }\n}\n\n/**\n * Boot-time sweep. Walks `.gh-router-meta/*.json`; any entry stuck in\n * `status:\"building\"` whose `buildPid` is DEAD is a crashed-build\n * escapee → reset to `status:\"failed\"` so the next search re-kicks a\n * build instead of routing to a never-finishing one.\n *\n * It NEVER kills anything: a live PID matching a stale `buildPid` from a\n * prior boot may be a recycled PID belonging to an unrelated process, so\n * the boot sweep only RECLASSIFIES metadata. The in-memory ledger (this\n * run's spawns) is the only thing the SIGINT/SIGTERM handler ever kills.\n *\n * Best-effort; never throws (wrapped by the caller in `ensurePaths`).\n */\nexport async function sweepStaleColbertMetaAtBoot(): Promise<void> {\n const metaDir = PATHS.COLBERT_META_DIR\n let names: Array<string>\n try {\n names = await fs.readdir(metaDir)\n } catch {\n return // no meta dir yet — nothing to sweep\n }\n for (const name of names) {\n if (!name.endsWith(\".json\")) continue\n const file = path.join(metaDir, name)\n let meta: Record<string, unknown>\n try {\n meta = JSON.parse(await fs.readFile(file, \"utf8\")) as Record<string, unknown>\n } catch {\n continue // corrupt — leave it; index-store re-derives on next access\n }\n if (meta.status !== \"building\") continue\n const buildPid = typeof meta.buildPid === \"number\" ? meta.buildPid : 0\n if (buildPid > 0 && isPidAlive(buildPid)) {\n // A live PID — could be ours (this run re-kicked) or a recycled\n // unrelated PID. Either way: never kill from the boot sweep. Leave\n // the entry; the runner's own ownership check governs.\n continue\n }\n // Dead build PID → reclassify to failed (atomic temp+rename). Stamp the\n // crash class so the per-query self-heal treats it as transient (re-kick)\n // rather than operator-actionable.\n meta.status = \"failed\"\n meta.failureClass = \"crashed\"\n const tmp = `${file}.${process.pid}.tmp`\n try {\n await fs.writeFile(tmp, JSON.stringify(meta, null, 2))\n await fs.rename(tmp, file)\n } catch {\n await fs.rm(tmp, { force: true }).catch(() => {})\n }\n }\n await sweepColbertStore()\n}\n\n/** An orphan stub younger than this may be a build that just started. */\nconst ORPHAN_GRACE_MS = 60 * 60 * 1000\n\n/**\n * Reclaim disk in the colgrep store, ordered so it cannot destroy good data.\n *\n * This runs AFTER the identity fix in this same release, which matters: the\n * scanner that decides \"unreachable\" is the one that was wrong. Under the old\n * comparison a perfectly healthy index looked unreachable, and a sweep written\n * against that scanner would have deleted it. Everything below therefore\n * leans on evidence that does not depend on our path matching at all.\n *\n * Three rules, in increasing order of caution:\n *\n * 1. ORPHAN STUB — no `project.json` AND an empty-or-absent `index/` AND\n * older than the grace window. All three required. A dir with a\n * `project.json` is real data and is never touched by this rule; a dir\n * with shards is never touched even without one. Deleted outright,\n * because there is provably nothing in it.\n *\n * 2. DEAD META — a sidecar whose `workspace` no longer exists on disk. Pure\n * bookkeeping, never index data. (48 such entries had accumulated on the\n * machine where this was diagnosed; nothing ever reaped them.)\n *\n * 3. QUARANTINE, NEVER DIRECT DELETE, for anything holding real bytes. A\n * dir with a `project.json` is left alone entirely, however unreachable\n * it looks. Reclaiming those would mean deleting real index data on our\n * own inference about what colgrep would use — and this incident is the\n * proof that such inference can be wrong. If it is ever added it must\n * rename first and delete only after a retention window, so the whole\n * period stays reversible.\n *\n * Skips any dir with a live lock owner or an in-flight init, and logs a\n * one-line count so the accumulation is observable rather than silent.\n */\nexport async function sweepColbertStore(): Promise<void> {\n const indicesDir = PATHS.COLBERT_INDICES_DIR\n let names: Array<string>\n try {\n names = await fs.readdir(indicesDir)\n } catch {\n return\n }\n\n const now = Date.now()\n let orphans = 0\n let deadMeta = 0\n\n for (const name of names) {\n if (name === \".gh-router-meta\") continue\n // Quarantined husks are owned by the corrupt-repair path, which deletes\n // them out of band. Never race it.\n if (name.includes(\".corrupt-\")) continue\n const dir = path.join(indicesDir, name)\n try {\n const st = await fs.stat(dir)\n if (!st.isDirectory()) continue\n // A live build claims its dir with a `.lock`; another proxy may own it.\n if (await exists(path.join(dir, \".lock\"))) continue\n // Real index data — leave it. Reclaiming this needs quarantine-first\n // semantics, not a boot-time `rm`.\n if (await exists(path.join(dir, \"project.json\"))) continue\n // Anything with shards is real work in progress, even mid-build.\n let indexEntries: Array<string> = []\n try {\n indexEntries = await fs.readdir(path.join(dir, \"index\"))\n } catch {\n /* absent index/ is the orphan shape */\n }\n if (indexEntries.length > 0) continue\n if (now - st.mtimeMs < ORPHAN_GRACE_MS) continue\n await fs.rm(dir, { recursive: true, force: true })\n orphans++\n } catch {\n // Vanished mid-sweep or unreadable — skip.\n }\n }\n\n // Dead sidecars: the workspace they describe is gone.\n try {\n const metaDir = PATHS.COLBERT_META_DIR\n for (const f of await fs.readdir(metaDir)) {\n if (!f.endsWith(\".json\")) continue\n const file = path.join(metaDir, f)\n try {\n const meta = JSON.parse(await fs.readFile(file, \"utf8\")) as {\n workspace?: string\n status?: string\n }\n const ws = meta.workspace\n if (typeof ws !== \"string\" || ws.length === 0) continue\n // Never reap a live build's bookkeeping.\n if (meta.status === \"building\") continue\n // Nor an entry this very sweep just reclassified. The reclassification\n // pass above rewrites `building` + dead-PID to `failed` so the runner\n // can self-heal; deleting it in the same pass would erase that state\n // before anything could act on it. Only reap entries that have been\n // settled for a while.\n const stMeta = await fs.stat(file).catch(() => null)\n if (stMeta && now - stMeta.mtimeMs < ORPHAN_GRACE_MS) continue\n if (await exists(ws)) continue\n await fs.rm(file, { force: true })\n deadMeta++\n } catch {\n // Corrupt/unreadable — leave it; index-store re-derives on next access.\n }\n }\n } catch {\n // No meta dir yet.\n }\n\n if (orphans > 0 || deadMeta > 0) {\n consola.debug(\n `colbert: store sweep reclaimed ${orphans} orphan index dir(s), ${deadMeta} dead metadata entr(ies)`,\n )\n }\n}\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await fs.stat(p)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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"}
package/dist/main.js CHANGED
@@ -46,26 +46,26 @@ const main = defineCommand({
46
46
  description: "A reverse proxy that exposes GitHub Copilot as OpenAI and Anthropic compatible API endpoints."
47
47
  },
48
48
  subCommands: {
49
- auth: () => import("./auth-JfxL9q7z.js").then((m) => m.auth),
50
- start: () => import("./start-DQTcm0o_.js").then((m) => m.start),
51
- claude: () => import("./claude-CJ4Nd2_3.js").then((m) => m.claude),
52
- codex: () => import("./codex-CwuLqNZF.js").then((m) => m.codex),
53
- serve: () => import("./serve-DU66K5kv.js").then((m) => m.serve),
54
- models: () => import("./models-CVzyU5mJ.js").then((m) => m.models),
55
- "check-usage": () => import("./check-usage-DXUEQzt_.js").then((m) => m.checkUsage),
56
- debug: () => import("./debug-CA7pg8V9.js").then((m) => m.debug),
57
- "internal-stop-hook": () => import("./internal-stop-hook-UN1JHtjj.js").then((m) => m.internalStopHook),
58
- "internal-prompt-submit": () => import("./internal-prompt-submit-fj5ccft_.js").then((m) => m.internalPromptSubmit),
59
- "internal-stop-review": () => import("./internal-stop-review-8v2llYDB.js").then((m) => m.internalStopReview),
60
- "internal-plan-review": () => import("./internal-plan-review-QUvNZmMR.js").then((m) => m.internalPlanReview),
61
- "internal-session-bind": () => import("./internal-session-bind-0Hmak8PR.js").then((m) => m.internalSessionBind),
62
- "internal-workspace-header": () => import("./internal-workspace-header-kg9FoNXK.js").then((m) => m.internalWorkspaceHeader),
63
- "internal-artifact-open": () => import("./internal-artifact-open-B3stUu74.js").then((m) => m.internalArtifactOpen),
64
- "internal-first-mate-guard": () => import("./internal-first-mate-guard-VgnHBp0s.js").then((m) => m.internalFirstMateGuard),
65
- "internal-worker-guard": () => import("./internal-worker-guard-BI4Pl0Ew.js").then((m) => m.internalWorkerGuard),
66
- "internal-fast-dispatch-guard": () => import("./internal-fast-dispatch-guard-2QjUlhqO.js").then((m) => m.internalFastDispatchGuard),
67
- "internal-max-dispatch-guard": () => import("./internal-max-dispatch-guard-Bz7a2Ntf.js").then((m) => m.internalMaxDispatchGuard),
68
- "internal-aic-status": () => import("./internal-aic-status-BN0xRLSF.js").then((m) => m.internalAicStatus)
49
+ auth: () => import("./auth-Iun7ftd8.js").then((m) => m.auth),
50
+ start: () => import("./start-DxGJKVd_.js").then((m) => m.start),
51
+ claude: () => import("./claude-UhOOquJW.js").then((m) => m.claude),
52
+ codex: () => import("./codex-CyuVJFDZ.js").then((m) => m.codex),
53
+ serve: () => import("./serve-B1yhiPls.js").then((m) => m.serve),
54
+ models: () => import("./models-CSFIVxAH.js").then((m) => m.models),
55
+ "check-usage": () => import("./check-usage-1c-0z3iy.js").then((m) => m.checkUsage),
56
+ debug: () => import("./debug-CDyXUPcC.js").then((m) => m.debug),
57
+ "internal-stop-hook": () => import("./internal-stop-hook-DZ6CCP4C.js").then((m) => m.internalStopHook),
58
+ "internal-prompt-submit": () => import("./internal-prompt-submit-dov4UvBo.js").then((m) => m.internalPromptSubmit),
59
+ "internal-stop-review": () => import("./internal-stop-review-MBVYtrYS.js").then((m) => m.internalStopReview),
60
+ "internal-plan-review": () => import("./internal-plan-review-C73snnD8.js").then((m) => m.internalPlanReview),
61
+ "internal-session-bind": () => import("./internal-session-bind-C3hrb9rP.js").then((m) => m.internalSessionBind),
62
+ "internal-workspace-header": () => import("./internal-workspace-header-_aEXOz5q.js").then((m) => m.internalWorkspaceHeader),
63
+ "internal-artifact-open": () => import("./internal-artifact-open-CA1trJ15.js").then((m) => m.internalArtifactOpen),
64
+ "internal-first-mate-guard": () => import("./internal-first-mate-guard-B_or0eN3.js").then((m) => m.internalFirstMateGuard),
65
+ "internal-worker-guard": () => import("./internal-worker-guard-dxGawAY3.js").then((m) => m.internalWorkerGuard),
66
+ "internal-fast-dispatch-guard": () => import("./internal-fast-dispatch-guard-D5kozMpo.js").then((m) => m.internalFastDispatchGuard),
67
+ "internal-max-dispatch-guard": () => import("./internal-max-dispatch-guard-BGOHRYRt.js").then((m) => m.internalMaxDispatchGuard),
68
+ "internal-aic-status": () => import("./internal-aic-status-BiblmImt.js").then((m) => m.internalAicStatus)
69
69
  }
70
70
  });
71
71
  /**
@@ -1,4 +1,4 @@
1
- import { t as buildSelfCommand } from "./self-invocation-cRl4RqfW.js";
1
+ import { t as buildSelfCommand } from "./self-invocation-DahN1gw9.js";
2
2
  //#region src/lib/mcp-workspace-header.ts
3
3
  /** Header the per-session MCP headersHelper emits, carrying the calling Claude
4
4
  * session's working directory. The /mcp handler uses it as the default
@@ -17,4 +17,4 @@ function buildWorkspaceHeaderHelperCommand(invocation) {
17
17
  //#endregion
18
18
  export { buildWorkspaceHeaderHelperCommand as n, buildWorkspaceHeaderJson as r, MCP_WORKSPACE_HEADER as t };
19
19
 
20
- //# sourceMappingURL=mcp-workspace-header-BYgWioLH.js.map
20
+ //# sourceMappingURL=mcp-workspace-header-mXRLE8kg.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-workspace-header-BYgWioLH.js","names":[],"sources":["../src/lib/mcp-workspace-header.ts"],"sourcesContent":["import { buildSelfCommand, type SelfInvocation } from \"./hook-launcher/self-invocation\"\n\n/** Header the per-session MCP headersHelper emits, carrying the calling Claude\n * session's working directory. The /mcp handler uses it as the default\n * `workspace` for repo-scoped tools so a machine-wide `serve` targets the\n * active repo, not the proxy launch cwd. */\nexport const MCP_WORKSPACE_HEADER = \"X-GH-Workspace\"\n\n/** stdout JSON the headersHelper prints; Claude merges it into the connection\n * headers. `cwd` is the helper's own process cwd = the session's project dir. */\nexport function buildWorkspaceHeaderJson(cwd: string): string {\n return JSON.stringify({ [MCP_WORKSPACE_HEADER]: cwd })\n}\n\n/** Command string Claude runs as the headersHelper. */\nexport function buildWorkspaceHeaderHelperCommand(invocation: SelfInvocation): string {\n return buildSelfCommand(invocation, \"internal-workspace-header\")\n}\n"],"mappings":";;;;;;AAMA,MAAa,uBAAuB;;;AAIpC,SAAgB,yBAAyB,KAAqB;CAC5D,OAAO,KAAK,UAAU,GAAG,uBAAuB,IAAI,CAAC;AACvD;;AAGA,SAAgB,kCAAkC,YAAoC;CACpF,OAAO,iBAAiB,YAAY,2BAA2B;AACjE"}
1
+ {"version":3,"file":"mcp-workspace-header-mXRLE8kg.js","names":[],"sources":["../src/lib/mcp-workspace-header.ts"],"sourcesContent":["import { buildSelfCommand, type SelfInvocation } from \"./hook-launcher/self-invocation\"\n\n/** Header the per-session MCP headersHelper emits, carrying the calling Claude\n * session's working directory. The /mcp handler uses it as the default\n * `workspace` for repo-scoped tools so a machine-wide `serve` targets the\n * active repo, not the proxy launch cwd. */\nexport const MCP_WORKSPACE_HEADER = \"X-GH-Workspace\"\n\n/** stdout JSON the headersHelper prints; Claude merges it into the connection\n * headers. `cwd` is the helper's own process cwd = the session's project dir. */\nexport function buildWorkspaceHeaderJson(cwd: string): string {\n return JSON.stringify({ [MCP_WORKSPACE_HEADER]: cwd })\n}\n\n/** Command string Claude runs as the headersHelper. */\nexport function buildWorkspaceHeaderHelperCommand(invocation: SelfInvocation): string {\n return buildSelfCommand(invocation, \"internal-workspace-header\")\n}\n"],"mappings":";;;;;;AAMA,MAAa,uBAAuB;;;AAIpC,SAAgB,yBAAyB,KAAqB;CAC5D,OAAO,KAAK,UAAU,GAAG,uBAAuB,IAAI,CAAC;AACvD;;AAGA,SAAgB,kCAAkC,YAAoC;CACpF,OAAO,iBAAiB,YAAY,2BAA2B;AACjE"}
@@ -1,5 +1,5 @@
1
- import { i as ensurePaths } from "./paths-CMxlQEfQ.js";
2
- import { p as getModels, r as setupGitHubToken, t as setupCopilotToken } from "./token-C1gthnbV.js";
1
+ import { i as ensurePaths } from "./paths-CHBAj_t9.js";
2
+ import { p as getModels, r as setupGitHubToken, t as setupCopilotToken } from "./token-86uk6y4P.js";
3
3
  import { defineCommand } from "citty";
4
4
  import consola from "consola";
5
5
  //#region src/models.ts
@@ -160,4 +160,4 @@ function formatTokens(n) {
160
160
  //#endregion
161
161
  export { models };
162
162
 
163
- //# sourceMappingURL=models-CVzyU5mJ.js.map
163
+ //# sourceMappingURL=models-CSFIVxAH.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"models-CVzyU5mJ.js","names":[],"sources":["../src/models.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupCopilotToken, setupGitHubToken, type StopCopilotTokenRefresh } from \"./lib/token\"\nimport { getModels, type Model } from \"./services/copilot/get-models\"\n\nexport const models = defineCommand({\n meta: {\n name: \"models\",\n description:\n \"List available GitHub Copilot models and their capabilities. Pass an optional pattern to filter (case-insensitive substring match on id, name, vendor, family).\",\n },\n args: {\n pattern: {\n type: \"positional\",\n required: false,\n description:\n \"Substring to filter models by (matches id, name, vendor, or family).\",\n },\n json: {\n type: \"boolean\",\n default: false,\n description: \"Emit raw JSON instead of the pretty layout.\",\n },\n },\n async run({ args }) {\n await ensurePaths()\n await setupGitHubToken()\n // One-shot command: stop the background refresh loop before returning, so\n // the timer is never what decides whether this process exits. (It is also\n // `unref()`d, so this is belt-and-braces — but ownership stays explicit.)\n let stopRefresh: StopCopilotTokenRefresh | undefined\n try {\n stopRefresh = await setupCopilotToken()\n } catch (err) {\n consola.error(\"Failed to obtain Copilot token:\", err)\n process.exit(1)\n }\n\n try {\n let catalog: Awaited<ReturnType<typeof getModels>>\n try {\n catalog = await getModels()\n } catch (err) {\n consola.error(\"Failed to fetch Copilot model catalog:\", err)\n process.exit(1)\n }\n\n const all = catalog.data\n const pattern = args.pattern?.toString().trim()\n const filtered = pattern ? filterModels(all, pattern) : all\n\n if (args.json) {\n process.stdout.write(`${JSON.stringify(filtered, null, 2)}\\n`)\n return\n }\n\n if (filtered.length === 0) {\n consola.warn(\n `No models matched \"${pattern}\". ${all.length} models available — try a different substring or run without an argument to list everything.`,\n )\n process.exit(1)\n }\n\n const grouped = groupByVendor(filtered)\n const lines: Array<string> = []\n const header = pattern\n ? `${filtered.length}/${all.length} models match \"${pattern}\"`\n : `${all.length} models available`\n lines.push(header)\n lines.push(\"\")\n for (const [vendor, list] of grouped) {\n lines.push(`▾ ${vendor} (${list.length})`)\n for (const model of list) {\n lines.push(...formatModel(model))\n }\n lines.push(\"\")\n }\n process.stdout.write(lines.join(\"\\n\"))\n } finally {\n stopRefresh?.()\n }\n },\n})\n\nfunction filterModels(models: Array<Model>, pattern: string): Array<Model> {\n const needle = pattern.toLowerCase()\n return models.filter((m) => {\n const haystack = [\n m.id,\n m.name,\n m.vendor,\n m.capabilities.family,\n m.capabilities.type,\n m.model_picker_category ?? \"\",\n ]\n .join(\" \")\n .toLowerCase()\n return haystack.includes(needle)\n })\n}\n\nfunction groupByVendor(models: Array<Model>): Array<[string, Array<Model>]> {\n const map = new Map<string, Array<Model>>()\n for (const m of models) {\n const key = m.vendor || \"(unknown vendor)\"\n const bucket = map.get(key)\n if (bucket) bucket.push(m)\n else map.set(key, [m])\n }\n // Stable vendor order: alphabetical for predictability.\n return [...map.entries()].sort(([a], [b]) => a.localeCompare(b))\n}\n\n/**\n * Test-only exports. Not part of the public CLI surface — kept out of\n * the default export so they don't show up in IDE autocomplete on\n * `import { ... } from \"github-router/models\"` consumers.\n */\nexport const __testing = { filterModels, groupByVendor, formatModel, formatTokens }\n\nfunction formatModel(model: Model): Array<string> {\n const lines: Array<string> = []\n const tags: Array<string> = []\n if (model.preview) tags.push(\"preview\")\n if (model.is_chat_default) tags.push(\"chat-default\")\n if (model.is_chat_fallback) tags.push(\"chat-fallback\")\n if (model.billing?.is_premium) tags.push(\"premium\")\n if (model.billing?.restricted_to?.length) {\n tags.push(`restricted:${model.billing.restricted_to.join(\"/\")}`)\n }\n if (model.policy && model.policy.state !== \"enabled\") {\n tags.push(`policy:${model.policy.state}`)\n }\n const tagStr = tags.length > 0 ? ` [${tags.join(\", \")}]` : \"\"\n lines.push(` • ${model.id}${tagStr}`)\n if (model.name && model.name !== model.id) {\n lines.push(` name: ${model.name}`)\n }\n const meta: Array<string> = [\n `family: ${model.capabilities.family}`,\n `type: ${model.capabilities.type}`,\n ]\n if (model.capabilities.tokenizer) {\n meta.push(`tokenizer: ${model.capabilities.tokenizer}`)\n }\n if (model.version) {\n meta.push(`version: ${model.version}`)\n }\n lines.push(` ${meta.join(\" · \")}`)\n\n const limits = model.capabilities.limits\n const limitParts: Array<string> = []\n if (limits) {\n if (limits.max_context_window_tokens) {\n limitParts.push(`ctx ${formatTokens(limits.max_context_window_tokens)}`)\n } else if (limits.max_prompt_tokens) {\n limitParts.push(`prompt ${formatTokens(limits.max_prompt_tokens)}`)\n }\n if (limits.max_output_tokens) {\n limitParts.push(`out ${formatTokens(limits.max_output_tokens)}`)\n }\n if (\n limits.max_non_streaming_output_tokens\n && limits.max_non_streaming_output_tokens !== limits.max_output_tokens\n ) {\n limitParts.push(\n `out-non-stream ${formatTokens(limits.max_non_streaming_output_tokens)}`,\n )\n }\n if (limits.max_inputs) limitParts.push(`inputs ${limits.max_inputs}`)\n if (limits.vision?.max_prompt_images) {\n limitParts.push(`images ${limits.vision.max_prompt_images}`)\n }\n }\n if (limitParts.length > 0) {\n lines.push(` limits: ${limitParts.join(\" · \")}`)\n }\n\n const supports = model.capabilities.supports\n const supportFlags: Array<string> = []\n if (supports) {\n if (supports.tool_calls) supportFlags.push(\"tools\")\n if (supports.parallel_tool_calls) supportFlags.push(\"parallel-tools\")\n if (supports.streaming) supportFlags.push(\"streaming\")\n if (supports.vision) supportFlags.push(\"vision\")\n if (supports.structured_outputs) supportFlags.push(\"structured-outputs\")\n if (supports.dimensions) supportFlags.push(\"dimensions\")\n if (supports.adaptive_thinking) {\n const min = supports.min_thinking_budget\n const max = supports.max_thinking_budget\n const range\n = min !== undefined && max !== undefined\n ? `(${formatTokens(min)}-${formatTokens(max)})`\n : \"\"\n supportFlags.push(`adaptive-thinking${range}`)\n }\n if (supports.reasoning_effort && supports.reasoning_effort.length > 0) {\n supportFlags.push(`reasoning:${supports.reasoning_effort.join(\"/\")}`)\n }\n }\n if (supportFlags.length > 0) {\n lines.push(` supports: ${supportFlags.join(\", \")}`)\n }\n\n if (model.supported_endpoints && model.supported_endpoints.length > 0) {\n lines.push(` endpoints: ${model.supported_endpoints.join(\", \")}`)\n }\n if (model.billing) {\n const billParts: Array<string> = []\n if (model.billing.is_premium) billParts.push(\"premium\")\n if (typeof model.billing.multiplier === \"number\") {\n billParts.push(`×${model.billing.multiplier}`)\n }\n if (billParts.length > 0) {\n lines.push(` billing: ${billParts.join(\" \")}`)\n }\n }\n return lines\n}\n\n/**\n * Format a token count in a compact human-readable form: `1024` →\n * `1k`, `4096` → `4k`, `131072` → `128k`, `1048576` → `1M`. Prefer\n * binary multiples (mebi, kibi) since Claude Code / Copilot context\n * windows are reported in binary units (`1M context` = 1024 × 1024\n * tokens). Fall back to decimal (`64k` for `64000`) when the value\n * is a clean decimal multiple but not binary.\n */\nfunction formatTokens(n: number): string {\n if (n >= 1_048_576 && n % 1_048_576 === 0) return `${n / 1_048_576}M`\n if (n >= 1024 && n % 1024 === 0) return `${n / 1024}k`\n if (n >= 1_000_000 && n % 1_000_000 === 0) return `${n / 1_000_000}M`\n if (n >= 1000 && n % 1000 === 0) return `${n / 1000}k`\n return `${n}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"models-CSFIVxAH.js","names":[],"sources":["../src/models.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupCopilotToken, setupGitHubToken, type StopCopilotTokenRefresh } from \"./lib/token\"\nimport { getModels, type Model } from \"./services/copilot/get-models\"\n\nexport const models = defineCommand({\n meta: {\n name: \"models\",\n description:\n \"List available GitHub Copilot models and their capabilities. Pass an optional pattern to filter (case-insensitive substring match on id, name, vendor, family).\",\n },\n args: {\n pattern: {\n type: \"positional\",\n required: false,\n description:\n \"Substring to filter models by (matches id, name, vendor, or family).\",\n },\n json: {\n type: \"boolean\",\n default: false,\n description: \"Emit raw JSON instead of the pretty layout.\",\n },\n },\n async run({ args }) {\n await ensurePaths()\n await setupGitHubToken()\n // One-shot command: stop the background refresh loop before returning, so\n // the timer is never what decides whether this process exits. (It is also\n // `unref()`d, so this is belt-and-braces — but ownership stays explicit.)\n let stopRefresh: StopCopilotTokenRefresh | undefined\n try {\n stopRefresh = await setupCopilotToken()\n } catch (err) {\n consola.error(\"Failed to obtain Copilot token:\", err)\n process.exit(1)\n }\n\n try {\n let catalog: Awaited<ReturnType<typeof getModels>>\n try {\n catalog = await getModels()\n } catch (err) {\n consola.error(\"Failed to fetch Copilot model catalog:\", err)\n process.exit(1)\n }\n\n const all = catalog.data\n const pattern = args.pattern?.toString().trim()\n const filtered = pattern ? filterModels(all, pattern) : all\n\n if (args.json) {\n process.stdout.write(`${JSON.stringify(filtered, null, 2)}\\n`)\n return\n }\n\n if (filtered.length === 0) {\n consola.warn(\n `No models matched \"${pattern}\". ${all.length} models available — try a different substring or run without an argument to list everything.`,\n )\n process.exit(1)\n }\n\n const grouped = groupByVendor(filtered)\n const lines: Array<string> = []\n const header = pattern\n ? `${filtered.length}/${all.length} models match \"${pattern}\"`\n : `${all.length} models available`\n lines.push(header)\n lines.push(\"\")\n for (const [vendor, list] of grouped) {\n lines.push(`▾ ${vendor} (${list.length})`)\n for (const model of list) {\n lines.push(...formatModel(model))\n }\n lines.push(\"\")\n }\n process.stdout.write(lines.join(\"\\n\"))\n } finally {\n stopRefresh?.()\n }\n },\n})\n\nfunction filterModels(models: Array<Model>, pattern: string): Array<Model> {\n const needle = pattern.toLowerCase()\n return models.filter((m) => {\n const haystack = [\n m.id,\n m.name,\n m.vendor,\n m.capabilities.family,\n m.capabilities.type,\n m.model_picker_category ?? \"\",\n ]\n .join(\" \")\n .toLowerCase()\n return haystack.includes(needle)\n })\n}\n\nfunction groupByVendor(models: Array<Model>): Array<[string, Array<Model>]> {\n const map = new Map<string, Array<Model>>()\n for (const m of models) {\n const key = m.vendor || \"(unknown vendor)\"\n const bucket = map.get(key)\n if (bucket) bucket.push(m)\n else map.set(key, [m])\n }\n // Stable vendor order: alphabetical for predictability.\n return [...map.entries()].sort(([a], [b]) => a.localeCompare(b))\n}\n\n/**\n * Test-only exports. Not part of the public CLI surface — kept out of\n * the default export so they don't show up in IDE autocomplete on\n * `import { ... } from \"github-router/models\"` consumers.\n */\nexport const __testing = { filterModels, groupByVendor, formatModel, formatTokens }\n\nfunction formatModel(model: Model): Array<string> {\n const lines: Array<string> = []\n const tags: Array<string> = []\n if (model.preview) tags.push(\"preview\")\n if (model.is_chat_default) tags.push(\"chat-default\")\n if (model.is_chat_fallback) tags.push(\"chat-fallback\")\n if (model.billing?.is_premium) tags.push(\"premium\")\n if (model.billing?.restricted_to?.length) {\n tags.push(`restricted:${model.billing.restricted_to.join(\"/\")}`)\n }\n if (model.policy && model.policy.state !== \"enabled\") {\n tags.push(`policy:${model.policy.state}`)\n }\n const tagStr = tags.length > 0 ? ` [${tags.join(\", \")}]` : \"\"\n lines.push(` • ${model.id}${tagStr}`)\n if (model.name && model.name !== model.id) {\n lines.push(` name: ${model.name}`)\n }\n const meta: Array<string> = [\n `family: ${model.capabilities.family}`,\n `type: ${model.capabilities.type}`,\n ]\n if (model.capabilities.tokenizer) {\n meta.push(`tokenizer: ${model.capabilities.tokenizer}`)\n }\n if (model.version) {\n meta.push(`version: ${model.version}`)\n }\n lines.push(` ${meta.join(\" · \")}`)\n\n const limits = model.capabilities.limits\n const limitParts: Array<string> = []\n if (limits) {\n if (limits.max_context_window_tokens) {\n limitParts.push(`ctx ${formatTokens(limits.max_context_window_tokens)}`)\n } else if (limits.max_prompt_tokens) {\n limitParts.push(`prompt ${formatTokens(limits.max_prompt_tokens)}`)\n }\n if (limits.max_output_tokens) {\n limitParts.push(`out ${formatTokens(limits.max_output_tokens)}`)\n }\n if (\n limits.max_non_streaming_output_tokens\n && limits.max_non_streaming_output_tokens !== limits.max_output_tokens\n ) {\n limitParts.push(\n `out-non-stream ${formatTokens(limits.max_non_streaming_output_tokens)}`,\n )\n }\n if (limits.max_inputs) limitParts.push(`inputs ${limits.max_inputs}`)\n if (limits.vision?.max_prompt_images) {\n limitParts.push(`images ${limits.vision.max_prompt_images}`)\n }\n }\n if (limitParts.length > 0) {\n lines.push(` limits: ${limitParts.join(\" · \")}`)\n }\n\n const supports = model.capabilities.supports\n const supportFlags: Array<string> = []\n if (supports) {\n if (supports.tool_calls) supportFlags.push(\"tools\")\n if (supports.parallel_tool_calls) supportFlags.push(\"parallel-tools\")\n if (supports.streaming) supportFlags.push(\"streaming\")\n if (supports.vision) supportFlags.push(\"vision\")\n if (supports.structured_outputs) supportFlags.push(\"structured-outputs\")\n if (supports.dimensions) supportFlags.push(\"dimensions\")\n if (supports.adaptive_thinking) {\n const min = supports.min_thinking_budget\n const max = supports.max_thinking_budget\n const range\n = min !== undefined && max !== undefined\n ? `(${formatTokens(min)}-${formatTokens(max)})`\n : \"\"\n supportFlags.push(`adaptive-thinking${range}`)\n }\n if (supports.reasoning_effort && supports.reasoning_effort.length > 0) {\n supportFlags.push(`reasoning:${supports.reasoning_effort.join(\"/\")}`)\n }\n }\n if (supportFlags.length > 0) {\n lines.push(` supports: ${supportFlags.join(\", \")}`)\n }\n\n if (model.supported_endpoints && model.supported_endpoints.length > 0) {\n lines.push(` endpoints: ${model.supported_endpoints.join(\", \")}`)\n }\n if (model.billing) {\n const billParts: Array<string> = []\n if (model.billing.is_premium) billParts.push(\"premium\")\n if (typeof model.billing.multiplier === \"number\") {\n billParts.push(`×${model.billing.multiplier}`)\n }\n if (billParts.length > 0) {\n lines.push(` billing: ${billParts.join(\" \")}`)\n }\n }\n return lines\n}\n\n/**\n * Format a token count in a compact human-readable form: `1024` →\n * `1k`, `4096` → `4k`, `131072` → `128k`, `1048576` → `1M`. Prefer\n * binary multiples (mebi, kibi) since Claude Code / Copilot context\n * windows are reported in binary units (`1M context` = 1024 × 1024\n * tokens). Fall back to decimal (`64k` for `64000`) when the value\n * is a clean decimal multiple but not binary.\n */\nfunction formatTokens(n: number): string {\n if (n >= 1_048_576 && n % 1_048_576 === 0) return `${n / 1_048_576}M`\n if (n >= 1024 && n % 1024 === 0) return `${n / 1024}k`\n if (n >= 1_000_000 && n % 1_000_000 === 0) return `${n / 1_000_000}M`\n if (n >= 1000 && n % 1000 === 0) return `${n / 1000}k`\n return `${n}`\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { a as parseIntEnv, s as runCommandCapture } from "./exec-COUT4QsS.js";
2
- import "./stop-gate-hook-BUCxa5Kc.js";
2
+ import "./stop-gate-hook-D7N759TG.js";
3
3
  //#region src/lib/orchestration/live-exec.ts
4
4
  /**
5
5
  * Live `ExecFn` for the gate runner — runs the kernel's SEALED gate command in a
@@ -36,4 +36,4 @@ const liveExec = async ({ command, cwd }) => {
36
36
  //#endregion
37
37
  export { liveExec as t };
38
38
 
39
- //# sourceMappingURL=orchestration-Dc3mO0G3.js.map
39
+ //# sourceMappingURL=orchestration-B8kypXcZ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"orchestration-Dc3mO0G3.js","names":[],"sources":["../src/lib/orchestration/live-exec.ts"],"sourcesContent":["/**\n * Live `ExecFn` for the gate runner — runs the kernel's SEALED gate command in a\n * workspace via the project's Windows-safe exec helper (`runCommandCapture` →\n * `buildExecInvocation`, which handles cmd.exe quoting on Windows). Only the\n * kernel's sealed gate commands flow through here (tests/types/lint/build),\n * never a producer-authored string, so a simple whitespace split into argv is\n * safe for these fixed commands. A command that can't run (spawn error / killed)\n * counts as a non-zero exit, never a throw — the gate runner treats it as\n * not-passed.\n *\n * This is the one piece of the gate engine that touches a real subprocess; it is\n * model-free and cross-platform, so it is exercised directly in CI (unlike the\n * worker/worktree/model adapters, which need the gated E2E harness).\n */\n\nimport { runCommandCapture } from \"~/lib/exec\"\n\nimport { type ExecFn } from \"./gate-runner\"\nimport { parseIntEnv } from \"../exec\"\n\n/** Per-command wall-clock cap so a hung gate command (watch-mode test, a process\n * waiting on stdin, a stale lockfile) is tree-killed instead of hanging the\n * caller forever. Generous (a real typecheck/test/lint can take minutes) but\n * bounded; override with GH_ROUTER_GATE_CMD_TIMEOUT_MS. A timeout kills the\n * command (code null) which the gate runner treats as not-passed. */\nconst CMD_TIMEOUT_MS =\n parseIntEnv(process.env.GH_ROUTER_GATE_CMD_TIMEOUT_MS) ?? 600_000\n\nexport const liveExec: ExecFn = async ({ command, cwd }) => {\n const argv = command.trim().split(/\\s+/).filter(Boolean)\n if (argv.length === 0) return { exitCode: 1 }\n try {\n const r = await runCommandCapture(argv, { cwd, timeoutMs: CMD_TIMEOUT_MS })\n // `code` is null when killed by signal/timeout → treat as a failed gate.\n return { exitCode: r.code ?? 1 }\n } catch {\n return { exitCode: 1 }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,iBACJ,YAAY,QAAQ,IAAI,6BAA6B,KAAK;AAE5D,MAAa,WAAmB,OAAO,EAAE,SAAS,UAAU;CAC1D,MAAM,OAAO,QAAQ,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CACvD,IAAI,KAAK,WAAW,GAAG,OAAO,EAAE,UAAU,EAAE;CAC5C,IAAI;EAGF,OAAO,EAAE,WAAU,MAFH,kBAAkB,MAAM;GAAE;GAAK,WAAW;EAAe,CAAC,EAAA,CAErD,QAAQ,EAAE;CACjC,QAAQ;EACN,OAAO,EAAE,UAAU,EAAE;CACvB;AACF"}
1
+ {"version":3,"file":"orchestration-B8kypXcZ.js","names":[],"sources":["../src/lib/orchestration/live-exec.ts"],"sourcesContent":["/**\n * Live `ExecFn` for the gate runner — runs the kernel's SEALED gate command in a\n * workspace via the project's Windows-safe exec helper (`runCommandCapture` →\n * `buildExecInvocation`, which handles cmd.exe quoting on Windows). Only the\n * kernel's sealed gate commands flow through here (tests/types/lint/build),\n * never a producer-authored string, so a simple whitespace split into argv is\n * safe for these fixed commands. A command that can't run (spawn error / killed)\n * counts as a non-zero exit, never a throw — the gate runner treats it as\n * not-passed.\n *\n * This is the one piece of the gate engine that touches a real subprocess; it is\n * model-free and cross-platform, so it is exercised directly in CI (unlike the\n * worker/worktree/model adapters, which need the gated E2E harness).\n */\n\nimport { runCommandCapture } from \"~/lib/exec\"\n\nimport { type ExecFn } from \"./gate-runner\"\nimport { parseIntEnv } from \"../exec\"\n\n/** Per-command wall-clock cap so a hung gate command (watch-mode test, a process\n * waiting on stdin, a stale lockfile) is tree-killed instead of hanging the\n * caller forever. Generous (a real typecheck/test/lint can take minutes) but\n * bounded; override with GH_ROUTER_GATE_CMD_TIMEOUT_MS. A timeout kills the\n * command (code null) which the gate runner treats as not-passed. */\nconst CMD_TIMEOUT_MS =\n parseIntEnv(process.env.GH_ROUTER_GATE_CMD_TIMEOUT_MS) ?? 600_000\n\nexport const liveExec: ExecFn = async ({ command, cwd }) => {\n const argv = command.trim().split(/\\s+/).filter(Boolean)\n if (argv.length === 0) return { exitCode: 1 }\n try {\n const r = await runCommandCapture(argv, { cwd, timeoutMs: CMD_TIMEOUT_MS })\n // `code` is null when killed by signal/timeout → treat as a failed gate.\n return { exitCode: r.code ?? 1 }\n } catch {\n return { exitCode: 1 }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAM,iBACJ,YAAY,QAAQ,IAAI,6BAA6B,KAAK;AAE5D,MAAa,WAAmB,OAAO,EAAE,SAAS,UAAU;CAC1D,MAAM,OAAO,QAAQ,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CACvD,IAAI,KAAK,WAAW,GAAG,OAAO,EAAE,UAAU,EAAE;CAC5C,IAAI;EAGF,OAAO,EAAE,WAAU,MAFH,kBAAkB,MAAM;GAAE;GAAK,WAAW;EAAe,CAAC,EAAA,CAErD,QAAQ,EAAE;CACjC,QAAQ;EACN,OAAO,EAAE,UAAU,EAAE;CACvB;AACF"}
@@ -473,12 +473,12 @@ async function ensurePaths() {
473
473
  consola.debug("Peer-agent .md sweep skipped:", err);
474
474
  });
475
475
  await (async () => {
476
- await (await import("./lifecycle-CL2WiyeQ.js")).sweepStaleWorktreesAtBoot();
476
+ await (await import("./lifecycle-DimEguJO.js")).sweepStaleWorktreesAtBoot();
477
477
  })().catch((err) => {
478
478
  consola.debug("Worker worktree boot sweep skipped:", err);
479
479
  });
480
480
  await (async () => {
481
- await (await import("./lifecycle-BpqAyaAj.js")).sweepStaleColbertMetaAtBoot();
481
+ await (await import("./lifecycle-D0KsoSqz.js")).sweepStaleColbertMetaAtBoot();
482
482
  })().catch((err) => {
483
483
  consola.debug("ColBERT meta boot sweep skipped:", err);
484
484
  });
@@ -1303,6 +1303,38 @@ async function sweepStalePeerAgentMdFiles() {
1303
1303
  }
1304
1304
  }
1305
1305
  /**
1306
+ * Remove user-supplied subagent definitions from the router-owned mirror's
1307
+ * `agents/` dir on pinned-profile launches (fast/cheap/cheap1m/cheapest),
1308
+ * which run router-provided surfaces only. Deletes every `*.md` file that
1309
+ * does NOT match `PEER_AGENT_MD_FILENAME` — i.e. files the proxy could
1310
+ * never have written, which can only have arrived via the `~/.claude/`
1311
+ * snapshot copy. Router-shaped files are left alone (the PID-liveness
1312
+ * sweep above owns them, and this launch's own peer files are written
1313
+ * after this purge runs).
1314
+ *
1315
+ * SCOPE IS THE MIRROR ONLY: the operator's real `~/.claude/agents/`
1316
+ * is never touched. Returns the number of files removed (for launch
1317
+ * logging). Missing dir → 0.
1318
+ */
1319
+ async function purgeNonRouterAgentsFromMirror() {
1320
+ const dir = path.join(PATHS.CLAUDE_CONFIG_DIR, "agents");
1321
+ let entries;
1322
+ try {
1323
+ entries = await fs.readdir(dir);
1324
+ } catch (err) {
1325
+ if (err.code === "ENOENT") return 0;
1326
+ throw err;
1327
+ }
1328
+ let removed = 0;
1329
+ for (const name of entries) {
1330
+ if (!name.endsWith(".md")) continue;
1331
+ if (PEER_AGENT_MD_FILENAME.exec(name)) continue;
1332
+ await fs.unlink(path.join(dir, name)).catch(() => {});
1333
+ removed += 1;
1334
+ }
1335
+ return removed;
1336
+ }
1337
+ /**
1306
1338
  * Strict regex matching only files this proxy writes:
1307
1339
  * peer-<pid>-<8 hex>-<exact persona/coordinator/native/dispatcher name>.md
1308
1340
  * The name allowlist is the load-bearing protection against deleting user
@@ -1395,6 +1427,6 @@ async function removeOwnClaudeConfigMirror() {
1395
1427
  });
1396
1428
  }
1397
1429
  //#endregion
1398
- export { isUnderClaudeConfigMirror as a, sweepStalePeerAgentMdFiles as c, writeRuntimeFileSecure as d, ensurePaths as i, sweepStaleRuntimeFiles as l, PEER_AGENT_MD_FILENAME as n, removeOwnClaudeConfigMirror as o, ensureClaudeConfigMirror as r, sweepStaleClaudeConfigMirrors as s, PATHS as t, writeArtifactCredsToMirror as u };
1430
+ export { isUnderClaudeConfigMirror as a, sweepStaleClaudeConfigMirrors as c, writeArtifactCredsToMirror as d, writeRuntimeFileSecure as f, ensurePaths as i, sweepStalePeerAgentMdFiles as l, PEER_AGENT_MD_FILENAME as n, purgeNonRouterAgentsFromMirror as o, ensureClaudeConfigMirror as r, removeOwnClaudeConfigMirror as s, PATHS as t, sweepStaleRuntimeFiles as u };
1399
1431
 
1400
- //# sourceMappingURL=paths-CMxlQEfQ.js.map
1432
+ //# sourceMappingURL=paths-CHBAj_t9.js.map