github-router 0.3.293 → 0.3.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/{attribution-settings-Dx8TBPE8.js → attribution-settings-pkIop1NU.js} +67 -28
  2. package/dist/attribution-settings-pkIop1NU.js.map +1 -0
  3. package/dist/{auth-BwUHopJz.js → auth-CluG5e-l.js} +3 -3
  4. package/dist/{auth-BwUHopJz.js.map → auth-CluG5e-l.js.map} +1 -1
  5. package/dist/browser-ext/manifest.json +1 -1
  6. package/dist/{check-usage-BTda5753.js → check-usage-iOzfnKJe.js} +4 -4
  7. package/dist/{check-usage-BTda5753.js.map → check-usage-iOzfnKJe.js.map} +1 -1
  8. package/dist/{claude-BoAd_L2W.js → claude-DNmxk-Rm.js} +142 -30
  9. package/dist/claude-DNmxk-Rm.js.map +1 -0
  10. package/dist/{codex-BdJgGT6Q.js → codex-Dl4gpHn-.js} +5 -5
  11. package/dist/{codex-BdJgGT6Q.js.map → codex-Dl4gpHn-.js.map} +1 -1
  12. package/dist/{debug-B3UrZTHQ.js → debug-DsEwleGg.js} +2 -2
  13. package/dist/{debug-B3UrZTHQ.js.map → debug-DsEwleGg.js.map} +1 -1
  14. package/dist/engine-XnluJUs6.js +2 -0
  15. package/dist/{gate-discovery-C30rfnyo.js → gate-discovery-WcduofL4.js} +5 -5
  16. package/dist/{gate-discovery-C30rfnyo.js.map → gate-discovery-WcduofL4.js.map} +1 -1
  17. package/dist/{get-copilot-usage-CRrf1ZSC.js → get-copilot-usage-B7Z4XVWj.js} +2 -2
  18. package/dist/{get-copilot-usage-CRrf1ZSC.js.map → get-copilot-usage-B7Z4XVWj.js.map} +1 -1
  19. package/dist/hooks.mjs +180 -3
  20. package/dist/hooks.sha256 +1 -1
  21. package/dist/{internal-artifact-open-BsEQRvDi.js → internal-artifact-open-D6tKMg8v.js} +2 -2
  22. package/dist/{internal-artifact-open-BsEQRvDi.js.map → internal-artifact-open-D6tKMg8v.js.map} +1 -1
  23. package/dist/internal-fast-dispatch-guard-CPlCrTlU.js +2 -0
  24. package/dist/internal-fast-dispatch-guard-Xyu17k9X.js +187 -0
  25. package/dist/internal-fast-dispatch-guard-Xyu17k9X.js.map +1 -0
  26. package/dist/{internal-first-mate-guard-CVFFJriA.js → internal-first-mate-guard-C9BV7J4U.js} +3 -3
  27. package/dist/{internal-first-mate-guard-CVFFJriA.js.map → internal-first-mate-guard-C9BV7J4U.js.map} +1 -1
  28. package/dist/{internal-first-mate-guard-DJdX6t48.js → internal-first-mate-guard-WJApyc8j.js} +1 -1
  29. package/dist/{internal-plan-review-8TKDLco6.js → internal-plan-review-t3PuNfuL.js} +3 -3
  30. package/dist/{internal-plan-review-8TKDLco6.js.map → internal-plan-review-t3PuNfuL.js.map} +1 -1
  31. package/dist/{internal-prompt-submit-Da7pxqua.js → internal-prompt-submit-C3KovHQG.js} +4 -4
  32. package/dist/{internal-prompt-submit-Da7pxqua.js.map → internal-prompt-submit-C3KovHQG.js.map} +1 -1
  33. package/dist/{internal-session-bind-BOFytA1f.js → internal-session-bind-d2uoja8I.js} +2 -2
  34. package/dist/{internal-session-bind-BOFytA1f.js.map → internal-session-bind-d2uoja8I.js.map} +1 -1
  35. package/dist/{internal-stop-hook-OnfK3BxE.js → internal-stop-hook-DJFtGCsE.js} +5 -5
  36. package/dist/{internal-stop-hook-OnfK3BxE.js.map → internal-stop-hook-DJFtGCsE.js.map} +1 -1
  37. package/dist/{internal-stop-review-CdouacHL.js → internal-stop-review-YtDUGXpX.js} +2 -2
  38. package/dist/{internal-stop-review-CdouacHL.js.map → internal-stop-review-YtDUGXpX.js.map} +1 -1
  39. package/dist/{internal-worker-guard-Bx-itoP8.js → internal-worker-guard-CdkeS9PN.js} +2 -2
  40. package/dist/{internal-worker-guard-Bx-itoP8.js.map → internal-worker-guard-CdkeS9PN.js.map} +1 -1
  41. package/dist/{internal-workspace-header-8WT0iB5K.js → internal-workspace-header-tuBXJV22.js} +2 -2
  42. package/dist/{internal-workspace-header-8WT0iB5K.js.map → internal-workspace-header-tuBXJV22.js.map} +1 -1
  43. package/dist/{lifecycle-nuOHfwgj.js → lifecycle-Bg6doY3-.js} +2 -2
  44. package/dist/{lifecycle-nuOHfwgj.js.map → lifecycle-Bg6doY3-.js.map} +1 -1
  45. package/dist/lifecycle-CGLt1cJQ.js +2 -0
  46. package/dist/{lifecycle-LeSfa7wH.js → lifecycle-CM9eTzvk.js} +2 -2
  47. package/dist/{lifecycle-LeSfa7wH.js.map → lifecycle-CM9eTzvk.js.map} +1 -1
  48. package/dist/lifecycle-DoUwpVDB.js +2 -0
  49. package/dist/main.js +19 -18
  50. package/dist/main.js.map +1 -1
  51. package/dist/{mcp-workspace-header-q34H_4wL.js → mcp-workspace-header-dERl2YTT.js} +2 -2
  52. package/dist/{mcp-workspace-header-q34H_4wL.js.map → mcp-workspace-header-dERl2YTT.js.map} +1 -1
  53. package/dist/{models-hhJcrZhr.js → models-DZ4hYsR7.js} +3 -3
  54. package/dist/{models-hhJcrZhr.js.map → models-DZ4hYsR7.js.map} +1 -1
  55. package/dist/{orchestration-pzbrKkgD.js → orchestration-BiGCEwbH.js} +2 -2
  56. package/dist/{orchestration-pzbrKkgD.js.map → orchestration-BiGCEwbH.js.map} +1 -1
  57. package/dist/{paths-BH4J7slC.js → paths-Ci485qSJ.js} +4 -4
  58. package/dist/{paths-BH4J7slC.js.map → paths-Ci485qSJ.js.map} +1 -1
  59. package/dist/paths-GD7bgGGy.js +2 -0
  60. package/dist/{peer-mcp-personas-DhI7ZPSx.js → peer-mcp-personas-BwNtC8jt.js} +385 -75
  61. package/dist/peer-mcp-personas-BwNtC8jt.js.map +1 -0
  62. package/dist/{plan-review-hook-CfcanA7_.js → plan-review-hook-DuW0pSoM.js} +3 -3
  63. package/dist/{plan-review-hook-CfcanA7_.js.map → plan-review-hook-DuW0pSoM.js.map} +1 -1
  64. package/dist/{prompt-submit-hook-Bqf9ORgb.js → prompt-submit-hook-IMl7ssfr.js} +3 -3
  65. package/dist/{prompt-submit-hook-Bqf9ORgb.js.map → prompt-submit-hook-IMl7ssfr.js.map} +1 -1
  66. package/dist/{provision-CZJ4EWls.js → provision-DeNqzvSM.js} +4 -4
  67. package/dist/{provision-CZJ4EWls.js.map → provision-DeNqzvSM.js.map} +1 -1
  68. package/dist/{self-invocation-DhO1Z8iD.js → self-invocation-DAB_od0C.js} +2 -2
  69. package/dist/{self-invocation-DhO1Z8iD.js.map → self-invocation-DAB_od0C.js.map} +1 -1
  70. package/dist/{serve-RxWYzoOl.js → serve-BS5EmCpM.js} +12 -12
  71. package/dist/{serve-RxWYzoOl.js.map → serve-BS5EmCpM.js.map} +1 -1
  72. package/dist/{server-setup-DbvbW5Ve.js → server-setup-DsGJnJ_O.js} +379 -271
  73. package/dist/server-setup-DsGJnJ_O.js.map +1 -0
  74. package/dist/{start-BNcGsXsY.js → start-vJdt2MiM.js} +3 -3
  75. package/dist/{start-BNcGsXsY.js.map → start-vJdt2MiM.js.map} +1 -1
  76. package/dist/{stop-gate-hook-BiBp5aGm.js → stop-gate-hook-DQnh_KfI.js} +3 -3
  77. package/dist/{stop-gate-hook-BiBp5aGm.js.map → stop-gate-hook-DQnh_KfI.js.map} +1 -1
  78. package/dist/{stop-gate-policy-BGd6b5hR.js → stop-gate-policy-DMr3KVmw.js} +2 -2
  79. package/dist/{stop-gate-policy-BGd6b5hR.js.map → stop-gate-policy-DMr3KVmw.js.map} +1 -1
  80. package/dist/{token-8drORhXg.js → token-BtJhjXXu.js} +54 -7
  81. package/dist/token-BtJhjXXu.js.map +1 -0
  82. package/dist/{worker-dispatch-D5fGroNr.js → worker-dispatch-BEHkTbnH.js} +2 -2
  83. package/dist/{worker-dispatch-D5fGroNr.js.map → worker-dispatch-BEHkTbnH.js.map} +1 -1
  84. package/package.json +1 -1
  85. package/dist/attribution-settings-Dx8TBPE8.js.map +0 -1
  86. package/dist/claude-BoAd_L2W.js.map +0 -1
  87. package/dist/engine-CK2b_cTt.js +0 -2
  88. package/dist/lifecycle-C8fOsQke.js +0 -2
  89. package/dist/lifecycle-D4Yc1aap.js +0 -2
  90. package/dist/paths-DJZoXfAS.js +0 -2
  91. package/dist/peer-mcp-personas-DhI7ZPSx.js.map +0 -1
  92. package/dist/server-setup-DbvbW5Ve.js.map +0 -1
  93. package/dist/token-8drORhXg.js.map +0 -1
@@ -1,14 +1,14 @@
1
1
  import { n as explicitPackageRoot } from "./package-root-B-osctCk.js";
2
2
  import { t as getPackageVersion } from "./version-C8x2hQrZ.js";
3
- import { t as PATHS } from "./paths-BH4J7slC.js";
4
- import { A as state, C as GITHUB_GRAPHQL_URL, D as githubAgentGraphQLHeaders, E as copilotVersion, O as githubAgentHeaders, S as GITHUB_API_BASE_URL, T as copilotHeaders, b as sanitizeTransportText, d as resolveModel, f as sleep$2, g as HTTPError, h as isAllowedCopilotHost, i as tryRefreshAndRetry, v as classifyTransportError, w as copilotBaseUrl, x as withTransientRetry, y as fetchWithTransientRetry } from "./token-8drORhXg.js";
3
+ import { t as PATHS } from "./paths-Ci485qSJ.js";
4
+ import { A as state, C as GITHUB_GRAPHQL_URL, D as githubAgentGraphQLHeaders, E as copilotVersion, O as githubAgentHeaders, S as GITHUB_API_BASE_URL, T as copilotHeaders, b as sanitizeTransportText, d as resolveModel, f as sleep$2, g as HTTPError, h as isAllowedCopilotHost, i as tryRefreshAndRetry, v as classifyTransportError, w as copilotBaseUrl, x as withTransientRetry, y as fetchWithTransientRetry } from "./token-BtJhjXXu.js";
5
5
  import { a as resolveExecutable, c as runManagedExeCapture, i as parseIntEnv, l as spawnTaskkillBestEffort, r as parseBoolEnv } from "./exec-y8C_MU8A.js";
6
- import { i as registerExitHandlers$1, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-nuOHfwgj.js";
7
- import { t as MCP_WORKSPACE_HEADER } from "./mcp-workspace-header-q34H_4wL.js";
6
+ import { i as registerExitHandlers$1, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-Bg6doY3-.js";
7
+ import { t as MCP_WORKSPACE_HEADER } from "./mcp-workspace-header-dERl2YTT.js";
8
8
  import { n as ArtifactError, r as applyInsecureTls, t as ArtifactClient } from "./client-CQMfroGV.js";
9
- import { n as isPidAlive$1, r as registerColbertExitHandlers, s as trackChild, t as getColbertInstanceUuid } from "./lifecycle-LeSfa7wH.js";
10
- import { f as resolveSealedGate, m as runGateChecks, p as sealedGateIds } from "./stop-gate-hook-BiBp5aGm.js";
11
- import { t as liveExec } from "./orchestration-pzbrKkgD.js";
9
+ import { n as isPidAlive$1, r as registerColbertExitHandlers, s as trackChild, t as getColbertInstanceUuid } from "./lifecycle-CM9eTzvk.js";
10
+ import { f as resolveSealedGate, m as runGateChecks, p as sealedGateIds } from "./stop-gate-hook-DQnh_KfI.js";
11
+ import { t as liveExec } from "./orchestration-BiGCEwbH.js";
12
12
  import { createRequire } from "node:module";
13
13
  import consola from "consola";
14
14
  import { chmodSync, closeSync, constants, cpSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
@@ -96,6 +96,25 @@ async function tryCreateLock(p) {
96
96
  }
97
97
  }
98
98
  //#endregion
99
+ //#region src/lib/model-suffix.ts
100
+ /** Strip every trailing `[1m]` marker, case-insensitively. */
101
+ function stripTrailingOneMSuffix(id) {
102
+ const match = id.match(/(?:\[1m\])+$/i);
103
+ if (!match || match.index === void 0) return {
104
+ base: id,
105
+ hadSuffix: false
106
+ };
107
+ return {
108
+ base: id.slice(0, match.index),
109
+ hadSuffix: true
110
+ };
111
+ }
112
+ /** Reapply at most one canonical `[1m]` marker when the input had one. */
113
+ function normalizeTrailingOneMSuffix(id) {
114
+ const { base, hadSuffix } = stripTrailingOneMSuffix(id);
115
+ return hadSuffix ? `${base}[1m]` : base;
116
+ }
117
+ //#endregion
99
118
  //#region src/lib/one-m-context.ts
100
119
  /**
101
120
  * Context-window threshold at which Claude Code's `[1m]` accounting unlock is
@@ -160,8 +179,9 @@ function oneMContextDisabled() {
160
179
  * conservative 200K accounting rather than being over-budgeted into an overflow.
161
180
  */
162
181
  function withOneMSuffix(id) {
163
- if (oneMContextDisabled()) return id;
164
- return catalogAdvertises1M(id) ? `${id}[1m]` : id;
182
+ const base = normalizeTrailingOneMSuffix(id).replace(/\[1m\]$/i, "");
183
+ if (oneMContextDisabled()) return base;
184
+ return catalogAdvertises1M(base) ? `${base}[1m]` : base;
165
185
  }
166
186
  /**
167
187
  * Decorate a slug the USER named — a `-m` argument or a launcher default — with
@@ -208,9 +228,48 @@ function withOneMSuffix(id) {
208
228
  * default — under-accounting, never overflow.
209
229
  */
210
230
  function withOneMSuffixForLead(slug) {
211
- if (oneMContextDisabled()) return slug;
212
- if (/\[1m\]$/i.test(slug)) return slug;
213
- return catalogAdvertises1M(resolveModel(slug)) ? `${slug}[1m]` : slug;
231
+ const base = normalizeTrailingOneMSuffix(slug).replace(/\[1m\]$/i, "");
232
+ if (oneMContextDisabled()) return base;
233
+ return catalogAdvertises1M(resolveModel(base)) ? `${base}[1m]` : base;
234
+ }
235
+ //#endregion
236
+ //#region src/lib/fast-endpoint.ts
237
+ const ENDPOINTS = {
238
+ chat: /* @__PURE__ */ new Set(["/chat/completions", "/v1/chat/completions"]),
239
+ responses: /* @__PURE__ */ new Set(["/responses", "/v1/responses"]),
240
+ messages: /* @__PURE__ */ new Set(["/messages", "/v1/messages"])
241
+ };
242
+ /**
243
+ * Return the endpoint mandated by a fast-profile roster model.
244
+ *
245
+ * This is intentionally independent of the standard `pickEndpoint`: that
246
+ * resolver prefers chat when a catalog entry advertises both clients, while
247
+ * fast roles have a fixed provider contract. Gemini stays on chat; Luna, Sol,
248
+ * and Grok stay on Responses; the Opus Oracle stays on native Messages.
249
+ * Unknown ids return undefined so a fast caller cannot guess a transport.
250
+ */
251
+ function fastEndpointRequirement(modelId) {
252
+ const id = modelId.replace(/(?:\[1m\])+$/i, "");
253
+ if (id === "gemini-3.7-flash") return "chat";
254
+ if (id === "gpt-5.6-luna" || id === "gpt-5.6-sol" || id === "grok-4.6") return "responses";
255
+ if (id === "claude-opus-5") return "messages";
256
+ }
257
+ /**
258
+ * Resolve the fixed fast transport only when the catalog explicitly advertises
259
+ * the required endpoint. An entry that advertises both still resolves to the
260
+ * policy endpoint, regardless of advertisement order.
261
+ */
262
+ function fastEndpointForModel(model) {
263
+ const required = fastEndpointRequirement(model.id);
264
+ if (!required) return void 0;
265
+ const advertised = model.supported_endpoints;
266
+ if (!Array.isArray(advertised) || advertised.length === 0) return void 0;
267
+ return advertised.some((endpoint) => ENDPOINTS[required].has(endpoint)) ? required : void 0;
268
+ }
269
+ /** Resolve a fast endpoint from a catalog id, without selecting a fallback. */
270
+ function fastEndpointForCatalogId(modelId, catalog) {
271
+ const found = catalog?.find((model) => model.id === modelId);
272
+ return found ? fastEndpointForModel(found) : void 0;
214
273
  }
215
274
  //#endregion
216
275
  //#region src/services/copilot/endpoint.ts
@@ -368,12 +427,16 @@ function isClaudeModel(modelId, model, originalModelId) {
368
427
  * `originalModelId` is the optional pre-resolution request id; when supplied it
369
428
  * is checked for Claude-likeness alongside the resolved id so an alias that
370
429
  * resolves to a non-Claude-looking id can't slip past.
430
+ *
431
+ * `fastProfile` is an authenticated launch-policy signal. It replaces only the
432
+ * endpoint selection step with the fixed fast roster policy; Claude identity
433
+ * still wins first, and standard/BYO callers retain `pickEndpoint` unchanged.
371
434
  */
372
- function classifyMessagesRoute(modelId, model, originalModelId) {
435
+ function classifyMessagesRoute(modelId, model, originalModelId, fastProfile = false) {
373
436
  if (!modelId) return "claude-passthrough";
374
437
  if (isClaudeModel(modelId, model, originalModelId)) return "claude-passthrough";
375
438
  if (!model) return "claude-passthrough";
376
- const endpoint = pickEndpoint(model);
439
+ const endpoint = fastProfile ? fastEndpointForModel(model) : pickEndpoint(model);
377
440
  if (endpoint === "responses") return "responses-shim";
378
441
  if (endpoint === "chat") return "chat-shim";
379
442
  return "claude-passthrough";
@@ -451,7 +514,7 @@ const DEFAULT_OPUS_FAMILY = "5";
451
514
  * The lead `-m fast` selects. `gpt-5.6-luna` — a distinct Luna-driven
452
515
  * profile (see `./launch-profile`), NOT a Claude Sonnet budget lead. This
453
516
  * REPLACES the earlier `-m fast` → `claude-sonnet-5` mapping: `fast` now
454
- * names a deliberately lean Luna surface (three native agents, one peer
517
+ * names a deliberately lean Luna surface (five native agents, one peer
455
518
  * persona, `peers`/`search` MCP groups only), not "budget Sonnet with the
456
519
  * full standard surface". `resolveLaunchProfile` in `./launch-profile`
457
520
  * keys off the same raw `-m` argument this constant is selected by, so the
@@ -18926,7 +18989,7 @@ function logAudit$1(record) {
18926
18989
  try {
18927
18990
  const fs = await import("node:fs/promises");
18928
18991
  const path = await import("node:path");
18929
- const { PATHS } = await import("./paths-DJZoXfAS.js");
18992
+ const { PATHS } = await import("./paths-GD7bgGGy.js");
18930
18993
  const dir = path.join(PATHS.APP_DIR, "browser-mcp");
18931
18994
  await fs.mkdir(dir, { recursive: true });
18932
18995
  const line = JSON.stringify({
@@ -27454,6 +27517,247 @@ function shimDefaultsToXhigh(id) {
27454
27517
  return XHIGH_DEFAULT_SHIM_MODELS.includes(normalizeModelId(id));
27455
27518
  }
27456
27519
  //#endregion
27520
+ //#region src/lib/launch-profile.ts
27521
+ const STANDARD_PROFILE = Object.freeze({
27522
+ id: "standard",
27523
+ hasCoordinator: true
27524
+ });
27525
+ /**
27526
+ * The `-m fast` roster: exactly five native agents (`scout`, `implementer`,
27527
+ * `reviewer`, `planner`, `critic`), the fast-only `oracle` peer tool, no coordinator,
27528
+ * and only `peers`/`search` plus the ordinary opt-in `browser` group.
27529
+ * `workers`/`orchestrate`/`decide`/`fleet`/`first-mate` are hard denies even
27530
+ * when their independent standard-profile gates pass.
27531
+ */
27532
+ const FAST_PROFILE = Object.freeze({
27533
+ id: "fast",
27534
+ nativeRoster: /* @__PURE__ */ new Set([
27535
+ "scout",
27536
+ "implementer",
27537
+ "reviewer",
27538
+ "planner",
27539
+ "critic"
27540
+ ]),
27541
+ personaAllowlist: /* @__PURE__ */ new Set(["oracle"]),
27542
+ allowedGroups: /* @__PURE__ */ new Set([
27543
+ "peers",
27544
+ "search",
27545
+ "browser"
27546
+ ]),
27547
+ hasCoordinator: false
27548
+ });
27549
+ function profileDescriptor(id) {
27550
+ return id === "fast" ? FAST_PROFILE : STANDARD_PROFILE;
27551
+ }
27552
+ /**
27553
+ * Resolve the parsed `-m` argument to a launch profile.
27554
+ *
27555
+ * Deliberately keyed on the RAW alias string (trimmed, case-insensitive
27556
+ * `"fast"`), never on a resolved model id: `resolveLeadSlugArg` maps `fast`
27557
+ * to `FAST_LEAD_MODEL` (`./port`) before this is of any use to a caller who
27558
+ * only has the resolved id, so callers that already resolved the lead must
27559
+ * pass the ORIGINAL `-m` value here, not the resolved one. This is what
27560
+ * keeps `-m gpt-5.6-luna` (a direct pin of the same underlying model) a
27561
+ * standard-surface launch — only the literal alias narrows the surface.
27562
+ */
27563
+ function resolveLaunchProfile(modelArg) {
27564
+ return modelArg?.trim().toLowerCase() === "fast" ? "fast" : "standard";
27565
+ }
27566
+ /**
27567
+ * Router-owned alias id for the fast profile's Sonnet-tier row
27568
+ * (`ANTHROPIC_DEFAULT_SONNET_MODEL`). Never sent upstream — canonicalized to
27569
+ * `LUNA_REAL_MODEL_ID` by `canonicalizeAliasModel` before the request
27570
+ * reaches Copilot.
27571
+ */
27572
+ const LUNA_DRIVER_ALIAS_ID = "gh-router-luna-driver-max";
27573
+ /** Fast native-agent alias ids preserve role-specific effort provenance until
27574
+ * the authenticated request boundary. They both canonicalize to Luna, but the
27575
+ * scout is fixed high while the implementer is fixed max. */
27576
+ const LUNA_SCOUT_ALIAS_ID = "gh-router-luna-scout-high";
27577
+ const LUNA_IMPLEMENTER_ALIAS_ID = "gh-router-luna-implementer-max";
27578
+ /** Fast-profile critic alias. It preserves Gemini's fixed medium effort until
27579
+ * the authenticated request boundary; bare Gemini remains high for the lead
27580
+ * and Advisor traffic. */
27581
+ const FAST_CRITIC_ALIAS_ID = "gh-router-fast-critic-medium";
27582
+ const LUNA_SONNET_ALIAS_ID = "gh-router-luna-sonnet-xhigh";
27583
+ /**
27584
+ * Router-owned alias id for the fast profile's Haiku-tier row
27585
+ * (`ANTHROPIC_DEFAULT_HAIKU_MODEL` / `ANTHROPIC_SMALL_FAST_MODEL`).
27586
+ */
27587
+ const LUNA_HAIKU_ALIAS_ID = "gh-router-luna-haiku-high";
27588
+ /** The real Copilot catalog id every Luna alias (including the driver
27589
+ * itself) canonicalizes to. */
27590
+ const LUNA_REAL_MODEL_ID = "gpt-5.6-luna";
27591
+ /**
27592
+ * The full alias table, keyed by `aliasId`. A simpler model-id-only table is
27593
+ * rejected by design: the driver, the Sonnet tier, and the Haiku tier all
27594
+ * resolve to the SAME Luna catalog id, while the fast critic alias resolves
27595
+ * to Gemini. After early canonicalization a table keyed on the real id could
27596
+ * no longer tell which absent-effort default applies. Alias provenance is the
27597
+ * minimum discriminator that survives from tier selection through to request
27598
+ * preprocessing, which is why canonicalization must happen LAST (in the
27599
+ * `/v1/messages` identity preflight), after the effort default has already
27600
+ * been read off the alias.
27601
+ */
27602
+ const MODEL_ALIAS_TABLE = /* @__PURE__ */ new Map([
27603
+ [LUNA_DRIVER_ALIAS_ID, {
27604
+ aliasId: LUNA_DRIVER_ALIAS_ID,
27605
+ realModel: LUNA_REAL_MODEL_ID,
27606
+ absentEffortDefault: "max"
27607
+ }],
27608
+ [LUNA_SCOUT_ALIAS_ID, {
27609
+ aliasId: LUNA_SCOUT_ALIAS_ID,
27610
+ realModel: LUNA_REAL_MODEL_ID,
27611
+ absentEffortDefault: "high"
27612
+ }],
27613
+ [LUNA_IMPLEMENTER_ALIAS_ID, {
27614
+ aliasId: LUNA_IMPLEMENTER_ALIAS_ID,
27615
+ realModel: LUNA_REAL_MODEL_ID,
27616
+ absentEffortDefault: "max"
27617
+ }],
27618
+ [FAST_CRITIC_ALIAS_ID, {
27619
+ aliasId: FAST_CRITIC_ALIAS_ID,
27620
+ realModel: "gemini-3.7-flash",
27621
+ absentEffortDefault: "medium"
27622
+ }],
27623
+ [LUNA_SONNET_ALIAS_ID, {
27624
+ aliasId: LUNA_SONNET_ALIAS_ID,
27625
+ realModel: LUNA_REAL_MODEL_ID,
27626
+ absentEffortDefault: "xhigh"
27627
+ }],
27628
+ [LUNA_HAIKU_ALIAS_ID, {
27629
+ aliasId: LUNA_HAIKU_ALIAS_ID,
27630
+ realModel: LUNA_REAL_MODEL_ID,
27631
+ absentEffortDefault: "high"
27632
+ }]
27633
+ ]);
27634
+ /**
27635
+ * Look up the alias descriptor for a wire-facing model id (with or without
27636
+ * a trailing `[1m]` bracket — the bracket is stripped before the table
27637
+ * lookup and is orthogonal to alias identity). Returns undefined for any
27638
+ * id that isn't one of the registered aliases (including bare `claude-*`
27639
+ * ids and every other real Copilot catalog id).
27640
+ */
27641
+ function resolveModelAlias(id) {
27642
+ const { base } = stripTrailingOneMSuffix(id);
27643
+ return MODEL_ALIAS_TABLE.get(base);
27644
+ }
27645
+ /**
27646
+ * Strip alias provenance and return the real catalog id to send upstream.
27647
+ * Idempotent passthrough for any id that isn't a registered alias (a bare
27648
+ * `claude-*` slug, an already-real Copilot id, or anything else) — this is
27649
+ * safe to call unconditionally on every `body.model` at the outbound
27650
+ * boundary. Preserves a trailing `[1m]` bracket: canonicalization only
27651
+ * erases ALIAS identity, not the 1M-context accounting decoration.
27652
+ */
27653
+ function canonicalizeAliasModel(id) {
27654
+ const { base, hadSuffix } = stripTrailingOneMSuffix(id);
27655
+ const alias = MODEL_ALIAS_TABLE.get(base);
27656
+ if (!alias) return normalizeTrailingOneMSuffix(id);
27657
+ return hadSuffix ? `${alias.realModel}[1m]` : alias.realModel;
27658
+ }
27659
+ const FAST_REQUIRED_CONTEXT_TOKENS = 1e6;
27660
+ function findModel(catalog, id) {
27661
+ return catalog?.data?.find((m) => m.id === id);
27662
+ }
27663
+ function hasToolCalls(model) {
27664
+ return model?.capabilities?.supports?.tool_calls === true;
27665
+ }
27666
+ function hasContextAtLeast(model, tokens) {
27667
+ return (model?.capabilities?.limits?.max_context_window_tokens ?? 0) >= tokens;
27668
+ }
27669
+ function supportsEffort(model, effort) {
27670
+ const list = model?.capabilities?.supports?.reasoning_effort;
27671
+ return Array.isArray(list) && list.includes(effort);
27672
+ }
27673
+ const FAST_REVIEWER_MIN_PROMPT_TOKENS = 2e5;
27674
+ function supportsEndpoint(model, endpoint) {
27675
+ return model !== void 0 && fastEndpointForModel(model) === endpoint;
27676
+ }
27677
+ function hasUsablePromptMetadata(model) {
27678
+ const prompt = model?.capabilities?.limits?.max_prompt_tokens;
27679
+ return typeof prompt === "number" && Number.isFinite(prompt) && prompt > 0;
27680
+ }
27681
+ function hasPromptAtLeast(model, tokens) {
27682
+ const prompt = model?.capabilities?.limits?.max_prompt_tokens;
27683
+ return typeof prompt === "number" && Number.isFinite(prompt) && prompt >= tokens;
27684
+ }
27685
+ /**
27686
+ * Validate the live Copilot catalog carries every model the fast profile's
27687
+ * EXACT roster depends on, with the specific capabilities each assignment
27688
+ * needs. These are capability-availability PREREQUISITES for constructing
27689
+ * the roster — not an allowlist of models the user may select later in the
27690
+ * session — so a partial catalog fails the whole `-m fast` launch rather
27691
+ * than silently substituting or dropping an agent.
27692
+ *
27693
+ * Checks, per the fast-launch-profile design:
27694
+ * - Luna lead/scout/implementer: tool calls, >=1M, high+max, Responses.
27695
+ * - Sol planner: tool calls, >=1M, high, Responses.
27696
+ * - Grok reviewer: tool calls, medium, Responses, usable prompt metadata.
27697
+ * - Gemini Advisor: >=1M, high, chat-completions.
27698
+ * - Gemini critic: tool calls, >=1M, medium, chat-completions.
27699
+ * - Opus Oracle: exact Opus 5, >=1M, adaptive/high, Messages, prompt metadata.
27700
+ *
27701
+ * Pure over the passed-in catalog snapshot so it's unit-testable without
27702
+ * `state` — callers pass `state.models` at call time.
27703
+ */
27704
+ function validateFastProfilePrerequisites(catalog) {
27705
+ const missing = [];
27706
+ const luna = findModel(catalog, LUNA_REAL_MODEL_ID);
27707
+ if (!luna) missing.push(`${LUNA_REAL_MODEL_ID}: absent from the live catalog`);
27708
+ else {
27709
+ if (!hasToolCalls(luna)) missing.push(`${LUNA_REAL_MODEL_ID}: does not advertise tool_calls`);
27710
+ if (!hasContextAtLeast(luna, FAST_REQUIRED_CONTEXT_TOKENS)) missing.push(`${LUNA_REAL_MODEL_ID}: advertised context window is below 1M`);
27711
+ if (!supportsEffort(luna, "high") || !supportsEffort(luna, "max")) missing.push(`${LUNA_REAL_MODEL_ID}: does not advertise both "high" and "max" reasoning effort`);
27712
+ if (!supportsEndpoint(luna, "responses")) missing.push(`${LUNA_REAL_MODEL_ID}: does not advertise a supported Responses endpoint`);
27713
+ }
27714
+ const sol = findModel(catalog, "gpt-5.6-sol");
27715
+ if (!sol) missing.push("gpt-5.6-sol: absent from the live catalog");
27716
+ else {
27717
+ if (!hasToolCalls(sol)) missing.push("gpt-5.6-sol: does not advertise tool_calls");
27718
+ if (!hasContextAtLeast(sol, FAST_REQUIRED_CONTEXT_TOKENS)) missing.push("gpt-5.6-sol: advertised context window is below 1M");
27719
+ if (!supportsEffort(sol, "high")) missing.push("gpt-5.6-sol: does not advertise a \"high\" reasoning effort");
27720
+ if (!supportsEndpoint(sol, "responses")) missing.push("gpt-5.6-sol: does not advertise a supported Responses endpoint");
27721
+ }
27722
+ const grok = findModel(catalog, "grok-4.6");
27723
+ if (!grok) missing.push("grok-4.6: absent from the live catalog");
27724
+ else {
27725
+ if (!hasToolCalls(grok)) missing.push("grok-4.6: does not advertise tool_calls");
27726
+ if (!supportsEffort(grok, "medium")) missing.push("grok-4.6: does not advertise a \"medium\" reasoning effort");
27727
+ if (!hasPromptAtLeast(grok, 2e5)) missing.push(`grok-4.6: advertised max_prompt_tokens is below ${FAST_REVIEWER_MIN_PROMPT_TOKENS}`);
27728
+ if (!supportsEndpoint(grok, "responses")) missing.push("grok-4.6: does not advertise a supported Responses endpoint");
27729
+ }
27730
+ const gemini = findModel(catalog, "gemini-3.7-flash");
27731
+ if (!gemini) missing.push("gemini-3.7-flash: absent from the live catalog");
27732
+ else {
27733
+ if (!hasToolCalls(gemini)) missing.push("gemini-3.7-flash: does not advertise tool_calls for the native critic");
27734
+ if (!hasContextAtLeast(gemini, FAST_REQUIRED_CONTEXT_TOKENS)) missing.push("gemini-3.7-flash: advertised context window is below 1M");
27735
+ if (!supportsEffort(gemini, "high")) missing.push("gemini-3.7-flash: does not advertise a \"high\" reasoning effort for Advisor");
27736
+ if (!supportsEffort(gemini, "medium")) missing.push("gemini-3.7-flash: does not advertise a \"medium\" reasoning effort for the native critic");
27737
+ if (!supportsEndpoint(gemini, "chat")) missing.push("gemini-3.7-flash: does not advertise a supported chat-completions endpoint");
27738
+ }
27739
+ const opus = findModel(catalog, "claude-opus-5");
27740
+ if (!opus) missing.push("claude-opus-5: absent from the live catalog");
27741
+ else {
27742
+ if (!hasContextAtLeast(opus, FAST_REQUIRED_CONTEXT_TOKENS)) missing.push("claude-opus-5: advertised context window is below 1M");
27743
+ if (!supportsEffort(opus, "high")) missing.push("claude-opus-5: does not advertise a \"high\" reasoning effort");
27744
+ if (opus.capabilities?.supports?.adaptive_thinking !== true) missing.push("claude-opus-5: does not advertise adaptive_thinking");
27745
+ if (!hasUsablePromptMetadata(opus)) missing.push("claude-opus-5: no usable max_prompt_tokens metadata");
27746
+ if (!supportsEndpoint(opus, "messages")) missing.push("claude-opus-5: does not advertise a supported Messages endpoint");
27747
+ }
27748
+ return {
27749
+ ok: missing.length === 0,
27750
+ missing
27751
+ };
27752
+ }
27753
+ /**
27754
+ * Format `validateFastProfilePrerequisites`'s failure list into the launch
27755
+ * error message: every missing/invalid model, plus the rollback command.
27756
+ */
27757
+ function formatFastPrerequisiteFailure(missing) {
27758
+ return "github-router claude -m fast requires the following live-catalog capabilities, which this account's catalog does not fully provide:\n" + missing.map((m) => ` - ${m}`).join("\n") + "\n\nFalling back or silently dropping an agent is not supported for the fast profile's exact roster. Run plain `github-router claude` instead.";
27759
+ }
27760
+ //#endregion
27457
27761
  //#region src/lib/mcp-capabilities.ts
27458
27762
  /**
27459
27763
  * Capability-gate predicates for the proxy's MCP tool surface.
@@ -27707,22 +28011,30 @@ const FAST_IMPLEMENTER_MODEL = "gpt-5.6-luna";
27707
28011
  * and is gated by max_prompt_tokens rather than the 1M floor. */
27708
28012
  const FAST_REVIEWER_MODEL = "grok-4.6";
27709
28013
  const FAST_PLANNER_MODEL = "gpt-5.6-sol";
28014
+ const FAST_CRITIC_MODEL = "gemini-3.7-flash";
27710
28015
  const FAST_ORACLE_MODEL = "claude-opus-5";
27711
28016
  /** Fixed effort pins for the fast profile. */
27712
28017
  const FAST_SCOUT_EFFORT = "high";
27713
28018
  const FAST_REVIEWER_EFFORT = "medium";
27714
28019
  const FAST_PLANNER_EFFORT = "high";
28020
+ const FAST_CRITIC_EFFORT = "medium";
27715
28021
  function fastScoutModel() {
27716
- return firstPresentInCatalog([FAST_SCOUT_MODEL], {
28022
+ const id = firstPresentInCatalog([FAST_SCOUT_MODEL], {
27717
28023
  requireToolCalls: true,
27718
28024
  minContextTokens: ONE_M_TOKENS
27719
28025
  });
28026
+ if (!id) return void 0;
28027
+ const found = state.models?.data.find((m) => m.id === id);
28028
+ return found && fastEndpointForModel(found) === "responses" ? id : void 0;
27720
28029
  }
27721
28030
  function fastImplementerModel() {
27722
- return firstPresentInCatalog([FAST_IMPLEMENTER_MODEL], {
28031
+ const id = firstPresentInCatalog([FAST_IMPLEMENTER_MODEL], {
27723
28032
  requireToolCalls: true,
27724
28033
  minContextTokens: ONE_M_TOKENS
27725
28034
  });
28035
+ if (!id) return void 0;
28036
+ const found = state.models?.data.find((m) => m.id === id);
28037
+ return found && fastEndpointForModel(found) === "responses" ? id : void 0;
27726
28038
  }
27727
28039
  function fastPlannerModel() {
27728
28040
  const id = firstPresentInCatalog([FAST_PLANNER_MODEL], {
@@ -27733,7 +28045,7 @@ function fastPlannerModel() {
27733
28045
  const found = state.models?.data.find((m) => m.id === id);
27734
28046
  const efforts = found?.capabilities?.supports?.reasoning_effort;
27735
28047
  if (!Array.isArray(efforts) || !efforts.includes("high")) return void 0;
27736
- if (pickEndpoint(found) !== "responses") return void 0;
28048
+ if (!found || fastEndpointForModel(found) !== "responses") return void 0;
27737
28049
  return id;
27738
28050
  }
27739
28051
  /** Gate Grok on the prompt limit that actually constrains pasted review input. */
@@ -27746,9 +28058,21 @@ function fastReviewerModel() {
27746
28058
  const efforts = found.capabilities?.supports?.reasoning_effort;
27747
28059
  if (!Array.isArray(efforts) || !efforts.includes("medium")) return void 0;
27748
28060
  if ((found.capabilities?.limits?.max_prompt_tokens ?? 0) < 2e5) return void 0;
27749
- if (pickEndpoint(found) !== "responses") return void 0;
28061
+ if (fastEndpointForModel(found) !== "responses") return void 0;
27750
28062
  return FAST_REVIEWER_MODEL;
27751
28063
  }
28064
+ /** Exact Gemini 3.7 Flash only: the fast native critic has no fallback.
28065
+ * Unlike the fast Advisor, it needs tool calls and a medium effort choice. */
28066
+ function fastCriticModel() {
28067
+ const found = state.models?.data.find((m) => m.id === FAST_CRITIC_MODEL);
28068
+ if (!found) return void 0;
28069
+ if (found.capabilities?.supports?.tool_calls !== true) return void 0;
28070
+ if ((found.capabilities?.limits?.max_context_window_tokens ?? 0) < 1e6) return void 0;
28071
+ const efforts = found.capabilities?.supports?.reasoning_effort;
28072
+ if (!Array.isArray(efforts) || !efforts.includes("medium")) return void 0;
28073
+ if (fastEndpointForModel(found) !== "chat") return void 0;
28074
+ return FAST_CRITIC_MODEL;
28075
+ }
27752
28076
  /** Exact Opus 5 only: the fast Oracle never inherits standard opus_critic's
27753
28077
  * older-family fallback. */
27754
28078
  function fastOracleModel() {
@@ -27759,7 +28083,7 @@ function fastOracleModel() {
27759
28083
  const efforts = found.capabilities?.supports?.reasoning_effort;
27760
28084
  if (!Array.isArray(efforts) || !efforts.includes("high")) return void 0;
27761
28085
  if (found.capabilities?.supports?.adaptive_thinking !== true) return void 0;
27762
- if (!(found.supported_endpoints ?? []).some((endpoint) => endpoint === "/messages" || endpoint === "/v1/messages")) return void 0;
28086
+ if (fastEndpointForModel(found) !== "messages") return void 0;
27763
28087
  return FAST_ORACLE_MODEL;
27764
28088
  }
27765
28089
  /**
@@ -29602,31 +29926,16 @@ const ADVISOR_MIN_EFFORT = "high";
29602
29926
  * the decorrelation instrument and are untouched. `GH_ROUTER_ADVISOR_MODEL`
29603
29927
  * keeps a cross-lab advisor one env var away for anyone who wants it back. */
29604
29928
  const ADVISOR_ESCALATION_MODEL = "claude-opus-5";
29605
- /** The Advisor model for the fast Luna profile. Gemini 3.7 Flash is a
29606
- * different lab from BOTH the Luna lead (OpenAI) and the fast profile's
29607
- * `gemini-critic` persona shares this same model see
29608
- * `docs/default-models.md` "Fast launch profile" for the roster this
29609
- * belongs to. Kept distinct from `ADVISOR_DEFAULT_MODEL` so the two never
29610
- * have to agree; `resolveAdvisorModel` picks between them purely on lead
29611
- * identity, never model availability heuristics beyond a live-catalog
29612
- * presence check (mirrors `shouldEscalateAdvisor`'s pattern). */
29929
+ /** The Advisor model for an authenticated fast primary lead. Gemini 3.7 Flash
29930
+ * is cross-lab from the OpenAI-backed Luna/Sol leads and is selected only when
29931
+ * its live catalog entry advertises the required Chat endpoint. Kept distinct
29932
+ * from `ADVISOR_DEFAULT_MODEL` so standard launches remain unchanged. */
29613
29933
  const ADVISOR_FAST_PROFILE_MODEL = "gemini-3.7-flash";
29614
- /**
29615
- * True when `leadModel` names the fast-profile Luna lead (bare, or with the
29616
- * `[1m]` context decoration `withOneMSuffixForLead` applies to it).
29617
- */
29618
- function isFastProfileLead(leadModel) {
29619
- if (!leadModel) return false;
29620
- const bare = leadModel.replace(/\[1m\]$/, "").trim();
29621
- const lastSegment = bare.slice(bare.lastIndexOf("/") + 1);
29622
- return bare === "gpt-5.6-luna" || lastSegment === "gpt-5.6-luna";
29623
- }
29624
- /** True when the live catalog actually carries `ADVISOR_FAST_PROFILE_MODEL`.
29625
- * Mirrors `shouldEscalateAdvisor`'s catalog probe: never advertise a model
29626
- * the account cannot reach, and fall back to the cross-lab default instead
29627
- * of a hard failure when it's absent. */
29934
+ /** True only when the live Gemini entry satisfies the fixed fast transport.
29935
+ * An ID-only presence check is insufficient: selecting a model whose catalog
29936
+ * row lost Chat support would silently degrade every fast Advisor call. */
29628
29937
  function fastProfileAdvisorAvailable() {
29629
- return state.models?.data?.some((m) => m.id === "gemini-3.7-flash") ?? false;
29938
+ return fastEndpointForCatalogId(ADVISOR_FAST_PROFILE_MODEL, state.models?.data) === "chat";
29630
29939
  }
29631
29940
  /** Output cap for the Anthropic-branch advisor call when the catalog carries no
29632
29941
  * limits for the resolved model. The value the branch used unconditionally
@@ -29673,10 +29982,18 @@ function advisorUsesResponses(resolvedAdvisorModel) {
29673
29982
  * chat, mirroring `pickEndpoint`'s "omits supported_endpoints => chat-eligible"
29674
29983
  * convention — the same convention `classifyMessagesRoute` relies on for a
29675
29984
  * lead model, applied here to the advisor's OWN model instead.
29985
+ *
29986
+ * The authenticated fast Advisor passes `fastProfile:true`, which uses the same
29987
+ * fixed endpoint policy as its lead/agent roster. Standard calls leave this
29988
+ * false and retain the historical catalog/name behavior.
29676
29989
  */
29677
- function advisorTransport(resolvedAdvisorModel) {
29990
+ function advisorTransport(resolvedAdvisorModel, fastProfile = false) {
29678
29991
  const bare = resolvedAdvisorModel.slice(resolvedAdvisorModel.lastIndexOf("/") + 1);
29679
29992
  const entry = state.models?.data?.find((m) => m.id === resolvedAdvisorModel || m.id === bare);
29993
+ if (fastProfile && entry) {
29994
+ const fixed = fastEndpointForModel(entry);
29995
+ if (fixed === "messages" || fixed === "responses" || fixed === "chat") return fixed;
29996
+ }
29680
29997
  if (isClaudeModel(resolvedAdvisorModel, entry)) return "messages";
29681
29998
  if (advisorUsesResponses(resolvedAdvisorModel)) return "responses";
29682
29999
  return "chat";
@@ -29707,29 +30024,20 @@ function shouldEscalateAdvisor(leadModel) {
29707
30024
  return state.models?.data?.some((m) => m.id === "claude-opus-5") ?? false;
29708
30025
  }
29709
30026
  /**
29710
- * Pick the advisor model for one request from the LEAD model that request is
29711
- * running on.
29712
- *
29713
- * Resolved per request rather than at launch because the lead changes
29714
- * mid-session via the `/model` picker; launch-time env plumbing would pin the
29715
- * advisor to whatever was selected at spawn.
30027
+ * Pick the Advisor model for one request. Standard selection follows the
30028
+ * current lead so a `/model` switch can change budget escalation; authenticated
30029
+ * fast selection follows launch identity instead, so changing among the fixed
30030
+ * fast lead models never removes Gemini Advisor.
29716
30031
  *
29717
30032
  * Precedence:
29718
- * 1. `GH_ROUTER_ADVISOR_MODEL` (trimmed) the operator pin, checked first so
29719
- * it works on every lead.
29720
- * 2. An authenticated fast launch whose current lead is Luna, with
29721
- * `ADVISOR_FAST_PROFILE_MODEL` present in the live catalog.
29722
- * 3. A lighter Claude lead with the escalation model in the catalog.
29723
- * 4. `ADVISOR_DEFAULT_MODEL`.
29724
- *
29725
- * Steps 2 and 3 are mutually exclusive lead families (non-Claude Luna vs. a
29726
- * lighter Claude tier) so their relative order does not matter functionally;
29727
- * fast-profile is checked first only because it is the more specific match.
30033
+ * 1. `GH_ROUTER_ADVISOR_MODEL` (trimmed), on every launch and lead.
30034
+ * 2. Authenticated fast primary lead, when Gemini advertises the required
30035
+ * Chat endpoint.
30036
+ * 3. Standard lighter Claude lead with Opus escalation available.
30037
+ * 4. The literal `ADVISOR_DEFAULT_MODEL`.
29728
30038
  *
29729
- * Step 4 returns the LITERAL constant rather than walking the OpenAI frontier
29730
- * chain. An Opus lead must resolve to exactly what it resolves to today, and a
29731
- * frontier walk could yield `gpt-5.5` on a catalog missing `gpt-5.6-sol` —
29732
- * a silent change to the one path that is required not to move.
30039
+ * Step 4 deliberately does not walk the OpenAI frontier chain. That would
30040
+ * silently change the standard Opus-lead path when Sol is absent.
29733
30041
  */
29734
30042
  /**
29735
30043
  * Map an operator pin onto the id the catalog actually carries.
@@ -29762,7 +30070,7 @@ function resolveAdvisorModel(leadModel, fastProfile = false) {
29762
30070
  escalated: false,
29763
30071
  fastProfile: false
29764
30072
  };
29765
- if (fastProfile && leadModel && isFastProfileLead(leadModel) && fastProfileAdvisorAvailable()) return {
30073
+ if (fastProfile && fastProfileAdvisorAvailable()) return {
29766
30074
  model: ADVISOR_FAST_PROFILE_MODEL,
29767
30075
  escalated: false,
29768
30076
  fastProfile: true
@@ -29842,7 +30150,7 @@ Give the advice serious weight. If you follow a step and it fails empirically, o
29842
30150
  If you've already retrieved data pointing one way and the advisor points another: don't silently switch. Surface the conflict in one more advisor call -- "I found X, you suggest Y, which constraint breaks the tie?" The advisor saw your evidence but may have underweighted it; a reconcile call is cheaper than committing to the wrong branch.`;
29843
30151
  /** Fast-profile lead-only policy. Unlike the standard Claude Code instructions
29844
30152
  * above, this makes consultation optional and leaves decision ownership with
29845
- * the Luna lead. Fast Task subagents never receive an advisor tool at all. */
30153
+ * the authenticated fast primary lead. Fast Task subagents never receive an advisor tool at all. */
29846
30154
  const FAST_ADVISOR_TOOL_INSTRUCTIONS = `# Advisor Tool
29847
30155
 
29848
30156
  You have access to an optional, transcript-aware \`advisor\` tool. It takes no parameters and returns non-binding consultation. You remain responsible for every decision.
@@ -30065,7 +30373,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
30065
30373
  maxUnits = ADVISOR_MAX_CONVERSATION_CHARS;
30066
30374
  }
30067
30375
  const conversationText = renderConversationAsText(conversation, maxUnits, measure);
30068
- const transport = advisorTransport(resolvedAdvisorModel);
30376
+ const transport = advisorTransport(resolvedAdvisorModel, fastProfile);
30069
30377
  if (transport === "responses") {
30070
30378
  const payload = applyResponsesCachePolicy({
30071
30379
  model: resolvedAdvisorModel,
@@ -30157,7 +30465,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
30157
30465
  * 1. A real Anthropic `toolu_*` id whose suffix is already in the
30158
30466
  * `^[a-zA-Z0-9_]+$` charset: `srvtoolu_<suffix>`, byte-for-byte
30159
30467
  * identical to the historical (Claude-lead) behavior.
30160
- * 2. Anything else — a Responses `call_*` id (the fast Luna profile's
30468
+ * 2. Anything else — a Responses `call_*` id (an authenticated fast lead's
30161
30469
  * lead, once its `tool_use{__anthropic_advisor}` block is synthesized
30162
30470
  * by the anthropic-translate shim from a Copilot `/responses` tool
30163
30471
  * call), a hyphenated or otherwise non-conforming id, an empty string,
@@ -30180,7 +30488,7 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
30180
30488
  * Historically this threw "advisor tool_use id is not round-trippable" for
30181
30489
  * any non-`toolu_` shape. That was correct for a Claude-only advisor lead —
30182
30490
  * Copilot's native `/v1/messages` never emits anything else — but became a
30183
- * live defect once the advisor loop could run on a non-Claude (Luna) lead
30491
+ * live defect once the advisor loop could run on a non-Claude fast lead
30184
30492
  * shimmed through `/responses`: `responses-egress.ts` forwards a Responses
30185
30493
  * `call_*` id VERBATIM as the synthesized `tool_use.id` (see
30186
30494
  * `makeToolUseId` — it only synthesizes a `toolu_*` id when the upstream id
@@ -30210,7 +30518,7 @@ function sseEvent(type, data) {
30210
30518
  * passthrough (`createMessages`) plus signed-thinking-history repair-and-retry.
30211
30519
  * Extracted verbatim from the loop body so the behavior is byte-identical to
30212
30520
  * before `continueTurn` became injectable, and so a non-Claude
30213
- * `continueTurn` (the fast Luna profile's shim-backed one) can omit this
30521
+ * `continueTurn` (the fast profile's shim-backed one) can omit this
30214
30522
  * Claude-only repair path entirely rather than inherit dead code that would
30215
30523
  * never fire for it.
30216
30524
  */
@@ -35575,7 +35883,8 @@ function buildPeerAwarenessSnippet(opts) {
35575
35883
  "",
35576
35884
  `This is the fast launch profile. Advisor is an optional, non-binding, lead-only transcript-aware sounding board for consequential unresolved uncertainty or a genuinely stuck path, not routine progress, waiting, verification, approval, or completion. \`mcp__${fastPeersKey}__oracle\` is exact Opus 5 (1M/high), a stateless last-resort consultant available to the lead, reviewer, and planner.`,
35577
35885
  "",
35578
- `\`mcp__${fastSearchKey}__code\` is semantic-first code search and \`mcp__${fastSearchKey}__web\` surfaces citable sources. Native Task roster: \`scout\` (broad discovery), \`implementer\` (mechanical implementation), \`reviewer\` (repo-aware verification/reproduction), and \`planner\` (Sol plan consultant/approver after Luna's draft). Before implementation obtain planner approval; before declaring done run relevant tests and ask reviewer to verify.${opts.browseAvailable ? ` \`mcp__${key("browser")}__*\` is the opt-in browser surface.` : ""}`
35886
+ `\`mcp__${fastSearchKey}__code\` is semantic-first code search and \`mcp__${fastSearchKey}__web\` surfaces citable sources. Native Task roster: \`scout\` (broad discovery), \`implementer\` (mechanical implementation), \`reviewer\` (repo-aware verification/reproduction), \`planner\` (Sol plan consultant/approver after Luna's draft), and \`critic\` (fresh-context cross-lab review). Before implementation obtain planner approval; before declaring done run relevant tests and ask reviewer to verify.${opts.browseAvailable ? ` \`mcp__${key("browser")}__*\` is the opt-in browser surface.` : ""}`,
35887
+ "Native delegation is ACL-scoped: the lead may invoke all five; planner may invoke reviewer, scout, and critic; implementer may invoke reviewer and critic; reviewer, scout, and critic cannot invoke native subagents."
35579
35888
  ].join("\n");
35580
35889
  }
35581
35890
  const peersKey = key("peers");
@@ -35634,7 +35943,8 @@ function buildPeerAwarenessSummary(opts) {
35634
35943
  if (opts.profile === "fast") return [
35635
35944
  "## Injected capabilities (summary)",
35636
35945
  "",
35637
- "Fast launch profile. Task roster: `scout`, `implementer`, `reviewer`, `planner`. Luna investigates and drafts; `planner` must approve before implementation. Before declaring done, run relevant tests and ask `reviewer` to verify.",
35946
+ "Fast launch profile. Task roster: `scout`, `implementer`, `reviewer`, `planner`, `critic`. Luna investigates and drafts; `planner` must approve before implementation. Before declaring done, run relevant tests and ask `reviewer` to verify.",
35947
+ "Native delegation is ACL-scoped: the lead may invoke all five; `planner` may invoke `reviewer`, `scout`, and `critic`; `implementer` may invoke `reviewer` and `critic`; `reviewer`, `scout`, and `critic` cannot invoke native subagents.",
35638
35948
  `Advisor is optional, non-binding, transcript-aware, and lead-only; use it for consequential unresolved uncertainty, not routine progress or workflow gates. \`mcp__${key("peers")}__oracle\` is exact Opus 5 (1M/high), stateless and last resort for the lead, reviewer, and planner. \`mcp__${key("search")}__code\` and \`mcp__${key("search")}__web\` provide search.${opts.browseAvailable ? ` \`mcp__${key("browser")}__*\` provides the opt-in browser.` : ""}`
35639
35949
  ].join("\n");
35640
35950
  const renderNative = (name) => {
@@ -36908,6 +37218,6 @@ function enumerateInjectedMcpToolNames(groupKeys, opts = {}) {
36908
37218
  return [...new Set(names)];
36909
37219
  }
36910
37220
  //#endregion
36911
- export { UNKNOWN_EFFORT_ANCHOR as $, provisionTreeSitterAssets as $t, assetFor as A, shimDefaultsToXhigh as At, resolveAdvisorEffort as B, createResponses as Bt, buildEnv as C, withOneMSuffix as Cn, resolveGeminiReviewModel as Ct, toolbeltSkipSet as D, scribeModel as Dt, toolbeltEnabled as E, scoutModel as Et, FAST_ADVISOR_TOOL_INSTRUCTIONS as F, registerLaunch as Ft, repairRejectedThinkingHistory as G, normalizeOpenAIUsage as Gt, formatThinkingRepairDecline as H, MAX_RESPONSE_BODY_BYTES as Ht, buildAdvisorStream as I, unregisterLaunch as It, isControllerClosedError as J, colbertDegradedWarning as Jt, buildAnthropicErrorEvent as K, provisionBrowserAssets as Kt, injectAdvisorTool as L, assembleResponsesPayload as Lt, searchWeb as M, createMessages as Mt, ADVISOR_INTERNAL_TOOL_NAME as N, getTokenCount as Nt, vscodeRipgrepPath as O, standInToolEnabled as Ot, ADVISOR_TOOL_INSTRUCTIONS as P, findLaunchBySecret as Pt, EFFORT_ORDER as Q, warmTreeSitterPool as Qt, isAdvisorRequested as R, warnOnTokenPriceDrift as Rt, runWorkerAgent as S, oneMContextDisabled as Sn, nativeSubagentModel as St, buildToolbeltAwareness as T, withInstallLock as Tn, reviewerModel as Tt, rememberThinkingHistoryRepair as U, readResponseBodyCapped as Ut, resolveAdvisorModel as V, createChatCompletions as Vt, repairKnownThinkingHistory as W, parseJsonOrDiagnose as Wt, readIteratorWithTimeout as X, extractTarGzMember as Xt, logStreamError as Y, provisionAndIndexColbert as Yt, relayAnthropicStream as Z, extractZipMember as Zt, TEST_DEFAULT_MODEL as _, upstreamAllowH2 as _n, fastScoutModel as _t, buildAgentPrompt as a, BUDGET_SMALL_FAST_CATALOG_ID as an, FAST_REVIEWER_EFFORT as at, resolveModeDefaults as b, pickEndpoint as bn, generalPurposeFastModel as bt, enumerateInjectedMcpToolNames as c, DEFAULT_CODEX_MODEL as cn, artifactToolsEnabled as ct, DEFAULT_MODEL_CHAIN as d, UPSTREAM_FETCH_TIMEOUT_MS as dn, browserCompoundToolsEnabled as dt, CONDENSED_OPERATING_SEQUENCE as en, bucketEffort as et, EXPLORE_DEFAULT_MODEL as f, UPSTREAM_INACTIVITY_TIMEOUT_MS as fn, browserToolsEnabled as ft, REVIEW_DEFAULT_MODEL as g, resolveLeadSlugArg as gn, fastReviewerModel as gt, PLAN_DEFAULT_MODEL as h, pickClaudeDefault as hn, fastPlannerModel as ht, assertMcpToolSurfaceConsistent as i, toolbeltPathOverride as in, FAST_PLANNER_EFFORT as it, satisfiesMinVersion as j, countTokens as jt, TOOLBELT_TOOLS$1 as k, workerToolsEnabled as kt, personasFor as l, DEFAULT_CODEX_MODEL_FALLBACKS as ln, brainstormModel as lt, IMPLEMENT_DEFAULT_MODEL as m, isBudgetClaudeLead as mn, fastOracleModel as mt, MCP_GROUPS as n, shouldUseInsecureTls as nn, handleMcpDelete as nt, buildPeerAwarenessSnippet as o, BUDGET_SMALL_FAST_SLUG as on, FAST_SCOUT_EFFORT as ot, EXPLORE_DEFAULT_THINKING as p, generateRandomPort as pn, fastImplementerModel as pt, buildOpenAIErrorEvent as q, hasSupportedBrowserInstalled as qt, agentNamesForToolAllowlist as r, collapsePathKeys as rn, handleMcpPost as rt, buildPeerAwarenessSummary as s, DEFAULT_CLAUDE_MODEL_FALLBACKS as sn, agentToolsEnabled as st, GROUP_META as t, DEFINITION_OF_GREATNESS as tn, clampEffort as tt, BROWSE_DEFAULT_MODEL as u, DEFAULT_PORT as un, browseAgentEnabled as ut, appendPlanReminder as v, upstreamMaxConnections as vn, fleetToolsEnabled as vt, availableToolCommands as w, withOneMSuffixForLead as wn, reviewerFastModel as wt, resolveWorkerRunOpts as x, catalogAdvertises1M as xn, implementerFastModel as xt, resolveDefaultModel as y, classifyMessagesRoute as yn, geminiAvailable as yt, isFastProfileLead as z, resolveMcpToolTimeoutMs as zt };
37221
+ export { bucketEffort as $, assembleResponsesPayload as $t, assetFor as A, isBudgetClaudeLead as An, workerToolsEnabled as At, resolveAdvisorModel as B, stripTrailingOneMSuffix as Bn, profileDescriptor as Bt, buildEnv as C, DEFAULT_CLAUDE_MODEL_FALLBACKS as Cn, nativeSubagentModel as Ct, toolbeltSkipSet as D, UPSTREAM_FETCH_TIMEOUT_MS as Dn, scoutModel as Dt, toolbeltEnabled as E, DEFAULT_PORT as En, reviewerModel as Et, FAST_ADVISOR_TOOL_INSTRUCTIONS as F, classifyMessagesRoute as Fn, LUNA_REAL_MODEL_ID as Ft, buildAnthropicErrorEvent as G, countTokens as Gt, rememberThinkingHistoryRepair as H, resolveModelAlias as Ht, buildAdvisorStream as I, catalogAdvertises1M as In, LUNA_SCOUT_ALIAS_ID as It, logStreamError as J, getTokenCount as Jt, buildOpenAIErrorEvent as K, createMessages as Kt, injectAdvisorTool as L, oneMContextDisabled as Ln, LUNA_SONNET_ALIAS_ID as Lt, searchWeb as M, resolveLeadSlugArg as Mn, LUNA_DRIVER_ALIAS_ID as Mt, ADVISOR_INTERNAL_TOOL_NAME as N, upstreamAllowH2 as Nn, LUNA_HAIKU_ALIAS_ID as Nt, vscodeRipgrepPath as O, UPSTREAM_INACTIVITY_TIMEOUT_MS as On, scribeModel as Ot, ADVISOR_TOOL_INSTRUCTIONS as P, upstreamMaxConnections as Pn, LUNA_IMPLEMENTER_ALIAS_ID as Pt, UNKNOWN_EFFORT_ANCHOR as Q, unregisterLaunch as Qt, isAdvisorRequested as R, withOneMSuffix as Rn, canonicalizeAliasModel as Rt, runWorkerAgent as S, BUDGET_SMALL_FAST_SLUG as Sn, implementerFastModel as St, buildToolbeltAwareness as T, DEFAULT_CODEX_MODEL_FALLBACKS as Tn, reviewerFastModel as Tt, repairKnownThinkingHistory as U, validateFastProfilePrerequisites as Ut, formatThinkingRepairDecline as V, withInstallLock as Vn, resolveLaunchProfile as Vt, repairRejectedThinkingHistory as W, shimDefaultsToXhigh as Wt, relayAnthropicStream as X, findLaunchBySecret as Xt, readIteratorWithTimeout as Y, getTokenizerFromModel as Yt, EFFORT_ORDER as Z, registerLaunch as Zt, TEST_DEFAULT_MODEL as _, DEFINITION_OF_GREATNESS as _n, fastReviewerModel as _t, buildAgentPrompt as a, readResponseBodyCapped as an, FAST_REVIEWER_EFFORT as at, resolveModeDefaults as b, toolbeltPathOverride as bn, geminiAvailable as bt, enumerateInjectedMcpToolNames as c, provisionBrowserAssets as cn, artifactToolsEnabled as ct, DEFAULT_MODEL_CHAIN as d, provisionAndIndexColbert as dn, browserCompoundToolsEnabled as dt, warnOnTokenPriceDrift as en, clampEffort as et, EXPLORE_DEFAULT_MODEL as f, extractTarGzMember as fn, browserToolsEnabled as ft, REVIEW_DEFAULT_MODEL as g, CONDENSED_OPERATING_SEQUENCE as gn, fastPlannerModel as gt, PLAN_DEFAULT_MODEL as h, provisionTreeSitterAssets as hn, fastOracleModel as ht, assertMcpToolSurfaceConsistent as i, MAX_RESPONSE_BODY_BYTES as in, FAST_PLANNER_EFFORT as it, satisfiesMinVersion as j, pickClaudeDefault as jn, FAST_CRITIC_ALIAS_ID as jt, TOOLBELT_TOOLS$1 as k, generateRandomPort as kn, standInToolEnabled as kt, personasFor as l, hasSupportedBrowserInstalled as ln, brainstormModel as lt, IMPLEMENT_DEFAULT_MODEL as m, warmTreeSitterPool as mn, fastImplementerModel as mt, MCP_GROUPS as n, createResponses as nn, handleMcpPost as nt, buildPeerAwarenessSnippet as o, parseJsonOrDiagnose as on, FAST_SCOUT_EFFORT as ot, EXPLORE_DEFAULT_THINKING as p, extractZipMember as pn, fastCriticModel as pt, isControllerClosedError as q, getTextTokenCount as qt, agentNamesForToolAllowlist as r, createChatCompletions as rn, FAST_CRITIC_EFFORT as rt, buildPeerAwarenessSummary as s, normalizeOpenAIUsage as sn, agentToolsEnabled as st, GROUP_META as t, resolveMcpToolTimeoutMs as tn, handleMcpDelete as tt, BROWSE_DEFAULT_MODEL as u, colbertDegradedWarning as un, browseAgentEnabled as ut, appendPlanReminder as v, shouldUseInsecureTls as vn, fastScoutModel as vt, availableToolCommands as w, DEFAULT_CODEX_MODEL as wn, resolveGeminiReviewModel as wt, resolveWorkerRunOpts as x, BUDGET_SMALL_FAST_CATALOG_ID as xn, generalPurposeFastModel as xt, resolveDefaultModel as y, collapsePathKeys as yn, fleetToolsEnabled as yt, resolveAdvisorEffort as z, withOneMSuffixForLead as zn, formatFastPrerequisiteFailure as zt };
36912
37222
 
36913
- //# sourceMappingURL=peer-mcp-personas-DhI7ZPSx.js.map
37223
+ //# sourceMappingURL=peer-mcp-personas-BwNtC8jt.js.map