github-router 0.3.258 → 0.3.261
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.
- package/dist/{attribution-settings-CYBfjHzd.js → attribution-settings-Bl0HHJQF.js} +4 -4
- package/dist/{attribution-settings-CYBfjHzd.js.map → attribution-settings-Bl0HHJQF.js.map} +1 -1
- package/dist/{auth-CP4q2CjO.js → auth-I8k2OOvA.js} +3 -3
- package/dist/{auth-CP4q2CjO.js.map → auth-I8k2OOvA.js.map} +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{check-usage-6PB5IUFb.js → check-usage-SOn7-cWc.js} +4 -4
- package/dist/{check-usage-6PB5IUFb.js.map → check-usage-SOn7-cWc.js.map} +1 -1
- package/dist/{claude-DZ1VmE7I.js → claude-CEaI47j7.js} +17 -14
- package/dist/claude-CEaI47j7.js.map +1 -0
- package/dist/{codex-DwabDTOC.js → codex-C6Eyz3EV.js} +9 -6
- package/dist/codex-C6Eyz3EV.js.map +1 -0
- package/dist/{debug-CjmE9sgG.js → debug-vshv4nIv.js} +2 -2
- package/dist/{debug-CjmE9sgG.js.map → debug-vshv4nIv.js.map} +1 -1
- package/dist/engine-B4Ohhv5i.js +2 -0
- package/dist/{file-log-reporter-IFQcL5IK.js → file-log-reporter-vI8qCFNM.js} +2 -2
- package/dist/{file-log-reporter-IFQcL5IK.js.map → file-log-reporter-vI8qCFNM.js.map} +1 -1
- package/dist/{gate-discovery-BpHK5pIq.js → gate-discovery-BnPhH_AC.js} +5 -5
- package/dist/{gate-discovery-BpHK5pIq.js.map → gate-discovery-BnPhH_AC.js.map} +1 -1
- package/dist/{get-copilot-usage-DD1N0Xlh.js → get-copilot-usage-CYpAsYrM.js} +2 -2
- package/dist/{get-copilot-usage-DD1N0Xlh.js.map → get-copilot-usage-CYpAsYrM.js.map} +1 -1
- package/dist/{internal-artifact-open-D2cQ2QCw.js → internal-artifact-open-CZGO831M.js} +2 -2
- package/dist/{internal-artifact-open-D2cQ2QCw.js.map → internal-artifact-open-CZGO831M.js.map} +1 -1
- package/dist/{internal-plan-review-ipnT3bhj.js → internal-plan-review-6gkcOAjT.js} +3 -3
- package/dist/{internal-plan-review-ipnT3bhj.js.map → internal-plan-review-6gkcOAjT.js.map} +1 -1
- package/dist/{internal-prompt-submit-DOj75_hh.js → internal-prompt-submit-CSJR24-y.js} +4 -4
- package/dist/{internal-prompt-submit-DOj75_hh.js.map → internal-prompt-submit-CSJR24-y.js.map} +1 -1
- package/dist/{internal-session-bind-DwK2iGW0.js → internal-session-bind-DtSBTkQe.js} +2 -2
- package/dist/{internal-session-bind-DwK2iGW0.js.map → internal-session-bind-DtSBTkQe.js.map} +1 -1
- package/dist/{internal-stop-hook-DiqatTCG.js → internal-stop-hook-CiflCJO-.js} +5 -5
- package/dist/{internal-stop-hook-DiqatTCG.js.map → internal-stop-hook-CiflCJO-.js.map} +1 -1
- package/dist/{internal-stop-review-AUcFssrW.js → internal-stop-review-_1Mqz7eT.js} +2 -2
- package/dist/{internal-stop-review-AUcFssrW.js.map → internal-stop-review-_1Mqz7eT.js.map} +1 -1
- package/dist/{lifecycle-Byw5s1G8.js → lifecycle-Bd1Fyf4B.js} +100 -3
- package/dist/lifecycle-Bd1Fyf4B.js.map +1 -0
- package/dist/lifecycle-CRTWEbiP.js +2 -0
- package/dist/lifecycle-DUPXAOQS.js +2 -0
- package/dist/{lifecycle-DD88ka3f.js → lifecycle-DjC9tC1A.js} +2 -2
- package/dist/{lifecycle-DD88ka3f.js.map → lifecycle-DjC9tC1A.js.map} +1 -1
- package/dist/main.js +14 -14
- package/dist/{models-B6UnGyRo.js → models-uQgsKNNC.js} +3 -3
- package/dist/{models-B6UnGyRo.js.map → models-uQgsKNNC.js.map} +1 -1
- package/dist/{orchestration-hRU4TdWr.js → orchestration-BiZviyam.js} +2 -2
- package/dist/{orchestration-hRU4TdWr.js.map → orchestration-BiZviyam.js.map} +1 -1
- package/dist/{paths-C1cLZ7Ga.js → paths-BXrbboAS.js} +3 -3
- package/dist/{paths-C1cLZ7Ga.js.map → paths-BXrbboAS.js.map} +1 -1
- package/dist/paths-Dz1WyRJG.js +2 -0
- package/dist/{peer-mcp-personas-DYT9RZBk.js → peer-mcp-personas-V4AXhm9B.js} +346 -58
- package/dist/peer-mcp-personas-V4AXhm9B.js.map +1 -0
- package/dist/{plan-review-hook-C8x0uKPf.js → plan-review-hook-Bo4y1r3j.js} +2 -2
- package/dist/{plan-review-hook-C8x0uKPf.js.map → plan-review-hook-Bo4y1r3j.js.map} +1 -1
- package/dist/{prompt-submit-hook-BrkkLAAB.js → prompt-submit-hook-BFRFLSk5.js} +2 -2
- package/dist/{prompt-submit-hook-BrkkLAAB.js.map → prompt-submit-hook-BFRFLSk5.js.map} +1 -1
- package/dist/{provision-C2wCxRK_.js → provision-BLwsOFCI.js} +4 -4
- package/dist/{provision-C2wCxRK_.js.map → provision-BLwsOFCI.js.map} +1 -1
- package/dist/{serve-DHTgMHKU.js → serve-De-kqWXM.js} +10 -10
- package/dist/{serve-DHTgMHKU.js.map → serve-De-kqWXM.js.map} +1 -1
- package/dist/{server-setup-CJzxzYks.js → server-setup-BeeT8m-w.js} +6 -6
- package/dist/{server-setup-CJzxzYks.js.map → server-setup-BeeT8m-w.js.map} +1 -1
- package/dist/{start-DePkmXJt.js → start-DEC0M8UH.js} +8 -5
- package/dist/start-DEC0M8UH.js.map +1 -0
- package/dist/{stop-gate-hook-DQc2KV7B.js → stop-gate-hook-DoYD9Wt8.js} +2 -2
- package/dist/{stop-gate-hook-DQc2KV7B.js.map → stop-gate-hook-DoYD9Wt8.js.map} +1 -1
- package/dist/{stop-gate-policy-CHwWbuF_.js → stop-gate-policy-CoMKWSsS.js} +2 -2
- package/dist/{stop-gate-policy-CHwWbuF_.js.map → stop-gate-policy-CoMKWSsS.js.map} +1 -1
- package/dist/{token-R2ocFC3M.js → token-DWknHe9M.js} +23 -2
- package/dist/token-DWknHe9M.js.map +1 -0
- package/package.json +1 -1
- package/dist/claude-DZ1VmE7I.js.map +0 -1
- package/dist/codex-DwabDTOC.js.map +0 -1
- package/dist/engine-Cpu-wQ6p.js +0 -2
- package/dist/lifecycle-Byw5s1G8.js.map +0 -1
- package/dist/lifecycle-D80CaWie.js +0 -2
- package/dist/lifecycle-DzqCqDlM.js +0 -2
- package/dist/paths-CwKwc2pi.js +0 -2
- package/dist/peer-mcp-personas-DYT9RZBk.js.map +0 -1
- package/dist/start-DePkmXJt.js.map +0 -1
- package/dist/token-R2ocFC3M.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-DWknHe9M.js","names":["process"],"sources":["../src/lib/state.ts","../src/lib/api-config.ts","../src/lib/error.ts","../src/lib/upstream-retry.ts","../src/services/github/get-copilot-token.ts","../src/services/github/get-device-code.ts","../src/services/github/get-user.ts","../src/services/copilot/get-models.ts","../src/services/get-copilot-version.ts","../src/services/get-vscode-version.ts","../src/lib/editor-version-cache.ts","../src/lib/utils.ts","../src/services/github/poll-access-token.ts","../src/lib/token.ts"],"sourcesContent":["import { randomBytes, randomUUID } from \"node:crypto\"\n\nimport type { ModelsResponse } from \"~/services/copilot/get-models\"\n\nexport interface State {\n githubToken?: string\n copilotToken?: string\n\n /**\n * Second, WRITE-capable GitHub token for the first-mate\n * agent-orchestration surface (`--agents`). Minted by a separate\n * device-flow login against the GitHub CLI's OAuth client\n * (`GITHUB_AGENT_CLIENT_ID`, scopes `repo workflow read:org`) and\n * stored at `PATHS.GITHUB_AGENT_TOKEN_PATH`, apart from `githubToken`\n * (the Copilot App token, read:user). The first-mate GitHub service\n * layer uses this for ALL its calls (reads included, so it also works\n * on private repos). Undefined until the agent login has run.\n */\n githubAgentToken?: string\n\n accountType: string\n copilotApiUrl?: string\n models?: ModelsResponse\n vsCodeVersion?: string\n copilotVersion?: string\n\n manualApprove: boolean\n rateLimitWait: boolean\n showToken: boolean\n extendedBetas: boolean\n\n /**\n * Opt-in flag for the browser-control MCP tools (`browser_*`). Set by\n * `setupAndServe` from the `--browse` CLI flag or\n * `GH_ROUTER_ENABLE_BROWSE=1` env var. When false, all `browser_*`\n * tools are dropped from `tools/list` AND `tools/call` returns\n * -32601 — same defense-in-depth pattern as `workerToolsEnabled()` /\n * `standInToolEnabled()`. See `browserToolsEnabled()` in\n * `src/routes/mcp/handler.ts`.\n */\n browseEnabled: boolean\n\n /**\n * Opt-in flag for the fleet session-control MCP tools (`mcp__fleet__*`).\n * Set by `setupAndServe` from the `--fleet` CLI flag or\n * `GH_ROUTER_ENABLE_FLEET=1` env var. When false, all fleet tools are\n * dropped from `tools/list` AND `tools/call` returns -32601 — same\n * defense-in-depth pattern as `workerToolsEnabled()` /\n * `standInToolEnabled()`. See `fleetToolsEnabled()` in\n * `src/lib/mcp-capabilities.ts`.\n */\n fleetEnabled: boolean\n\n /**\n * Opt-in flag for the first-mate agent-orchestration MCP tools\n * (`mcp__first-mate__*`) and the in-process GitHub cloud-agent driving\n * layer. Set by `setupAndServe` from the `--agents` CLI flag or\n * `GH_ROUTER_ENABLE_AGENTS=1`. When false, the first-mate tools are\n * dropped from `tools/list` AND `tools/call` returns -32601 — same\n * defense-in-depth pattern as `fleetToolsEnabled()`. Additionally\n * requires `state.githubAgentToken` present (the write login), so the\n * surface is invisible rather than 401-ing when unauthenticated. See\n * `agentToolsEnabled()` in `src/lib/mcp-capabilities.ts`.\n */\n agentsEnabled: boolean\n\n /**\n * When true, --power-browse was passed (or GH_ROUTER_ENABLE_POWER_BROWSE=1\n * is set). Exposes the FULL browser MCP surface (~18 tools) on /mcp,\n * including the L0/L1 primitives that hand DOM details (refs,\n * bboxes, role/name dumps) to the lead model. Default --browse mode\n * exposes only the 6 lead-model tools (act, observe, extract,\n * navigate, screenshot, open_tab). Always implies browseEnabled.\n */\n powerBrowseEnabled: boolean\n\n /**\n * Humanlike pacing override:\n * \"on\" - --humanlike CLI flag or GH_ROUTER_HUMANLIKE=1 env;\n * inject Beta-distributed inter-action delays, Bezier\n * mouse paths, per-keystroke jitter, scroll chunking\n * into every browser_* action dispatch.\n * \"off\" - GH_ROUTER_BROWSER_NO_HUMANLIKE=1; HARD disable, wins\n * over \"on\" so tests are reproducible.\n * \"auto\" - default; pacing engages only when bot-challenge\n * detection fires (Phase 4-future).\n *\n * Lead model never sees this state — it's an internal concern.\n */\n humanlikeForce: \"on\" | \"off\" | \"auto\"\n\n // Rate limiting configuration\n rateLimitSeconds?: number\n lastRequestTimestamp?: number\n\n // Persistent session identifiers to match VS Code fingerprint\n sessionId: string\n machineId: string\n\n /**\n * Per-launch nonce for the loopback `/mcp` endpoint. Set by the\n * `claude` subcommand after `setupAndServe` and before spawning\n * Claude Code; the spawned MCP client reads it from the\n * `--mcp-config` tempfile and presents it as `Authorization: Bearer`.\n * When unset, `/mcp` rejects all requests — closes the\n * loopback-no-auth gap (DNS rebinding, malicious browser-ext\n * native messaging, sibling-process probe).\n */\n peerMcpNonce?: string\n\n /**\n * Set by `github-router serve` (a single machine-wide control plane serving\n * many repos). When true, repo-scoped worker tools require an explicit/So-derived\n * `workspace` and must NOT silently default to the proxy launch cwd.\n */\n serveMode?: boolean\n}\n\nexport const state: State = {\n accountType: \"enterprise\",\n manualApprove: false,\n rateLimitWait: false,\n showToken: false,\n extendedBetas: false,\n browseEnabled: false,\n fleetEnabled: false,\n agentsEnabled: false,\n powerBrowseEnabled: false,\n humanlikeForce: \"auto\",\n sessionId: randomUUID(),\n machineId: randomBytes(32).toString(\"hex\"),\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { State } from \"./state\"\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nconst DEFAULT_COPILOT_VERSION = \"0.43.2026033101\"\n\nexport function copilotVersion(state: State): string {\n return state.copilotVersion ?? DEFAULT_COPILOT_VERSION\n}\n\nconst API_VERSION = \"2026-01-09\"\n\nexport const copilotBaseUrl = (state: State) =>\n state.copilotApiUrl ?? \"https://api.githubcopilot.com\"\nexport const copilotHeaders = (\n state: State,\n vision: boolean = false,\n integrationId: string = \"vscode-chat\",\n) => {\n const version = copilotVersion(state)\n const headers: Record<string, string> = {\n Authorization: `Bearer ${state.copilotToken}`,\n \"content-type\": standardHeaders()[\"content-type\"],\n \"copilot-integration-id\": integrationId,\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": `copilot-chat/${version}`,\n \"user-agent\": `GitHubCopilotChat/${version}`,\n \"openai-intent\": \"conversation-panel\",\n \"x-interaction-type\": \"conversation-panel\",\n \"x-github-api-version\": API_VERSION,\n \"x-request-id\": randomUUID(),\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n \"VScode-SessionId\": state.sessionId,\n \"VScode-MachineId\": state.machineId,\n }\n\n if (vision) headers[\"copilot-vision-request\"] = \"true\"\n\n return headers\n}\n\nexport const GITHUB_API_BASE_URL =\n process.env.GITHUB_API_URL ?? \"https://api.github.com\"\nexport const githubHeaders = (state: State) => ({\n ...standardHeaders(),\n authorization: `token ${state.githubToken}`,\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": `copilot-chat/${copilotVersion(state)}`,\n \"user-agent\": `GitHubCopilotChat/${copilotVersion(state)}`,\n \"x-github-api-version\": API_VERSION,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n})\n\nexport const GITHUB_BASE_URL = \"https://github.com\"\nexport const GITHUB_CLIENT_ID = \"Iv1.b507a08c87ecfe98\"\nexport const GITHUB_APP_SCOPES = [\"read:user\"].join(\" \")\n\n// --- First-mate agent-orchestration write identity (`--agents`) ---------\n// The Copilot device-login token above (a GitHub *App* token, read:user)\n// can READ the agent surface but 403s on any write/push. The first-mate\n// controller needs a WRITE-capable token to create issues, assign cloud\n// coding agents, comment, dispatch workflows, and merge. We mint a second,\n// isolated token via a separate device-flow login against the GitHub CLI's\n// public OAuth client (a classic OAuth App that honors the requested\n// scopes — verified to accept `repo workflow read:org` at\n// `/login/device/code`). Stored apart at PATHS.GITHUB_AGENT_TOKEN_PATH;\n// never mixed with `state.githubToken`.\nexport const GITHUB_AGENT_CLIENT_ID = \"178c6fc778ccc68e1d6a\"\nexport const GITHUB_AGENT_SCOPES = [\"repo\", \"workflow\", \"read:org\"].join(\" \")\nexport const GITHUB_GRAPHQL_URL =\n process.env.GITHUB_GRAPHQL_URL ?? \"https://api.github.com/graphql\"\n// GitHub's documented REST/GraphQL API version for the general-purpose\n// (non-Copilot) surface the first-mate layer drives. The Agent-Tasks\n// preview client overrides this per-call with its own dated version.\nexport const GITHUB_REST_API_VERSION = \"2022-11-28\"\nexport const githubAgentHeaders = (state: State) => ({\n ...standardHeaders(),\n authorization: `token ${state.githubAgentToken}`,\n \"x-github-api-version\": GITHUB_REST_API_VERSION,\n \"user-agent\": \"github-router-first-mate\",\n})\nexport const githubAgentGraphQLHeaders = (state: State, features?: string) => ({\n ...githubAgentHeaders(state),\n ...(features ? { \"GraphQL-Features\": features } : {}),\n})\n","import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nexport class HTTPError extends Error {\n response: Response\n\n constructor(message: string, response: Response) {\n super(message)\n this.response = response\n }\n}\n\nexport async function forwardError(c: Context, error: unknown) {\n consola.error(`Error occurred at ${c.req.path}:`, error)\n\n if (error instanceof HTTPError) {\n const errorText = await error.response.text().catch(() => \"\")\n let errorJson: unknown\n try {\n errorJson = JSON.parse(errorText)\n } catch {\n errorJson = undefined\n }\n\n // Map upstream context-overflow errors (413, or 400 with a known\n // overflow substring) to Anthropic's \"prompt is too long\" 400 shape so\n // Claude Code triggers self-compaction instead of bubbling the error.\n // Note: a live probe of an oversized prompt against Copilot returned\n // 200 with stop_reason:\"refusal\" rather than 413/400 — this guard is\n // defensive for the documented Anthropic contract, not load-bearing.\n if (isContextOverflow(error.response.status, errorJson, errorText)) {\n const upstream = resolveErrorMessage(errorJson, errorText)\n consola.error(\"HTTP error (mapped to overflow):\", errorJson ?? errorText)\n return c.json(\n {\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: `prompt is too long: ${upstream}`,\n },\n },\n 400,\n )\n }\n\n // Remap upstream 401 to 503 — maintain the no-401 invariant on the\n // Anthropic-shape boundary. Claude Code's reactive refresh path\n // (function `SZ1` → `D3(0,true,...)` in v2.1.140 binary) fires on\n // any 401 from upstream and attempts to refresh the OAuth token.\n // Spawned-via-proxy sessions use a synthetic credential\n // (`ensureClaudeConfigMirror`'s SYNTHETIC_CREDENTIAL); refreshing\n // it would fail and degrade the session. Mapping 401 → 503 lets\n // the upstream message still reach the user while side-stepping\n // the refresh path. 503 maps to Anthropic's \"overloaded_error\"\n // type — semantically reasonable for \"proxy got an upstream\n // failure, retry later\".\n const responseStatus =\n error.response.status === 401 ? 503 : error.response.status\n\n // Forward upstream Anthropic-format errors as-is (with remapped status)\n if (isAnthropicError(errorJson)) {\n consola.error(\"HTTP error:\", errorJson)\n return c.json(errorJson, responseStatus as ContentfulStatusCode)\n }\n\n const message = resolveErrorMessage(errorJson, errorText)\n consola.error(\"HTTP error:\", errorJson ?? errorText)\n return c.json(\n {\n type: \"error\",\n error: {\n type: resolveErrorType(responseStatus),\n message,\n },\n },\n responseStatus as ContentfulStatusCode,\n )\n }\n\n return c.json(\n {\n type: \"error\",\n error: {\n type: \"api_error\",\n message: error instanceof Error ? error.message : String(error),\n },\n },\n 500,\n )\n}\n\n// Extracts error message from { message } or { error: { message } } payloads.\nfunction resolveErrorMessage(errorJson: unknown, fallback: string): string {\n if (typeof errorJson !== \"object\" || errorJson === null) return fallback\n\n const errorRecord = errorJson as Record<string, unknown>\n if (errorRecord.message !== undefined) return String(errorRecord.message)\n\n if (typeof errorRecord.error === \"object\" && errorRecord.error !== null) {\n const nestedRecord = errorRecord.error as Record<string, unknown>\n if (nestedRecord.message !== undefined) return String(nestedRecord.message)\n }\n\n return fallback\n}\n\n/**\n * Check if a parsed JSON body is already in Anthropic error format:\n * { type: \"error\", error: { type: \"...\", message: \"...\" } }\n */\nfunction isAnthropicError(json: unknown): boolean {\n if (typeof json !== \"object\" || json === null) return false\n const record = json as Record<string, unknown>\n if (record.type !== \"error\") return false\n if (typeof record.error !== \"object\" || record.error === null) return false\n const inner = record.error as Record<string, unknown>\n return typeof inner.type === \"string\" && typeof inner.message === \"string\"\n}\n\nconst CONTEXT_OVERFLOW_SUBSTRINGS = [\n \"prompt is too long\",\n \"context_length_exceeded\",\n \"context length exceeded\",\n \"input is too long\",\n \"maximum context length\",\n \"too many tokens\",\n]\n\n/**\n * Detect upstream context-overflow errors so we can remap them to a 400\n * \"prompt is too long\" shape that triggers Claude Code self-compaction.\n *\n * Always remaps 413 (treated as a hard payload-size signal regardless of\n * body wording). Remaps 400 only when the error text contains one of the\n * known overflow substrings — a regular 400 (e.g. \"model not found\") must\n * NOT remap.\n */\nexport function isContextOverflow(\n status: number,\n errorJson: unknown,\n errorText: string,\n): boolean {\n if (status === 413) return true\n if (status !== 400) return false\n\n const haystack = (\n errorText +\n \" \" +\n (typeof errorJson === \"object\" && errorJson !== null\n ? JSON.stringify(errorJson)\n : \"\")\n ).toLowerCase()\n\n return CONTEXT_OVERFLOW_SUBSTRINGS.some((s) => haystack.includes(s))\n}\n\n/**\n * Map HTTP status to Anthropic error type.\n *\n * Note: a 401 from upstream is remapped to 503 in `forwardError` BEFORE\n * this function is called (no-401 invariant — see comment there). The\n * 401 → \"authentication_error\" mapping below is preserved for\n * defensive coverage in case any code path calls `resolveErrorType`\n * directly with an unsanitized status.\n */\nfunction resolveErrorType(status: number): string {\n if (status === 400) return \"invalid_request_error\"\n if (status === 401) return \"authentication_error\"\n if (status === 403) return \"permission_error\"\n if (status === 404) return \"not_found_error\"\n if (status === 429) return \"rate_limit_error\"\n if (status === 503) return \"overloaded_error\"\n if (status === 529) return \"overloaded_error\"\n return \"api_error\"\n}\n","/**\n * Shared transient-failure retry for upstream calls (`src/lib/upstream-retry.ts`).\n *\n * Retries ONLY on transient conditions — never on success, never on a\n * deterministic 4xx (400/401/403/404…), so a malformed request or auth\n * failure fails fast instead of being hammered. 401 is intentionally NOT\n * retried here: it stays with the existing token-refresh path so the\n * 401→503 forwardError invariant is preserved.\n *\n * Retryable:\n * - HTTP 429 + 5xx (500/502/503/504) — the \"upstream is sick\" class.\n * - network errors (ECONNRESET / \"fetch failed\" / \"terminated\" / EPIPE …).\n * - upstream TIMEOUT aborts: an AbortError thrown while the CALLER'S\n * `signal` is NOT aborted (a user cancel aborts the caller's signal\n * and is rethrown immediately, never retried).\n *\n * Exponential backoff + FULL jitter, capped, honoring `Retry-After`. The\n * inter-attempt sleep is abortable so a user cancel during backoff\n * returns promptly. Bounded `attempts` keep a single call from holding an\n * inflight slot indefinitely (robust AND fast).\n *\n * Streaming note: the user-facing passthrough and the worker loop must\n * only retry in the PRE-FIRST-BYTE window — a retry after bytes have\n * streamed would duplicate output. Callers there pass a `doFetch` that\n * has not yet handed its body to the consumer.\n */\n\nimport consola from \"consola\"\n\nexport interface TransientRetryOpts {\n /** Total attempts including the first (default 3 → up to 2 retries). */\n attempts?: number\n /** Retryable HTTP statuses (default 429, 500, 502, 503, 504). */\n retryStatuses?: ReadonlyArray<number>\n /** Backoff base (default 250ms) and cap (default 4000ms). */\n baseDelayMs?: number\n maxDelayMs?: number\n /** Caller's abort signal — a user cancel fails fast (never retried). */\n signal?: AbortSignal\n /** Short label for debug logging (e.g. \"codex_critic\", \"advisor\"). */\n label?: string\n}\n\nconst DEFAULT_RETRY_STATUSES: ReadonlyArray<number> = [429, 500, 502, 503, 504]\n\nfunction parseRetryAfter(headerValue: string | null): number | undefined {\n if (!headerValue) return undefined\n const secs = Number(headerValue)\n if (Number.isFinite(secs)) return Math.max(0, secs * 1000)\n const dateMs = Date.parse(headerValue)\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now())\n return undefined\n}\n\n/**\n * A thrown fetch error that is worth retrying — network resets, broken\n * pipes, DNS hiccups, and timeout aborts. NOTE: the caller must already\n * have ruled out a user cancel (caller signal aborted) before calling\n * this, since a timeout abort and a user-cancel abort look identical.\n */\nfunction isTransientNetworkError(err: unknown): boolean {\n const e = err as\n | { name?: string; message?: string; code?: string; cause?: { code?: string } }\n | undefined\n if (!e) return false\n if (e.name === \"AbortError\" || e.name === \"TimeoutError\") return true\n const msg = (e.message ?? \"\").toLowerCase()\n if (\n msg.includes(\"terminated\") ||\n msg.includes(\"fetch failed\") ||\n msg.includes(\"network\") ||\n msg.includes(\"socket\") ||\n msg.includes(\"econnreset\") ||\n msg.includes(\"etimedout\") ||\n msg.includes(\"econnrefused\") ||\n msg.includes(\"epipe\") ||\n msg.includes(\"enotfound\")\n ) {\n return true\n }\n const code = e.code ?? e.cause?.code\n return (\n code !== undefined &&\n [\"ECONNRESET\", \"ETIMEDOUT\", \"ECONNREFUSED\", \"EPIPE\", \"ENOTFOUND\"].includes(code)\n )\n}\n\n/** Sleep that resolves early (does not reject) when `signal` aborts — the\n * retry loop re-checks `signal.aborted` at the top and throws there. */\nfunction abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n if (ms <= 0) return Promise.resolve()\n return new Promise<void>((resolve) => {\n let settled = false\n const done = (): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n signal?.removeEventListener(\"abort\", done)\n resolve()\n }\n const timer = setTimeout(done, ms)\n if (signal) {\n if (signal.aborted) {\n done()\n return\n }\n signal.addEventListener(\"abort\", done, { once: true })\n }\n })\n}\n\n/**\n * Run `doFetch` with bounded transient-failure retries. `doFetch` is a\n * thunk so each attempt issues a FRESH request (bodies can't be replayed\n * from a consumed stream). Returns the final `Response` — which may still\n * carry a retryable status if all attempts are exhausted (the caller\n * handles that as it would a single-shot failure).\n */\nexport async function fetchWithTransientRetry(\n doFetch: (attempt: number) => Promise<Response>,\n opts: TransientRetryOpts = {},\n): Promise<Response> {\n const attempts = Math.max(1, opts.attempts ?? 3)\n const retryStatuses = opts.retryStatuses ?? DEFAULT_RETRY_STATUSES\n const baseDelayMs = opts.baseDelayMs ?? 250\n const maxDelayMs = opts.maxDelayMs ?? 4000\n const { signal, label } = opts\n\n for (let attempt = 1; ; attempt++) {\n if (signal?.aborted) {\n throw new DOMException(\"This operation was aborted\", \"AbortError\")\n }\n\n let res: Response | undefined\n let caught: unknown\n try {\n res = await doFetch(attempt)\n } catch (err) {\n caught = err\n }\n\n // Success or a non-retryable status → done.\n if (res && !retryStatuses.includes(res.status)) return res\n\n // A thrown error: a user cancel fails fast; otherwise only retry the\n // transient network/timeout class.\n if (caught !== undefined) {\n if (signal?.aborted) throw caught\n if (!isTransientNetworkError(caught)) throw caught\n }\n\n // Out of attempts → return the last error response (or rethrow).\n if (attempt >= attempts) {\n if (res) return res\n throw caught\n }\n\n // Free the connection before retrying a retryable-status response.\n const retryAfterMs = res ? parseRetryAfter(res.headers.get(\"retry-after\")) : undefined\n if (res?.body) {\n try {\n await res.body.cancel()\n } catch {\n /* already torn down */\n }\n }\n\n // Full jitter (random within the exponential cap), Retry-After wins.\n const expCap = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1))\n const delay = Math.min(\n maxDelayMs,\n retryAfterMs ?? Math.round(Math.random() * expCap),\n )\n if (label) {\n const why = res\n ? `HTTP ${res.status}`\n : (caught as { name?: string } | undefined)?.name ?? \"error\"\n consola.debug(\n `[upstream-retry] ${label}: attempt ${attempt}/${attempts} failed (${why}); retrying in ${delay}ms`,\n )\n }\n await abortableSleep(delay, signal)\n }\n}\n\n/** Extract an HTTP status from a thrown error (HTTPError carries\n * `.response.status`; others may carry `.status`/`.statusCode`; last\n * resort parses `\"HTTP <code>\"` from the message). */\nfunction errorStatus(err: unknown): number | undefined {\n const e = err as\n | {\n status?: unknown\n statusCode?: unknown\n response?: { status?: unknown }\n message?: string\n }\n | undefined\n for (const v of [e?.status, e?.statusCode, e?.response?.status]) {\n if (typeof v === \"number\") return v\n }\n const m = /\\bHTTP (\\d{3})\\b/.exec(e?.message ?? \"\")\n return m ? Number(m[1]) : undefined\n}\n\n/**\n * Generic transient-retry for a non-`Response`-returning call (e.g. the\n * Copilot service clients, which throw `HTTPError` on non-OK and throw on\n * network errors). Retries `fn()` when it throws a transient error — an\n * `HTTPError`-like with a retryable status (429/5xx) OR a transient\n * network/timeout error — using the same backoff + abort semantics as\n * `fetchWithTransientRetry`. Never retries a deterministic 4xx (incl.\n * 401), a non-transient throw, or a user cancel.\n */\nexport async function withTransientRetry<T>(\n fn: (attempt: number) => Promise<T>,\n opts: TransientRetryOpts = {},\n): Promise<T> {\n const attempts = Math.max(1, opts.attempts ?? 3)\n const retryStatuses = opts.retryStatuses ?? DEFAULT_RETRY_STATUSES\n const baseDelayMs = opts.baseDelayMs ?? 250\n const maxDelayMs = opts.maxDelayMs ?? 4000\n const { signal, label } = opts\n\n for (let attempt = 1; ; attempt++) {\n if (signal?.aborted) {\n throw new DOMException(\"This operation was aborted\", \"AbortError\")\n }\n try {\n return await fn(attempt)\n } catch (err) {\n if (signal?.aborted) throw err\n const status = errorStatus(err)\n const retryable =\n (status !== undefined && retryStatuses.includes(status)) ||\n isTransientNetworkError(err)\n if (!retryable || attempt >= attempts) throw err\n\n const retryAfterMs = parseRetryAfter(\n (err as { response?: { headers?: { get?: (k: string) => string | null } } })\n ?.response?.headers?.get?.(\"retry-after\") ?? null,\n )\n const expCap = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1))\n const delay = Math.min(\n maxDelayMs,\n retryAfterMs ?? Math.round(Math.random() * expCap),\n )\n if (label) {\n consola.debug(\n `[upstream-retry] ${label}: attempt ${attempt}/${attempts} threw (${\n status !== undefined ? `HTTP ${status}` : (err as { name?: string })?.name ?? \"error\"\n }); retrying in ${delay}ms`,\n )\n }\n await abortableSleep(delay, signal)\n }\n }\n}\n","import consola from \"consola\"\n\nimport { GITHUB_API_BASE_URL, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\nimport { fetchWithTransientRetry } from \"~/lib/upstream-retry\"\n\n/**\n * Allowlist of hosts the router will trust as the Copilot API base URL.\n * Anything else returned in `endpoints.api` (e.g. via a tampered or\n * misconfigured token-exchange response) is rejected — otherwise a\n * malicious value would receive the long-lived GitHub PAT we send to\n * `/mcp` for web search (see `src/services/copilot/web-search.ts`).\n */\nconst COPILOT_HOST_ALLOWLIST = [\n \"api.githubcopilot.com\",\n \"api.individual.githubcopilot.com\",\n \"api.business.githubcopilot.com\",\n \"api.enterprise.githubcopilot.com\",\n]\n\n/**\n * True iff `rawUrl` is an HTTPS URL whose host is a trusted Copilot API host.\n * Exported so every path that sends a long-lived token to a discovered\n * Copilot host (web search, first-mate CAPI session logs) shares one allowlist.\n */\nexport function isAllowedCopilotHost(rawUrl: string): boolean {\n let parsed: URL\n try {\n parsed = new URL(rawUrl)\n } catch {\n return false\n }\n if (parsed.protocol !== \"https:\") return false\n return COPILOT_HOST_ALLOWLIST.includes(parsed.hostname)\n}\n\nexport const getCopilotToken = async () => {\n // GitHub PAT → Copilot token exchange. A transient 429/5xx/network blip\n // here aborts launch (and the interval-driven refresh that keeps the\n // session alive), so retry the transient class with bounded backoff. NO\n // 401-refresh compose: this call IS the token source, and a 401 means a\n // bad/expired GitHub PAT — deterministic, fail fast (retrying would burn\n // budget against a credential that can't recover without re-auth).\n const response = await fetchWithTransientRetry(\n () =>\n fetch(`${GITHUB_API_BASE_URL}/copilot_internal/v2/token`, {\n headers: githubHeaders(state),\n }),\n { label: \"/copilot_internal/v2/token\" },\n )\n\n if (!response.ok) throw new HTTPError(\"Failed to get Copilot token\", response)\n\n const data = (await response.json()) as GetCopilotTokenResponse\n\n // Use the API base URL from the token response if available, matching\n // how VS Code determines the CAPI endpoint dynamically — but only when\n // it points at a github-controlled host (see allowlist above).\n // We deliberately do NOT clobber an existing `state.copilotApiUrl` in\n // the disallowed branch: when the user sets `COPILOT_API_URL` themselves\n // (e.g. for local testing or a CI mock), that's an explicit opt-in and\n // a different threat model than a tampered token-exchange response.\n // Allowlist-failing token-response values are simply ignored.\n if (data.endpoints?.api) {\n if (isAllowedCopilotHost(data.endpoints.api)) {\n state.copilotApiUrl = data.endpoints.api\n } else {\n consola.warn(\n `Refusing to honor Copilot API endpoint \"${data.endpoints.api}\" from ` +\n `the token-exchange response — not in allowlist ` +\n `(${COPILOT_HOST_ALLOWLIST.join(\", \")}). ` +\n (state.copilotApiUrl\n ? `Keeping existing override \"${state.copilotApiUrl}\".`\n : `Falling back to the default api.githubcopilot.com.`),\n )\n }\n }\n\n return data\n}\n\ninterface GetCopilotTokenResponse {\n expires_at: number\n refresh_in: number\n token: string\n endpoints?: {\n api?: string\n proxy?: string\n telemetry?: string\n \"origin-tracker\"?: string\n }\n}\n","import {\n GITHUB_APP_SCOPES,\n GITHUB_BASE_URL,\n GITHUB_CLIENT_ID,\n standardHeaders,\n} from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { fetchWithTransientRetry } from \"~/lib/upstream-retry\"\n\nexport interface DeviceApp {\n clientId: string\n scope: string\n}\n\nconst DEFAULT_DEVICE_APP: DeviceApp = {\n clientId: GITHUB_CLIENT_ID,\n scope: GITHUB_APP_SCOPES,\n}\n\nexport async function getDeviceCode(\n app: DeviceApp = DEFAULT_DEVICE_APP,\n): Promise<DeviceCodeResponse> {\n // Idempotent device-code bootstrap POST (just requests a fresh code) — a\n // transient 429/5xx/network blip here aborts the whole login flow, so\n // retry the transient class. No auth on this call, so no 401 concern.\n //\n // `app` is parameterized so a SECOND identity (the first-mate write\n // token via the GitHub CLI OAuth client) can reuse this exact flow\n // without a copy-paste fork; defaults preserve the original Copilot\n // App device login.\n const response = await fetchWithTransientRetry(\n () =>\n fetch(`${GITHUB_BASE_URL}/login/device/code`, {\n method: \"POST\",\n headers: standardHeaders(),\n body: JSON.stringify({\n client_id: app.clientId,\n scope: app.scope,\n }),\n }),\n { label: \"/login/device/code\" },\n )\n\n if (!response.ok) throw new HTTPError(\"Failed to get device code\", response)\n\n return (await response.json()) as DeviceCodeResponse\n}\n\nexport interface DeviceCodeResponse {\n device_code: string\n user_code: string\n verification_uri: string\n expires_in: number\n interval: number\n}\n","import { GITHUB_API_BASE_URL, standardHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\nimport { fetchWithTransientRetry } from \"~/lib/upstream-retry\"\n\nexport async function getGitHubUser() {\n // GitHub PAT GET — retry transient 429/5xx/network; a 401 (bad PAT) fails\n // fast (not retried by the helper).\n const response = await fetchWithTransientRetry(\n () =>\n fetch(`${GITHUB_API_BASE_URL}/user`, {\n headers: {\n authorization: `token ${state.githubToken}`,\n ...standardHeaders(),\n },\n }),\n { label: \"/user\" },\n )\n\n if (!response.ok) throw new HTTPError(\"Failed to get GitHub user\", response)\n\n return (await response.json()) as GithubUserResponse\n}\n\n// Trimmed for the sake of simplicity\ninterface GithubUserResponse {\n login: string\n}\n","import { copilotBaseUrl, copilotHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\nimport { tryRefreshAndRetry } from \"~/lib/token\"\nimport { fetchWithTransientRetry } from \"~/lib/upstream-retry\"\n\nexport const getModels = async () => {\n // Startup catalog fetch — a transient 429/5xx/network blip here leaves\n // the model catalog empty for the whole session, so retry it. This is a\n // Copilot-token GET, so it keeps the 401-refresh path\n // (`tryRefreshAndRetry`) nested INSIDE the transient retry: 401 →\n // refresh once (never retried by the transient layer); 429/5xx/network\n // → bounded retry with backoff. A consumed (non-streamed) GET body is\n // safe to replay.\n const response = await fetchWithTransientRetry(\n () =>\n tryRefreshAndRetry(\n () =>\n fetch(`${copilotBaseUrl(state)}/models`, {\n headers: copilotHeaders(state),\n }),\n \"/models\",\n ),\n { label: \"/models\" },\n )\n\n if (!response.ok) throw new HTTPError(\"Failed to get models\", response)\n\n return (await response.json()) as ModelsResponse\n}\n\nexport interface ModelsResponse {\n data: Array<Model>\n object: string\n}\n\ninterface ModelLimits {\n max_context_window_tokens?: number\n max_output_tokens?: number\n max_prompt_tokens?: number\n max_inputs?: number\n max_non_streaming_output_tokens?: number\n vision?: {\n max_prompt_image_size?: number\n max_prompt_images?: number\n supported_media_types?: string[]\n }\n}\n\ninterface ModelSupports {\n tool_calls?: boolean\n parallel_tool_calls?: boolean\n dimensions?: boolean\n streaming?: boolean\n vision?: boolean\n structured_outputs?: boolean\n adaptive_thinking?: boolean\n max_thinking_budget?: number\n min_thinking_budget?: number\n reasoning_effort?: Array<string>\n}\n\ninterface ModelCapabilities {\n family: string\n limits?: ModelLimits\n object: string\n supports?: ModelSupports\n tokenizer: string\n type: string\n}\n\nexport interface Model {\n capabilities: ModelCapabilities\n id: string\n model_picker_enabled: boolean\n name: string\n object: string\n preview: boolean\n vendor: string\n version: string\n supported_endpoints?: Array<string>\n requestHeaders?: Record<string, string>\n policy?: {\n state: string\n terms: string\n }\n billing?: {\n is_premium: boolean\n multiplier: number\n restricted_to?: string[]\n /**\n * Per-token prices, scaled by 1e9. Present on the live catalog even\n * where `is_premium` / `multiplier` are absent (usage-based-billing\n * accounts populate this instead), which is why both shapes are\n * modelled: reading only the premium pair silently yields nothing\n * on those accounts.\n */\n token_prices?: {\n input_price?: number\n output_price?: number\n cache_price?: number\n batch_size?: number\n }\n }\n is_chat_default?: boolean\n is_chat_fallback?: boolean\n model_picker_category?: string\n /** Vendor-authored cost tier (`low` | `medium` | `high` | `very_high`). */\n model_picker_price_category?: string\n info_messages?: Array<{ code: string; message: string }>\n warning_messages?: Array<{ code: string; message: string }>\n}\n","/**\n * Last-resort floor when the lookup fails. A fallback must never become the\n * cached steady state — see the note on `VSCODE_VERSION_FALLBACK` in\n * `./get-vscode-version`.\n */\nexport const COPILOT_CHAT_VERSION_FALLBACK = \"0.43.2026033101\"\nconst FALLBACK = COPILOT_CHAT_VERSION_FALLBACK\n\ninterface MarketplaceResult {\n results: Array<{\n extensions: Array<{\n versions: Array<{ version: string }>\n }>\n }>\n}\n\n/**\n * The lookup, reporting failure as `undefined` rather than as the fallback.\n * See `getVSCodeVersionOrUndefined` for why the distinction matters to the\n * version cache.\n */\nexport async function getCopilotChatVersionOrUndefined(): Promise<\n string | undefined\n> {\n const controller = new AbortController()\n const timeout = setTimeout(() => {\n controller.abort()\n }, 5000)\n\n try {\n const response = await fetch(\n \"https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery\",\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json;api-version=7.1-preview.1\",\n },\n body: JSON.stringify({\n filters: [\n {\n criteria: [{ filterType: 7, value: \"GitHub.copilot-chat\" }],\n },\n ],\n flags: 914,\n }),\n signal: controller.signal,\n },\n )\n\n if (!response.ok) return undefined\n\n const data = (await response.json()) as MarketplaceResult\n const version =\n data?.results?.[0]?.extensions?.[0]?.versions?.[0]?.version\n\n return version\n } catch {\n return undefined\n } finally {\n clearTimeout(timeout)\n }\n}\n\n/** Fallback-substituting wrapper, for callers that just want a version. */\nexport async function getCopilotChatVersion(): Promise<string> {\n return (await getCopilotChatVersionOrUndefined()) ?? FALLBACK\n}\n","/**\n * Last-resort floor when the lookup fails. A fallback must never become the\n * cached steady state, or a transient outage would freeze the version we\n * impersonate. See `src/lib/editor-version-cache.ts`.\n */\nexport const VSCODE_VERSION_FALLBACK = \"1.104.3\"\nconst FALLBACK = VSCODE_VERSION_FALLBACK\n\n/**\n * The lookup, reporting failure as `undefined` rather than as the fallback.\n *\n * The distinction is load-bearing for the cache: it must not infer \"the fetch\n * failed\" from `value === FALLBACK`, because the live version legitimately\n * equals the constant right after someone bumps `VSCODE_VERSION_FALLBACK` to\n * the then-current release — and that reading would refuse to cache a perfectly\n * good result, making every launch re-pay the ~1.5s network call forever.\n */\nexport async function getVSCodeVersionOrUndefined(): Promise<string | undefined> {\n const controller = new AbortController()\n const timeout = setTimeout(() => {\n controller.abort()\n }, 5000)\n\n try {\n const response = await fetch(\n \"https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=visual-studio-code-bin\",\n {\n signal: controller.signal,\n },\n )\n\n const pkgbuild = await response.text()\n const pkgverRegex = /pkgver=([0-9.]+)/\n const match = pkgbuild.match(pkgverRegex)\n\n return match ? match[1] : undefined\n } catch {\n return undefined\n } finally {\n clearTimeout(timeout)\n }\n}\n\n/** Fallback-substituting wrapper, for callers that just want a version. */\nexport async function getVSCodeVersion(): Promise<string> {\n return (await getVSCodeVersionOrUndefined()) ?? FALLBACK\n}\n\n","/**\n * Disk-backed TTL cache for the EDITOR IDENTITY versions the proxy sends\n * upstream: the VS Code version (`editor-version`) and the Copilot Chat\n * extension version (`editor-plugin-version`), see `src/lib/api-config.ts`.\n *\n * Why this exists: impersonating VS Code is non-negotiable — Copilot is\n * addressed as if by the real editor, so those headers must carry real,\n * current versions. But the two lookups are NETWORK calls to third parties\n * (the Arch AUR PKGBUILD and the VS Marketplace), and they sat on the\n * blocking launch path of EVERY invocation. The AUR fetch alone measured\n * ~1.5-1.8s, which every `github-router` process paid — including the\n * per-turn Claude Code hooks (`internal-prompt-submit`, `internal-stop-hook`,\n * …), each of which is a fresh `node dist/main.js` spawn.\n *\n * The fix is a cache, NOT a removal. Both fetches are still made; they are\n * just made at most once per `TTL_HOURS` instead of once (twice, before the\n * duplicate top-level await in `get-vscode-version.ts` was dropped) per\n * launch. A 12h TTL keeps the impersonated versions well inside the real\n * editor's release cadence.\n *\n * Two invariants that keep impersonation honest:\n *\n * 1. A FALLBACK is NEVER cached. `getVSCodeVersion()` /\n * `getCopilotChatVersion()` return a hardcoded constant when the network\n * fails, and that constant must stay a last-resort floor for the current\n * process — never the persisted steady state. Persisting it would let a\n * transient outage silently freeze the advertised version for 12h, and\n * (worse) survive restarts. `writeEntry` is therefore only reached on a\n * value that differs from the fallback.\n * 2. The timestamp is written only on SUCCESS. An offline machine retries\n * on the next launch rather than pinning a stale entry, which is the\n * opposite of `claude-version-check.ts`'s throttle (that one stamps\n * after the probes regardless, so an offline box pays both timeouts\n * hourly). Do not copy that detail here.\n *\n * The file lives beside `last-update-check` / `last-self-update-check` and is\n * deliberately named distinctly so the three throttles never cross-suppress.\n */\n\nimport fs from \"node:fs/promises\"\nimport path from \"node:path\"\nimport process from \"node:process\"\n\nimport consola from \"consola\"\n\nimport { PATHS } from \"./paths\"\n\nconst TTL_HOURS = 12\n\n/** One cached lookup: the resolved value plus when it was resolved. */\ninterface CacheEntry {\n value: string\n checkedAt: string\n}\n\ninterface EditorVersionCache {\n vscode?: CacheEntry\n copilotChat?: CacheEntry\n}\n\n/** Which upstream identity a cached entry belongs to. */\nexport type EditorVersionKey = \"vscode\" | \"copilotChat\"\n\nfunction cacheFilePath(): string {\n return path.join(PATHS.APP_DIR, \"last-editor-versions\")\n}\n\n/**\n * True when running under a test runner.\n *\n * Detected from the entry file, not an env var: `bun test` sets no marker of\n * its own and this repo builds with `NODE_ENV=production`, so `argv[1]` (the\n * spec file being executed) is the reliable signal. `BUN_TEST` / `NODE_ENV`\n * are still honored for other runners.\n *\n * The cache's own tests sandbox `homedir()` and opt back in via\n * `__allowEditorVersionWritesForTests`, so they still cover the write path.\n */\nfunction isTestRunner(): boolean {\n if (_allowWriteInTests) return false\n if (process.env.BUN_TEST === \"1\" || process.env.NODE_ENV === \"test\") {\n return true\n }\n const entry = process.argv[1] ?? \"\"\n return /\\.(test|spec)\\.[cm]?[jt]sx?$/.test(entry)\n}\n\n/** Set by the cache's own (sandboxed) tests so they can cover the write path. */\nlet _allowWriteInTests = false\n\n/**\n * Test-only: re-enable persistence. ONLY safe from a test that has redirected\n * `homedir()` to a temp dir — otherwise it writes the caller's fixture into the\n * developer's real cache and the proxy advertises it upstream.\n */\nexport function __allowEditorVersionWritesForTests(on: boolean): void {\n _allowWriteInTests = on\n}\n\nasync function readCache(): Promise<EditorVersionCache> {\n try {\n const parsed = JSON.parse(\n await fs.readFile(cacheFilePath(), \"utf8\"),\n ) as EditorVersionCache\n // A hand-edited or truncated file must degrade to \"no cache\", not throw.\n if (!parsed || typeof parsed !== \"object\") return {}\n return parsed\n } catch {\n return {}\n }\n}\n\nfunction isFresh(entry: CacheEntry | undefined): entry is CacheEntry {\n if (!entry || typeof entry.value !== \"string\" || entry.value.length === 0) {\n return false\n }\n const last = new Date(entry.checkedAt).getTime()\n if (Number.isNaN(last)) return false\n // A clock moved backwards would make `age` negative; treating that as stale\n // costs one refetch, whereas treating it as fresh could pin a bad value.\n const ageHours = (Date.now() - last) / 1000 / 3600\n return ageHours >= 0 && ageHours < TTL_HOURS\n}\n\n/**\n * Serializes the read-modify-write below.\n *\n * `setupAndServe` resolves both keys concurrently (`Promise.all`). Without\n * this, both callers would `readCache()` into their OWN object, spend ~1.5s in\n * their network fetch, and then write that stale object back — so whichever\n * finished last would clobber the other's entry, and one of the two versions\n * would re-fetch on every launch forever.\n *\n * The chain also re-reads immediately before mutating, so the write is against\n * current disk state rather than the snapshot taken before the fetch.\n */\nlet writeChain: Promise<void> = Promise.resolve()\n\nfunction serializeWrite(fn: () => Promise<void>): Promise<void> {\n const next = writeChain.then(fn, fn)\n // Never let a rejection poison the chain for later writers.\n writeChain = next.catch(() => {})\n return next\n}\n\n/**\n * Resolve `key` through the cache, calling `fetchFresh` only on a miss.\n *\n * `fetchFresh` signals failure by returning `undefined` — NOT by returning the\n * fallback constant. Inferring failure from `value === fallback` would break\n * exactly when the live version happens to equal the hardcoded constant, which\n * is the normal state right after someone bumps that constant to the\n * then-current version: the entry would never be written and every launch\n * would pay the ~1.5s fetch forever, silently defeating the cache.\n *\n * `fallback` is returned when the lookup fails and no usable cached value\n * exists. It is never persisted (invariant 1 above), so a transient outage\n * cannot freeze the version we impersonate.\n */\nexport async function resolveEditorVersion(\n key: EditorVersionKey,\n fetchFresh: () => Promise<string | undefined>,\n fallback: string,\n): Promise<string> {\n const entry = (await readCache())[key]\n const cachedValue = isFresh(entry) ? entry.value : undefined\n if (cachedValue !== undefined) return cachedValue\n\n const fetched = await fetchFresh()\n\n if (fetched !== undefined && fetched.length > 0) {\n // Never persist from a test process. The cached value is sent upstream as\n // the `editor-version` header, so a test that stubs the fetcher would\n // otherwise write its fixture into the DEVELOPER'S real cache and the\n // proxy would advertise it to Copilot for the full 12h TTL. That is not\n // hypothetical — a smoke test caught `editor-version: vscode/1.2.3` live\n // on this machine, sourced from a unit-test stub.\n //\n // Belt-and-braces with sandboxing the cache's own tests: any FUTURE test\n // that reaches this function through some other caller is covered too,\n // without every such test having to remember to mock homedir(). Reads stay\n // enabled, so warm-cache behavior is still exercisable.\n if (isTestRunner()) return fetched\n await serializeWrite(async () => {\n // Re-read INSIDE the chain: another key's write may have landed while\n // this one was in its fetch, and that entry must survive.\n const cache = await readCache()\n cache[key] = { value: fetched, checkedAt: new Date().toISOString() }\n try {\n await fs.mkdir(path.dirname(cacheFilePath()), { recursive: true })\n await fs.writeFile(cacheFilePath(), JSON.stringify(cache), {\n mode: 0o600,\n })\n } catch (err) {\n // A write failure only costs a refetch next launch.\n consola.debug(\"Failed to write editor-version cache:\", err)\n }\n })\n return fetched\n }\n\n // The lookup failed. A stale-but-real cached value beats the hardcoded\n // constant: a version that was genuinely current when it was cached is a\n // closer impersonation than one frozen at release time. Left un-restamped,\n // so the next launch still retries.\n if (typeof entry?.value === \"string\" && entry.value.length > 0) {\n return entry.value\n }\n return fallback\n}\n\n/** Test seam: the on-disk path, so tests can point HOME at a temp dir. */\nexport function editorVersionCachePath(): string {\n return cacheFilePath()\n}\n","import consola from \"consola\"\n\nimport { getModels } from \"~/services/copilot/get-models\"\nimport {\n COPILOT_CHAT_VERSION_FALLBACK,\n getCopilotChatVersionOrUndefined,\n} from \"~/services/get-copilot-version\"\nimport {\n getVSCodeVersionOrUndefined,\n VSCODE_VERSION_FALLBACK,\n} from \"~/services/get-vscode-version\"\n\nimport { resolveEditorVersion } from \"./editor-version-cache\"\nimport { state } from \"./state\"\n\nexport const sleep = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nexport const isNullish = (value: unknown): value is null | undefined =>\n value === null || value === undefined\n\n/**\n * Beta prefixes VS Code Copilot Chat v0.43 actually sends.\n * Default mode — makes proxy traffic indistinguishable from VS Code.\n */\nconst VSCODE_BETA_PREFIXES = [\n \"interleaved-thinking-\",\n \"context-management-\",\n \"advanced-tool-use-\",\n]\n\n/**\n * Extended beta prefixes for Claude CLI compatibility.\n * Enabled via --extended-betas flag. Includes all betas confirmed\n * to work with the Copilot API.\n *\n * Notably absent (Copilot 400s on these — verified live):\n * context-1m-, skills-, files-api-, code-execution-, output-128k-,\n * advisor-tool- (see EXPLICITLY_STRIPPED_BETA_PREFIXES below).\n * 1M context is unlocked by selecting `claude-opus-4.7-1m-internal`\n * as the model id, not via a beta header.\n *\n * Empirical verification (2026-05-11 against api.enterprise.githubcopilot.com):\n * task-budgets-2026-03-13 → 200 ACCEPTED (cost-ceiling leverage)\n * token-efficient-tools-2026-03-28 → 200 ACCEPTED (per-tool token saving)\n * summarize-connector-text-2026-03-13 → 200 (Anthropic-internal feature flag,\n * won't fire for non-ant users; allowlisted defensively for ant edge case)\n * afk-mode-2026-01-31 → 200 (Anthropic-internal feature flag)\n * cli-internal-2026-02-09 → 200 (USER_TYPE=ant only)\n * oauth-2025-04-20 → 200 (Files-API path; Files-API itself\n * is not supportable via Copilot, but the header alone is harmless)\n * prompt-caching-scope-2026-01-05 → 200 even with body cache_control.scope\n * stripped (already covered by `prompt-caching-` prefix above)\n */\nconst EXTENDED_BETA_PREFIXES = [\n ...VSCODE_BETA_PREFIXES,\n \"claude-code-\",\n \"effort-\",\n \"prompt-caching-\",\n \"computer-use-\",\n \"pdfs-\",\n \"max-tokens-\",\n \"token-counting-\",\n \"compact-\",\n \"structured-outputs-\",\n \"fast-mode-\",\n \"mcp-client-\",\n \"mcp-servers-\",\n \"redact-thinking-\",\n \"web-search-\",\n // Empirically accepted by Copilot, sent by Claude Code v2.1.138+\n \"task-budgets-\",\n \"token-efficient-tools-\",\n // Anthropic-internal feature flags (won't reach proxy from non-ant users\n // due to Bun build-time dead-code elimination, but allowlisted so the rare\n // ant-user / managed-deployment case flows cleanly).\n \"summarize-connector-text-\",\n \"afk-mode-\",\n \"cli-internal-\",\n \"oauth-\",\n]\n\n/**\n * Beta prefixes the proxy explicitly STRIPS even from the extended\n * allowlist (and even if a future leverage mode broadens the allowlist\n * further). Defensive layer: today's allowlist-only filter would already\n * drop these because they're not in any allowlist, but keeping an\n * explicit deny-list catches future changes that broaden allow rules\n * (e.g. a hypothetical pattern-based mode that lets `claude-*` through).\n *\n * Empirical (2026-05-11): Copilot returns HTTP 400\n * `unsupported beta header(s): advisor-tool-2026-03-01`\n * on every request that includes `advisor-tool-`. Stripping it is the\n * difference between a working request (no ADVISOR semantics) and a\n * fully-failed request. Document upstream limitation in CLAUDE.md.\n */\nconst EXPLICITLY_STRIPPED_BETA_PREFIXES = [\n \"advisor-tool-\",\n] as const\n\n/**\n * Filter an `anthropic-beta` header value, keeping only beta flags\n * in the active whitelist AND not in the explicit-strip list.\n * Uses extended prefixes when --extended-betas is enabled, VS Code-only\n * prefixes otherwise. Returns the filtered comma-separated string,\n * or undefined if nothing remains.\n */\nexport function filterBetaHeader(value: string): string | undefined {\n const prefixes = state.extendedBetas\n ? EXTENDED_BETA_PREFIXES\n : VSCODE_BETA_PREFIXES\n const filtered = value\n .split(\",\")\n .map((v) => v.trim())\n .filter(\n (v) =>\n v\n && prefixes.some((prefix) => v.startsWith(prefix))\n && !EXPLICITLY_STRIPPED_BETA_PREFIXES.some((p) => v.startsWith(p)),\n )\n .join(\",\")\n return filtered || undefined\n}\n\n/**\n * Normalize a model ID for fuzzy comparison: lowercase, replace dots with\n * dashes, insert dash at letter→digit boundaries, and collapse repeated\n * dashes. E.g. \"gpt5.3-codex\" → \"gpt-5-3-codex\", \"GPT-5.3-Codex\" → \"gpt-5-3-codex\".\n */\nexport function normalizeModelId(id: string): string {\n return id\n .toLowerCase()\n .replace(/\\./g, \"-\")\n .replace(/([a-z])(\\d)/g, \"$1-$2\")\n .replace(/-{2,}/g, \"-\")\n}\n\n/**\n * Resolve a model name to the best available variant in the Copilot model list.\n *\n * Resolution cascade:\n * 0. `[1m]` literal-bracket suffix: strip, delegate, warn if downgraded.\n * Bracketed slug must never reach Copilot (400s on it). See cc-backup\n * `src/utils/context.ts:35-40` for Claude Code's 1M unlock mechanism.\n * 1. Exact match\n * 2. Case-insensitive match\n * 3. Family preference (opus→1m, codex→highest version)\n * 4. Normalized match (dots→dashes, letter-digit boundaries)\n * 5. Anthropic dated-slug retry: if the input matches `claude-...-YYYYMMDD`,\n * strip the date and re-run the cascade once. Family-guarded so non-claude\n * 8-digit suffixes can't be mis-stripped; runs after Steps 1-4 so explicit\n * version pinning (a dated catalog id matched at Step 1) always wins.\n * 6. Return as-is with a warning\n */\n/**\n * `[1m]`-downgrade warnings we've already emitted this process. `resolveModel`\n * is called many times per launch (env setup, model validation, the banner,\n * and — under `serve` — on every model-picker `/models` poll), so the downgrade\n * notice is deduped to once per model id to avoid log spam.\n */\nconst warnedOneMDowngrade = new Set<string>()\n\n/** Test-only: clear the once-per-process `[1m]`-downgrade warning cache. */\nexport function __resetOneMDowngradeWarnings(): void {\n warnedOneMDowngrade.clear()\n}\n\nexport function resolveModel(modelId: string): string {\n const models = state.models?.data\n if (!models) return modelId\n\n // [1m] literal-bracket suffix: Claude Code's request for 1M context\n // accounting. cc-backup `src/utils/context.ts:35-40` has1mContext\n // matches `/\\[1m\\]/i`; getContextWindowForModel returns 1_000_000\n // when true. parseUserSpecifiedModel (model.ts:445-506) reattaches\n // the bracket after alias resolution, so Claude Code SENDS the\n // bracketed slug verbatim on the wire (`model: \"claude-opus-4-7[1m]\"`).\n // Copilot doesn't recognize the bracket → 400.\n //\n // Strip for the catalog lookup and delegate. If the stripped\n // resolution lands on a `-1m` variant (enterprise opus path via\n // family preference), perfect — the upstream call routes to the 1M\n // backend and Claude Code's local accounting was right. Otherwise\n // (non-enterprise for opus, or any [1m] on sonnet/haiku where\n // Copilot has no -1m backend), warn and return the 200K resolution\n // so the request still succeeds — at the cost of Claude Code\n // over-accounting context against the proxy (it will compact early\n // because it thinks the window is 1M).\n //\n // Bounded recursion: the stripped form no longer matches the regex,\n // so the inner resolveModel call cannot re-enter this branch.\n const oneMMatch = modelId.match(/^(.*)\\[1m\\]$/i)\n if (oneMMatch) {\n const stripped = oneMMatch[1]\n const resolved = resolveModel(stripped)\n // The resolved slug delivers 1M context either as a `-1m` variant OR\n // as a base slug that natively advertises a >=1M window (how opus-4.8\n // and opus-5 ship — a single slug, no `-1m` sibling). Only warn about a\n // real downgrade: a resolved model whose catalog window is under 1M.\n const resolvedIs1M =\n /-1m(?:$|-)/.test(resolved)\n || (models.find((m) => m.id === resolved)?.capabilities?.limits\n ?.max_context_window_tokens ?? 0) >= 1_000_000\n if (!resolvedIs1M && !warnedOneMDowngrade.has(modelId)) {\n warnedOneMDowngrade.add(modelId)\n consola.warn(\n `Model \"${modelId}\" requested 1M context but no 1M backend is in Copilot's catalog for this tier/family; downgrading upstream to \"${resolved}\" (200K). Claude Code's local context accounting will still assume 1M — expect premature auto-compact. Drop the [1m] suffix (or unset CLAUDE_CODE_DISABLE_1M_CONTEXT if you set it) to silence.`,\n )\n }\n return resolved\n }\n\n // 1. Exact match\n if (models.some((m) => m.id === modelId)) return modelId\n\n // 2. Case-insensitive match\n const lower = modelId.toLowerCase()\n const ciMatch = models.find((m) => m.id.toLowerCase() === lower)\n if (ciMatch) return ciMatch.id\n\n // 3. Family preference — before normalization so product aliases\n // (opus→1m, codex→latest) take priority over fuzzy matches\n if (lower.includes(\"opus\")) {\n // Match ...-1m or ...-1m-<anything> (e.g. claude-opus-4.7-1m-internal).\n // Prefer the 1M variant whose major.minor matches the requested version,\n // otherwise find() would silently downgrade claude-opus-4.7 to a\n // claude-opus-4.6-1m if the latter happens to come first in the list.\n // Accept both dotted (\"opus-4.7\") and dashed (\"opus-4-7\") inputs —\n // Claude Code historically sends the dashed form.\n const oneMs = models.filter(\n (m) => m.id.includes(\"opus\") && /-1m(?:$|-)/.test(m.id),\n )\n // Capture the requested version. The minor segment is OPTIONAL so a\n // single-segment slug like `claude-opus-5` registers as a specific\n // version (\"5\") rather than falling through to the `oneMs[0]` wildcard\n // below — which would silently downgrade a catalog-absent `claude-opus-5`\n // to the first unrelated opus `-1m` sibling (e.g. claude-opus-4.6-1m).\n const versionMatch = lower.match(/opus-(\\d+)(?:[.-](\\d+))?/)\n const requestedVersion = versionMatch\n ? versionMatch[2]\n ? `${versionMatch[1]}.${versionMatch[2]}`\n : versionMatch[1]\n : undefined\n const preferred = requestedVersion\n ? oneMs.find((m) => m.id.includes(`opus-${requestedVersion}-`))\n : undefined\n // When a specific version was requested (e.g. \"claude-opus-4-8\") but\n // no matching 1M variant exists in the catalog, do NOT fall back to a\n // different version's 1M variant — that silently downgrades (e.g.\n // 4.8 → 4.6-1m). Instead, fall through to step 4 (normalization)\n // which will resolve the bare dotted entry (claude-opus-4.8).\n const oneM = preferred ?? (requestedVersion ? undefined : oneMs[0])\n if (oneM) return oneM.id\n }\n\n if (lower.includes(\"codex\")) {\n const codexModels = models.filter(\n (m) => m.id.includes(\"codex\") && !m.id.includes(\"mini\"),\n )\n if (codexModels.length > 0) {\n codexModels.sort((a, b) => b.id.localeCompare(a.id))\n return codexModels[0].id\n }\n }\n\n // 4. Normalized match (dots → dashes, letter-digit boundaries)\n const normalized = normalizeModelId(modelId)\n const normMatch = models.find(\n (m) => normalizeModelId(m.id) === normalized,\n )\n if (normMatch) return normMatch.id\n\n // 5. Anthropic dated-slug retry. Claude Code's /model UI ships Anthropic's\n // published slugs (e.g. \"claude-haiku-4-5-20251001\") that carry a\n // -YYYYMMDD suffix Copilot's catalog doesn't use. Strip the date and\n // re-run the cascade once so the request maps to the floating tag\n // (claude-haiku-4.5). Family-guarded to `claude-` so a hypothetical\n // \"gpt-...-20260101\" can't be silently stripped. Bounded recursion:\n // the stripped id no longer matches the regex, so the retry's own\n // Step 5 is a no-op.\n const dateStripped = modelId.replace(/^(claude-[\\w.-]+)-20\\d{6}$/i, \"$1\")\n if (dateStripped !== modelId) {\n const retried = resolveModel(dateStripped)\n // resolveModel returns the input unchanged on miss; treat unchanged-and-\n // not-in-catalog as miss to avoid logging a misleading \"resolved\" hop.\n const retryHit =\n retried !== dateStripped || models.some((m) => m.id === dateStripped)\n if (retryHit) {\n consola.info(\n `Resolved Anthropic dated slug \"${modelId}\" → \"${retried}\" (stripped -YYYYMMDD; pass an explicit catalog id to pin a snapshot)`,\n )\n return retried\n }\n }\n\n // 6. Legacy family fallback. Claude Code's settings.json may pin slugs\n // whose Copilot equivalent does not exist (e.g. claude-3-7-sonnet-20250219\n // or claude-sonnet-4-0 — neither is in Copilot's enterprise catalog as\n // of 2026-05-11; a request for either returns HTTP 400 \"model not\n // supported\"). Step 5's dated-retry strips the date but the resulting\n // \"claude-3-7-sonnet\" still has no Copilot equivalent. Rather than\n // dead-end the request, fall back to the highest available family\n // member (sonnet → highest sonnet, haiku → highest haiku). Surfaces\n // via consola.info so the user sees the substitution. Opus is already\n // handled by the family preference in Step 3.\n //\n // Guards (codex-reviewer findings):\n // (a) Family fires only for `claude-` prefixed inputs — protects\n // against custom-sonnet-future or any non-Anthropic provider\n // coincidentally containing \"sonnet\"/\"haiku\" in its slug.\n // (b) Family token must be word-bounded (`-sonnet-` / `-sonnet$`)\n // so a hypothetical claude-supersonnet-* doesn't match.\n // (c) Sort uses numeric collation (`{numeric: true}`) so a future\n // claude-sonnet-4.10 sorts higher than claude-sonnet-4.6\n // (lexicographic alone would invert).\n if (lower.startsWith(\"claude-\")) {\n const matchSonnet = /(?:^|-)sonnet(?:-|$)/.test(lower)\n const matchHaiku = /(?:^|-)haiku(?:-|$)/.test(lower)\n if (matchSonnet || matchHaiku) {\n const family = matchSonnet ? \"sonnet\" : \"haiku\"\n const familyMembers = models.filter((m) =>\n new RegExp(`(?:^|-)${family}(?:-|$|\\\\.)`).test(m.id),\n )\n if (familyMembers.length > 0) {\n familyMembers.sort((a, b) =>\n b.id.localeCompare(a.id, undefined, { numeric: true }),\n )\n const best = familyMembers[0].id\n consola.info(\n `Model \"${modelId}\" not in Copilot catalog; falling back to highest available \"${best}\" (legacy ${family} slug). Pin a current catalog id to silence.`,\n )\n return best\n }\n }\n }\n\n // 7. No match — warn and return as-is\n consola.warn(\n `Model \"${modelId}\" not found in Copilot model list. Available: ${models.map((m) => m.id).join(\", \")}`,\n )\n return modelId\n}\n\n/**\n * Resolve a codex model ID, falling back to the best available codex model.\n * Used by the codex subcommand for model selection.\n */\nexport function resolveCodexModel(modelId: string): string {\n const resolved = resolveModel(modelId)\n const models = state.models?.data\n if (!models) return resolved\n\n // Check if the resolved model exists in the model list\n if (models.some((m) => m.id === resolved)) return resolved\n\n // Fall back to the best available codex-class model. The /responses\n // endpoint is the discriminator — gpt-5.5 dropped the -codex suffix but\n // still routes through /responses. Prefer explicit -codex ids when both\n // exist, otherwise pick the highest version-like id.\n const candidates = models.filter((m) => {\n const endpoints = m.supported_endpoints ?? []\n if (m.id.includes(\"mini\") || m.id.includes(\"nano\")) return false\n return endpoints.length === 0 || endpoints.includes(\"/responses\")\n })\n\n if (candidates.length > 0) {\n candidates.sort((a, b) => {\n const aCodex = a.id.includes(\"codex\") ? 1 : 0\n const bCodex = b.id.includes(\"codex\") ? 1 : 0\n if (aCodex !== bCodex) return bCodex - aCodex\n return b.id.localeCompare(a.id)\n })\n const best = candidates[0].id\n consola.warn(`Model \"${modelId}\" not available, using \"${best}\" instead`)\n return best\n }\n\n return resolved\n}\n\nexport async function cacheModels(): Promise<void> {\n const models = await getModels()\n state.models = models\n}\n\nexport const cacheVSCodeVersion = async () => {\n const response = await resolveEditorVersion(\n \"vscode\",\n getVSCodeVersionOrUndefined,\n VSCODE_VERSION_FALLBACK,\n )\n state.vsCodeVersion = response\n\n consola.info(`Using VSCode version: ${response}`)\n}\n\nexport const cacheCopilotVersion = async () => {\n const version = await resolveEditorVersion(\n \"copilotChat\",\n getCopilotChatVersionOrUndefined,\n COPILOT_CHAT_VERSION_FALLBACK,\n )\n state.copilotVersion = version\n\n consola.info(`Using Copilot Chat version: ${version}`)\n}\n","import consola from \"consola\"\n\nimport {\n GITHUB_BASE_URL,\n GITHUB_CLIENT_ID,\n standardHeaders,\n} from \"~/lib/api-config\"\nimport { sleep } from \"~/lib/utils\"\n\nimport type { DeviceCodeResponse } from \"./get-device-code\"\n\nexport async function pollAccessToken(\n deviceCode: DeviceCodeResponse,\n clientId: string = GITHUB_CLIENT_ID,\n): Promise<string> {\n // Interval is in seconds, we need to multiply by 1000 to get milliseconds\n // I'm also adding another second, just to be safe\n const sleepDuration = (deviceCode.interval + 1) * 1000\n consola.debug(`Polling access token with interval of ${sleepDuration}ms`)\n const expiresAt = Date.now() + deviceCode.expires_in * 1000\n\n while (Date.now() < expiresAt) {\n const response = await fetch(\n `${GITHUB_BASE_URL}/login/oauth/access_token`,\n {\n method: \"POST\",\n headers: standardHeaders(),\n body: JSON.stringify({\n client_id: clientId,\n device_code: deviceCode.device_code,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n }),\n },\n )\n\n if (!response.ok) {\n consola.error(\"Failed to poll access token:\", await response.text())\n if (Date.now() >= expiresAt) break\n await sleep(sleepDuration)\n continue\n }\n\n const json = await response.json()\n consola.debug(\"Polling access token response:\", json)\n\n const { access_token } = json as AccessTokenResponse\n\n if (access_token) {\n return access_token\n }\n\n if (Date.now() >= expiresAt) break\n await sleep(sleepDuration)\n }\n\n throw new Error(\"Device code expired. Please run auth again.\")\n}\n\ninterface AccessTokenResponse {\n access_token: string\n token_type: string\n scope: string\n}\n","import consola from \"consola\"\nimport fs from \"node:fs/promises\"\n\nimport { PATHS } from \"~/lib/paths\"\nimport { getCopilotToken } from \"~/services/github/get-copilot-token\"\nimport { getDeviceCode } from \"~/services/github/get-device-code\"\nimport { getGitHubUser } from \"~/services/github/get-user\"\nimport { pollAccessToken } from \"~/services/github/poll-access-token\"\n\nimport {\n GITHUB_AGENT_CLIENT_ID,\n GITHUB_AGENT_SCOPES,\n GITHUB_API_BASE_URL,\n githubAgentHeaders,\n} from \"./api-config\"\nimport { HTTPError } from \"./error\"\nimport { state } from \"./state\"\n\nconst readGithubToken = () => fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n\nconst writeGithubToken = (token: string) =>\n fs.writeFile(PATHS.GITHUB_TOKEN_PATH, token)\n\nconst readGithubAgentToken = () =>\n fs.readFile(PATHS.GITHUB_AGENT_TOKEN_PATH, \"utf8\")\n\nconst writeGithubAgentToken = (token: string) =>\n fs.writeFile(PATHS.GITHUB_AGENT_TOKEN_PATH, token, { mode: 0o600 })\n\nexport const setupCopilotToken = async () => {\n const { token, refresh_in } = await getCopilotToken()\n state.copilotToken = token\n\n // Display the Copilot token to the screen\n consola.debug(\"GitHub Copilot Token fetched successfully!\")\n if (state.showToken) {\n consola.info(\"Copilot token:\", token)\n }\n\n const refreshInterval = Math.max((refresh_in - 60) * 1000, 1000)\n setInterval(() => {\n void refreshCopilotToken(\"interval\")\n }, refreshInterval)\n}\n\n// Single-flight mutex around the refresh fetch. Concurrent triggers (interval\n// + a 401-retry path) share one in-flight refresh promise so we never\n// overlap network calls or race writes to state.copilotToken.\nlet inflightRefresh: Promise<void> | undefined\n// Cooldowns are keyed off the OUTCOME of the last refresh, not the attempt:\n// - lastRefreshSuccess: throttles 401-retries when the token is fresh\n// (don't pointlessly re-fetch a token we just got).\n// - lastRefreshFailure: shorter backoff so a transient upstream blip\n// doesn't suppress legitimate refresh attempts for a full 30s, but\n// still prevents a thundering-herd refresh-storm against an upstream\n// that's persistently failing.\nlet lastRefreshSuccess = 0\nlet lastRefreshFailure = 0\nconst REFRESH_SUCCESS_COOLDOWN_MS = 30_000\nconst REFRESH_FAILURE_COOLDOWN_MS = 5_000\n\nexport async function refreshCopilotToken(\n reason: \"interval\" | \"401-retry\",\n): Promise<void> {\n if (inflightRefresh) return inflightRefresh\n // Refresh-storm protection: if a recent refresh already completed,\n // decline new 401-retry attempts. Interval refreshes always proceed\n // (they're spaced by `refresh_in - 60s` which is well outside the\n // window). 401-retry attempts respect both cooldowns:\n // - skip if a refresh succeeded within the last 30s (token is fresh)\n // - skip if a refresh failed within the last 5s (back off briefly)\n if (reason === \"401-retry\") {\n const now = Date.now()\n if (now - lastRefreshSuccess < REFRESH_SUCCESS_COOLDOWN_MS) {\n consola.debug(\n `refreshCopilotToken(${reason}) skipped: prior success within ${REFRESH_SUCCESS_COOLDOWN_MS}ms`,\n )\n return\n }\n if (now - lastRefreshFailure < REFRESH_FAILURE_COOLDOWN_MS) {\n consola.debug(\n `refreshCopilotToken(${reason}) skipped: prior failure within ${REFRESH_FAILURE_COOLDOWN_MS}ms`,\n )\n return\n }\n }\n\n inflightRefresh = (async () => {\n consola.debug(`Refreshing Copilot token (reason=${reason})`)\n try {\n const { token } = await getCopilotToken()\n state.copilotToken = token\n lastRefreshSuccess = Date.now()\n consola.debug(\"Copilot token refreshed\")\n if (state.showToken) {\n consola.info(\"Refreshed Copilot token:\", token)\n }\n } catch (error) {\n lastRefreshFailure = Date.now()\n consola.error(\n `Failed to refresh Copilot token (reason=${reason}):`,\n error,\n )\n } finally {\n inflightRefresh = undefined\n }\n })()\n return inflightRefresh\n}\n\n/**\n * Try `request()`. If it returns a 401, refresh the Copilot token (subject\n * to the single-flight + refresh-storm-protection of `refreshCopilotToken`)\n * and retry once. After one retry, propagate whatever the second attempt\n * returned — the caller's existing 401-handling path is preserved.\n *\n * The `request` callback is responsible for capturing `state.copilotToken`\n * locally before any await; this helper does NOT re-build the request\n * itself, just re-invokes the callback after a refresh.\n */\nexport async function tryRefreshAndRetry(\n request: () => Promise<Response>,\n routePath: string,\n): Promise<Response> {\n const first = await request()\n if (first.status !== 401) return first\n\n consola.warn(\n `${routePath}: upstream returned 401, attempting one token refresh + retry`,\n )\n await refreshCopilotToken(\"401-retry\")\n // Re-invoke the request with the (possibly) new token in state.\n return request()\n}\n\ninterface SetupGitHubTokenOptions {\n force?: boolean\n}\n\nexport async function setupGitHubToken(\n options?: SetupGitHubTokenOptions,\n): Promise<void> {\n try {\n const githubToken = await readGithubToken()\n\n if (githubToken && !options?.force) {\n state.githubToken = githubToken\n if (state.showToken) {\n consola.info(\"GitHub token:\", githubToken)\n }\n await logUser()\n\n return\n }\n\n consola.info(\"Not logged in, getting new access token\")\n const response = await getDeviceCode()\n consola.debug(\"Device code response:\", response)\n\n consola.info(\n `Please enter the code \"${response.user_code}\" in ${response.verification_uri}`,\n )\n\n const token = await pollAccessToken(response)\n await writeGithubToken(token)\n state.githubToken = token\n\n if (state.showToken) {\n consola.info(\"GitHub token:\", token)\n }\n await logUser()\n } catch (error) {\n if (error instanceof HTTPError) {\n consola.error(\"Failed to get GitHub token:\", await error.response.json())\n throw error\n }\n\n consola.error(\"Failed to get GitHub token:\", error)\n throw error\n }\n}\n\nasync function logUser() {\n const user = await getGitHubUser()\n consola.info(`Logged in as ${user.login}`)\n}\n\n/**\n * Set up the SECOND, write-capable GitHub token used by the first-mate\n * agent-orchestration surface (`--agents`). Mirrors `setupGitHubToken`\n * but authenticates against the GitHub CLI's OAuth client\n * (`GITHUB_AGENT_CLIENT_ID`) requesting `repo workflow read:org`, and\n * stores the result apart at `PATHS.GITHUB_AGENT_TOKEN_PATH`. The Copilot\n * App token (`state.githubToken`) is left completely untouched — this is\n * a distinct identity for a distinct capability.\n *\n * Long-lived (device-flow user token) → no refresh loop; a later 401 is\n * surfaced to the caller as a revoked grant to re-run the login. Called\n * once from `setupAndServe` when `state.agentsEnabled` is true.\n */\nexport async function setupGitHubAgentToken(\n options?: SetupGitHubTokenOptions,\n): Promise<void> {\n try {\n const existing = (await readGithubAgentToken().catch(() => \"\")).trim()\n\n if (existing && !options?.force) {\n state.githubAgentToken = existing\n if (state.showToken) {\n consola.info(\"GitHub agent token:\", existing)\n }\n await warnIfAgentScopesInsufficient()\n return\n }\n\n consola.info(\n \"Agent mode (--agents): a second GitHub login is required for a write-capable token (repo, workflow, read:org).\",\n )\n const response = await getDeviceCode({\n clientId: GITHUB_AGENT_CLIENT_ID,\n scope: GITHUB_AGENT_SCOPES,\n })\n consola.debug(\"Agent device code response:\", response)\n\n consola.info(\n `Please enter the code \"${response.user_code}\" in ${response.verification_uri} to authorize github-router's cloud-agent orchestration to act on your repositories.`,\n )\n\n const token = await pollAccessToken(response, GITHUB_AGENT_CLIENT_ID)\n await writeGithubAgentToken(token)\n state.githubAgentToken = token\n\n if (state.showToken) {\n consola.info(\"GitHub agent token:\", token)\n }\n await warnIfAgentScopesInsufficient()\n } catch (error) {\n if (error instanceof HTTPError) {\n consola.error(\n \"Failed to get GitHub agent token:\",\n await error.response.json(),\n )\n throw error\n }\n\n consola.error(\"Failed to get GitHub agent token:\", error)\n throw error\n }\n}\n\n/**\n * Best-effort check that the agent token actually carries the scopes we\n * asked for. The GitHub CLI OAuth client is a classic OAuth App, so the\n * granted scopes are echoed in the `x-oauth-scopes` response header on\n * any authenticated call. Warn loudly (not fatal) if `repo`/`workflow`\n * are missing so the failure is diagnosable at login rather than at the\n * first write 403.\n */\nasync function warnIfAgentScopesInsufficient(): Promise<void> {\n try {\n const res = await fetch(`${GITHUB_API_BASE_URL}/user`, {\n headers: githubAgentHeaders(state),\n })\n if (res.status === 401) {\n consola.warn(\n \"GitHub agent token was rejected (401) — the grant may have been revoked. Re-run with --agents to log in again.\",\n )\n return\n }\n const scopes = (res.headers.get(\"x-oauth-scopes\") ?? \"\")\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean)\n const missing = [\"repo\", \"workflow\"].filter((s) => !scopes.includes(s))\n if (missing.length > 0) {\n consola.warn(\n `GitHub agent token is missing scope(s): ${missing.join(\", \")}. `\n + \"The first-mate surface needs 'repo' + 'workflow' to create issues, \"\n + \"assign cloud agents, and dispatch workflows. Re-run the agent login \"\n + \"and grant the requested scopes.\",\n )\n }\n } catch (err) {\n consola.debug(\"Agent token scope check skipped:\", err)\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"claude-DZ1VmE7I.js","names":["nodePath"],"sources":["../src/lib/injected-skills/artifact-review-skill.ts","../src/claude.ts"],"sourcesContent":["export const ARTIFACT_REVIEW_SKILL = {\n name: \"gh-artifact-review\",\n md: `---\nname: gh-artifact-review\ndescription: Reviews plans and artifacts in the ai-or-die panel. Defaults to authoring a self-contained HTML artifact (rich, annotatable, optionally interactive) and opening THAT for the human, then drains feedback with artifact_await, revises, and ends the loop. Use when running inside an ai-or-die tab and there is a plan, comparison, diagram, table, diff, or report the user should see before proceeding.\nuser-invocable: true\n---\n\n# gh-artifact-review: human review in the ai-or-die panel\n\nUse this when you finish a plan or produce something a user should review and you are inside an ai-or-die tab (the \\`mcp__peers__artifact_*\\` tools drive a live panel). The human can click any block or select text to attach a comment, and can click declarative action controls you emit — both come back to you as typed events.\n\n## Default: present HTML, not raw markdown\n\nHTML is the canonical review artifact — it renders richly and is annotatable element-by-element. When the content is anything visual or structured (a comparison, table, diagram, diff, dashboard, or a plan you want to look polished), **author a self-contained \\`.html\\` file** (inline CSS, no external deps, readable typography) and open THAT.\n\n- Plan-mode plans: the panel auto-opens them already rendered to HTML — you do not need to convert them by hand.\n- Do not paste raw markdown into an \\`.html\\`; write real HTML (headings, lists, tables, \\`<pre>\\` for code). Opening a raw \\`.md\\` still renders, but a purpose-built HTML artifact reads better and annotates cleanly.\n\n## When to reach for an artifact\n\nDefault to one for anything easier to grasp visually than as terminal prose: plans, design proposals, comparisons / trade-offs, decisions that need the user's input, diagrams / architecture, tables, code diffs, reports. Skip it only for trivial one-line answers.\n\n## Playbooks (what a good artifact of each type contains)\n\n- **plan**: goal, current state, the proposed approach (high-level decisions, not every line), then risks and open questions at the end.\n- **comparison**: options as columns, trade-offs as rows, current-vs-target where relevant, and an explicit recommendation. Do not make the reader infer the winner.\n- **table**: scannable rows, a sticky header, aligned numeric columns; group/section dense records rather than one flat wall.\n- **diagram**: boxes + arrows. Mermaid when automatic layout matters; positioned SVG/CSS when each node needs prose, code, or controls.\n- **code / diff**: \\`<pre>\\` with before/after or unified-diff styling; keep line context tight; call out the changed lines.\n- **report / dashboard**: lead with the headline number / verdict, then supporting detail; keep one idea per section.\n\n## Design system\n\nArtifacts stay portable (they must render identically opened standalone), so do not depend on a server-injected theme. Pick the look in priority order: (1) a look the user named; (2) the **subject project's** own design system, its Tailwind / theme config, CSS variables / tokens, component library, or existing styled pages, especially when the artifact previews that app's UI; (3) only when both come up empty, clean readable defaults (system font stack, generous spacing, a single accent).\n\n## Loop\n\n1. Open: \\`mcp__peers__artifact_open\\` with the absolute path of the \\`.html\\` (or the file). Pass \\`mode:\"interactive\"\\` when the HTML carries \\`data-aod-*\\` action controls (below). Relay the returned \\`viewUrl\\` and tell the user to review in the panel.\n2. Drain: \\`mcp__peers__artifact_await\\`. It long-holds for the human's next events and returns \\`{events, status, cursor}\\`. **Pass the returned \\`cursor\\` on your next call** so you only receive newer events; if \\`events\\` is empty, call again with that cursor.\n3. Act on each event by \\`kind\\`:\n - \\`comment\\` — a free-text note anchored to the artifact (\\`selector\\`, quoted \\`text\\`, \\`sourceLine\\`); apply the requested change at that exact spot.\n - \\`action\\` — the human clicked a control you emitted (\\`action\\` verb, \\`elementId\\`, optional \\`value\\`; a multi-select submit arrives as one action carrying the selected set). Do what the verb means (approve a step, choose an option, apply a toggle set).\n4. Reply: \\`mcp__peers__artifact_reply\\` with a concise summary of what you changed. Optionally \\`mcp__peers__artifact_update({file})\\` or \\`({html})\\` to replace the artifact content in place, or \\`mcp__peers__artifact_refresh\\` to reload it from disk.\n5. Repeat 2-4 until the user is satisfied; then \\`mcp__peers__artifact_end\\`. Use \\`mcp__peers__artifact_dismiss\\` to hide the panel while keeping the review alive (queued feedback preserved) if the user wants it out of the way without ending.\n\n### Push arrival (you do not have to be polling)\n\nWhen you are idle at the prompt, panel feedback can arrive on its own as a new turn (the tab injects it). Structured actions that answer a pending decision are routed to you directly. Either way, the durable record is the \\`artifact_await\\` drain, so when in doubt call \\`artifact_await\\` (with your last cursor) to reconcile — it replays anything you missed.\n\n### \\`artifact_poll\\` (frozen legacy)\n\n\\`mcp__peers__artifact_poll\\` still resolves for back-compat but returns the OLD payload (human comments only, no structured actions). Prefer \\`artifact_await\\`.\n\n## Interactive controls (data-aod-* authoring)\n\nTo let the human act on the artifact (not just comment), emit declarative controls; the panel wires them and delivers a typed \\`action\\` event. No JS in the artifact.\n\n- **choose-one (fires immediately):** each option is a button that posts the moment it is clicked.\n \\`<button data-aod-action=\"choose\" data-aod-group=\"decision-1\" data-aod-id=\"opt-jwt\" data-aod-value=\"jwt\">JWT</button>\\`\n- **multi-select (toggle then submit):** checkboxes sharing a \\`data-aod-group\\`, plus one submit button with the same group. The submit delivers ONE action carrying \\`{group, selected:[{elementId, value?}]}\\`.\n \\`<input type=\"checkbox\" data-aod-action=\"check\" data-aod-group=\"opts\" data-aod-id=\"a\" data-aod-value=\"a\">\\` … \\`<button data-aod-action=\"submit\" data-aod-group=\"opts\" data-aod-id=\"opts-submit\">Apply</button>\\`\n- **plan steps:** \\`<li class=\"aod-step\" data-aod-id=\"plan-step-3\" data-source-line=\"14\">\\` with per-step \\`data-aod-action=\"approve\"/\"skip\"\\` buttons.\n- **Required attrs:** \\`data-aod-action\\` (verb), \\`data-aod-id\\` (stable, echoed back as \\`elementId\\`; a control missing it is ignored), optional \\`data-aod-value\\`. Keep \\`data-source-line\\` for comment mapping.\n\n## Honest limits\n\n- If a tool errors (e.g. \\`NOT_IN_AIORDIE_TAB\\`, \\`UNREACHABLE\\`, \\`INVALID_REQUEST\\`), report the code/message verbatim; do not claim the panel opened.\n- The panel is a review surface, not an approver: outward/irreversible actions still need explicit user confirmation.\n`,\n} as const\n","import process from \"node:process\"\n\nimport { defineCommand, type ArgsDef } from \"citty\"\nimport consola from \"consola\"\n\nimport {\n checkClaudeVersion,\n updateClaude,\n} from \"./lib/claude-version-check\"\nimport { runSelfUpdate } from \"./lib/self-update\"\nimport {\n injectPeerMcpIntoMirror,\n resolveCodexCliBackend,\n resolveGroupKeysFromMirror,\n workersKeyOf,\n writePeerMcpRuntimeFiles,\n} from \"./lib/codex-mcp-config\"\nimport { enableFileLogging } from \"./lib/file-log-reporter\"\nimport { getCodexVersion, launchChild } from \"./lib/launch\"\nimport { injectAllowRules, planModeAllowRules } from \"./lib/mcp-permissions-settings\"\nimport { listModelsForEndpoint } from \"./lib/model-validation\"\nimport { ensureClaudeConfigMirror, PATHS, removeOwnClaudeConfigMirror, writeArtifactCredsToMirror } from \"./lib/paths\"\nimport {\n buildArtifactOpenHookCommand,\n buildSessionBindHookCommand,\n buildStopHookCommand,\n captureLaunchBaseline,\n injectStopHookIntoSettingsFile,\n stopGateId,\n stopGateDisabled,\n} from \"./lib/orchestration/stop-gate-hook\"\nimport { buildPlanReviewHookCommand, planReviewEnabled } from \"./lib/orchestration/plan-review-hook\"\nimport {\n fileBaselineStore,\n repoFingerprint,\n repoRoot,\n stopGateEnabledForRepo,\n trustRepo,\n} from \"./lib/orchestration/stop-gate-policy\"\nimport {\n checksForDescriptor,\n descriptorHash,\n parseGateDescriptor,\n type GateDescriptor,\n} from \"./lib/orchestration/harness-parse\"\nimport {\n discoverGateCommands,\n readDiscoveredGate,\n sourcesHash,\n writeDiscoveredGate,\n} from \"./lib/orchestration/gate-discovery\"\nimport { liveExec } from \"./lib/orchestration/live-exec\"\nimport { hookMcpRuntimeFromEnv } from \"./lib/orchestration/hook-mcp-client\"\nimport { buildPromptSubmitHookCommand } from \"./lib/orchestration/prompt-submit-hook\"\nimport {\n FIRST_MATE_GUARD_MATCHER,\n buildFirstMateGuardHookCommand,\n} from \"./internal-first-mate-guard\"\nimport { assertShapingInstalled } from \"./lib/first-mate/operator-shaping\"\nimport {\n activeDispatchModes,\n buildWorkerGuardHookCommand,\n guardToolMatcher,\n} from \"./lib/worker-dispatch\"\nimport { ARTIFACT_REVIEW_SKILL, INJECTED_SKILLS, writeInjectedSkill } from \"./lib/injected-skills\"\nimport { shouldUseInsecureTls } from \"./lib/artifact/tools\"\nimport { parseBoolEnv } from \"./lib/exec\"\nimport nodePath from \"node:path\"\nimport { tmpdir } from \"node:os\"\nimport { randomBytes } from \"node:crypto\"\nimport { buildPeerAwarenessSnippet, buildPeerAwarenessSummary, type McpGroup } from \"./lib/peer-mcp-personas\"\nimport { injectAttributionSuppressionIntoSettingsFile } from \"./lib/attribution-settings\"\nimport { appendPeerAwarenessToMirroredClaudeMd, appendToolbeltAwarenessToMirroredClaudeMd, OPERATING_DEFAULTS_DIGEST, prependArtifactPanelDirectiveToMirroredClaudeMd, prependOperatingDefaultsToMirroredClaudeMd, prependStyleDirectiveToMirroredClaudeMd } from \"./lib/claude-md-injection\"\nimport { availableToolCommands, buildToolbeltAwareness, toolbeltEnabled } from \"./lib/toolbelt\"\nimport { provisionToolbelt } from \"./lib/toolbelt/provision\"\nimport { provisionAndIndexColbert } from \"./lib/colbert\"\nimport { startKeepAwake, stopKeepAwake } from \"./lib/keep-awake\"\nimport { warmTreeSitterPool } from \"./lib/tree-sitter-pool/pool\"\nimport { provisionBrowserAssets } from \"./lib/browser-mcp/provision\"\nimport {\n DEFAULT_CLAUDE_MODEL_FALLBACKS,\n pickClaudeDefault,\n} from \"./lib/port\"\nimport {\n agentToolsEnabled,\n artifactToolsEnabled,\n browseAgentEnabled,\n browserCompoundToolsEnabled,\n browserToolsEnabled,\n fleetToolsEnabled,\n geminiAvailable,\n nativeSubagentModel,\n brainstormModel,\n reviewerModel,\n scoutModel,\n scribeModel,\n standInToolEnabled,\n workerToolsEnabled,\n} from \"./lib/mcp-capabilities\"\nimport {\n getClaudeCodeEnvVars,\n parseSharedArgs,\n setupAndServe,\n sharedServerArgs,\n} from \"./lib/server-setup\"\nimport { state } from \"./lib/state\"\nimport { resolveModel } from \"./lib/utils\"\n\nfunction isFirstMateSkillName(name: string): boolean {\n return (\n name === \"gh-first-mate\" ||\n name === \"gh-first-mate-scaffold\" ||\n name === \"gh-first-mate-operate\" ||\n name === \"gh-first-mate-conduct\"\n )\n}\n\nexport const claudeArgs = {\n ...sharedServerArgs,\n model: {\n alias: \"m\",\n type: \"string\",\n description:\n \"Override the default model for Claude Code. Accepts a full slug (e.g. claude-opus-4-7) or an Opus family shorthand (e.g. 4.7, 4.8, 4.6) which expands to the best variant for that family — adding the [1m] suffix when a 1M-context backend is in the catalog.\",\n },\n \"codex-mcp\": {\n type: \"boolean\" as const,\n default: true,\n description:\n \"Wire peer-model MCP personas (codex-critic, codex-reviewer, gemini-critic) into the spawned Claude Code session\",\n },\n \"codex-cli\": {\n type: \"boolean\" as const,\n default: false,\n description:\n \"Add a `codex mcp-server` stdio backend so codex-implementer can mutate files. Requires codex CLI 0.129+; gracefully falls back to HTTP-only if absent.\",\n },\n \"codex-mcp-only\": {\n type: \"boolean\" as const,\n default: false,\n description:\n \"Pass --strict-mcp-config to claude code so only github-router's MCP servers are loaded (hides user's existing MCP servers)\",\n },\n stealth: {\n type: \"boolean\" as const,\n default: false,\n description:\n \"Opt back into VS Code-only beta header filtering. Loses leverage features (task budgets, token-efficient tools, prompt caching, etc.) but minimizes the wire-fingerprint difference from VS Code Copilot Chat. By default the `claude` subcommand enables extended/leverage betas because the spawned Claude Code already identifies itself via UA and other headers — partial stealth doesn't buy much.\",\n },\n \"trust-gate\": {\n type: \"boolean\" as const,\n default: false,\n description:\n \"Explicitly record consent for the structural Stop-gate in THIS repo (pinned to the repo's root-commit). The gate is ON BY DEFAULT when a harness is detected (consent-by-launching), so this is now mostly redundant; it stays for explicit/scripted use. Disable the gate entirely with GH_ROUTER_DISABLE_STOP_GATE=1.\",\n },\n \"no-stop-gate\": {\n type: \"boolean\" as const,\n default: false,\n description:\n \"Disable the structural Stop-gate for THIS session (same effect as GH_ROUTER_DISABLE_STOP_GATE=1). Intended for driven/automated sessions where a blocking Stop hook would hang the turn-end while a fleet driver waits.\",\n },\n \"auto-update\": {\n type: \"boolean\" as const,\n default: true,\n description:\n \"Check for and install the latest Claude Code on launch via `claude update` (throttled to once per hour via ~/.local/share/github-router/last-update-check). `claude update` respects the real install method (native installer or npm), so it never creates a conflicting second install; builds too old to support it fall back to `npm install -g @anthropic-ai/claude-code@latest`. Set to false (--no-auto-update) to check and warn only. Falls back gracefully if claude/npm/network unavailable.\",\n },\n \"update-check\": {\n type: \"boolean\" as const,\n default: true,\n description:\n \"Check the npm registry for a newer Claude Code version on launch and warn if stale (non-blocking ~500ms cost). Set to false (--no-update-check) to skip the check entirely (useful for offline/CI). Independent from --auto-update: --no-update-check implies no auto-install (nothing to install since we never check).\",\n },\n} satisfies ArgsDef\n\n/**\n * Build the argv to forward to the spawned `claude` child from citty's\n * rawArgs (every token after the `claude` subcommand). citty is non-strict,\n * so an unknown flag such as `--print`/`-p`… `--output-format` is absorbed\n * into the parsed `args` object AND its value swallowed, instead of landing\n * in `args._`; forwarding only `args._` therefore drops headless flags unless\n * the user wrapped them in `--`. Here we walk rawArgs and forward every token\n * that is NOT one of github-router's OWN declared flags (or that flag's\n * consumed value). Everything after a literal `--` is forwarded verbatim,\n * preserving the prior explicit-passthrough behavior.\n *\n * A child flag whose NAME collides with a github-router flag (`-p`/`--port`,\n * `-v`/`--verbose`, `-m`/`--model`, `-a`/`--account-type`, `-r`/`--rate-limit`,\n * `-g`/`--github-token`) is owned by github-router; forward it to the child\n * explicitly after `--` (e.g. `github-router claude -- -p`). Every other Claude\n * flag (`--print`, `--output-format`, `--resume`, `--continue`, …) flows\n * through automatically.\n */\nexport function collectChildPassthroughArgs(\n rawArgs: ReadonlyArray<string>,\n argsDef: ArgsDef,\n): string[] {\n const known = new Set<string>()\n const stringTyped = new Set<string>()\n for (const [name, def] of Object.entries(argsDef)) {\n const rawAlias = \"alias\" in def ? def.alias : undefined\n const aliases =\n rawAlias === undefined\n ? []\n : Array.isArray(rawAlias)\n ? rawAlias\n : [rawAlias]\n for (const n of [name, ...aliases]) {\n known.add(n)\n if (def.type === \"string\") stringTyped.add(n)\n }\n }\n\n const forwarded: string[] = []\n for (let i = 0; i < rawArgs.length; i++) {\n const tok = rawArgs[i]\n // Explicit passthrough separator: forward everything after `--` verbatim\n // (matches the prior `args._`-only behavior, where citty routed post-`--`\n // tokens into `_`).\n if (tok === \"--\") {\n forwarded.push(...rawArgs.slice(i + 1))\n break\n }\n // A bare `-` or a non-dash token is a positional / value → forward.\n if (tok === \"-\" || !tok.startsWith(\"-\")) {\n forwarded.push(tok)\n continue\n }\n const doubleDash = tok.startsWith(\"--\")\n const afterDashes = tok.slice(doubleDash ? 2 : 1)\n const eq = afterDashes.indexOf(\"=\")\n const rawName = eq >= 0 ? afterDashes.slice(0, eq) : afterDashes\n const hasInlineValue = eq >= 0\n const negated = doubleDash && rawName.startsWith(\"no-\")\n const baseName = negated ? rawName.slice(3) : rawName\n\n const isKnown = known.has(rawName) || (negated && known.has(baseName))\n if (!isKnown) {\n // Unknown to github-router → belongs to the child claude (e.g. --print).\n forwarded.push(tok)\n continue\n }\n // Own flag: drop it. Also drop a separately-tokenized value for a\n // string-typed flag written as `--flag value` / `-a value` (no inline\n // `=`, next token present and not itself a flag). Booleans and `--no-…`\n // negations never consume a following token under citty/mri.\n const consumesValue =\n !negated\n && !hasInlineValue\n && stringTyped.has(rawName)\n && i + 1 < rawArgs.length\n && rawArgs[i + 1] !== \"--\"\n && !rawArgs[i + 1].startsWith(\"-\")\n if (consumesValue) i++\n }\n return forwarded\n}\n\nexport const claude = defineCommand({\n meta: {\n name: \"claude\",\n description: \"Start the proxy server and launch Claude Code\",\n },\n args: claudeArgs,\n async run({ args, rawArgs }) {\n if (!process.stdout.isTTY) {\n consola.error(\"The claude subcommand requires a TTY (interactive terminal).\")\n process.exit(1)\n }\n\n const parsed = parseSharedArgs(args as unknown as Record<string, unknown>)\n\n // Phase E P2.2: stealth-vs-leverage policy.\n // The `claude` subcommand defaults to LEVERAGE mode (extended-betas\n // ON) because the spawned Claude Code already identifies itself via\n // UA / editor-version / x-app headers — partial stealth doesn't\n // meaningfully reduce the wire fingerprint, and the cost of stealth\n // is losing features the user explicitly chose to install Claude\n // Code for (--max-budget-usd, token-efficient tools, prompt caching,\n // structured outputs, MCP, etc.).\n //\n // The `--stealth` flag opts back into the VS Code-only filter for\n // users who specifically want minimal wire diff over leverage.\n // The shared `--extended-betas` flag still works (treated as alias).\n //\n // Note: `advisor-tool-` is stripped in BOTH modes regardless of this\n // setting (Phase A: Copilot 400s on it). ADVISOR will be served via\n // Phase I's proxy-side translate path independently.\n if (args.stealth) {\n // Stealth wins if explicitly requested.\n parsed.extendedBetas = false\n consola.info(\n \"Stealth mode: VS Code-only beta filtering. Leverage features disabled.\",\n )\n } else if (!args[\"extended-betas\"]) {\n // No explicit --extended-betas AND no --stealth → default ON.\n parsed.extendedBetas = true\n }\n // If user passed --extended-betas explicitly, parsed already reflects it.\n\n // Phase H P2: Claude Code version check + opt-in auto-update.\n // Default: check + auto-install if newer version available\n // (throttled to once per hour). The user explicitly chose to install\n // a Claude Code wrapper — they want the latest features and bug\n // fixes. Opt-out via --no-auto-update (check only, warn) or\n // --no-update-check (silence entirely). Best-effort: skips silently\n // if npm is offline or claude is not on PATH. The check happens\n // BEFORE setupAndServe so a stale version doesn't get spawned.\n if (args[\"update-check\"] !== false) {\n try {\n const versionCheck = await checkClaudeVersion({\n noCheck: false,\n })\n if (versionCheck.skipped && versionCheck.skipReason === \"no-claude\") {\n // Claude isn't on PATH — let launchChild surface the more\n // contextual \"claude not found\" error in the spawn step.\n consola.debug(\n \"claude --version probe failed; skipping auto-update.\",\n )\n } else if (versionCheck.skipped && versionCheck.skipReason === \"no-npm\") {\n // npm view failed — likely offline. Don't block launch.\n consola.debug(\n \"npm view @anthropic-ai/claude-code failed; skipping auto-update check (likely offline).\",\n )\n } else if (\n versionCheck.needsUpdate\n && versionCheck.installedVersion\n && versionCheck.latestVersion\n ) {\n if (args[\"auto-update\"] !== false) {\n try {\n await updateClaude(versionCheck.latestVersion)\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n consola.warn(\n `Auto-update of Claude Code from ${versionCheck.installedVersion} to ${versionCheck.latestVersion} failed (${msg}); continuing with installed version. Run \\`claude update\\` (or \\`npm install -g @anthropic-ai/claude-code@latest\\`) manually to retry.`,\n )\n }\n } else {\n consola.warn(\n `Claude Code v${versionCheck.installedVersion} is installed; v${versionCheck.latestVersion} is available. Run with --auto-update (the default) to install on launch, or \\`claude update\\` manually.`,\n )\n }\n }\n } catch (err) {\n // Whole version-check should never block launch.\n consola.debug(\"Claude version check failed:\", err)\n }\n }\n\n let server: Awaited<ReturnType<typeof setupAndServe>>[\"server\"]\n let serverUrl: string\n try {\n const result = await setupAndServe({\n ...parsed,\n port: parsed.port, // undefined = random port\n silent: true,\n })\n server = result.server\n serverUrl = result.serverUrl\n } catch (error) {\n consola.error(\"Failed to start server:\", error instanceof Error ? error.message : error)\n process.exit(1)\n }\n\n // Queue a best-effort self-update of github-router itself (detached,\n // applies next launch). Fire-and-forget so the bounded npm probe\n // never delays spawning Claude Code.\n void runSelfUpdate({ selfUpdate: args[\"self-update\"] !== false })\n\n // Provision the router-owned CLAUDE_CONFIG_DIR with our synthetic\n // .credentials.json + a snapshot copy of the user's ~/.claude/.\n // The spawned Claude Code (and any teammates it spawns via the\n // agent-teams primitive) reads this dir instead of ~/.claude/,\n // finds our synthetic credential, and authenticates — closing the\n // teammate-spawn allowlist gap that drops ANTHROPIC_AUTH_TOKEN.\n // See ensureClaudeConfigMirror in src/lib/paths.ts.\n //\n // Run BEFORE enableFileLogging so a fatal credentials-write failure\n // surfaces on the user's terminal (we only throw on the credentials\n // write — copy failures of individual user files are debug-logged\n // and skipped).\n try {\n await ensureClaudeConfigMirror()\n } catch (err) {\n consola.error(\n `Failed to provision CLAUDE_CONFIG_DIR mirror: ${\n err instanceof Error ? err.message : String(err)\n }. Spawned Claude Code would not be able to authenticate.`,\n )\n process.exit(1)\n }\n\n enableFileLogging() // redirect errors/warnings to file; suppress terminal output\n\n // Two slugs flow through this code:\n // * `chosenSlug` — the value we set for `ANTHROPIC_MODEL`. Must be an\n // Anthropic-published slug (e.g. `claude-opus-4-8`) so Claude Code's\n // hardcoded `/model` registry matches it and the UI shows the right\n // menu entry. The proxy's resolver translates this back to a Copilot\n // slug at request time, so the actual upstream call still works.\n // * `resolvedSlug` — the Copilot-side slug after `resolveModel`. Used\n // only for cache-presence validation (fallback chain) and the\n // launch banner.\n //\n // For the implicit-default path only, we walk\n // DEFAULT_CLAUDE_MODEL_FALLBACKS when neither the default nor any\n // earlier fallback resolves to a model present in the Copilot cache.\n // Explicit `--model` is respected as-is — including Copilot slugs\n // (which Claude Code's UI won't recognize, but power users may want\n // for explicit pinning).\n // `-m 4.7` / `-m 4.8` shorthand: treat a bare \"N.M\" value as an\n // Opus-family preference and let pickClaudeDefault pick the best\n // variant for that family (adding [1m] when an opus-N.M-1m backend\n // exists in the catalog). Full slugs and any non-matching string\n // continue to pass through unchanged. `usingDefault` stays false\n // for shorthand so the DEFAULT_CLAUDE_MODEL_FALLBACKS walk doesn't\n // override an explicit family request with the next-older Opus.\n const usingDefault = !args.model\n const opusFamilyShorthand = args.model?.match(/^(\\d+\\.\\d+)$/)?.[1]\n const requestedSlug = opusFamilyShorthand\n ? pickClaudeDefault(opusFamilyShorthand)\n : (args.model ?? pickClaudeDefault())\n let chosenSlug = requestedSlug\n let resolvedSlug = resolveModel(chosenSlug)\n\n if (usingDefault && state.models) {\n const inCache = (slug: string) =>\n state.models?.data.some((m) => m.id === resolveModel(slug)) ?? false\n if (!inCache(chosenSlug)) {\n for (const fallback of DEFAULT_CLAUDE_MODEL_FALLBACKS) {\n if (inCache(fallback)) {\n // Re-derive through pickClaudeDefault so 1M detection runs against\n // the FALLBACK's own family. The constant holds bare slugs, so\n // assigning one directly silently drops [1m] accounting on a\n // family that is 1M-capable — the user lands on a working model\n // with a 200k context budget it did not need to accept.\n // `inCache` already proved the family is present, so this cannot\n // trigger pickClaudeDefault's family-absent warning.\n const fallbackSlug = pickClaudeDefault(\n fallback.replace(/^claude-opus-/, \"\"),\n )\n consola.info(\n `Default model \"${chosenSlug}\" not in your Copilot model list; falling back to \"${fallbackSlug}\".`,\n )\n chosenSlug = fallbackSlug\n resolvedSlug = resolveModel(fallbackSlug)\n break\n }\n }\n }\n }\n\n if (resolvedSlug !== chosenSlug) {\n consola.info(`Model \"${chosenSlug}\" resolved to \"${resolvedSlug}\"`)\n }\n const modelEntry = state.models?.data.find((m) => m.id === resolvedSlug)\n if (!modelEntry) {\n const available = listModelsForEndpoint(\"/v1/messages\")\n consola.warn(\n `Model \"${resolvedSlug}\" not found. Available claude models: ${available.join(\", \")}`,\n )\n }\n\n // Banner shows the round-trip so the user sees both names. Claude Code's\n // UI will display the chosenSlug; Copilot upstream sees resolvedSlug.\n const banner =\n chosenSlug === resolvedSlug\n ? chosenSlug\n : `${chosenSlug} → ${resolvedSlug}`\n // Print to stderr directly — consola's terminal reporter is already gone\n process.stderr.write(`Server ready on ${serverUrl}, launching Claude Code (${banner})...\\n`)\n\n const envVars = getClaudeCodeEnvVars(serverUrl, chosenSlug)\n // Forward unrecognized flags (e.g. --print / --output-format / --resume)\n // to the spawned Claude Code child. citty is non-strict, so those land in\n // the parsed `args` object with their values swallowed rather than in\n // `args._`; walking rawArgs and dropping only github-router's OWN flags\n // makes `github-router claude -m <model> --print \"…\"` work headless\n // without requiring the `--` separator. See collectChildPassthroughArgs.\n const extraArgs = collectChildPassthroughArgs(rawArgs, claudeArgs)\n\n // LLM toolbelt: materialize curated CLI tools (rg/fd/jq/sd/sg/yq)\n // into the router bin dir prepended to the agent's PATH (the prepend\n // itself is done in getClaudeCodeEnvVars). Materialization runs in\n // the BACKGROUND so it never delays launch; the awareness one-liner\n // lists EVERY curated tool reachable this launch (system installs +\n // the toolbelt bin), appended to the mirrored CLAUDE.md so the main\n // agent AND descendants know which fast tools to prefer. Best-effort.\n if (toolbeltEnabled()) {\n void provisionToolbelt().catch((err) =>\n consola.debug(\"Toolbelt provisioning failed:\", err),\n )\n const toolbeltLine = buildToolbeltAwareness(availableToolCommands())\n if (toolbeltLine) {\n try {\n await appendToolbeltAwarenessToMirroredClaudeMd(toolbeltLine)\n } catch (err) {\n consola.warn(\n `Toolbelt CLAUDE.md append failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n }\n }\n\n // Best-effort ColBERT semantic-search provision + background index of\n // the launch cwd (if a git repo). ON by default; never blocks launch,\n // never throws. Opt out with GH_ROUTER_DISABLE_SEMANTIC_SEARCH=1.\n void provisionAndIndexColbert()\n\n // Best-effort, bounded launch warm-up: ready one tree-sitter worker.\n // Opt out with GH_ROUTER_DISABLE_TS_POOL_WARMUP=1.\n void warmTreeSitterPool()\n\n // Best-effort: keep the machine awake while the proxy/Claude Code\n // session runs (win32 default-on; opt out GH_ROUTER_DISABLE_KEEP_AWAKE=1).\n // Released via stopKeepAwake() in the shutdown chain below AND via the\n // module's own self-registered SIGINT/SIGTERM/exit reaper.\n startKeepAwake()\n\n // Best-effort: materialize the browser extension + bridge into the\n // stable app-dir and stamp the running version, so a one-time \"Load\n // unpacked\" survives npx/bunx upgrades. Gated on --browse; never\n // blocks launch, never throws.\n if (browserToolsEnabled()) {\n void provisionBrowserAssets().catch((err) =>\n consola.debug(\"Browser extension provisioning failed:\", err),\n )\n }\n\n // Peer-MCP wiring. Default-on. When enabled:\n // 1. Decide between HTTP backend (always works, read-only personas)\n // and the `--codex-cli` stdio backend (requires codex 0.129+,\n // adds the implementer persona).\n // 2. Probe the live Copilot catalog for gemini-3.1-pro-preview.\n // 3. Generate a per-launch nonce, write the MCP config tempfile\n // under PATHS.CLAUDE_RUNTIME_DIR with mode 0o600, AND write\n // one .md subagent file per peer agent into ~/.claude/agents/\n // (Phase 2.5 — `--agents` JSON does NOT populate Claude Code's\n // Task subagent_type enum on v2.1.138; .md files in the canonical\n // agents dir do).\n // 4. Inject `--mcp-config <path>` into the spawned Claude Code's\n // argv. Add `--strict-mcp-config` if the user explicitly opts\n // out of their existing MCP servers. The `--agents` JSON path\n // is intentionally NOT passed: the .md registration in the\n // canonical agents dir is the authoritative surface.\n // 5. Plumb `cleanup()` into launchChild's onShutdown so tempfiles\n // are unlinked on signal exit.\n //\n // The per-launch CLAUDE_CONFIG_DIR mirror is ALWAYS cleaned up on\n // shutdown (regardless of codex-mcp), since `ensureClaudeConfigMirror`\n // above always provisioned it. We chain the peer-MCP cleanup\n // (if any) ahead of the mirror removal so files inside the mirror\n // get unlinked first via known paths; the recursive `fs.rm` is\n // belt-and-braces for everything else.\n const baseShutdown = async (): Promise<void> => {\n await stopKeepAwake()\n await removeOwnClaudeConfigMirror()\n }\n let onShutdown: () => Promise<void> = baseShutdown\n // Captured inside the codex-mcp block (when built) so the always-on\n // operating-defaults injection below can ride the peer-awareness snippet\n // along in the SAME --append-system-prompt arg. Undefined when codex-mcp is\n // off (then only the operating-defaults directive is injected).\n let peerAwarenessSnippet: string | undefined\n let peerAwarenessSummary: string | undefined\n const codexMcpEnabled = (args as Record<string, unknown>)[\"codex-mcp\"] !== false\n if (codexMcpEnabled) {\n try {\n const requestedCli =\n ((args as Record<string, unknown>)[\"codex-cli\"] as boolean | undefined) ?? false\n const backend = resolveCodexCliBackend({\n requested: requestedCli,\n codexInfo: requestedCli ? getCodexVersion() : null,\n })\n const geminiModelsAvailable = geminiAvailable()\n if (!geminiModelsAvailable) {\n consola.info(\n \"gemini-3.1-pro-preview not found in your Copilot model catalog; gemini-critic persona will not be registered.\",\n )\n }\n\n // Which scoped MCP servers to register. `peers` + `search` are\n // always on; `workers` / `decide` / `browser` only when their gate\n // passes at launch (avoids registering a server whose tools would\n // all be filtered out of tools/list). Resolve each group's\n // `.claude.json` config key ONCE (bare, or `gh-router-<group>` on\n // collision) and thread it into every channel — the mcpServers\n // entries, the persona .md routing strings, and the awareness\n // snippet — so a user-side `browser`/`search` MCP never silently\n // hijacks or drops one of ours.\n const enabledGroups: Array<McpGroup> = [\"peers\", \"search\", \"orchestrate\"]\n if (workerToolsEnabled()) enabledGroups.push(\"workers\")\n if (standInToolEnabled()) enabledGroups.push(\"decide\")\n if (browserToolsEnabled()) enabledGroups.push(\"browser\")\n if (fleetToolsEnabled()) enabledGroups.push(\"fleet\")\n if (agentToolsEnabled()) enabledGroups.push(\"first-mate\")\n const { keys: groupKeys, skipped: skippedGroups } =\n await resolveGroupKeysFromMirror(enabledGroups)\n\n const runtime = await writePeerMcpRuntimeFiles(serverUrl, {\n codexCli: backend === \"cli\",\n geminiAvailable: geminiModelsAvailable,\n groupKeys,\n workerToolsAvailable: workerToolsEnabled(),\n browseAvailable: browseAgentEnabled(),\n nativeSubagentModel: nativeSubagentModel(),\n reviewerModel: reviewerModel(),\n brainstormModel: brainstormModel(),\n scoutModel: scoutModel(),\n scribeModel: scribeModel(),\n })\n state.peerMcpNonce = runtime.nonce\n // Reach-back channel for the advisory-review hooks (hook V2): the\n // detached Stop reviewer and the UserPromptSubmit hook run as separate\n // processes with no in-proc state, so they call the proxy over loopback\n // HTTP using this URL + the per-launch nonce (the same nonce the /mcp\n // handler validates). Set on the spawned child's env so the hooks — and\n // the detached reviewer they spawn — inherit it.\n envVars.GH_ROUTER_HOOK_MCP_URL = serverUrl\n envVars.GH_ROUTER_HOOK_NONCE = runtime.nonce\n onShutdown = async (): Promise<void> => {\n await runtime.cleanup()\n await baseShutdown()\n }\n\n // Subagent MCP visibility: inject `gh-router-peers` (and the\n // `codex-cli` stdio entry when enabled) into the mirrored\n // `<CLAUDE_CONFIG_DIR>/.claude.json` so subagents — Agent-tool\n // subagents, forks, agent-teams subprocesses — discover the peer\n // MCP from persistent (user-scope) config rather than the parent's\n // ephemeral --mcp-config CLI flag. Same nonce as runtime files\n // (the proxy validates Authorization against the launch nonce\n // regardless of which channel the request came through).\n //\n // On collision with a user-side entry of the same name, this\n // returns ok:false; we then keep --mcp-config as the fallback so\n // at least the parent session retains the peer tools (subagents\n // remain blind in that case, by design — explicit branch, not\n // silent precedence).\n const injected = await injectPeerMcpIntoMirror(serverUrl, {\n codexCli: backend === \"cli\",\n geminiAvailable: geminiModelsAvailable,\n groupKeys,\n nonce: runtime.nonce,\n })\n\n // Channel selection: prefer the mirror (subagent-visible) when\n // injection succeeded. Only fall back to --mcp-config when\n // injection refused due to a user-side collision. Pushing BOTH\n // would register the same server name twice (mirror + CLI flag),\n // which is ambiguous across Claude Code versions.\n if (!injected.ok) {\n extraArgs.push(\"--mcp-config\", runtime.mcpConfigPath)\n if ((args as Record<string, unknown>)[\"codex-mcp-only\"] === true) {\n extraArgs.push(\"--strict-mcp-config\")\n }\n } else if ((args as Record<string, unknown>)[\"codex-mcp-only\"] === true) {\n // User asked for strict-MCP-only but the mirror inject path\n // can't enforce that (other user-scope MCPs already in the\n // mirror's snapshot are visible). Warn so the flag's mismatch\n // with the new behavior is obvious.\n consola.warn(\n \"--codex-mcp-only has no effect when peer MCP is wired via the \"\n + \"mirrored .claude.json (the user's existing user-scope MCPs in \"\n + \"the snapshot are still visible). Pass --no-codex-mcp to skip \"\n + \"peer-MCP wiring entirely.\",\n )\n }\n\n const personaNames = runtime.personas.map((p) => p.agentName).join(\", \")\n const subagentVisibility = injected.ok\n ? `subagent-visible (mirrored mcpServers: [${injected.serversAdded.join(\", \")}])`\n : `subagent-INVISIBLE (collision on user-side mcpServers: [${injected.conflictingServers.join(\", \")}]; parent-only via --mcp-config)`\n const skippedNote =\n skippedGroups.length > 0\n ? ` WARNING: groups [${skippedGroups.join(\", \")}] skipped — both the bare and \\`gh-router-<group>\\` keys collide with your own mcpServers; those tools are unavailable this session (rename the user-side server to re-enable).`\n : \"\"\n process.stderr.write(\n `Peer MCP wired (backend=${backend}, personas=[${personaNames}], `\n + `subagent .md files=${runtime.agentMdPaths.length}, ${subagentVisibility}).${skippedNote}\\n`,\n )\n\n // ── Floor-raising agent surface (docs/floor-raising-agent-surface.md) ──\n // NOTE (intentional coupling, gemini integration review): this block is\n // nested in `if (codexMcpEnabled)` — the github-router \"enhancement\n // layer\" master switch — alongside the MCP server injection above. The\n // skills REQUIRE that injection (they call mcp__workers__* /\n // mcp__orchestrate__* / mcp__search__* tools), so coupling them here is\n // correct. The Stop-gate is MCP-independent and could live outside;\n // keeping it here means `--no-codex-mcp` opts out of the whole layer\n // (consistent: that flag also hides --trust-gate, so the gate was never\n // enabled in such a session anyway). Decoupling the Stop-gate + moving\n // the per-prompt reset out is a clean follow-up.\n // When the worker/orchestrate backend is available, materialize the\n // injected skills into the mirror and register the front-end\n // UserPromptSubmit hook (per-prompt Stop-gate budget reset + advisory\n // goal steer). gh-first-mate is additionally gated on the first-mate MCP\n // server so the slash command does not advertise an unavailable tool.\n // The hooks scope themselves to the top-level session via the payload's\n // agent_type, so subagents/teammates are untouched.\n const sessionCwd = process.cwd()\n if (workerToolsEnabled()) {\n const skillsToWrite = INJECTED_SKILLS.filter(\n (s) => !isFirstMateSkillName(s.name) || agentToolsEnabled(),\n )\n let skillsWritten = 0\n for (const s of skillsToWrite) {\n const r = await writeInjectedSkill(s.name, s.md).catch(() => ({ written: false }))\n if (r.written) skillsWritten++\n }\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const cmd = buildPromptSubmitHookCommand(process.execPath, process.argv[1])\n // Raise the host hook timeout to 45s (default 30s): the V2 path may\n // make one gpt-5.6-sol scope call + a parallel code search. The hook's\n // own enrichment is bounded well under this (≈22s) and fails open,\n // so 45s is headroom, not a tax the user routinely pays.\n await injectStopHookIntoSettingsFile(settingsPath, cmd, \"UserPromptSubmit\", 45)\n } catch (err) {\n consola.warn(`Could not register the UserPromptSubmit hook: ${String(err)}`)\n }\n // Auto-approve github-router's own injected MCP servers so their tools\n // run WITHOUT a permission prompt in every mode — including plan mode,\n // where the research tools (search / peers / workers-explore / stand_in /\n // orchestrate) are exactly what the model reaches for. MCP tools aren't\n // subject to plan mode's file-edit/shell-write restriction, so a bare\n // `mcp__<server>` allow rule (on the RESOLVED keys) auto-runs them there\n // too. deny/ask and the user's own allow entries are preserved.\n // Auto-approve github-router's own injected MCP servers PLUS the native\n // read-only research tools (WebSearch/WebFetch) so they run WITHOUT a\n // permission prompt in every mode — including plan mode, where that\n // research surface is exactly what the model reaches for. MCP tools and\n // read-only natives aren't subject to plan mode's file-edit/shell-write\n // restriction, so an allow rule auto-runs them there too. deny/ask and\n // the user's own allow entries are preserved.\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n // Authoritative list of the servers we actually injected — on the\n // mirror path that's `injected.serversAdded` (includes the `codex-cli`\n // stdio server, not just the HTTP groups); on the collision fallback,\n // reconstruct it from the resolved group keys + codex-cli.\n const injectedServerKeys = injected.ok\n ? injected.serversAdded\n : [\n ...Object.values(groupKeys).filter((k): k is string => Boolean(k)),\n ...(backend === \"cli\" ? [\"codex-cli\"] : []),\n ]\n const res = await injectAllowRules(settingsPath, planModeAllowRules(injectedServerKeys))\n if (res.added.length) {\n consola.debug(`Auto-approved for plan mode: ${res.added.join(\", \")}`)\n }\n } catch (err) {\n consola.warn(`Could not auto-approve injected tools: ${String(err)}`)\n }\n // Workers non-blocking guard: a PreToolUse hook scoped (matcher) to the\n // active worker tools that DENIES a raw `mcp__<workersKey>__<mode>` call\n // from the main agent (redirecting it to the `worker-<mode>` background\n // dispatcher) and ALLOWS it only from that dispatcher subagent. The\n // resolved workersKey + active modes are baked into the command\n // (dedup-distinct) and the matcher. Local + instant, so a short 10s\n // timeout is ample.\n //\n // GATED ON `injected.ok`: the guard only makes sense when subagents can\n // SEE the workers MCP (mirror injection succeeded). On the collision\n // fallback (parent-only --mcp-config, subagents blind), a `worker-*`\n // dispatcher could not reach the worker tool — registering the guard\n // then would deny the main agent AND leave it no working dispatcher, a\n // fully-broken state. So when injection failed we skip the guard and\n // leave raw (blocking) workers usable on the main thread.\n //\n // HYBRID opt-out (`GH_ROUTER_DISABLE_WORKER_GUARD=1`): keeps the\n // non-blocking guarantee ON by default, but lets an operator restore the\n // raw `mcp__workers__*` escape hatch on demand (e.g. for guaranteed\n // structured-arg fidelity, or when a blocking call is acceptable) — so\n // the worker-* agents COMPLEMENT rather than hard-replace the raw tools.\n if (!injected.ok) {\n consola.warn(\n \"Workers non-blocking guard NOT registered: subagent MCP injection \"\n + \"fell back to parent-only (--mcp-config), so worker-* dispatchers \"\n + \"cannot reach the workers server. Raw (blocking) worker tools remain \"\n + \"usable on the main thread this session.\",\n )\n } else if (process.env.GH_ROUTER_DISABLE_WORKER_GUARD === \"1\") {\n consola.info(\n \"Workers non-blocking guard disabled via GH_ROUTER_DISABLE_WORKER_GUARD=1 \"\n + \"— raw mcp__workers__* is callable on the main thread (blocking); the \"\n + \"worker-* background agents remain the steered, non-blocking default.\",\n )\n } else {\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const workersKey = workersKeyOf(groupKeys)\n const modes = activeDispatchModes({ browse: browseAgentEnabled() })\n const cmd = buildWorkerGuardHookCommand(\n process.execPath,\n process.argv[1],\n workersKey,\n modes,\n )\n const matcher = guardToolMatcher(workersKey, modes)\n await injectStopHookIntoSettingsFile(settingsPath, cmd, \"PreToolUse\", 10, matcher)\n } catch (err) {\n consola.warn(`Could not register the workers PreToolUse guard hook: ${String(err)}`)\n }\n }\n if (skillsWritten > 0) {\n const skillNames = skillsToWrite.map((s) => `/${s.name}`).join(\", \")\n process.stderr.write(\n `Injected skills (${skillsWritten}/${skillsToWrite.length}): ${skillNames}.\\n`,\n )\n }\n }\n\n // In operator/`--agents` mode, keep local worker/orchestrate MCP tools\n // subagent-only for the MAIN operator. File-authoring tools and Bash stay\n // available; steering to cloud-agent delegation lives in the banner and\n // /gh-first-mate skill. Fail-closed only for this reduced PreToolUse guard.\n if (agentToolsEnabled()) {\n let shapingInstalled = false\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const guardCmd = buildFirstMateGuardHookCommand(process.execPath, process.argv[1])\n await injectStopHookIntoSettingsFile(\n settingsPath,\n guardCmd,\n \"PreToolUse\",\n undefined,\n FIRST_MATE_GUARD_MATCHER,\n )\n shapingInstalled = true\n } catch (err) {\n consola.error(\n `Could not register the operator worker/orchestrate guard hook: ${String(err)}`,\n )\n }\n assertShapingInstalled(true, shapingInstalled)\n }\n\n // ai-or-die session binding. When launched inside an ai-or-die Terminal\n // tab, ai-or-die sets AIORDIE_CLAUDE_BIND to a per-tab sidecar path.\n // Register a SessionStart/SessionEnd hook that records the active claude\n // session id + transcript path to that sidecar on every startup / resume\n // / clear / compact, so ai-or-die can bind the tab's sticky-note\n // summariser to the exact transcript (surviving /resume, /clear and\n // exit→relaunch). The path is baked into the hook command (not env), and\n // AIORDIE_CLAUDE_BIND is stripped from the child's env (sanitizeParentEnv),\n // so a nested `github-router claude` can't hijack the parent tab; the hook\n // itself also skips subagent/teammate payloads (agent_id/agent_type).\n const aiordieSidecar = (process.env.AIORDIE_CLAUDE_BIND ?? \"\").trim()\n // The path is baked into the hook command string (double-quoted, like\n // buildStopHookCommand). It is app-controlled (ai-or-die's data dir + a\n // uuid), so it never contains a double-quote; refuse defensively if it\n // somehow does rather than emit a malformed command (ai-or-die then falls\n // back to its inference path).\n if (aiordieSidecar.length > 0 && !aiordieSidecar.includes('\"')) {\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const command = buildSessionBindHookCommand(process.execPath, process.argv[1], aiordieSidecar)\n await injectStopHookIntoSettingsFile(settingsPath, command, \"SessionStart\")\n await injectStopHookIntoSettingsFile(settingsPath, command, \"SessionEnd\")\n } catch (err) {\n consola.warn(`Could not register the ai-or-die session-bind hook: ${String(err)}`)\n }\n }\n\n // Default-on plan review in the artifact panel: when inside an ai-or-die\n // tab (AIORDIE_SESSION_ID set), the artifact_* tools reach a real review\n // panel, so materialize the gh-artifact-review skill and steer the agent\n // to open plans there by default. Skill is written here (not in\n // INJECTED_SKILLS) so it only appears inside a tab; the CLAUDE.md\n // directive reaches descendants. Both best-effort.\n if (artifactToolsEnabled()) {\n await writeInjectedSkill(ARTIFACT_REVIEW_SKILL.name, ARTIFACT_REVIEW_SKILL.md)\n .catch(() => ({ written: false }))\n try {\n await prependArtifactPanelDirectiveToMirroredClaudeMd(groupKeys.peers)\n } catch (err) {\n consola.warn(`Artifact-panel directive prepend failed: ${String(err)}`)\n }\n // Hands-off auto-open: write the artifact creds to a mode-600 mirror\n // file (AIORDIE_TOKEN is stripped from the child env, and argv leaks to\n // ps), then register a PostToolUse(ExitPlanMode) hook so the finalized\n // plan opens in the panel without the model calling artifact_open.\n try {\n await writeArtifactCredsToMirror(shouldUseInsecureTls(process.env.AIORDIE_BASE_URL ?? \"\"))\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const cmd = buildArtifactOpenHookCommand(process.execPath, process.argv[1])\n await injectStopHookIntoSettingsFile(settingsPath, cmd, \"PostToolUse\", undefined, \"ExitPlanMode\")\n } catch (err) {\n consola.warn(`Could not register the artifact auto-open hook: ${String(err)}`)\n }\n }\n\n // Advisory plan review: a SECOND PostToolUse(ExitPlanMode) hook, separate\n // from artifact auto-open. It is default-on when the hook MCP runtime is\n // wired, top-level-only at runtime, and never blocks ExitPlanMode; material\n // findings are written to the shared findings store for the next prompt.\n if (hookMcpRuntimeFromEnv(envVars) && planReviewEnabled()) {\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const command = buildPlanReviewHookCommand(process.execPath, process.argv[1])\n await injectStopHookIntoSettingsFile(settingsPath, command, \"PostToolUse\", undefined, \"ExitPlanMode\")\n } catch (err) {\n consola.warn(`Could not register the advisory plan-review hook: ${String(err)}`)\n }\n }\n\n // Structural-gate Stop hook — CONSENT-GATED per repo (default OFF; it\n // never runs an UNTRUSTED repo's scripts). `--trust-gate` records consent\n // for this repo; GH_ROUTER_ENABLE_STOP_GATE force-enables. The hook also\n // re-checks trust at runtime and scopes to the top-level session.\n if ((args as Record<string, unknown>)[\"trust-gate\"] === true) {\n try {\n const root = await trustRepo(sessionCwd)\n process.stderr.write(\n `Structural gate trusted for this repo (${root}); it will run on launch here from now on.\\n`,\n )\n } catch (err) {\n consola.warn(`Could not record gate trust: ${String(err)}`)\n }\n }\n const gateDisabled = stopGateDisabled(args as Record<string, unknown>)\n const forceEnabled = parseBoolEnv(process.env.GH_ROUTER_ENABLE_STOP_GATE) === true\n const includeTests = parseBoolEnv(process.env.GH_ROUTER_STOP_GATE_RUN_TESTS) === true\n const gateRoot = await repoRoot(sessionCwd).catch(() => sessionCwd)\n // Resolve the gate source: the deterministic parser first (the bun/TS\n // sealed fast-path OR a parsed multi-language check set), then the cached\n // model-discovered record. A cache MISS marks `discovering` (background\n // discovery arms the gate on the NEXT launch — no launch-time model\n // latency). Discovery is OPT-IN (GH_ROUTER_ENABLE_GATE_DISCOVERY=1): it\n // runs a read-only worker that reads repo files, so it stays off by\n // default; the parser covers the common ecosystems with no model at all.\n let descriptor: GateDescriptor | null = null\n let discovering = false\n if (!gateDisabled) {\n descriptor = await parseGateDescriptor(gateRoot, { includeTests }).catch(() => null)\n if (!descriptor) {\n const cached = await readDiscoveredGate(gateRoot).catch(() => null)\n if (cached) {\n descriptor = {\n kind: \"discovered\",\n checks: cached.checks,\n ecosystem: cached.ecosystem,\n workdir: gateRoot,\n evidence: cached.evidence,\n }\n // Force-enable wants a gate NOW (not a next-launch background discovery);\n // when nothing parsed/cached, it falls back to the sealed default below.\n } else if (!forceEnabled && workerToolsEnabled() && parseBoolEnv(process.env.GH_ROUTER_ENABLE_GATE_DISCOVERY) === true) {\n discovering = true\n }\n }\n }\n // Enable when forced-on, already trusted, or auto-trust-on-detection (a\n // resolved descriptor OR a kicked discovery is consent-by-launching). The\n // gate only ever runs the launched repo's own checks and is\n // baseline-isolated; opt out entirely with GH_ROUTER_DISABLE_STOP_GATE=1.\n let gateEnabled = gateDisabled ? false : await stopGateEnabledForRepo(sessionCwd).catch(() => false)\n let autoTrusted = false\n if (!gateEnabled && !gateDisabled && (descriptor || discovering)) {\n try {\n await trustRepo(sessionCwd)\n gateEnabled = true\n autoTrusted = true\n } catch (err) {\n consola.warn(`Could not auto-trust this repo for the structural gate: ${String(err)}`)\n }\n }\n // Force-enable (GH_ROUTER_ENABLE_STOP_GATE / --trust-gate) with no parsed\n // or discovered descriptor falls back to the sealed default gate, matching\n // the legacy behavior of explicit enablement (takes precedence over a\n // background discovery, which `forceEnabled` already suppressed above).\n if (gateEnabled && !descriptor && !discovering) {\n descriptor = { kind: \"sealed\", gateId: stopGateId(), workdir: gateRoot }\n }\n if (gateEnabled && descriptor) {\n const armed = descriptor\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n const command = buildStopHookCommand(process.execPath, process.argv[1])\n // Arm the matching runtime resolver (env reaches the child via envVars).\n if (armed.kind === \"sealed\") envVars.GH_ROUTER_STOP_GATE_ID = armed.gateId\n else if (armed.kind === \"parsed\") envVars.GH_ROUTER_STOP_GATE_PARSED = \"1\"\n else envVars.GH_ROUTER_STOP_GATE_DISCOVERED = \"1\"\n if (includeTests) envVars.GH_ROUTER_STOP_GATE_RUN_TESTS = \"1\"\n await injectStopHookIntoSettingsFile(settingsPath, command)\n // Capture the baseline at LAUNCH (pre-mutation) for the dynamic paths,\n // so an agent-introduced failure is a regression — not silently adopted\n // as the baseline at first stop. Sealed keeps its legacy first-stop\n // baseline (unchanged). Fire-and-forget: never blocks the launch.\n if (armed.kind !== \"sealed\") {\n const checks = checksForDescriptor(armed)\n const descriptorKey = descriptorHash(armed)\n // Per-launch token isolates concurrent sessions' baselines; the child\n // reads it back to recompute the same launch-baseline key.\n const token = randomBytes(8).toString(\"hex\")\n envVars.GH_ROUTER_STOP_GATE_BASELINE_TOKEN = token\n void (async () => {\n await captureLaunchBaseline({\n checks,\n workdir: armed.workdir,\n exec: liveExec,\n descriptorKey,\n token,\n baseline: fileBaselineStore(nodePath.join(tmpdir(), \"gh-router-stopgate-baseline\")),\n })\n })().catch(() => {})\n }\n const tail =\n \"A regression or a gate-weakening diff blocks stopping until fixed (per-prompt, max 2). \"\n + \"Opt out with GH_ROUTER_DISABLE_STOP_GATE=1.\\n\"\n if (armed.kind === \"sealed\") {\n process.stderr.write(\n (autoTrusted\n ? `Structural-gate Stop hook enabled by default for this repo (gate=${armed.gateId}; runs typecheck/test/lint at stop). `\n : `Structural-gate Stop hook enabled (gate=${armed.gateId}). `) + tail,\n )\n } else {\n const ids = checksForDescriptor(armed).map((c) => c.id).join(\", \")\n const testNote = includeTests\n ? \"Full test suite ON. \"\n : \"Full test suite off by default (enable with GH_ROUTER_STOP_GATE_RUN_TESTS=1). \"\n process.stderr.write(\n `Structural-gate Stop hook armed for this ${armed.ecosystem} repo (checks: ${ids}; `\n + `from ${armed.evidence.join(\", \")}). ${testNote}${tail}`,\n )\n }\n } catch (err) {\n // Never fail the launch over the hook; surface and continue.\n consola.warn(`Could not register the structural-gate Stop hook: ${String(err)}`)\n }\n } else if (gateEnabled && discovering) {\n // Kick a background discovery; the gate arms on the next launch.\n process.stderr.write(\n \"Structural Stop-gate: no checks parsed for this repo — discovering check commands in the \"\n + \"background (arms on the next launch). Disable with GH_ROUTER_ENABLE_GATE_DISCOVERY=0.\\n\",\n )\n void (async () => {\n try {\n const disc = await discoverGateCommands(gateRoot, {\n includeTests,\n signal: AbortSignal.timeout(120_000),\n })\n if (disc) {\n const [fingerprint, sh] = await Promise.all([\n repoFingerprint(gateRoot).catch(() => \"\"),\n sourcesHash(gateRoot).catch(() => \"\"),\n ])\n await writeDiscoveredGate({\n root: gateRoot,\n fingerprint,\n sourcesHash: sh,\n discoveredAt: new Date().toISOString(),\n model: \"gemini-3.5-flash\",\n ecosystem: disc.ecosystem,\n checks: disc.checks,\n confidence: disc.confidence,\n evidence: disc.evidence,\n })\n }\n } catch {\n /* best-effort: discovery never blocks or fails the launch. */\n }\n })().catch(() => {})\n } else if (!gateDisabled && !descriptor && !discovering) {\n // The original \"silent skip\" fix: say WHY the gate is off.\n process.stderr.write(\n \"Structural Stop-gate not enabled: no checks found in this repo \"\n + \"(looked at package.json scripts / CI workflows / Makefile / Cargo.toml / go.mod / pyproject). \"\n + \"Force a sealed gate with GH_ROUTER_ENABLE_STOP_GATE=1\"\n + (workerToolsEnabled() ? \", or let a model discover them with GH_ROUTER_ENABLE_GATE_DISCOVERY=1\" : \"\")\n + \".\\n\",\n )\n }\n\n // Awareness snippet: append a short, descriptive system-prompt\n // section telling Claude *what* peer-review tools exist — Claude\n // decides *whether* to call them based on each tool's own\n // `description` (the routing layer). This is the awareness layer.\n //\n // Delivery is dual-surface, both unconditional:\n // 1. --append-system-prompt — system-turn position, strongest\n // attention weight; reaches the main agent only.\n // 2. <CLAUDE_CONFIG_DIR>/CLAUDE.md (appended) — user-turn\n // <claudeMd> wrapper; reaches Agent-tool subagents and\n // agent-teams teammates that inherit CLAUDE_CONFIG_DIR but\n // not --append-system-prompt.\n //\n // Capability gates: worker_* and stand_in mentions are gated on\n // the same predicates the /mcp tools/list uses (workerToolsEnabled\n // / standInToolEnabled), so the snippet never names a tool that\n // is missing from the live catalog.\n //\n // Previously gated by GH_ROUTER_PEER_AWARENESS (default-on).\n // The flag was dropped (per plan: it served no purpose now that\n // the snippet is default-on across both surfaces). Existing\n // `GH_ROUTER_PEER_AWARENESS=0` shell exports are silent no-ops.\n const peerSnippet = buildPeerAwarenessSnippet({\n codexCli: backend === \"cli\",\n geminiAvailable: geminiModelsAvailable,\n workerToolsAvailable: workerToolsEnabled(),\n standInAvailable: standInToolEnabled(),\n browseAvailable: browserToolsEnabled(),\n compoundBrowseAvailable: browserCompoundToolsEnabled(),\n powerBrowseAvailable: state.powerBrowseEnabled,\n fleetAvailable: fleetToolsEnabled(),\n agentToolsAvailable: agentToolsEnabled(),\n scoutAvailable: scoutModel() != null,\n groupKeys,\n })\n // Capture the peer-awareness snippet; the always-on operating-defaults\n // injection after this block composes the single --append-system-prompt\n // (operating-defaults first, then this summary).\n peerAwarenessSnippet = peerSnippet\n peerAwarenessSummary = buildPeerAwarenessSummary({\n workerToolsAvailable: workerToolsEnabled(),\n standInAvailable: standInToolEnabled(),\n browseAvailable: browserToolsEnabled(),\n fleetAvailable: fleetToolsEnabled(),\n agentToolsAvailable: agentToolsEnabled(),\n groupKeys,\n })\n // Ordering invariant: this MUST run AFTER ensureClaudeConfigMirror()\n // has resolved (above in this same handler), so the snapshot of\n // the user's ~/.claude/CLAUDE.md is already in place before we\n // append our marker block. The helper's own mirror-only safety\n // guard rejects writes outside CLAUDE_CONFIG_DIR as defence in\n // depth. Failures warn-and-continue: this CLAUDE.md copy is now the\n // primary full-inventory surface (the system prompt carries only a\n // summary of it), so on the rare write failure the agent falls back to\n // the tool descriptions in tools/list, not a launch-blocker.\n try {\n await appendPeerAwarenessToMirroredClaudeMd(peerSnippet)\n } catch (err) {\n consola.warn(\n `Peer-awareness CLAUDE.md append failed (agent keeps tool descriptions via tools/list, loses the inventory overview): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n // Style directive: prepend a short writing/communication style\n // directive at the TOP of the mirrored CLAUDE.md so every\n // spawned agent reads it first. Independent marker fence from\n // the peer-awareness block, so the two coexist (style at top,\n // user content in the middle, peer-awareness at the bottom).\n // Best-effort like the append above.\n try {\n await prependStyleDirectiveToMirroredClaudeMd()\n } catch (err) {\n consola.warn(\n `Style-directive CLAUDE.md prepend failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n } catch (err) {\n consola.warn(\n `Peer MCP wiring failed (claude will launch without it): ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n }\n\n // Operating-defaults directive (orchestrator posture + hybrid excellence\n // lens): injected by DEFAULT, NOT gated on codex-mcp, so the posture always\n // reaches the agent even under --no-codex-mcp. Two surfaces:\n // 1. --append-system-prompt (main agent, highest salience): the single\n // such arg for the session carries a condensed digest, with a compact\n // summary of the peer-awareness inventory riding along after it.\n // 2. the mirrored CLAUDE.md TOP (main agent and descendant agents): the\n // full directive is prepended after the style directive when that ran.\n // ensureClaudeConfigMirror() ran unconditionally above, so the mirror exists\n // regardless of codex-mcp; the prepend is best-effort (warn-and-continue).\n // Peer-awareness inventory lives ONCE, in the mirrored CLAUDE.md (appended\n // above), which the main agent and descendants both read. The system prompt\n // carries only a compact summary, not the full ~1.1k-token snippet, so\n // the inventory is not duplicated in the context window on every turn. The\n // full operating-defaults directive lives in CLAUDE.md, while a condensed\n // digest keeps those defaults at top salience in --append-system-prompt.\n // If the CLAUDE.md append happened to fail, the agent still has every tool's\n // own description in tools/list (the real routing signal); it just loses this\n // higher-level overview.\n extraArgs.push(\n \"--append-system-prompt\",\n peerAwarenessSnippet && peerAwarenessSummary\n ? `${OPERATING_DEFAULTS_DIGEST}\\n\\n${peerAwarenessSummary}`\n : OPERATING_DEFAULTS_DIGEST,\n )\n try {\n await prependOperatingDefaultsToMirroredClaudeMd()\n } catch (err) {\n consola.warn(\n `Operating-defaults CLAUDE.md prepend failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n\n // Deterministic backstop for the injected no-attribution style directive:\n // write `attribution: {commit:\"\", pr:\"\"}` into the mirrored settings.json so\n // Claude Code's harness suppresses the commit/PR bylines at the source, not\n // just via the advisory CLAUDE.md prose. Presence-guarded (an existing\n // user `attribution`/`includeCoAuthoredBy` wins) and best-effort. Opt out\n // with GH_ROUTER_DISABLE_NO_ATTRIBUTION=1.\n if (process.env.GH_ROUTER_DISABLE_NO_ATTRIBUTION !== \"1\") {\n try {\n const settingsPath = nodePath.join(PATHS.CLAUDE_CONFIG_DIR, \"settings.json\")\n await injectAttributionSuppressionIntoSettingsFile(settingsPath)\n } catch (err) {\n consola.warn(\n `No-attribution settings backstop skipped: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n }\n\n launchChild(\n { kind: \"claude-code\", envVars, extraArgs, model: chosenSlug },\n server,\n { onShutdown },\n )\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,MAAa,wBAAwB;CACnC,MAAM;CACN,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEN;;;ACsCA,SAAS,qBAAqB,MAAuB;CACnD,OACE,SAAS,mBACT,SAAS,4BACT,SAAS,2BACT,SAAS;AAEb;AAEA,MAAa,aAAa;CACxB,GAAG;CACH,OAAO;EACL,OAAO;EACP,MAAM;EACN,aACE;CACJ;CACA,aAAa;EACX,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,aAAa;EACX,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,kBAAkB;EAChB,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,SAAS;EACP,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,cAAc;EACZ,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,gBAAgB;EACd,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,eAAe;EACb,MAAM;EACN,SAAS;EACT,aACE;CACJ;CACA,gBAAgB;EACd,MAAM;EACN,SAAS;EACT,aACE;CACJ;AACF;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,4BACd,SACA,SACU;CACV,MAAM,wBAAQ,IAAI,IAAY;CAC9B,MAAM,8BAAc,IAAI,IAAY;CACpC,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,OAAO,GAAG;EACjD,MAAM,WAAW,WAAW,MAAM,IAAI,QAAQ,KAAA;EAC9C,MAAM,UACJ,aAAa,KAAA,IACT,CAAC,IACD,MAAM,QAAQ,QAAQ,IACpB,WACA,CAAC,QAAQ;EACjB,KAAK,MAAM,KAAK,CAAC,MAAM,GAAG,OAAO,GAAG;GAClC,MAAM,IAAI,CAAC;GACX,IAAI,IAAI,SAAS,UAAU,YAAY,IAAI,CAAC;EAC9C;CACF;CAEA,MAAM,YAAsB,CAAC;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,MAAM,QAAQ;EAIpB,IAAI,QAAQ,MAAM;GAChB,UAAU,KAAK,GAAG,QAAQ,MAAM,IAAI,CAAC,CAAC;GACtC;EACF;EAEA,IAAI,QAAQ,OAAO,CAAC,IAAI,WAAW,GAAG,GAAG;GACvC,UAAU,KAAK,GAAG;GAClB;EACF;EACA,MAAM,aAAa,IAAI,WAAW,IAAI;EACtC,MAAM,cAAc,IAAI,MAAM,aAAa,IAAI,CAAC;EAChD,MAAM,KAAK,YAAY,QAAQ,GAAG;EAClC,MAAM,UAAU,MAAM,IAAI,YAAY,MAAM,GAAG,EAAE,IAAI;EACrD,MAAM,iBAAiB,MAAM;EAC7B,MAAM,UAAU,cAAc,QAAQ,WAAW,KAAK;EACtD,MAAM,WAAW,UAAU,QAAQ,MAAM,CAAC,IAAI;EAG9C,IAAI,EADY,MAAM,IAAI,OAAO,KAAM,WAAW,MAAM,IAAI,QAAQ,IACtD;GAEZ,UAAU,KAAK,GAAG;GAClB;EACF;EAYA,IANE,CAAC,WACE,CAAC,kBACD,YAAY,IAAI,OAAO,KACvB,IAAI,IAAI,QAAQ,UAChB,QAAQ,IAAI,OAAO,QACnB,CAAC,QAAQ,IAAI,EAAE,CAAC,WAAW,GAAG,GAChB;CACrB;CACA,OAAO;AACT;AAEA,MAAa,SAAS,cAAc;CAClC,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CACA,MAAM;CACN,MAAM,IAAI,EAAE,MAAM,WAAW;EAC3B,IAAI,CAAC,QAAQ,OAAO,OAAO;GACzB,QAAQ,MAAM,8DAA8D;GAC5E,QAAQ,KAAK,CAAC;EAChB;EAEA,MAAM,SAAS,gBAAgB,IAA0C;EAkBzE,IAAI,KAAK,SAAS;GAEhB,OAAO,gBAAgB;GACvB,QAAQ,KACN,wEACF;EACF,OAAO,IAAI,CAAC,KAAK,mBAEf,OAAO,gBAAgB;EAYzB,IAAI,KAAK,oBAAoB,OAC3B,IAAI;GACF,MAAM,eAAe,MAAM,mBAAmB,EAC5C,SAAS,MACX,CAAC;GACD,IAAI,aAAa,WAAW,aAAa,eAAe,aAGtD,QAAQ,MACN,sDACF;QACK,IAAI,aAAa,WAAW,aAAa,eAAe,UAE7D,QAAQ,MACN,yFACF;QACK,IACL,aAAa,eACV,aAAa,oBACb,aAAa,eAEhB,IAAI,KAAK,mBAAmB,OAC1B,IAAI;IACF,MAAM,aAAa,aAAa,aAAa;GAC/C,SAAS,KAAK;IACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;IAC3D,QAAQ,KACN,mCAAmC,aAAa,iBAAiB,MAAM,aAAa,cAAc,WAAW,IAAI,wIACnH;GACF;QAEA,QAAQ,KACN,gBAAgB,aAAa,iBAAiB,kBAAkB,aAAa,cAAc,yGAC7F;EAGN,SAAS,KAAK;GAEZ,QAAQ,MAAM,gCAAgC,GAAG;EACnD;EAGF,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,MAAM,SAAS,MAAM,cAAc;IACjC,GAAG;IACH,MAAM,OAAO;IACb,QAAQ;GACV,CAAC;GACD,SAAS,OAAO;GAChB,YAAY,OAAO;EACrB,SAAS,OAAO;GACd,QAAQ,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,KAAK;GACvF,QAAQ,KAAK,CAAC;EAChB;EAKA,cAAmB,EAAE,YAAY,KAAK,mBAAmB,MAAM,CAAC;EAchE,IAAI;GACF,MAAM,yBAAyB;EACjC,SAAS,KAAK;GACZ,QAAQ,MACN,iDACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EAChD,yDACH;GACA,QAAQ,KAAK,CAAC;EAChB;EAEA,kBAAkB;EAyBlB,MAAM,eAAe,CAAC,KAAK;EAC3B,MAAM,sBAAsB,KAAK,OAAO,MAAM,cAAc,CAAC,GAAG;EAIhE,IAAI,aAHkB,sBAClB,kBAAkB,mBAAmB,IACpC,KAAK,SAAS,kBAAkB;EAErC,IAAI,eAAe,aAAa,UAAU;EAE1C,IAAI,gBAAgB,MAAM,QAAQ;GAChC,MAAM,WAAW,SACf,MAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,aAAa,IAAI,CAAC,KAAK;GACjE,IAAI,CAAC,QAAQ,UAAU,GAChB;SAAA,MAAM,YAAY,gCACrB,IAAI,QAAQ,QAAQ,GAAG;KAQrB,MAAM,eAAe,kBACnB,SAAS,QAAQ,iBAAiB,EAAE,CACtC;KACA,QAAQ,KACN,kBAAkB,WAAW,qDAAqD,aAAa,GACjG;KACA,aAAa;KACb,eAAe,aAAa,YAAY;KACxC;IACF;;EAGN;EAEA,IAAI,iBAAiB,YACnB,QAAQ,KAAK,UAAU,WAAW,iBAAiB,aAAa,EAAE;EAGpE,IAAI,CADe,MAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,YAAY,GACtD;GACf,MAAM,YAAY,sBAAsB,cAAc;GACtD,QAAQ,KACN,UAAU,aAAa,wCAAwC,UAAU,KAAK,IAAI,GACpF;EACF;EAIA,MAAM,SACJ,eAAe,eACX,aACA,GAAG,WAAW,KAAK;EAEzB,QAAQ,OAAO,MAAM,mBAAmB,UAAU,2BAA2B,OAAO,OAAO;EAE3F,MAAM,UAAU,qBAAqB,WAAW,UAAU;EAO1D,MAAM,YAAY,4BAA4B,SAAS,UAAU;EASjE,IAAI,gBAAgB,GAAG;GACrB,kBAAuB,CAAC,CAAC,OAAO,QAC9B,QAAQ,MAAM,iCAAiC,GAAG,CACpD;GACA,MAAM,eAAe,uBAAuB,sBAAsB,CAAC;GACnE,IAAI,cACF,IAAI;IACF,MAAM,0CAA0C,YAAY;GAC9D,SAAS,KAAK;IACZ,QAAQ,KACN,qCACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;GACF;EAEJ;EAKA,yBAA8B;EAI9B,mBAAwB;EAMxB,eAAe;EAMf,IAAI,oBAAoB,GACtB,uBAA4B,CAAC,CAAC,OAAO,QACnC,QAAQ,MAAM,0CAA0C,GAAG,CAC7D;EA4BF,MAAM,eAAe,YAA2B;GAC9C,MAAM,cAAc;GACpB,MAAM,4BAA4B;EACpC;EACA,IAAI,aAAkC;EAKtC,IAAI;EACJ,IAAI;EAEJ,IADyB,KAAiC,iBAAiB,OAEzE,IAAI;GACF,MAAM,eACF,KAAiC,gBAAwC;GAC7E,MAAM,UAAU,uBAAuB;IACrC,WAAW;IACX,WAAW,eAAe,gBAAgB,IAAI;GAChD,CAAC;GACD,MAAM,wBAAwB,gBAAgB;GAC9C,IAAI,CAAC,uBACH,QAAQ,KACN,+GACF;GAYF,MAAM,gBAAiC;IAAC;IAAS;IAAU;GAAa;GACxE,IAAI,mBAAmB,GAAG,cAAc,KAAK,SAAS;GACtD,IAAI,mBAAmB,GAAG,cAAc,KAAK,QAAQ;GACrD,IAAI,oBAAoB,GAAG,cAAc,KAAK,SAAS;GACvD,IAAI,kBAAkB,GAAG,cAAc,KAAK,OAAO;GACnD,IAAI,kBAAkB,GAAG,cAAc,KAAK,YAAY;GACxD,MAAM,EAAE,MAAM,WAAW,SAAS,kBAChC,MAAM,2BAA2B,aAAa;GAEhD,MAAM,UAAU,MAAM,yBAAyB,WAAW;IACxD,UAAU,YAAY;IACtB,iBAAiB;IACjB;IACA,sBAAsB,mBAAmB;IACzC,iBAAiB,mBAAmB;IACpC,qBAAqB,oBAAoB;IACzC,eAAe,cAAc;IAC7B,iBAAiB,gBAAgB;IACjC,YAAY,WAAW;IACvB,aAAa,YAAY;GAC3B,CAAC;GACD,MAAM,eAAe,QAAQ;GAO7B,QAAQ,yBAAyB;GACjC,QAAQ,uBAAuB,QAAQ;GACvC,aAAa,YAA2B;IACtC,MAAM,QAAQ,QAAQ;IACtB,MAAM,aAAa;GACrB;GAgBA,MAAM,WAAW,MAAM,wBAAwB,WAAW;IACxD,UAAU,YAAY;IACtB,iBAAiB;IACjB;IACA,OAAO,QAAQ;GACjB,CAAC;GAOD,IAAI,CAAC,SAAS,IAAI;IAChB,UAAU,KAAK,gBAAgB,QAAQ,aAAa;IACpD,IAAK,KAAiC,sBAAsB,MAC1D,UAAU,KAAK,qBAAqB;GAExC,OAAO,IAAK,KAAiC,sBAAsB,MAKjE,QAAQ,KACN,oNAIF;GAGF,MAAM,eAAe,QAAQ,SAAS,KAAK,MAAM,EAAE,SAAS,CAAC,CAAC,KAAK,IAAI;GACvE,MAAM,qBAAqB,SAAS,KAChC,2CAA2C,SAAS,aAAa,KAAK,IAAI,EAAE,MAC5E,2DAA2D,SAAS,mBAAmB,KAAK,IAAI,EAAE;GACtG,MAAM,cACJ,cAAc,SAAS,IACnB,qBAAqB,cAAc,KAAK,IAAI,EAAE,mLAC9C;GACN,QAAQ,OAAO,MACb,2BAA2B,QAAQ,cAAc,aAAa,wBACpC,QAAQ,aAAa,OAAO,IAAI,mBAAmB,IAAI,YAAY,GAC/F;GAoBA,MAAM,aAAa,QAAQ,IAAI;GAC/B,IAAI,mBAAmB,GAAG;IACxB,MAAM,gBAAgB,gBAAgB,QACnC,MAAM,CAAC,qBAAqB,EAAE,IAAI,KAAK,kBAAkB,CAC5D;IACA,IAAI,gBAAgB;IACpB,KAAK,MAAM,KAAK,eAEd,KAAI,MADY,mBAAmB,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,EAAA,CAC3E,SAAS;IAEjB,IAAI;KACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;KAC3E,MAAM,MAAM,6BAA6B,QAAQ,UAAU,QAAQ,KAAK,EAAE;KAK1E,MAAM,+BAA+B,cAAc,KAAK,oBAAoB,EAAE;IAChF,SAAS,KAAK;KACZ,QAAQ,KAAK,iDAAiD,OAAO,GAAG,GAAG;IAC7E;IAeA,IAAI;KACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;KAK3E,MAAM,qBAAqB,SAAS,KAChC,SAAS,eACT,CACE,GAAG,OAAO,OAAO,SAAS,CAAC,CAAC,QAAQ,MAAmB,QAAQ,CAAC,CAAC,GACjE,GAAI,YAAY,QAAQ,CAAC,WAAW,IAAI,CAAC,CAC3C;KACJ,MAAM,MAAM,MAAM,iBAAiB,cAAc,mBAAmB,kBAAkB,CAAC;KACvF,IAAI,IAAI,MAAM,QACZ,QAAQ,MAAM,gCAAgC,IAAI,MAAM,KAAK,IAAI,GAAG;IAExE,SAAS,KAAK;KACZ,QAAQ,KAAK,0CAA0C,OAAO,GAAG,GAAG;IACtE;IAsBA,IAAI,CAAC,SAAS,IACZ,QAAQ,KACN,gPAIF;SACK,IAAI,QAAQ,IAAI,mCAAmC,KACxD,QAAQ,KACN,oNAGF;SAEA,IAAI;KACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;KAC3E,MAAM,aAAa,aAAa,SAAS;KACzC,MAAM,QAAQ,oBAAoB,EAAE,QAAQ,mBAAmB,EAAE,CAAC;KAClE,MAAM,MAAM,4BACV,QAAQ,UACR,QAAQ,KAAK,IACb,YACA,KACF;KACA,MAAM,UAAU,iBAAiB,YAAY,KAAK;KAClD,MAAM,+BAA+B,cAAc,KAAK,cAAc,IAAI,OAAO;IACnF,SAAS,KAAK;KACZ,QAAQ,KAAK,yDAAyD,OAAO,GAAG,GAAG;IACrF;IAEF,IAAI,gBAAgB,GAAG;KACrB,MAAM,aAAa,cAAc,KAAK,MAAM,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI;KACnE,QAAQ,OAAO,MACb,oBAAoB,cAAc,GAAG,cAAc,OAAO,KAAK,WAAW,IAC5E;IACF;GACF;GAMA,IAAI,kBAAkB,GAAG;IACvB,IAAI,mBAAmB;IACvB,IAAI;KACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;KAC3E,MAAM,WAAW,+BAA+B,QAAQ,UAAU,QAAQ,KAAK,EAAE;KACjF,MAAM,+BACJ,cACA,UACA,cACA,KAAA,GACA,wBACF;KACA,mBAAmB;IACrB,SAAS,KAAK;KACZ,QAAQ,MACN,kEAAkE,OAAO,GAAG,GAC9E;IACF;IACA,uBAAuB,MAAM,gBAAgB;GAC/C;GAYA,MAAM,kBAAkB,QAAQ,IAAI,uBAAuB,GAAA,CAAI,KAAK;GAMpE,IAAI,eAAe,SAAS,KAAK,CAAC,eAAe,SAAS,IAAG,GAC3D,IAAI;IACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;IAC3E,MAAM,UAAU,4BAA4B,QAAQ,UAAU,QAAQ,KAAK,IAAI,cAAc;IAC7F,MAAM,+BAA+B,cAAc,SAAS,cAAc;IAC1E,MAAM,+BAA+B,cAAc,SAAS,YAAY;GAC1E,SAAS,KAAK;IACZ,QAAQ,KAAK,uDAAuD,OAAO,GAAG,GAAG;GACnF;GASF,IAAI,qBAAqB,GAAG;IAC1B,MAAM,mBAAmB,sBAAsB,MAAM,sBAAsB,EAAE,CAAC,CAC3E,aAAa,EAAE,SAAS,MAAM,EAAE;IACnC,IAAI;KACF,MAAM,gDAAgD,UAAU,KAAK;IACvE,SAAS,KAAK;KACZ,QAAQ,KAAK,4CAA4C,OAAO,GAAG,GAAG;IACxE;IAKA,IAAI;KACF,MAAM,2BAA2B,qBAAqB,QAAQ,IAAI,oBAAoB,EAAE,CAAC;KACzF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;KAC3E,MAAM,MAAM,6BAA6B,QAAQ,UAAU,QAAQ,KAAK,EAAE;KAC1E,MAAM,+BAA+B,cAAc,KAAK,eAAe,KAAA,GAAW,cAAc;IAClG,SAAS,KAAK;KACZ,QAAQ,KAAK,mDAAmD,OAAO,GAAG,GAAG;IAC/E;GACF;GAMA,IAAI,sBAAsB,OAAO,KAAK,kBAAkB,GACtD,IAAI;IACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;IAC3E,MAAM,UAAU,2BAA2B,QAAQ,UAAU,QAAQ,KAAK,EAAE;IAC5E,MAAM,+BAA+B,cAAc,SAAS,eAAe,KAAA,GAAW,cAAc;GACtG,SAAS,KAAK;IACZ,QAAQ,KAAK,qDAAqD,OAAO,GAAG,GAAG;GACjF;GAOF,IAAK,KAAiC,kBAAkB,MACtD,IAAI;IACF,MAAM,OAAO,MAAM,UAAU,UAAU;IACvC,QAAQ,OAAO,MACb,0CAA0C,KAAK,6CACjD;GACF,SAAS,KAAK;IACZ,QAAQ,KAAK,gCAAgC,OAAO,GAAG,GAAG;GAC5D;GAEF,MAAM,eAAe,iBAAiB,IAA+B;GACrE,MAAM,eAAe,aAAa,QAAQ,IAAI,0BAA0B,MAAM;GAC9E,MAAM,eAAe,aAAa,QAAQ,IAAI,6BAA6B,MAAM;GACjF,MAAM,WAAW,MAAM,SAAS,UAAU,CAAC,CAAC,YAAY,UAAU;GAQlE,IAAI,aAAoC;GACxC,IAAI,cAAc;GAClB,IAAI,CAAC,cAAc;IACjB,aAAa,MAAM,oBAAoB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,YAAY,IAAI;IACnF,IAAI,CAAC,YAAY;KACf,MAAM,SAAS,MAAM,mBAAmB,QAAQ,CAAC,CAAC,YAAY,IAAI;KAClE,IAAI,QACF,aAAa;MACX,MAAM;MACN,QAAQ,OAAO;MACf,WAAW,OAAO;MAClB,SAAS;MACT,UAAU,OAAO;KACnB;UAGK,IAAI,CAAC,gBAAgB,mBAAmB,KAAK,aAAa,QAAQ,IAAI,+BAA+B,MAAM,MAChH,cAAc;IAElB;GACF;GAKA,IAAI,cAAc,eAAe,QAAQ,MAAM,uBAAuB,UAAU,CAAC,CAAC,YAAY,KAAK;GACnG,IAAI,cAAc;GAClB,IAAI,CAAC,eAAe,CAAC,iBAAiB,cAAc,cAClD,IAAI;IACF,MAAM,UAAU,UAAU;IAC1B,cAAc;IACd,cAAc;GAChB,SAAS,KAAK;IACZ,QAAQ,KAAK,2DAA2D,OAAO,GAAG,GAAG;GACvF;GAMF,IAAI,eAAe,CAAC,cAAc,CAAC,aACjC,aAAa;IAAE,MAAM;IAAU,QAAQ,WAAW;IAAG,SAAS;GAAS;GAEzE,IAAI,eAAe,YAAY;IAC7B,MAAM,QAAQ;IACd,IAAI;KACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;KAC3E,MAAM,UAAU,qBAAqB,QAAQ,UAAU,QAAQ,KAAK,EAAE;KAEtE,IAAI,MAAM,SAAS,UAAU,QAAQ,yBAAyB,MAAM;UAC/D,IAAI,MAAM,SAAS,UAAU,QAAQ,6BAA6B;UAClE,QAAQ,iCAAiC;KAC9C,IAAI,cAAc,QAAQ,gCAAgC;KAC1D,MAAM,+BAA+B,cAAc,OAAO;KAK1D,IAAI,MAAM,SAAS,UAAU;MAC3B,MAAM,SAAS,oBAAoB,KAAK;MACxC,MAAM,gBAAgB,eAAe,KAAK;MAG1C,MAAM,QAAQ,YAAY,CAAC,CAAC,CAAC,SAAS,KAAK;MAC3C,QAAQ,qCAAqC;MAC7C,CAAM,YAAY;OAChB,MAAM,sBAAsB;QAC1B;QACA,SAAS,MAAM;QACf,MAAM;QACN;QACA;QACA,UAAU,kBAAkBA,KAAS,KAAK,OAAO,GAAG,6BAA6B,CAAC;OACpF,CAAC;MACH,EAAA,CAAG,CAAC,CAAC,YAAY,CAAC,CAAC;KACrB;KACA,MAAM,OACJ;KAEF,IAAI,MAAM,SAAS,UACjB,QAAQ,OAAO,OACZ,cACG,oEAAoE,MAAM,OAAO,yCACjF,2CAA2C,MAAM,OAAO,QAAQ,IACtE;UACK;MACL,MAAM,MAAM,oBAAoB,KAAK,CAAC,CAAC,KAAK,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI;MACjE,MAAM,WAAW,eACb,yBACA;MACJ,QAAQ,OAAO,MACb,4CAA4C,MAAM,UAAU,iBAAiB,IAAI,SACrE,MAAM,SAAS,KAAK,IAAI,EAAE,KAAK,WAAW,MACxD;KACF;IACF,SAAS,KAAK;KAEZ,QAAQ,KAAK,qDAAqD,OAAO,GAAG,GAAG;IACjF;GACF,OAAO,IAAI,eAAe,aAAa;IAErC,QAAQ,OAAO,MACb,kLAEF;IACA,CAAM,YAAY;KAChB,IAAI;MACF,MAAM,OAAO,MAAM,qBAAqB,UAAU;OAChD;OACA,QAAQ,YAAY,QAAQ,IAAO;MACrC,CAAC;MACD,IAAI,MAAM;OACR,MAAM,CAAC,aAAa,MAAM,MAAM,QAAQ,IAAI,CAC1C,gBAAgB,QAAQ,CAAC,CAAC,YAAY,EAAE,GACxC,YAAY,QAAQ,CAAC,CAAC,YAAY,EAAE,CACtC,CAAC;OACD,MAAM,oBAAoB;QACxB,MAAM;QACN;QACA,aAAa;QACb,+BAAc,IAAI,KAAK,EAAA,CAAE,YAAY;QACrC,OAAO;QACP,WAAW,KAAK;QAChB,QAAQ,KAAK;QACb,YAAY,KAAK;QACjB,UAAU,KAAK;OACjB,CAAC;MACH;KACF,QAAQ,CAER;IACF,EAAA,CAAG,CAAC,CAAC,YAAY,CAAC,CAAC;GACrB,OAAO,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,aAE1C,QAAQ,OAAO,MACb,wNAGK,mBAAmB,IAAI,0EAA0E,MAClG,KACN;GAyBF,MAAM,cAAc,0BAA0B;IAC5C,UAAU,YAAY;IACtB,iBAAiB;IACjB,sBAAsB,mBAAmB;IACzC,kBAAkB,mBAAmB;IACrC,iBAAiB,oBAAoB;IACrC,yBAAyB,4BAA4B;IACrD,sBAAsB,MAAM;IAC5B,gBAAgB,kBAAkB;IAClC,qBAAqB,kBAAkB;IACvC,gBAAgB,WAAW,KAAK;IAChC;GACF,CAAC;GAID,uBAAuB;GACvB,uBAAuB,0BAA0B;IAC/C,sBAAsB,mBAAmB;IACzC,kBAAkB,mBAAmB;IACrC,iBAAiB,oBAAoB;IACrC,gBAAgB,kBAAkB;IAClC,qBAAqB,kBAAkB;IACvC;GACF,CAAC;GAUD,IAAI;IACF,MAAM,sCAAsC,WAAW;GACzD,SAAS,KAAK;IACZ,QAAQ,KACN,wHACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;GACF;GAOA,IAAI;IACF,MAAM,wCAAwC;GAChD,SAAS,KAAK;IACZ,QAAQ,KACN,6CACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;GACF;EACF,SAAS,KAAK;GACZ,QAAQ,KACN,2DACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;EACF;EAsBF,UAAU,KACR,0BACA,wBAAwB,uBACpB,GAAG,0BAA0B,MAAM,yBACnC,yBACN;EACA,IAAI;GACF,MAAM,2CAA2C;EACnD,SAAS,KAAK;GACZ,QAAQ,KACN,gDACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;EACF;EAQA,IAAI,QAAQ,IAAI,qCAAqC,KACnD,IAAI;GACF,MAAM,eAAeA,KAAS,KAAK,MAAM,mBAAmB,eAAe;GAC3E,MAAM,6CAA6C,YAAY;EACjE,SAAS,KAAK;GACZ,QAAQ,KACN,6CACE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAEnD;EACF;EAGF,YACE;GAAE,MAAM;GAAe;GAAS;GAAW,OAAO;EAAW,GAC7D,QACA,EAAE,WAAW,CACf;CACF;AACF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codex-DwabDTOC.js","names":[],"sources":["../src/codex.ts"],"sourcesContent":["import process from \"node:process\"\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { enableFileLogging } from \"./lib/file-log-reporter\"\nimport { launchChild } from \"./lib/launch\"\nimport { listModelsForEndpoint } from \"./lib/model-validation\"\nimport {\n DEFAULT_CODEX_MODEL,\n DEFAULT_CODEX_MODEL_FALLBACKS,\n} from \"./lib/port\"\nimport {\n getCodexEnvVars,\n parseSharedArgs,\n setupAndServe,\n sharedServerArgs,\n} from \"./lib/server-setup\"\nimport { runSelfUpdate } from \"./lib/self-update\"\nimport { state } from \"./lib/state\"\nimport { toolbeltEnabled } from \"./lib/toolbelt\"\nimport { provisionToolbelt } from \"./lib/toolbelt/provision\"\nimport { provisionAndIndexColbert } from \"./lib/colbert\"\nimport { startKeepAwake } from \"./lib/keep-awake\"\nimport { warmTreeSitterPool } from \"./lib/tree-sitter-pool/pool\"\nimport { provisionBrowserAssets } from \"./lib/browser-mcp/provision\"\nimport { hasSupportedBrowserInstalled } from \"./lib/browser-mcp/browser-detect\"\nimport { resolveCodexModel, resolveModel } from \"./lib/utils\"\n\nexport const codex = defineCommand({\n meta: {\n name: \"codex\",\n description: \"Start the proxy server and launch Codex CLI\",\n },\n args: {\n ...sharedServerArgs,\n model: {\n alias: \"m\",\n type: \"string\",\n description: \"Override the default model for Codex CLI\",\n },\n },\n async run({ args }) {\n if (!process.stdout.isTTY) {\n consola.error(\"The codex subcommand requires a TTY (interactive terminal).\")\n process.exit(1)\n }\n\n const parsed = parseSharedArgs(args as unknown as Record<string, unknown>)\n\n let server: Awaited<ReturnType<typeof setupAndServe>>[\"server\"]\n let serverUrl: string\n try {\n const result = await setupAndServe({\n ...parsed,\n port: parsed.port, // undefined = random port\n silent: true,\n })\n server = result.server\n serverUrl = result.serverUrl\n } catch (error) {\n consola.error(\"Failed to start server:\", error instanceof Error ? error.message : error)\n process.exit(1)\n }\n\n // Best-effort self-update (detached, applies next launch).\n void runSelfUpdate({ selfUpdate: args[\"self-update\"] !== false })\n\n // Materialize the LLM toolbelt in the background (PATH prepend is in\n // getCodexEnvVars). Best-effort; never blocks launch.\n if (toolbeltEnabled()) {\n void provisionToolbelt().catch(() => {})\n }\n\n // Best-effort ColBERT semantic-search provision + background index of\n // the launch cwd. ON by default; never blocks launch, never throws.\n void provisionAndIndexColbert()\n\n // Best-effort, bounded launch warm-up: ready one tree-sitter worker.\n // Opt out with GH_ROUTER_DISABLE_TS_POOL_WARMUP=1.\n void warmTreeSitterPool()\n\n // Best-effort: keep the machine awake while the proxy/Codex session\n // runs (win32 default-on; opt out GH_ROUTER_DISABLE_KEEP_AWAKE=1).\n // Self-registered SIGINT/SIGTERM/exit reaper releases the assertion.\n startKeepAwake()\n\n // Best-effort: materialize the browser extension + bridge into the\n // stable app-dir and stamp the running version. Gated inline (browse\n // opt-in + a supported browser) rather than via browserToolsEnabled()\n // so the codex command path doesn't eagerly pull the compressor /\n // worker-agent graph. Never blocks launch, never throws.\n const browseOptIn =\n state.browseEnabled || process.env.GH_ROUTER_ENABLE_BROWSE === \"1\"\n if (browseOptIn && hasSupportedBrowserInstalled()) {\n void provisionBrowserAssets().catch((err) =>\n consola.debug(\"Browser extension provisioning failed:\", err),\n )\n }\n\n const usingDefault = !args.model\n const requestedModel = args.model ?? DEFAULT_CODEX_MODEL\n\n // Resolve model before printing success message (so we show the actual model)\n // but enable file logging first so resolution warnings go to file, not terminal\n enableFileLogging()\n\n let codexModel = resolveCodexModel(requestedModel)\n if (codexModel !== requestedModel) {\n consola.info(`Model \"${requestedModel}\" resolved to \"${codexModel}\"`)\n }\n\n // For the implicit-default path only: honor the explicit fallback order\n // (DEFAULT_CODEX_MODEL, then DEFAULT_CODEX_MODEL_FALLBACKS led by gpt-5.5)\n // by cache presence BEFORE resolveCodexModel's codex-slug-preferring\n // \"best /responses model\" safety net. `resolveModel` (NOT resolveCodexModel)\n // walks the chain, so the codex-slug preference can't shadow an\n // earlier-in-chain named fallback (e.g. gpt-5.5) that IS present. The\n // resolveCodexModel result above stays as the FINAL safety net, used only\n // when none of the named chain entries is in the catalog.\n if (usingDefault && state.models) {\n const inCache = (id: string) =>\n state.models?.data.some((m) => m.id === id) ?? false\n const firstPresent = [DEFAULT_CODEX_MODEL, ...DEFAULT_CODEX_MODEL_FALLBACKS]\n .map((id) => resolveModel(id))\n .find((id) => inCache(id))\n if (firstPresent) {\n if (firstPresent !== codexModel) {\n consola.info(\n `Default model \"${DEFAULT_CODEX_MODEL}\" not in your Copilot model list; falling back to \"${firstPresent}\".`,\n )\n }\n codexModel = firstPresent\n }\n }\n\n // Validate model exists in Copilot model list\n const modelEntry = state.models?.data.find((m) => m.id === codexModel)\n if (!modelEntry) {\n const available = listModelsForEndpoint(\"/responses\")\n consola.warn(\n `Model \"${codexModel}\" not found. Available codex models: ${available.join(\", \")}`,\n )\n } else {\n const ctx = modelEntry.capabilities?.limits?.max_context_window_tokens\n if (ctx) consola.info(`Model context window: ${ctx.toLocaleString()} tokens`)\n }\n\n // Print to stderr directly — consola's terminal reporter is already gone\n process.stderr.write(`Server ready on ${serverUrl}, launching Codex CLI (${codexModel})...\\n`)\n\n const envVars = getCodexEnvVars(serverUrl)\n const extraArgs = ((args as unknown as Record<string, unknown>)._ as string[]) ?? []\n\n launchChild(\n {\n kind: \"codex\",\n envVars,\n extraArgs,\n model: codexModel,\n serverUrl,\n },\n server,\n )\n },\n})\n"],"mappings":";;;;;;;;;AA6BA,MAAa,QAAQ,cAAc;CACjC,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CACA,MAAM;EACJ,GAAG;EACH,OAAO;GACL,OAAO;GACP,MAAM;GACN,aAAa;EACf;CACF;CACA,MAAM,IAAI,EAAE,QAAQ;EAClB,IAAI,CAAC,QAAQ,OAAO,OAAO;GACzB,QAAQ,MAAM,6DAA6D;GAC3E,QAAQ,KAAK,CAAC;EAChB;EAEA,MAAM,SAAS,gBAAgB,IAA0C;EAEzE,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,MAAM,SAAS,MAAM,cAAc;IACjC,GAAG;IACH,MAAM,OAAO;IACb,QAAQ;GACV,CAAC;GACD,SAAS,OAAO;GAChB,YAAY,OAAO;EACrB,SAAS,OAAO;GACd,QAAQ,MAAM,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,KAAK;GACvF,QAAQ,KAAK,CAAC;EAChB;EAGA,cAAmB,EAAE,YAAY,KAAK,mBAAmB,MAAM,CAAC;EAIhE,IAAI,gBAAgB,GAClB,kBAAuB,CAAC,CAAC,YAAY,CAAC,CAAC;EAKzC,yBAA8B;EAI9B,mBAAwB;EAKxB,eAAe;EASf,KADE,MAAM,iBAAiB,QAAQ,IAAI,4BAA4B,QAC9C,6BAA6B,GAC9C,uBAA4B,CAAC,CAAC,OAAO,QACnC,QAAQ,MAAM,0CAA0C,GAAG,CAC7D;EAGF,MAAM,eAAe,CAAC,KAAK;EAC3B,MAAM,iBAAiB,KAAK,SAAA;EAI5B,kBAAkB;EAElB,IAAI,aAAa,kBAAkB,cAAc;EACjD,IAAI,eAAe,gBACjB,QAAQ,KAAK,UAAU,eAAe,iBAAiB,WAAW,EAAE;EAWtE,IAAI,gBAAgB,MAAM,QAAQ;GAChC,MAAM,WAAW,OACf,MAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,EAAE,KAAK;GACjD,MAAM,eAAe,CAAC,qBAAqB,GAAG,6BAA6B,CAAC,CACzE,KAAK,OAAO,aAAa,EAAE,CAAC,CAAC,CAC7B,MAAM,OAAO,QAAQ,EAAE,CAAC;GAC3B,IAAI,cAAc;IAChB,IAAI,iBAAiB,YACnB,QAAQ,KACN,kBAAkB,oBAAoB,qDAAqD,aAAa,GAC1G;IAEF,aAAa;GACf;EACF;EAGA,MAAM,aAAa,MAAM,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,UAAU;EACrE,IAAI,CAAC,YAAY;GACf,MAAM,YAAY,sBAAsB,YAAY;GACpD,QAAQ,KACN,UAAU,WAAW,uCAAuC,UAAU,KAAK,IAAI,GACjF;EACF,OAAO;GACL,MAAM,MAAM,WAAW,cAAc,QAAQ;GAC7C,IAAI,KAAK,QAAQ,KAAK,yBAAyB,IAAI,eAAe,EAAE,QAAQ;EAC9E;EAGA,QAAQ,OAAO,MAAM,mBAAmB,UAAU,yBAAyB,WAAW,OAAO;EAE7F,MAAM,UAAU,gBAAgB,SAAS;EACzC,MAAM,YAAc,KAA4C,KAAkB,CAAC;EAEnF,YACE;GACE,MAAM;GACN;GACA;GACA,OAAO;GACP;EACF,GACA,MACF;CACF;AACF,CAAC"}
|
package/dist/engine-Cpu-wQ6p.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-Byw5s1G8.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 { 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}\n"],"mappings":";;;;;;;;AAmCA,IAAI,gBAA+B;;;;;;;AAQnC,SAAgB,yBAAiC;CAC/C,IAAI,kBAAkB,MAAM,gBAAgB,WAAW;CACvD,OAAO;AACT;AAaA,MAAM,gCAAgB,IAAI,IAAkB;;;;;AAM5C,SAAgB,WAAW,OAA2B;CACpD,cAAc,IAAI,KAAK;CACvB,MAAM,KAAK,eAAe,cAAc,OAAO,KAAK,CAAC;CACrD,MAAM,KAAK,eAAe,cAAc,OAAO,KAAK,CAAC;AACvD;;;;;;AAiBA,SAAgB,oBAA0B;CACxC,MAAM,QAAQ,QAAQ,aAAa;CACnC,KAAK,MAAM,SAAS,eAClB,IAAI;EACF,gBAAgB,OAAO,KAAK;CAC9B,QAAQ,CAER;CAEF,cAAc,MAAM;AACtB;AAMA,IAAI,cAAc;AAClB,IAAI,eAAoC;AACxC,IAAI,iBAAsC;AAC1C,IAAI,kBAAuC;;;;;;;;AAS3C,SAAgB,8BAAoC;CAClD,IAAI,aAAa;CACjB,cAAc;CACd,qBAAqB,kBAAkB;CACvC,uBAAuB;EACrB,kBAAkB;EAClB,IAAI,gBAAgB,QAAQ,IAAI,UAAU,cAAc;EACxD,QAAQ,KAAK,QAAQ,KAAK,QAAQ;CACpC;CACA,wBAAwB;EACtB,kBAAkB;EAClB,IAAI,iBAAiB,QAAQ,IAAI,WAAW,eAAe;EAC3D,QAAQ,KAAK,QAAQ,KAAK,SAAS;CACrC;CACA,QAAQ,GAAG,UAAU,cAAc;CACnC,QAAQ,GAAG,WAAW,eAAe;CACrC,QAAQ,GAAG,QAAQ,YAAY;AACjC;;;;;;;AA8BA,SAAgB,WAAW,KAAsB;CAC/C,IAAI,CAAC,OAAO,UAAU,GAAG,KAAK,OAAO,GAAG,OAAO;CAC/C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,SAAS,KAAK;EACZ,IAAK,IAA8B,SAAS,SAAS,OAAO;EAC5D,OAAO;CACT;AACF;;;;;;;;;;;;;;AAeA,eAAsB,8BAA6C;CACjE,MAAM,UAAU,MAAM;CACtB,IAAI;CACJ,IAAI;EACF,QAAQ,MAAM,GAAG,QAAQ,OAAO;CAClC,QAAQ;EACN;CACF;CACA,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,KAAK,SAAS,OAAO,GAAG;EAC7B,MAAM,OAAO,KAAK,KAAK,SAAS,IAAI;EACpC,IAAI;EACJ,IAAI;GACF,OAAO,KAAK,MAAM,MAAM,GAAG,SAAS,MAAM,MAAM,CAAC;EACnD,QAAQ;GACN;EACF;EACA,IAAI,KAAK,WAAW,YAAY;EAChC,MAAM,WAAW,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;EACrE,IAAI,WAAW,KAAK,WAAW,QAAQ,GAIrC;EAKF,KAAK,SAAS;EACd,KAAK,eAAe;EACpB,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,IAAI;EACnC,IAAI;GACF,MAAM,GAAG,UAAU,KAAK,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;GACrD,MAAM,GAAG,OAAO,KAAK,IAAI;EAC3B,QAAQ;GACN,MAAM,GAAG,GAAG,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;EAClD;CACF;AACF"}
|
package/dist/paths-CwKwc2pi.js
DELETED