github-router 0.3.289 → 0.3.293
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-Cmz2jt7P.js → attribution-settings-Dx8TBPE8.js} +124 -34
- package/dist/attribution-settings-Dx8TBPE8.js.map +1 -0
- package/dist/{auth-DG4vh8-F.js → auth-BwUHopJz.js} +3 -3
- package/dist/{auth-DG4vh8-F.js.map → auth-BwUHopJz.js.map} +1 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{check-usage-BqN7mBYv.js → check-usage-BTda5753.js} +4 -4
- package/dist/{check-usage-BqN7mBYv.js.map → check-usage-BTda5753.js.map} +1 -1
- package/dist/{claude-C-9xFI4b.js → claude-BoAd_L2W.js} +118 -59
- package/dist/claude-BoAd_L2W.js.map +1 -0
- package/dist/{codex-DRW0yb8x.js → codex-BdJgGT6Q.js} +5 -5
- package/dist/{codex-DRW0yb8x.js.map → codex-BdJgGT6Q.js.map} +1 -1
- package/dist/{debug-B5TjPTTH.js → debug-B3UrZTHQ.js} +2 -2
- package/dist/{debug-B5TjPTTH.js.map → debug-B3UrZTHQ.js.map} +1 -1
- package/dist/engine-CK2b_cTt.js +2 -0
- package/dist/{gate-discovery-Cz6kwIVG.js → gate-discovery-C30rfnyo.js} +5 -5
- package/dist/{gate-discovery-Cz6kwIVG.js.map → gate-discovery-C30rfnyo.js.map} +1 -1
- package/dist/{get-copilot-usage-BjA0nyGR.js → get-copilot-usage-CRrf1ZSC.js} +2 -2
- package/dist/{get-copilot-usage-BjA0nyGR.js.map → get-copilot-usage-CRrf1ZSC.js.map} +1 -1
- package/dist/{internal-artifact-open-BskmUpnb.js → internal-artifact-open-BsEQRvDi.js} +2 -2
- package/dist/{internal-artifact-open-BskmUpnb.js.map → internal-artifact-open-BsEQRvDi.js.map} +1 -1
- package/dist/{internal-first-mate-guard-5XEHMaqy.js → internal-first-mate-guard-CVFFJriA.js} +3 -3
- package/dist/{internal-first-mate-guard-5XEHMaqy.js.map → internal-first-mate-guard-CVFFJriA.js.map} +1 -1
- package/dist/{internal-first-mate-guard-DHDQ6hFz.js → internal-first-mate-guard-DJdX6t48.js} +1 -1
- package/dist/{internal-plan-review-BUuMx4ku.js → internal-plan-review-8TKDLco6.js} +3 -3
- package/dist/{internal-plan-review-BUuMx4ku.js.map → internal-plan-review-8TKDLco6.js.map} +1 -1
- package/dist/{internal-prompt-submit-CQQ15xdO.js → internal-prompt-submit-Da7pxqua.js} +4 -4
- package/dist/{internal-prompt-submit-CQQ15xdO.js.map → internal-prompt-submit-Da7pxqua.js.map} +1 -1
- package/dist/{internal-session-bind-D04W2yWI.js → internal-session-bind-BOFytA1f.js} +2 -2
- package/dist/{internal-session-bind-D04W2yWI.js.map → internal-session-bind-BOFytA1f.js.map} +1 -1
- package/dist/{internal-stop-hook-Dvkppwo7.js → internal-stop-hook-OnfK3BxE.js} +5 -5
- package/dist/{internal-stop-hook-Dvkppwo7.js.map → internal-stop-hook-OnfK3BxE.js.map} +1 -1
- package/dist/{internal-stop-review-CdByyJLc.js → internal-stop-review-CdouacHL.js} +2 -2
- package/dist/{internal-stop-review-CdByyJLc.js.map → internal-stop-review-CdouacHL.js.map} +1 -1
- package/dist/{internal-worker-guard-BIPN6Rv9.js → internal-worker-guard-Bx-itoP8.js} +2 -2
- package/dist/{internal-worker-guard-BIPN6Rv9.js.map → internal-worker-guard-Bx-itoP8.js.map} +1 -1
- package/dist/{internal-workspace-header-BKqejstG.js → internal-workspace-header-8WT0iB5K.js} +2 -2
- package/dist/{internal-workspace-header-BKqejstG.js.map → internal-workspace-header-8WT0iB5K.js.map} +1 -1
- package/dist/lifecycle-C8fOsQke.js +2 -0
- package/dist/lifecycle-D4Yc1aap.js +2 -0
- package/dist/{lifecycle-SXaWssN9.js → lifecycle-LeSfa7wH.js} +2 -2
- package/dist/{lifecycle-SXaWssN9.js.map → lifecycle-LeSfa7wH.js.map} +1 -1
- package/dist/{lifecycle-DbM29FLK.js → lifecycle-nuOHfwgj.js} +2 -2
- package/dist/{lifecycle-DbM29FLK.js.map → lifecycle-nuOHfwgj.js.map} +1 -1
- package/dist/main.js +17 -17
- package/dist/{mcp-workspace-header-DRCCWlOi.js → mcp-workspace-header-q34H_4wL.js} +2 -2
- package/dist/{mcp-workspace-header-DRCCWlOi.js.map → mcp-workspace-header-q34H_4wL.js.map} +1 -1
- package/dist/{models-Dz8d_SnI.js → models-hhJcrZhr.js} +3 -3
- package/dist/{models-Dz8d_SnI.js.map → models-hhJcrZhr.js.map} +1 -1
- package/dist/{orchestration-BrJwZxMN.js → orchestration-pzbrKkgD.js} +2 -2
- package/dist/{orchestration-BrJwZxMN.js.map → orchestration-pzbrKkgD.js.map} +1 -1
- package/dist/{paths-D7_SAaIQ.js → paths-BH4J7slC.js} +4 -4
- package/dist/{paths-D7_SAaIQ.js.map → paths-BH4J7slC.js.map} +1 -1
- package/dist/paths-DJZoXfAS.js +2 -0
- package/dist/{peer-mcp-personas-Bd56EmiO.js → peer-mcp-personas-DhI7ZPSx.js} +569 -136
- package/dist/peer-mcp-personas-DhI7ZPSx.js.map +1 -0
- package/dist/{plan-review-hook-CVZsG9MZ.js → plan-review-hook-CfcanA7_.js} +3 -3
- package/dist/{plan-review-hook-CVZsG9MZ.js.map → plan-review-hook-CfcanA7_.js.map} +1 -1
- package/dist/{prompt-submit-hook-BW92FX2D.js → prompt-submit-hook-Bqf9ORgb.js} +3 -3
- package/dist/{prompt-submit-hook-BW92FX2D.js.map → prompt-submit-hook-Bqf9ORgb.js.map} +1 -1
- package/dist/{provision-B53wbHwa.js → provision-CZJ4EWls.js} +4 -4
- package/dist/{provision-B53wbHwa.js.map → provision-CZJ4EWls.js.map} +1 -1
- package/dist/{self-invocation-CP_SOkrr.js → self-invocation-DhO1Z8iD.js} +2 -2
- package/dist/{self-invocation-CP_SOkrr.js.map → self-invocation-DhO1Z8iD.js.map} +1 -1
- package/dist/{serve-aZCEYFe5.js → serve-RxWYzoOl.js} +12 -12
- package/dist/{serve-aZCEYFe5.js.map → serve-RxWYzoOl.js.map} +1 -1
- package/dist/{server-setup-DlztZAGT.js → server-setup-DbvbW5Ve.js} +627 -85
- package/dist/server-setup-DbvbW5Ve.js.map +1 -0
- package/dist/{start-DwNiXv5N.js → start-BNcGsXsY.js} +3 -3
- package/dist/{start-DwNiXv5N.js.map → start-BNcGsXsY.js.map} +1 -1
- package/dist/{stop-gate-hook-DriRc9xN.js → stop-gate-hook-BiBp5aGm.js} +3 -3
- package/dist/{stop-gate-hook-DriRc9xN.js.map → stop-gate-hook-BiBp5aGm.js.map} +1 -1
- package/dist/{stop-gate-policy-DMPanpoR.js → stop-gate-policy-BGd6b5hR.js} +2 -2
- package/dist/{stop-gate-policy-DMPanpoR.js.map → stop-gate-policy-BGd6b5hR.js.map} +1 -1
- package/dist/{token-BGCjZwtj.js → token-8drORhXg.js} +33 -3
- package/dist/token-8drORhXg.js.map +1 -0
- package/dist/{worker-dispatch-BCTMyNE-.js → worker-dispatch-D5fGroNr.js} +2 -2
- package/dist/{worker-dispatch-BCTMyNE-.js.map → worker-dispatch-D5fGroNr.js.map} +1 -1
- package/package.json +1 -1
- package/dist/attribution-settings-Cmz2jt7P.js.map +0 -1
- package/dist/claude-C-9xFI4b.js.map +0 -1
- package/dist/engine-iEqGdx6T.js +0 -2
- package/dist/lifecycle-BTodQvn4.js +0 -2
- package/dist/lifecycle-C7JYNz-F.js +0 -2
- package/dist/paths-CTr59UC6.js +0 -2
- package/dist/peer-mcp-personas-Bd56EmiO.js.map +0 -1
- package/dist/server-setup-DlztZAGT.js.map +0 -1
- package/dist/token-BGCjZwtj.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-
|
|
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-
|
|
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";
|
|
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-
|
|
7
|
-
import { t as MCP_WORKSPACE_HEADER } from "./mcp-workspace-header-
|
|
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";
|
|
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-
|
|
10
|
-
import { f as resolveSealedGate, m as runGateChecks, p as sealedGateIds } from "./stop-gate-hook-
|
|
11
|
-
import { t as liveExec } from "./orchestration-
|
|
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";
|
|
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";
|
|
@@ -447,9 +447,17 @@ const DEFAULT_CLAUDE_MODEL_FALLBACKS = [
|
|
|
447
447
|
* variant" — defaulting safe-side preserves the pre-change behavior).
|
|
448
448
|
*/
|
|
449
449
|
const DEFAULT_OPUS_FAMILY = "5";
|
|
450
|
-
/**
|
|
451
|
-
*
|
|
452
|
-
|
|
450
|
+
/**
|
|
451
|
+
* The lead `-m fast` selects. `gpt-5.6-luna` — a distinct Luna-driven
|
|
452
|
+
* profile (see `./launch-profile`), NOT a Claude Sonnet budget lead. This
|
|
453
|
+
* REPLACES the earlier `-m fast` → `claude-sonnet-5` mapping: `fast` now
|
|
454
|
+
* names a deliberately lean Luna surface (three native agents, one peer
|
|
455
|
+
* persona, `peers`/`search` MCP groups only), not "budget Sonnet with the
|
|
456
|
+
* full standard surface". `resolveLaunchProfile` in `./launch-profile`
|
|
457
|
+
* keys off the same raw `-m` argument this constant is selected by, so the
|
|
458
|
+
* two can never disagree about which launches count as "fast".
|
|
459
|
+
*/
|
|
460
|
+
const FAST_LEAD_MODEL = "gpt-5.6-luna";
|
|
453
461
|
/** Small/fast tier for a budget lead, in the two forms this codebase needs.
|
|
454
462
|
*
|
|
455
463
|
* `SLUG` is the Anthropic-published DASHED form and is what goes into
|
|
@@ -464,28 +472,29 @@ const BUDGET_SMALL_FAST_CATALOG_ID = "claude-haiku-4.5";
|
|
|
464
472
|
/**
|
|
465
473
|
* Resolve the `-m` argument to the lead slug to launch with.
|
|
466
474
|
*
|
|
467
|
-
* - `fast` → `
|
|
475
|
+
* - `fast` → `FAST_LEAD_MODEL` (the fast Luna profile — see
|
|
476
|
+
* `./launch-profile`, NOT the retired Sonnet budget lead)
|
|
468
477
|
* - `N.M` → the best variant of that Opus family, via `pickClaudeDefault`
|
|
469
478
|
* - a full slug → unchanged, including Copilot slugs a power user pins
|
|
470
479
|
* - absent → the ordinary default
|
|
471
480
|
*
|
|
472
481
|
* Every branch is `[1m]`-decorated against the live catalog, by
|
|
473
482
|
* `pickClaudeDefault` on the two Opus-family branches and by
|
|
474
|
-
* `withOneMSuffixForLead` on the other two.
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
* (
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
483
|
+
* `withOneMSuffixForLead` on the other two. `gpt-5.6-luna` advertises a 1M
|
|
484
|
+
* window, so `-m fast` gets local 1M accounting exactly like every other
|
|
485
|
+
* branch here; the decoration is catalog-gated per model, so a genuinely
|
|
486
|
+
* 200K model (`claude-haiku-4.5`) still comes back bare.
|
|
487
|
+
*
|
|
488
|
+
* `fast` resolves to an ordinary slug rather than setting a mode flag —
|
|
489
|
+
* `resolveLaunchProfile` (`./launch-profile`) is keyed off the SAME raw
|
|
490
|
+
* argument this function receives, so the two can never disagree about
|
|
491
|
+
* which launches are "fast". `isBudgetClaudeLead` (below) stays
|
|
492
|
+
* Claude-family-only and is UNRELATED to the fast profile: `gpt-5.6-luna`
|
|
493
|
+
* is not a Claude model, so `isBudgetClaudeLead(resolveLeadSlugArg("fast"))`
|
|
494
|
+
* is false — the old Sonnet "budget lead" surfaces (advisor escalation,
|
|
495
|
+
* delegation prose, small/fast Haiku tier) simply don't engage for `-m
|
|
496
|
+
* fast` any more; the fast profile has its own separate roster/tier
|
|
497
|
+
* mechanism instead.
|
|
489
498
|
*
|
|
490
499
|
* Callers must keep treating any explicit `-m` as explicit: the
|
|
491
500
|
* `DEFAULT_CLAUDE_MODEL_FALLBACKS` walk applies to the implicit-default path
|
|
@@ -494,7 +503,7 @@ const BUDGET_SMALL_FAST_CATALOG_ID = "claude-haiku-4.5";
|
|
|
494
503
|
function resolveLeadSlugArg(modelArg) {
|
|
495
504
|
const arg = modelArg?.trim();
|
|
496
505
|
if (!arg) return pickClaudeDefault();
|
|
497
|
-
if (arg.toLowerCase() === "fast") return withOneMSuffixForLead(
|
|
506
|
+
if (arg.toLowerCase() === "fast") return withOneMSuffixForLead(FAST_LEAD_MODEL);
|
|
498
507
|
const opusFamilyShorthand = arg.match(/^(\d+\.\d+)$/)?.[1];
|
|
499
508
|
if (opusFamilyShorthand) return pickClaudeDefault(opusFamilyShorthand);
|
|
500
509
|
return withOneMSuffixForLead(arg);
|
|
@@ -18917,7 +18926,7 @@ function logAudit$1(record) {
|
|
|
18917
18926
|
try {
|
|
18918
18927
|
const fs = await import("node:fs/promises");
|
|
18919
18928
|
const path = await import("node:path");
|
|
18920
|
-
const { PATHS } = await import("./paths-
|
|
18929
|
+
const { PATHS } = await import("./paths-DJZoXfAS.js");
|
|
18921
18930
|
const dir = path.join(PATHS.APP_DIR, "browser-mcp");
|
|
18922
18931
|
await fs.mkdir(dir, { recursive: true });
|
|
18923
18932
|
const line = JSON.stringify({
|
|
@@ -24248,8 +24257,8 @@ const FALLBACK_TOKEN_PRICES = Object.freeze({
|
|
|
24248
24257
|
out: 2500
|
|
24249
24258
|
},
|
|
24250
24259
|
"gpt-5.6-sol": {
|
|
24251
|
-
in:
|
|
24252
|
-
out:
|
|
24260
|
+
in: 200,
|
|
24261
|
+
out: 1e3
|
|
24253
24262
|
},
|
|
24254
24263
|
"grok-4.5": {
|
|
24255
24264
|
in: 200,
|
|
@@ -24260,8 +24269,8 @@ const FALLBACK_TOKEN_PRICES = Object.freeze({
|
|
|
24260
24269
|
out: 3e3
|
|
24261
24270
|
},
|
|
24262
24271
|
"gemini-3.6-flash": {
|
|
24263
|
-
in:
|
|
24264
|
-
out:
|
|
24272
|
+
in: 75,
|
|
24273
|
+
out: 375
|
|
24265
24274
|
},
|
|
24266
24275
|
"gemini-3.5-flash": {
|
|
24267
24276
|
in: 150,
|
|
@@ -26764,6 +26773,67 @@ function capToolResultText(content, capBytes) {
|
|
|
26764
26773
|
];
|
|
26765
26774
|
}
|
|
26766
26775
|
//#endregion
|
|
26776
|
+
//#region src/lib/launch-registry.ts
|
|
26777
|
+
/**
|
|
26778
|
+
* Register a new authenticated launch (a `github-router claude` process, or
|
|
26779
|
+
* `serve`'s per-repo session) in the keyed registry. Returns the stored
|
|
26780
|
+
* entry, including the generated `launchId` when the caller didn't supply
|
|
26781
|
+
* one.
|
|
26782
|
+
*
|
|
26783
|
+
* Callers are expected to mint `nonce` and `secret` as independent random
|
|
26784
|
+
* tokens (see `src/claude.ts` / `src/lib/serve/enhancements.ts`) — this
|
|
26785
|
+
* function stores whatever it's given without generating credentials
|
|
26786
|
+
* itself, so a caller cannot accidentally rely on it for randomness.
|
|
26787
|
+
*/
|
|
26788
|
+
function registerLaunch(params) {
|
|
26789
|
+
const entry = {
|
|
26790
|
+
launchId: params.launchId ?? randomUUID(),
|
|
26791
|
+
nonce: params.nonce,
|
|
26792
|
+
secret: params.secret,
|
|
26793
|
+
profileId: params.profileId,
|
|
26794
|
+
allowedGroups: params.allowedGroups,
|
|
26795
|
+
allowedPersonas: params.allowedPersonas,
|
|
26796
|
+
createdAt: Date.now()
|
|
26797
|
+
};
|
|
26798
|
+
state.launchRegistry.set(entry.launchId, entry);
|
|
26799
|
+
return entry;
|
|
26800
|
+
}
|
|
26801
|
+
/** Remove one launch's entry. Idempotent — removing an already-removed or
|
|
26802
|
+
* never-registered id is a no-op. Called from the launch's own cleanup
|
|
26803
|
+
* path so a torn-down session's credentials stop authenticating. */
|
|
26804
|
+
function unregisterLaunch(launchId) {
|
|
26805
|
+
state.launchRegistry.delete(launchId);
|
|
26806
|
+
}
|
|
26807
|
+
/**
|
|
26808
|
+
* Constant-time string compare. Per-launch credentials are random tokens,
|
|
26809
|
+
* not secrets an attacker gets many guesses at over the network within one
|
|
26810
|
+
* process lifetime, so timing attacks aren't a realistic concern here — but
|
|
26811
|
+
* it costs nothing and matches the prior nonce-compare's posture.
|
|
26812
|
+
*/
|
|
26813
|
+
function constantTimeStringEqual(a, b) {
|
|
26814
|
+
if (a.length !== b.length) return false;
|
|
26815
|
+
try {
|
|
26816
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
26817
|
+
} catch {
|
|
26818
|
+
return false;
|
|
26819
|
+
}
|
|
26820
|
+
}
|
|
26821
|
+
/**
|
|
26822
|
+
* Find the launch whose `/mcp` bearer (`nonce`) matches. Linear scan over
|
|
26823
|
+
* `state.launchRegistry` — expected to hold a handful of entries at most
|
|
26824
|
+
* (one per concurrently running `claude`/`serve` session), so this is not a
|
|
26825
|
+
* hot-path concern. Returns undefined (never throws) when nothing matches,
|
|
26826
|
+
* including when the registry is empty (the "not enabled" case).
|
|
26827
|
+
*/
|
|
26828
|
+
function findLaunchByNonce(nonce) {
|
|
26829
|
+
for (const entry of state.launchRegistry.values()) if (constantTimeStringEqual(entry.nonce, nonce)) return entry;
|
|
26830
|
+
}
|
|
26831
|
+
/** Find the launch whose `/v1/messages` identity-preflight bearer
|
|
26832
|
+
* (`secret`) matches. Mirrors `findLaunchByNonce`. */
|
|
26833
|
+
function findLaunchBySecret(secret) {
|
|
26834
|
+
for (const entry of state.launchRegistry.values()) if (constantTimeStringEqual(entry.secret, secret)) return entry;
|
|
26835
|
+
}
|
|
26836
|
+
//#endregion
|
|
26767
26837
|
//#region src/lib/peer-attachments.ts
|
|
26768
26838
|
/**
|
|
26769
26839
|
* Server-side image loading for peer-critic attachments (`imagePaths`).
|
|
@@ -27631,6 +27701,67 @@ function generalPurposeFastModel() {
|
|
|
27631
27701
|
minContextTokens: ONE_M_TOKENS
|
|
27632
27702
|
});
|
|
27633
27703
|
}
|
|
27704
|
+
const FAST_SCOUT_MODEL = "gpt-5.6-luna";
|
|
27705
|
+
const FAST_IMPLEMENTER_MODEL = "gpt-5.6-luna";
|
|
27706
|
+
/** Grok 4.6 advertises 500K total context / 372K max prompt, so it remains bare
|
|
27707
|
+
* and is gated by max_prompt_tokens rather than the 1M floor. */
|
|
27708
|
+
const FAST_REVIEWER_MODEL = "grok-4.6";
|
|
27709
|
+
const FAST_PLANNER_MODEL = "gpt-5.6-sol";
|
|
27710
|
+
const FAST_ORACLE_MODEL = "claude-opus-5";
|
|
27711
|
+
/** Fixed effort pins for the fast profile. */
|
|
27712
|
+
const FAST_SCOUT_EFFORT = "high";
|
|
27713
|
+
const FAST_REVIEWER_EFFORT = "medium";
|
|
27714
|
+
const FAST_PLANNER_EFFORT = "high";
|
|
27715
|
+
function fastScoutModel() {
|
|
27716
|
+
return firstPresentInCatalog([FAST_SCOUT_MODEL], {
|
|
27717
|
+
requireToolCalls: true,
|
|
27718
|
+
minContextTokens: ONE_M_TOKENS
|
|
27719
|
+
});
|
|
27720
|
+
}
|
|
27721
|
+
function fastImplementerModel() {
|
|
27722
|
+
return firstPresentInCatalog([FAST_IMPLEMENTER_MODEL], {
|
|
27723
|
+
requireToolCalls: true,
|
|
27724
|
+
minContextTokens: ONE_M_TOKENS
|
|
27725
|
+
});
|
|
27726
|
+
}
|
|
27727
|
+
function fastPlannerModel() {
|
|
27728
|
+
const id = firstPresentInCatalog([FAST_PLANNER_MODEL], {
|
|
27729
|
+
requireToolCalls: true,
|
|
27730
|
+
minContextTokens: ONE_M_TOKENS
|
|
27731
|
+
});
|
|
27732
|
+
if (!id) return void 0;
|
|
27733
|
+
const found = state.models?.data.find((m) => m.id === id);
|
|
27734
|
+
const efforts = found?.capabilities?.supports?.reasoning_effort;
|
|
27735
|
+
if (!Array.isArray(efforts) || !efforts.includes("high")) return void 0;
|
|
27736
|
+
if (pickEndpoint(found) !== "responses") return void 0;
|
|
27737
|
+
return id;
|
|
27738
|
+
}
|
|
27739
|
+
/** Gate Grok on the prompt limit that actually constrains pasted review input. */
|
|
27740
|
+
function fastReviewerModel() {
|
|
27741
|
+
const models = state.models?.data;
|
|
27742
|
+
if (!models) return void 0;
|
|
27743
|
+
const found = models.find((m) => m.id === FAST_REVIEWER_MODEL);
|
|
27744
|
+
if (!found) return void 0;
|
|
27745
|
+
if (found.capabilities?.supports?.tool_calls !== true) return void 0;
|
|
27746
|
+
const efforts = found.capabilities?.supports?.reasoning_effort;
|
|
27747
|
+
if (!Array.isArray(efforts) || !efforts.includes("medium")) return void 0;
|
|
27748
|
+
if ((found.capabilities?.limits?.max_prompt_tokens ?? 0) < 2e5) return void 0;
|
|
27749
|
+
if (pickEndpoint(found) !== "responses") return void 0;
|
|
27750
|
+
return FAST_REVIEWER_MODEL;
|
|
27751
|
+
}
|
|
27752
|
+
/** Exact Opus 5 only: the fast Oracle never inherits standard opus_critic's
|
|
27753
|
+
* older-family fallback. */
|
|
27754
|
+
function fastOracleModel() {
|
|
27755
|
+
const found = state.models?.data.find((m) => m.id === FAST_ORACLE_MODEL);
|
|
27756
|
+
if (!found) return void 0;
|
|
27757
|
+
if ((found.capabilities?.limits?.max_context_window_tokens ?? 0) < 1e6) return void 0;
|
|
27758
|
+
if ((found.capabilities?.limits?.max_prompt_tokens ?? 0) <= 0) return void 0;
|
|
27759
|
+
const efforts = found.capabilities?.supports?.reasoning_effort;
|
|
27760
|
+
if (!Array.isArray(efforts) || !efforts.includes("high")) return void 0;
|
|
27761
|
+
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;
|
|
27763
|
+
return FAST_ORACLE_MODEL;
|
|
27764
|
+
}
|
|
27634
27765
|
/**
|
|
27635
27766
|
* Gate for the worker tools (`explore`, `review`, `implement`).
|
|
27636
27767
|
*
|
|
@@ -27855,40 +27986,29 @@ function isLoopbackHost(host) {
|
|
|
27855
27986
|
const hostname = idx >= 0 ? host.slice(0, idx) : host;
|
|
27856
27987
|
return hostname === "127.0.0.1" || hostname === "localhost";
|
|
27857
27988
|
}
|
|
27858
|
-
/**
|
|
27859
|
-
* Constant-time bearer compare. Random per-launch nonces aren't really
|
|
27860
|
-
* timing-attackable in practice, but this costs nothing.
|
|
27861
|
-
*/
|
|
27862
|
-
function nonceMatches(provided, expected) {
|
|
27863
|
-
if (provided.length !== expected.length) return false;
|
|
27864
|
-
const a = Buffer.from(provided);
|
|
27865
|
-
const b = Buffer.from(expected);
|
|
27866
|
-
try {
|
|
27867
|
-
return timingSafeEqual(a, b);
|
|
27868
|
-
} catch {
|
|
27869
|
-
return false;
|
|
27870
|
-
}
|
|
27871
|
-
}
|
|
27872
27989
|
function checkAuth(c) {
|
|
27873
27990
|
if (!isLoopbackHost(c.req.header("host"))) return {
|
|
27874
27991
|
ok: false,
|
|
27875
27992
|
status: 403,
|
|
27876
27993
|
reason: "non-loopback Host header rejected"
|
|
27877
27994
|
};
|
|
27878
|
-
|
|
27879
|
-
if (!expected) return {
|
|
27995
|
+
if (state.launchRegistry.size === 0) return {
|
|
27880
27996
|
ok: false,
|
|
27881
27997
|
status: 401,
|
|
27882
27998
|
reason: "/mcp not enabled in this proxy session"
|
|
27883
27999
|
};
|
|
27884
28000
|
const auth = c.req.header("authorization") ?? "";
|
|
27885
28001
|
const m = /^Bearer\s+(.+)$/i.exec(auth);
|
|
27886
|
-
|
|
28002
|
+
const launch = m ? findLaunchByNonce(m[1]) : void 0;
|
|
28003
|
+
if (!launch) return {
|
|
27887
28004
|
ok: false,
|
|
27888
28005
|
status: 401,
|
|
27889
28006
|
reason: "missing or invalid Authorization bearer"
|
|
27890
28007
|
};
|
|
27891
|
-
return {
|
|
28008
|
+
return {
|
|
28009
|
+
ok: true,
|
|
28010
|
+
launch
|
|
28011
|
+
};
|
|
27892
28012
|
}
|
|
27893
28013
|
/**
|
|
27894
28014
|
* opus_critic's effective model, resolved against the live catalog.
|
|
@@ -27929,8 +28049,54 @@ function activePersonas() {
|
|
|
27929
28049
|
};
|
|
27930
28050
|
});
|
|
27931
28051
|
}
|
|
27932
|
-
function
|
|
27933
|
-
|
|
28052
|
+
function oracleToolEntry() {
|
|
28053
|
+
return {
|
|
28054
|
+
name: "oracle",
|
|
28055
|
+
description: "Fast-profile last-resort guidance from exact Opus 5 (1M context) at high effort. Stateless and tool-less: pass complete context plus one precise query only after the primary Luna path, Advisor, and the relevant reviewer/planner path remain stuck. It can advise or request missing information; it cannot inspect the repo, execute, merge, or authorize actions.",
|
|
28056
|
+
inputSchema: {
|
|
28057
|
+
type: "object",
|
|
28058
|
+
required: ["query", "context"],
|
|
28059
|
+
additionalProperties: false,
|
|
28060
|
+
properties: {
|
|
28061
|
+
query: {
|
|
28062
|
+
type: "string",
|
|
28063
|
+
description: "One precise unresolved question."
|
|
28064
|
+
},
|
|
28065
|
+
context: {
|
|
28066
|
+
type: "string",
|
|
28067
|
+
description: "Complete evidence and constraints needed to answer cold-start."
|
|
28068
|
+
}
|
|
28069
|
+
}
|
|
28070
|
+
}
|
|
28071
|
+
};
|
|
28072
|
+
}
|
|
28073
|
+
function toolEntries(scope, launch) {
|
|
28074
|
+
if (launch.profileId === "fast") {
|
|
28075
|
+
const entries = [];
|
|
28076
|
+
if ((scope === "all" || scope === "peers") && launch.allowedGroups?.has("peers") && launch.allowedPersonas?.has("oracle") && fastOracleModel()) entries.push(oracleToolEntry());
|
|
28077
|
+
for (const tool of NON_PERSONA_MCP_TOOLS) {
|
|
28078
|
+
if (scope !== "all" && tool.group !== scope) continue;
|
|
28079
|
+
if (!launch.allowedGroups?.has(tool.group)) continue;
|
|
28080
|
+
if (tool.group === "search") {
|
|
28081
|
+
entries.push({
|
|
28082
|
+
name: tool.toolNameHttp,
|
|
28083
|
+
description: tool.description,
|
|
28084
|
+
inputSchema: tool.inputSchema
|
|
28085
|
+
});
|
|
28086
|
+
continue;
|
|
28087
|
+
}
|
|
28088
|
+
if (tool.group !== "browser" || !browserToolsEnabled()) continue;
|
|
28089
|
+
if (tool.capability === "browser_compound" && !browserCompoundToolsEnabled()) continue;
|
|
28090
|
+
if (tool.capability === "browser_power" && !browserPowerToolsEnabled()) continue;
|
|
28091
|
+
entries.push({
|
|
28092
|
+
name: tool.toolNameHttp,
|
|
28093
|
+
description: tool.description,
|
|
28094
|
+
inputSchema: tool.inputSchema
|
|
28095
|
+
});
|
|
28096
|
+
}
|
|
28097
|
+
return entries;
|
|
28098
|
+
}
|
|
28099
|
+
const personaEntries = (!launch.allowedGroups || launch.allowedGroups.has("peers")) && (scope === "all" || scope === "peers") ? activePersonas().filter((p) => !launch.allowedPersonas || launch.allowedPersonas.has(p.toolNameHttp)).map((p) => ({
|
|
27934
28100
|
name: p.toolNameHttp,
|
|
27935
28101
|
description: p.description,
|
|
27936
28102
|
inputSchema: {
|
|
@@ -27961,6 +28127,7 @@ function toolEntries(scope) {
|
|
|
27961
28127
|
})) : [];
|
|
27962
28128
|
const nonPersonaEntries = NON_PERSONA_MCP_TOOLS.filter((t) => {
|
|
27963
28129
|
if (scope !== "all" && t.group !== scope) return false;
|
|
28130
|
+
if (launch.allowedGroups && !launch.allowedGroups.has(t.group)) return false;
|
|
27964
28131
|
if (t.capability === "worker") return workerToolsEnabled();
|
|
27965
28132
|
if (t.capability === "browse_agent") return browseAgentEnabled();
|
|
27966
28133
|
if (t.capability === "stand_in") return standInToolEnabled();
|
|
@@ -28333,16 +28500,87 @@ function applySessionWorkspace(args, sessionWorkspace, tool) {
|
|
|
28333
28500
|
}
|
|
28334
28501
|
return "absent";
|
|
28335
28502
|
}
|
|
28336
|
-
async function handleToolsCall(body, scope, sessionWorkspace) {
|
|
28503
|
+
async function handleToolsCall(body, scope, launch, sessionWorkspace) {
|
|
28337
28504
|
const params = body.params ?? {};
|
|
28338
28505
|
const name = typeof params.name === "string" ? params.name : "";
|
|
28339
28506
|
const args = params.arguments ?? {};
|
|
28340
28507
|
if (!name) return rpcError(body.id, RPC_INVALID_PARAMS, "tools/call missing name");
|
|
28508
|
+
if (launch.profileId === "fast" && name === "oracle") {
|
|
28509
|
+
if (scope !== "all" && scope !== "peers" || !launch.allowedGroups?.has("peers") || !launch.allowedPersonas?.has("oracle") || !fastOracleModel()) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28510
|
+
const query = typeof args.query === "string" ? args.query.trim() : "";
|
|
28511
|
+
const context = typeof args.context === "string" ? args.context.trim() : "";
|
|
28512
|
+
if (!query || !context) return rpcError(body.id, RPC_INVALID_PARAMS, "tools/call: oracle requires non-empty arguments.query and arguments.context");
|
|
28513
|
+
const MAX_ORACLE_INPUT_BYTES = 262144;
|
|
28514
|
+
const oracleInput = `Query:\n${query}\n\nContext:\n${context}`;
|
|
28515
|
+
const inputBytes = Buffer.byteLength(oracleInput, "utf8");
|
|
28516
|
+
if (inputBytes > MAX_ORACLE_INPUT_BYTES) return rpcResult(body.id, toolError(`pre-flight rejected: oracle input is ${inputBytes} bytes, over the ${MAX_ORACLE_INPUT_BYTES}-byte cap; narrow the context without silently truncating it`));
|
|
28517
|
+
const oraclePersona = {
|
|
28518
|
+
agentName: "oracle",
|
|
28519
|
+
toolNameHttp: "oracle",
|
|
28520
|
+
model: "claude-opus-5",
|
|
28521
|
+
endpoint: "/v1/messages",
|
|
28522
|
+
description: "Fast-profile Oracle",
|
|
28523
|
+
baseInstructions: "You are Oracle, a stateless last-resort consultant. You have no tools or repository access. Answer only from the supplied context. Give focused guidance or ask for the exact missing information. Never claim to execute, approve, merge, or authorize an action.",
|
|
28524
|
+
agentPrompt: "",
|
|
28525
|
+
writeCapable: false,
|
|
28526
|
+
requiresHttp: true,
|
|
28527
|
+
allowedEfforts: ["high"],
|
|
28528
|
+
defaultEffort: "high"
|
|
28529
|
+
};
|
|
28530
|
+
const overflow = await predictedWindowOverflow(oraclePersona, oracleInput, void 0);
|
|
28531
|
+
if (overflow) return rpcResult(body.id, toolError(overflow));
|
|
28532
|
+
const release = acquireInFlightSlot();
|
|
28533
|
+
if (!release) return rpcResult(body.id, toolError(`Peer MCP queue full (${MAX_INFLIGHT_TOOLS_CALL} in-flight). Retry shortly.`));
|
|
28534
|
+
const startedAt = Date.now();
|
|
28535
|
+
const abortKey = body.id !== void 0 && body.id !== null ? body.id : void 0;
|
|
28536
|
+
const aborter = new AbortController();
|
|
28537
|
+
const inflightEntry = {
|
|
28538
|
+
aborter,
|
|
28539
|
+
release
|
|
28540
|
+
};
|
|
28541
|
+
if (abortKey !== void 0) inflightAborts.set(abortKey, inflightEntry);
|
|
28542
|
+
try {
|
|
28543
|
+
const text = await dispatchModelCall({
|
|
28544
|
+
model: "claude-opus-5",
|
|
28545
|
+
endpoint: "/v1/messages",
|
|
28546
|
+
instructions: oraclePersona.baseInstructions,
|
|
28547
|
+
userText: oracleInput,
|
|
28548
|
+
effort: "high",
|
|
28549
|
+
signal: aborter.signal
|
|
28550
|
+
});
|
|
28551
|
+
logTelemetry({
|
|
28552
|
+
name: "oracle",
|
|
28553
|
+
model: "claude-opus-5",
|
|
28554
|
+
durationMs: Date.now() - startedAt,
|
|
28555
|
+
result: "ok"
|
|
28556
|
+
});
|
|
28557
|
+
return rpcResult(body.id, { content: [{
|
|
28558
|
+
type: "text",
|
|
28559
|
+
text
|
|
28560
|
+
}] });
|
|
28561
|
+
} catch (err) {
|
|
28562
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
28563
|
+
logTelemetry({
|
|
28564
|
+
name: "oracle",
|
|
28565
|
+
model: "claude-opus-5",
|
|
28566
|
+
durationMs: Date.now() - startedAt,
|
|
28567
|
+
result: "exception",
|
|
28568
|
+
errorMessage: message
|
|
28569
|
+
});
|
|
28570
|
+
return rpcResult(body.id, toolError(`oracle failed: ${message}`));
|
|
28571
|
+
} finally {
|
|
28572
|
+
if (abortKey !== void 0 && inflightAborts.get(abortKey) === inflightEntry) inflightAborts.delete(abortKey);
|
|
28573
|
+
release();
|
|
28574
|
+
}
|
|
28575
|
+
}
|
|
28576
|
+
if (launch.profileId === "fast" && PERSONAS_READ.some((p) => p.toolNameHttp === name)) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28341
28577
|
const persona = activePersonas().find((p) => p.toolNameHttp === name);
|
|
28342
28578
|
const nonPersonaTool = persona ? void 0 : NON_PERSONA_MCP_TOOLS.find((t) => t.toolNameHttp === name);
|
|
28343
28579
|
if (!persona && !nonPersonaTool) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28344
28580
|
const toolGroup = persona ? "peers" : nonPersonaTool.group;
|
|
28345
28581
|
if (scope !== "all" && toolGroup !== scope) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28582
|
+
if (launch.allowedGroups && !launch.allowedGroups.has(toolGroup)) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28583
|
+
if (persona && launch.allowedPersonas && !launch.allowedPersonas.has(persona.toolNameHttp)) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28346
28584
|
if (nonPersonaTool && nonPersonaTool.capability === "worker" && !workerToolsEnabled()) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28347
28585
|
if (nonPersonaTool && nonPersonaTool.capability === "browse_agent" && !browseAgentEnabled()) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
28348
28586
|
if (nonPersonaTool && nonPersonaTool.capability === "stand_in" && !standInToolEnabled()) return rpcError(body.id, RPC_METHOD_NOT_FOUND, `tools/call: unknown tool "${name}"`);
|
|
@@ -28450,7 +28688,7 @@ function handleCancelledNotification(body) {
|
|
|
28450
28688
|
}
|
|
28451
28689
|
cancelInflight(requestId, "client requested cancellation");
|
|
28452
28690
|
}
|
|
28453
|
-
async function handleRpc(_c, body, scope, sessionWorkspace) {
|
|
28691
|
+
async function handleRpc(_c, body, scope, launch, sessionWorkspace) {
|
|
28454
28692
|
if (body === null || typeof body !== "object" || Array.isArray(body)) return {
|
|
28455
28693
|
status: 200,
|
|
28456
28694
|
body: rpcError(null, RPC_INVALID_REQUEST, "jsonrpc 2.0 envelope required")
|
|
@@ -28492,7 +28730,7 @@ async function handleRpc(_c, body, scope, sessionWorkspace) {
|
|
|
28492
28730
|
};
|
|
28493
28731
|
return {
|
|
28494
28732
|
status: 200,
|
|
28495
|
-
body: rpcResult(body.id, { tools: toolEntries(scope) })
|
|
28733
|
+
body: rpcResult(body.id, { tools: toolEntries(scope, launch) })
|
|
28496
28734
|
};
|
|
28497
28735
|
case "tools/call":
|
|
28498
28736
|
if (isNotification) return {
|
|
@@ -28501,7 +28739,7 @@ async function handleRpc(_c, body, scope, sessionWorkspace) {
|
|
|
28501
28739
|
};
|
|
28502
28740
|
return {
|
|
28503
28741
|
status: 200,
|
|
28504
|
-
body: await handleToolsCall(body, scope, sessionWorkspace)
|
|
28742
|
+
body: await handleToolsCall(body, scope, launch, sessionWorkspace)
|
|
28505
28743
|
};
|
|
28506
28744
|
case "resources/list":
|
|
28507
28745
|
if (isNotification) return {
|
|
@@ -28581,6 +28819,7 @@ async function handleRpc(_c, body, scope, sessionWorkspace) {
|
|
|
28581
28819
|
async function handleMcpPost(c, scopeArg = "all") {
|
|
28582
28820
|
const auth = checkAuth(c);
|
|
28583
28821
|
if (!auth.ok) return c.json(rpcError(null, RPC_INVALID_REQUEST, auth.reason), auth.status);
|
|
28822
|
+
const { launch } = auth;
|
|
28584
28823
|
let scope;
|
|
28585
28824
|
if (scopeArg === "all") scope = "all";
|
|
28586
28825
|
else if (isMcpGroup(scopeArg)) scope = scopeArg;
|
|
@@ -28597,13 +28836,13 @@ async function handleMcpPost(c, scopeArg = "all") {
|
|
|
28597
28836
|
const nm = typeof body.params?.name === "string" ? body.params.name : "?";
|
|
28598
28837
|
process.stderr.write(`[peer-mcp] recv t=${Date.now()} name=${nm} scope=${scope} inflight=${currentInFlight()}\n`);
|
|
28599
28838
|
}
|
|
28600
|
-
if (typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call" && acceptsEventStream(c.req.header("accept"))) return handleToolsCallSSE(body, scope, sessionWorkspace);
|
|
28839
|
+
if (typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call" && acceptsEventStream(c.req.header("accept"))) return handleToolsCallSSE(body, scope, launch, sessionWorkspace);
|
|
28601
28840
|
if (typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call") {
|
|
28602
28841
|
const preflight = jsonPathPreflightCap(body, scope);
|
|
28603
28842
|
if (preflight) return c.json(preflight, 200);
|
|
28604
28843
|
}
|
|
28605
28844
|
try {
|
|
28606
|
-
const { status, body: respBody } = await handleRpc(c, body, scope, sessionWorkspace);
|
|
28845
|
+
const { status, body: respBody } = await handleRpc(c, body, scope, launch, sessionWorkspace);
|
|
28607
28846
|
if (respBody === null) return c.body(null, status);
|
|
28608
28847
|
return c.json(respBody, status);
|
|
28609
28848
|
} catch (err) {
|
|
@@ -28656,9 +28895,9 @@ function acceptsEventStream(accept) {
|
|
|
28656
28895
|
* "Invalid state: Controller is already closed" race without warning.
|
|
28657
28896
|
*/
|
|
28658
28897
|
const SSE_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
28659
|
-
async function handleToolsCallSSE(body, scope, sessionWorkspace) {
|
|
28898
|
+
async function handleToolsCallSSE(body, scope, launch, sessionWorkspace) {
|
|
28660
28899
|
const encoder = new TextEncoder();
|
|
28661
|
-
const callPromise = handleToolsCall(body, scope, sessionWorkspace);
|
|
28900
|
+
const callPromise = handleToolsCall(body, scope, launch, sessionWorkspace);
|
|
28662
28901
|
let heartbeatHandle;
|
|
28663
28902
|
const stream = new ReadableStream({
|
|
28664
28903
|
async start(controller) {
|
|
@@ -29335,13 +29574,18 @@ const ADVISOR_INTERNAL_TOOL_NAME = "__anthropic_advisor";
|
|
|
29335
29574
|
const ADVISOR_CLIENT_TOOL_NAME = "advisor";
|
|
29336
29575
|
/** Default advisor model + reasoning effort. Per gemini-critic + user
|
|
29337
29576
|
* direction: hardcode to a cross-lab model (gpt-5.6-sol — Copilot's
|
|
29338
|
-
* /responses-only flagship)
|
|
29339
|
-
*
|
|
29340
|
-
*
|
|
29341
|
-
*
|
|
29342
|
-
*
|
|
29577
|
+
* /responses-only flagship). The cross-lab choice gives a true "second set
|
|
29578
|
+
* of eyes" instead of the main model reviewing itself.
|
|
29579
|
+
*
|
|
29580
|
+
* Effort default is `high`, not the historical `xhigh`: `resolveAdvisorEffort`
|
|
29581
|
+
* no longer floors the picked effort (see that function), so `high` is both
|
|
29582
|
+
* the default AND the lowest the advisor will ever think at when the picker
|
|
29583
|
+
* expresses no preference — a deliberate, user-approved cost/depth trade
|
|
29584
|
+
* applied uniformly across every advisor target (Sol, the Opus escalation,
|
|
29585
|
+
* and the fast-profile Gemini advisor below all read this same constant). */
|
|
29343
29586
|
const ADVISOR_DEFAULT_MODEL = "gpt-5.6-sol";
|
|
29344
29587
|
const ADVISOR_DEFAULT_EFFORT = "xhigh";
|
|
29588
|
+
const ADVISOR_MIN_EFFORT = "high";
|
|
29345
29589
|
/** The Anthropic frontier model the advisor escalates to when the LEAD is a
|
|
29346
29590
|
* lighter Claude tier (sonnet, haiku).
|
|
29347
29591
|
*
|
|
@@ -29358,16 +29602,32 @@ const ADVISOR_DEFAULT_EFFORT = "xhigh";
|
|
|
29358
29602
|
* the decorrelation instrument and are untouched. `GH_ROUTER_ADVISOR_MODEL`
|
|
29359
29603
|
* keeps a cross-lab advisor one env var away for anyone who wants it back. */
|
|
29360
29604
|
const ADVISOR_ESCALATION_MODEL = "claude-opus-5";
|
|
29361
|
-
/**
|
|
29362
|
-
*
|
|
29363
|
-
*
|
|
29364
|
-
*
|
|
29365
|
-
*
|
|
29366
|
-
*
|
|
29367
|
-
*
|
|
29368
|
-
*
|
|
29369
|
-
|
|
29370
|
-
|
|
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). */
|
|
29613
|
+
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. */
|
|
29628
|
+
function fastProfileAdvisorAvailable() {
|
|
29629
|
+
return state.models?.data?.some((m) => m.id === "gemini-3.7-flash") ?? false;
|
|
29630
|
+
}
|
|
29371
29631
|
/** Output cap for the Anthropic-branch advisor call when the catalog carries no
|
|
29372
29632
|
* limits for the resolved model. The value the branch used unconditionally
|
|
29373
29633
|
* before it became reachable, kept so a catalog-less path is no worse off. */
|
|
@@ -29399,6 +29659,28 @@ function advisorUsesResponses(resolvedAdvisorModel) {
|
|
|
29399
29659
|
if (endpoints && endpoints.length > 0) return endpoints.some((e) => ADVISOR_RESPONSES_ENDPOINTS.has(e));
|
|
29400
29660
|
return /^(gpt-|o\d|.*codex)/i.test(bare);
|
|
29401
29661
|
}
|
|
29662
|
+
/**
|
|
29663
|
+
* Decide `advisorTransport` for a resolved advisor model id.
|
|
29664
|
+
*
|
|
29665
|
+
* Order matters: Claude identity is checked FIRST and wins even though
|
|
29666
|
+
* `claude-opus-5` also advertises `/chat/completions` in the live catalog —
|
|
29667
|
+
* the historical branch never sent Claude to chat, and this preserves that
|
|
29668
|
+
* byte-for-byte (reuses the SAME classifier `classifyMessagesRoute` uses for
|
|
29669
|
+
* the main `/v1/messages` shim fork, so the two surfaces cannot disagree
|
|
29670
|
+
* about what counts as a Claude model). Responses is checked next
|
|
29671
|
+
* (`advisorUsesResponses`, unchanged — catalog-first, name-regex fallback,
|
|
29672
|
+
* still exported and directly tested on its own). Anything else defaults to
|
|
29673
|
+
* chat, mirroring `pickEndpoint`'s "omits supported_endpoints => chat-eligible"
|
|
29674
|
+
* convention — the same convention `classifyMessagesRoute` relies on for a
|
|
29675
|
+
* lead model, applied here to the advisor's OWN model instead.
|
|
29676
|
+
*/
|
|
29677
|
+
function advisorTransport(resolvedAdvisorModel) {
|
|
29678
|
+
const bare = resolvedAdvisorModel.slice(resolvedAdvisorModel.lastIndexOf("/") + 1);
|
|
29679
|
+
const entry = state.models?.data?.find((m) => m.id === resolvedAdvisorModel || m.id === bare);
|
|
29680
|
+
if (isClaudeModel(resolvedAdvisorModel, entry)) return "messages";
|
|
29681
|
+
if (advisorUsesResponses(resolvedAdvisorModel)) return "responses";
|
|
29682
|
+
return "chat";
|
|
29683
|
+
}
|
|
29402
29684
|
/** True when the model advertises a usable reasoning-effort ladder. */
|
|
29403
29685
|
function advertisedEffortLadder(resolvedAdvisorModel) {
|
|
29404
29686
|
const supported = state.models?.data?.find((m) => m.id === resolvedAdvisorModel)?.capabilities?.supports?.reasoning_effort;
|
|
@@ -29435,10 +29717,16 @@ function shouldEscalateAdvisor(leadModel) {
|
|
|
29435
29717
|
* Precedence:
|
|
29436
29718
|
* 1. `GH_ROUTER_ADVISOR_MODEL` (trimmed) — the operator pin, checked first so
|
|
29437
29719
|
* it works on every lead.
|
|
29438
|
-
* 2.
|
|
29439
|
-
*
|
|
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.
|
|
29440
29728
|
*
|
|
29441
|
-
* Step
|
|
29729
|
+
* Step 4 returns the LITERAL constant rather than walking the OpenAI frontier
|
|
29442
29730
|
* chain. An Opus lead must resolve to exactly what it resolves to today, and a
|
|
29443
29731
|
* frontier walk could yield `gpt-5.5` on a catalog missing `gpt-5.6-sol` —
|
|
29444
29732
|
* a silent change to the one path that is required not to move.
|
|
@@ -29467,19 +29755,27 @@ function normalizeAdvisorPin(pinned) {
|
|
|
29467
29755
|
const bare = pinned.slice(pinned.lastIndexOf("/") + 1);
|
|
29468
29756
|
return bare !== pinned && models.some((m) => m.id === bare) ? bare : pinned;
|
|
29469
29757
|
}
|
|
29470
|
-
function resolveAdvisorModel(leadModel) {
|
|
29758
|
+
function resolveAdvisorModel(leadModel, fastProfile = false) {
|
|
29471
29759
|
const pinned = process.env.GH_ROUTER_ADVISOR_MODEL?.trim();
|
|
29472
29760
|
if (pinned) return {
|
|
29473
29761
|
model: normalizeAdvisorPin(pinned),
|
|
29474
|
-
escalated: false
|
|
29762
|
+
escalated: false,
|
|
29763
|
+
fastProfile: false
|
|
29764
|
+
};
|
|
29765
|
+
if (fastProfile && leadModel && isFastProfileLead(leadModel) && fastProfileAdvisorAvailable()) return {
|
|
29766
|
+
model: ADVISOR_FAST_PROFILE_MODEL,
|
|
29767
|
+
escalated: false,
|
|
29768
|
+
fastProfile: true
|
|
29475
29769
|
};
|
|
29476
29770
|
if (leadModel && shouldEscalateAdvisor(leadModel)) return {
|
|
29477
29771
|
model: ADVISOR_ESCALATION_MODEL,
|
|
29478
|
-
escalated: true
|
|
29772
|
+
escalated: true,
|
|
29773
|
+
fastProfile: false
|
|
29479
29774
|
};
|
|
29480
29775
|
return {
|
|
29481
29776
|
model: ADVISOR_DEFAULT_MODEL,
|
|
29482
|
-
escalated: false
|
|
29777
|
+
escalated: false,
|
|
29778
|
+
fastProfile: false
|
|
29483
29779
|
};
|
|
29484
29780
|
}
|
|
29485
29781
|
/**
|
|
@@ -29501,13 +29797,16 @@ function resolveAdvisorModel(leadModel) {
|
|
|
29501
29797
|
* 3. `ADVISOR_DEFAULT_EFFORT` — a request expressing no preference behaves
|
|
29502
29798
|
* exactly as it did before the picker was honored at all.
|
|
29503
29799
|
*
|
|
29504
|
-
*
|
|
29505
|
-
*
|
|
29506
|
-
*
|
|
29507
|
-
*
|
|
29800
|
+
* There is deliberately NO floor anymore (removed per the user-approved
|
|
29801
|
+
* "default high, no floor" change): the advisor follows the picker all the way
|
|
29802
|
+
* down as well as up, so an explicit `none`/`low` pick is honored rather than
|
|
29803
|
+
* clamped up to a minimum. The only remaining adjustment is the CEILING clamp
|
|
29804
|
+
* against the resolved advisor's own live `reasoning_effort` allowlist — a
|
|
29805
|
+
* model whose ladder tops out below the requested tier still needs to receive
|
|
29806
|
+
* something it accepts.
|
|
29508
29807
|
*/
|
|
29509
|
-
function resolveAdvisorEffort(rawRequestBody, advisorModel) {
|
|
29510
|
-
let requested = ADVISOR_DEFAULT_EFFORT;
|
|
29808
|
+
function resolveAdvisorEffort(rawRequestBody, advisorModel, fastProfile = false) {
|
|
29809
|
+
let requested = fastProfile ? "high" : ADVISOR_DEFAULT_EFFORT;
|
|
29511
29810
|
if (rawRequestBody) try {
|
|
29512
29811
|
const body = JSON.parse(rawRequestBody);
|
|
29513
29812
|
const oc = body.output_config;
|
|
@@ -29516,10 +29815,11 @@ function resolveAdvisorEffort(rawRequestBody, advisorModel) {
|
|
|
29516
29815
|
if (typeof explicit === "string" && EFFORT_ORDER.includes(explicit)) requested = explicit;
|
|
29517
29816
|
else if (thinking && typeof thinking === "object" && thinking.type === "enabled") requested = bucketEffort(thinking.budget_tokens);
|
|
29518
29817
|
} catch {}
|
|
29519
|
-
|
|
29818
|
+
if (fastProfile) requested = "high";
|
|
29819
|
+
else if (EFFORT_ORDER.indexOf(requested) < EFFORT_ORDER.indexOf(ADVISOR_MIN_EFFORT)) requested = ADVISOR_MIN_EFFORT;
|
|
29520
29820
|
const supported = state.models?.data?.find((m) => m.id === resolveModel(advisorModel))?.capabilities?.supports?.reasoning_effort;
|
|
29521
|
-
if (!Array.isArray(supported) || supported.length === 0) return
|
|
29522
|
-
return clampEffort(
|
|
29821
|
+
if (!Array.isArray(supported) || supported.length === 0) return requested;
|
|
29822
|
+
return clampEffort(requested, supported);
|
|
29523
29823
|
}
|
|
29524
29824
|
/** ADVISOR_TOOL_INSTRUCTIONS verbatim from cc-backup
|
|
29525
29825
|
* src/utils/advisor.ts — describes when the model should invoke
|
|
@@ -29540,6 +29840,18 @@ On tasks longer than a few steps, call advisor at least once before committing t
|
|
|
29540
29840
|
Give the advice serious weight. If you follow a step and it fails empirically, or you have primary-source evidence that contradicts a specific claim (the file says X, the code does Y), adapt. A passing self-test is not evidence the advice is wrong -- it's evidence your test doesn't check what the advice is checking.
|
|
29541
29841
|
|
|
29542
29842
|
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
|
+
/** Fast-profile lead-only policy. Unlike the standard Claude Code instructions
|
|
29844
|
+
* above, this makes consultation optional and leaves decision ownership with
|
|
29845
|
+
* the Luna lead. Fast Task subagents never receive an advisor tool at all. */
|
|
29846
|
+
const FAST_ADVISOR_TOOL_INSTRUCTIONS = `# Advisor Tool
|
|
29847
|
+
|
|
29848
|
+
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.
|
|
29849
|
+
|
|
29850
|
+
Use advisor only when a focused, consequential uncertainty remains after direct investigation: conflicting evidence, a materially changed assumption, a genuinely non-converging approach, a hard-to-reverse trade-off, or an explicit request for a fresh perspective. State the precise uncertainty in your response immediately before calling it.
|
|
29851
|
+
|
|
29852
|
+
Do not call advisor for routine progress, while waiting on a subagent, after ordinary tool output, for a fact that code or a command can verify, to obtain planner approval or reviewer verification, or as a ritual before implementation or completion.
|
|
29853
|
+
|
|
29854
|
+
Treat the result as advice, not authority. Weigh it against the user's intent, verified repository evidence, planner output, and reviewer findings. You may consult again when materially new evidence creates a different question or directly conflicts with earlier advice.`;
|
|
29543
29855
|
const ADVISOR_OPT_OUT_ENV = "CLAUDE_CODE_DISABLE_ADVISOR_TOOL";
|
|
29544
29856
|
/**
|
|
29545
29857
|
* Detect whether the request asked for ADVISOR (incoming
|
|
@@ -29572,8 +29884,8 @@ function isAdvisorRequested(rawBetaHeader) {
|
|
|
29572
29884
|
* client-shape `server_tool_use{name:"advisor"}` + `advisor_tool_result`
|
|
29573
29885
|
* blocks the client expects.
|
|
29574
29886
|
*/
|
|
29575
|
-
function injectAdvisorTool(rawBody) {
|
|
29576
|
-
if (rawBody.includes(`"name":"__anthropic_advisor"`) && !rawBody.includes("\"advisor_")) return rawBody;
|
|
29887
|
+
function injectAdvisorTool(rawBody, instructions = ADVISOR_TOOL_INSTRUCTIONS) {
|
|
29888
|
+
if (instructions === ADVISOR_TOOL_INSTRUCTIONS && rawBody.includes(`"name":"__anthropic_advisor"`) && !rawBody.includes("\"advisor_")) return rawBody;
|
|
29577
29889
|
let parsed;
|
|
29578
29890
|
try {
|
|
29579
29891
|
parsed = JSON.parse(rawBody);
|
|
@@ -29588,10 +29900,14 @@ function injectAdvisorTool(rawBody) {
|
|
|
29588
29900
|
});
|
|
29589
29901
|
const stripped = tools.length !== rawTools.length;
|
|
29590
29902
|
const alreadyInjected = tools.some((t) => t?.name === ADVISOR_INTERNAL_TOOL_NAME);
|
|
29591
|
-
|
|
29592
|
-
|
|
29903
|
+
const needsDescriptionUpdate = alreadyInjected && tools.some((t) => t?.name === "__anthropic_advisor" && t.description !== instructions);
|
|
29904
|
+
if (alreadyInjected && !stripped && !needsDescriptionUpdate) return rawBody;
|
|
29905
|
+
parsed.tools = alreadyInjected ? tools.map((tool) => tool?.name === "__anthropic_advisor" ? {
|
|
29906
|
+
...tool,
|
|
29907
|
+
description: instructions
|
|
29908
|
+
} : tool) : [...tools, {
|
|
29593
29909
|
name: ADVISOR_INTERNAL_TOOL_NAME,
|
|
29594
|
-
description:
|
|
29910
|
+
description: instructions,
|
|
29595
29911
|
input_schema: {
|
|
29596
29912
|
type: "object",
|
|
29597
29913
|
properties: {},
|
|
@@ -29732,9 +30048,9 @@ function truncateTailToUnits(text, maxUnits, measure) {
|
|
|
29732
30048
|
* Anthropic's own ADVISOR ("see the whole task + every tool call +
|
|
29733
30049
|
* every result").
|
|
29734
30050
|
*/
|
|
29735
|
-
async function runAdvisor(conversation, advisorModel, advisorEffort, signal, advisorEscalated = false) {
|
|
30051
|
+
async function runAdvisor(conversation, advisorModel, advisorEffort, signal, advisorEscalated = false, fastProfile = false) {
|
|
29736
30052
|
if (signal?.aborted) throw new Error("advisor call aborted before dispatch");
|
|
29737
|
-
const advisorSystem = "You are an expert advisor reviewing an in-progress Claude Code session. The transcript below is the work-in-progress (turns numbered, with tool calls and results inlined). Read carefully and provide concrete, actionable advice on the next step or course-correction. Be specific — cite the parts of the transcript you're responding to. If the assistant is on the right track, say so explicitly. If they're stuck or off-track, name the specific assumption or step to revisit. Aim for 2-5 paragraphs of substantive guidance." + (advisorEscalated ? " The requesting agent is running a lighter, faster model than you. Give a directive recommendation and commit to the decision rather than laying out options for it to weigh." : "");
|
|
30053
|
+
const advisorSystem = "You are an expert advisor reviewing an in-progress Claude Code session. The transcript below is the work-in-progress (turns numbered, with tool calls and results inlined). Read carefully and provide concrete, actionable advice on the next step or course-correction. Be specific — cite the parts of the transcript you're responding to. If the assistant is on the right track, say so explicitly. If they're stuck or off-track, name the specific assumption or step to revisit. Aim for 2-5 paragraphs of substantive guidance." + (fastProfile ? " You are a non-binding consultant to the primary lead. Analyze the focused uncertainty that prompted this call and provide a recommendation, its assumptions, material risks, credible alternatives, confidence, and any evidence gap that should be resolved. Do not approve, veto, dictate, or take ownership of the workflow; the lead will weigh your advice against the user's intent and verified evidence." : "") + (advisorEscalated && !fastProfile ? " The requesting agent is running a lighter, faster model than you. Give a directive recommendation and commit to the decision rather than laying out options for it to weigh." : "");
|
|
29738
30054
|
const resolvedAdvisorModel = resolveModel(advisorModel);
|
|
29739
30055
|
let measure;
|
|
29740
30056
|
let maxUnits;
|
|
@@ -29749,7 +30065,8 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
|
|
|
29749
30065
|
maxUnits = ADVISOR_MAX_CONVERSATION_CHARS;
|
|
29750
30066
|
}
|
|
29751
30067
|
const conversationText = renderConversationAsText(conversation, maxUnits, measure);
|
|
29752
|
-
|
|
30068
|
+
const transport = advisorTransport(resolvedAdvisorModel);
|
|
30069
|
+
if (transport === "responses") {
|
|
29753
30070
|
const payload = applyResponsesCachePolicy({
|
|
29754
30071
|
model: resolvedAdvisorModel,
|
|
29755
30072
|
instructions: advisorSystem,
|
|
@@ -29784,6 +30101,27 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
|
|
|
29784
30101
|
if (!text) throw new Error(`Advisor model ${resolvedAdvisorModel} returned empty assistant output`);
|
|
29785
30102
|
return text;
|
|
29786
30103
|
}
|
|
30104
|
+
if (transport === "chat") {
|
|
30105
|
+
const ladder = advertisedEffortLadder(resolvedAdvisorModel);
|
|
30106
|
+
const chatPayload = {
|
|
30107
|
+
model: resolvedAdvisorModel,
|
|
30108
|
+
messages: [{
|
|
30109
|
+
role: "system",
|
|
30110
|
+
content: advisorSystem
|
|
30111
|
+
}, {
|
|
30112
|
+
role: "user",
|
|
30113
|
+
content: conversationText
|
|
30114
|
+
}],
|
|
30115
|
+
stream: false,
|
|
30116
|
+
...ladder ? { reasoning_effort: advisorEffort } : {}
|
|
30117
|
+
};
|
|
30118
|
+
const text = (await withTransientRetry(() => createChatCompletions(chatPayload, void 0, signal), {
|
|
30119
|
+
signal,
|
|
30120
|
+
label: resolvedAdvisorModel
|
|
30121
|
+
})).choices?.[0]?.message?.content;
|
|
30122
|
+
if (typeof text !== "string" || text.length === 0) throw new Error(`Advisor model ${resolvedAdvisorModel} returned empty response`);
|
|
30123
|
+
return text;
|
|
30124
|
+
}
|
|
29787
30125
|
const advisorEntry = state.models?.data?.find((m) => m.id === resolvedAdvisorModel);
|
|
29788
30126
|
const limits = advisorEntry?.capabilities?.limits;
|
|
29789
30127
|
const maxTokens = limits?.max_non_streaming_output_tokens ?? limits?.max_output_tokens ?? ADVISOR_FALLBACK_MAX_OUTPUT_TOKENS;
|
|
@@ -29812,20 +30150,51 @@ async function runAdvisor(conversation, advisorModel, advisorEffort, signal, adv
|
|
|
29812
30150
|
/**
|
|
29813
30151
|
* Derive a spec-compliant `srvtoolu_*` id for a client-facing
|
|
29814
30152
|
* `server_tool_use` (and matching `advisor_tool_result.tool_use_id`)
|
|
29815
|
-
* from the upstream model's
|
|
29816
|
-
*
|
|
29817
|
-
*
|
|
29818
|
-
*
|
|
29819
|
-
*
|
|
29820
|
-
*
|
|
29821
|
-
*
|
|
29822
|
-
*
|
|
29823
|
-
|
|
29824
|
-
|
|
29825
|
-
|
|
29826
|
-
|
|
29827
|
-
|
|
29828
|
-
|
|
30153
|
+
* from the upstream model's tool-call id.
|
|
30154
|
+
*
|
|
30155
|
+
* TOTAL — never throws. Two paths:
|
|
30156
|
+
*
|
|
30157
|
+
* 1. A real Anthropic `toolu_*` id whose suffix is already in the
|
|
30158
|
+
* `^[a-zA-Z0-9_]+$` charset: `srvtoolu_<suffix>`, byte-for-byte
|
|
30159
|
+
* identical to the historical (Claude-lead) behavior.
|
|
30160
|
+
* 2. Anything else — a Responses `call_*` id (the fast Luna profile's
|
|
30161
|
+
* lead, once its `tool_use{__anthropic_advisor}` block is synthesized
|
|
30162
|
+
* by the anthropic-translate shim from a Copilot `/responses` tool
|
|
30163
|
+
* call), a hyphenated or otherwise non-conforming id, an empty string,
|
|
30164
|
+
* unicode, or a corrupt id — sanitize to the Anthropic charset and
|
|
30165
|
+
* prefix with `fallbackIndex` (the caller's per-block synthetic stream
|
|
30166
|
+
* index, unique within one `buildAdvisorStream` run) so two different
|
|
30167
|
+
* raw ids that happen to sanitize to the same string can never
|
|
30168
|
+
* collide. `fallbackIndex` is REQUIRED for this path's uniqueness
|
|
30169
|
+
* guarantee — callers must pass a value that is unique per call within
|
|
30170
|
+
* one advisor stream (every call site does: `myIndex` from the
|
|
30171
|
+
* turn processor's monotonic `nextSyntheticIndex`).
|
|
30172
|
+
*
|
|
30173
|
+
* This function ONLY has to produce a valid, deterministic, collision-free
|
|
30174
|
+
* LABEL — the original raw id is preserved separately for Copilot replay
|
|
30175
|
+
* (`CapturedBlock.advisorReplay.id`), never reconstructed from the derived
|
|
30176
|
+
* client id. That is what makes totality safe: there is no bijective-decode
|
|
30177
|
+
* requirement on this function itself, only on the (id, clientId) pairing a
|
|
30178
|
+
* caller keeps alongside it.
|
|
30179
|
+
*
|
|
30180
|
+
* Historically this threw "advisor tool_use id is not round-trippable" for
|
|
30181
|
+
* any non-`toolu_` shape. That was correct for a Claude-only advisor lead —
|
|
30182
|
+
* 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
|
|
30184
|
+
* shimmed through `/responses`: `responses-egress.ts` forwards a Responses
|
|
30185
|
+
* `call_*` id VERBATIM as the synthesized `tool_use.id` (see
|
|
30186
|
+
* `makeToolUseId` — it only synthesizes a `toolu_*` id when the upstream id
|
|
30187
|
+
* is EMPTY), so the advisor's `tool_use{__anthropic_advisor}` block on that
|
|
30188
|
+
* lead legitimately carries a `call_*` id and the throw fired on every
|
|
30189
|
+
* single advisor call.
|
|
30190
|
+
*/
|
|
30191
|
+
function toClientServerToolUseId(id, fallbackIndex) {
|
|
30192
|
+
if (id.startsWith("toolu_")) {
|
|
30193
|
+
const suffix = id.slice(6);
|
|
30194
|
+
if (/^[a-zA-Z0-9_]+$/.test(suffix)) return `srvtoolu_${suffix}`;
|
|
30195
|
+
}
|
|
30196
|
+
const sanitized = id.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
30197
|
+
return `srvtoolu_gen${fallbackIndex}${sanitized.length > 0 ? `_${sanitized}` : ""}`;
|
|
29829
30198
|
}
|
|
29830
30199
|
/**
|
|
29831
30200
|
* Build an SSE event line in the canonical Anthropic shape:
|
|
@@ -29837,6 +30206,35 @@ function sseEvent(type, data) {
|
|
|
29837
30206
|
return `event: ${type}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
29838
30207
|
}
|
|
29839
30208
|
/**
|
|
30209
|
+
* The default `continueTurn` for `buildAdvisorStream`: native Claude
|
|
30210
|
+
* passthrough (`createMessages`) plus signed-thinking-history repair-and-retry.
|
|
30211
|
+
* Extracted verbatim from the loop body so the behavior is byte-identical to
|
|
30212
|
+
* before `continueTurn` became injectable, and so a non-Claude
|
|
30213
|
+
* `continueTurn` (the fast Luna profile's shim-backed one) can omit this
|
|
30214
|
+
* Claude-only repair path entirely rather than inherit dead code that would
|
|
30215
|
+
* never fire for it.
|
|
30216
|
+
*/
|
|
30217
|
+
async function defaultContinueTurn(body, signal, requestHeaders) {
|
|
30218
|
+
let continuationSend = JSON.stringify(body);
|
|
30219
|
+
const knownRepair = repairKnownThinkingHistory(continuationSend);
|
|
30220
|
+
if (knownRepair) continuationSend = knownRepair.body;
|
|
30221
|
+
try {
|
|
30222
|
+
return await createMessages(continuationSend, requestHeaders, signal, true);
|
|
30223
|
+
} catch (continuationError) {
|
|
30224
|
+
if (!(continuationError instanceof HTTPError)) throw continuationError;
|
|
30225
|
+
const errorBody = await continuationError.response.clone().text().catch(() => "");
|
|
30226
|
+
const outcome = repairRejectedThinkingHistory(continuationSend, errorBody);
|
|
30227
|
+
if (!outcome.ok) {
|
|
30228
|
+
consola.warn(`Advisor continuation thinking-history repair declined: ${formatThinkingRepairDecline(outcome.decline)}`);
|
|
30229
|
+
throw continuationError;
|
|
30230
|
+
}
|
|
30231
|
+
consola.warn(`Advisor continuation: retrying without rejected thinking blocks: message=${outcome.repair.messageIndex} removed_blocks=${outcome.repair.removedBlocks}`);
|
|
30232
|
+
const response = await createMessages(outcome.repair.body, requestHeaders, signal, true);
|
|
30233
|
+
rememberThinkingHistoryRepair(outcome.repair.fingerprint);
|
|
30234
|
+
return response;
|
|
30235
|
+
}
|
|
30236
|
+
}
|
|
30237
|
+
/**
|
|
29840
30238
|
* The streaming translate-loop. Returns a ReadableStream<Uint8Array>
|
|
29841
30239
|
* suitable to wrap with Hono's c.body() / new Response().
|
|
29842
30240
|
*
|
|
@@ -29855,6 +30253,8 @@ function buildAdvisorStream(opts) {
|
|
|
29855
30253
|
const advisorModel = opts.advisorModel ?? "gpt-5.6-sol";
|
|
29856
30254
|
const advisorEffort = opts.advisorEffort ?? "xhigh";
|
|
29857
30255
|
const advisorEscalated = opts.advisorEscalated ?? false;
|
|
30256
|
+
const advisorFastProfile = opts.advisorFastProfile ?? false;
|
|
30257
|
+
const continueTurn = opts.continueTurn ?? ((body, signal) => defaultContinueTurn(body, signal, opts.requestHeaders));
|
|
29858
30258
|
const aborter = opts.externalAborter ?? new AbortController();
|
|
29859
30259
|
let conversation = [...opts.initialConversation];
|
|
29860
30260
|
return new ReadableStream({
|
|
@@ -30104,7 +30504,7 @@ function buildAdvisorStream(opts) {
|
|
|
30104
30504
|
const advisorConversation = conversation;
|
|
30105
30505
|
const advisorTexts = await Promise.all(advisorToolUses.map(async () => {
|
|
30106
30506
|
try {
|
|
30107
|
-
return await runAdvisor(advisorConversation, advisorModel, advisorEffort, aborter.signal, advisorEscalated);
|
|
30507
|
+
return await runAdvisor(advisorConversation, advisorModel, advisorEffort, aborter.signal, advisorEscalated, advisorFastProfile);
|
|
30108
30508
|
} catch (err) {
|
|
30109
30509
|
if (aborter.signal.aborted) throw err;
|
|
30110
30510
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -30152,27 +30552,11 @@ function buildAdvisorStream(opts) {
|
|
|
30152
30552
|
}))
|
|
30153
30553
|
});
|
|
30154
30554
|
if (aborter.signal.aborted) return;
|
|
30155
|
-
|
|
30555
|
+
response = await continueTurn({
|
|
30156
30556
|
...opts.baseBody,
|
|
30157
30557
|
messages: conversation,
|
|
30158
30558
|
stream: true
|
|
30159
|
-
});
|
|
30160
|
-
const knownRepair = repairKnownThinkingHistory(continuationSend);
|
|
30161
|
-
if (knownRepair) continuationSend = knownRepair.body;
|
|
30162
|
-
try {
|
|
30163
|
-
response = await createMessages(continuationSend, opts.requestHeaders, aborter.signal, true);
|
|
30164
|
-
} catch (continuationError) {
|
|
30165
|
-
if (!(continuationError instanceof HTTPError)) throw continuationError;
|
|
30166
|
-
const errorBody = await continuationError.response.clone().text().catch(() => "");
|
|
30167
|
-
const outcome = repairRejectedThinkingHistory(continuationSend, errorBody);
|
|
30168
|
-
if (!outcome.ok) {
|
|
30169
|
-
consola.warn(`Advisor continuation thinking-history repair declined: ${formatThinkingRepairDecline(outcome.decline)}`);
|
|
30170
|
-
throw continuationError;
|
|
30171
|
-
}
|
|
30172
|
-
consola.warn(`Advisor continuation: retrying without rejected thinking blocks: message=${outcome.repair.messageIndex} removed_blocks=${outcome.repair.removedBlocks}`);
|
|
30173
|
-
response = await createMessages(outcome.repair.body, opts.requestHeaders, aborter.signal, true);
|
|
30174
|
-
rememberThinkingHistoryRepair(outcome.repair.fingerprint);
|
|
30175
|
-
}
|
|
30559
|
+
}, aborter.signal);
|
|
30176
30560
|
}
|
|
30177
30561
|
if (aborter.signal.aborted) return;
|
|
30178
30562
|
const finalIndex = nextSyntheticIndex++;
|
|
@@ -31988,6 +32372,17 @@ const ADVISOR_PARAMS = Type$1.Object({ concern: Type$1.String({
|
|
|
31988
32372
|
description: "What you want a second pair of eyes on — your current approach, the blocker you're stuck on, or the decision you're about to commit. Required: the advisor needs a focal point.",
|
|
31989
32373
|
minLength: 1
|
|
31990
32374
|
}) });
|
|
32375
|
+
/**
|
|
32376
|
+
* Fixed reasoning effort for the WORKER'S own `advisor` tool call (distinct
|
|
32377
|
+
* from the server-side ADVISOR mechanism's `ADVISOR_DEFAULT_EFFORT` in
|
|
32378
|
+
* `src/services/advisor/advisor.ts`, which now follows the Claude Code
|
|
32379
|
+
* effort picker and defaults to `high`). This tool has no picker to follow —
|
|
32380
|
+
* it is a single fixed-effort consultation a worker triggers explicitly, not
|
|
32381
|
+
* a per-request value derived from client input — so it keeps its own
|
|
32382
|
+
* historical constant rather than sharing one that started varying for an
|
|
32383
|
+
* unrelated reason.
|
|
32384
|
+
*/
|
|
32385
|
+
const WORKER_ADVISOR_EFFORT = "xhigh";
|
|
31991
32386
|
/** Advisor transcript budget — leaves headroom in the advisor's
|
|
31992
32387
|
* context window after the system prompt + concern + reasoning
|
|
31993
32388
|
* overhead. Truncate-from-front so the most recent turn (where the
|
|
@@ -32089,7 +32484,7 @@ function advisorTool(getMessages) {
|
|
|
32089
32484
|
}]
|
|
32090
32485
|
}],
|
|
32091
32486
|
stream: false,
|
|
32092
|
-
reasoning: { effort:
|
|
32487
|
+
reasoning: { effort: WORKER_ADVISOR_EFFORT }
|
|
32093
32488
|
}, { workload: "reusable-prefix" });
|
|
32094
32489
|
const text = extractResponsesText(await createResponses(payload, void 0, signal, true));
|
|
32095
32490
|
if (!text) throw new Error("advisor returned empty output");
|
|
@@ -35172,6 +35567,17 @@ function buildAgentPrompt(persona, opts) {
|
|
|
35172
35567
|
*/
|
|
35173
35568
|
function buildPeerAwarenessSnippet(opts) {
|
|
35174
35569
|
const key = (g) => opts.groupKeys?.[g] ?? GROUP_META[g].preferredKey;
|
|
35570
|
+
if (opts.profile === "fast") {
|
|
35571
|
+
const fastPeersKey = key("peers");
|
|
35572
|
+
const fastSearchKey = key("search");
|
|
35573
|
+
return [
|
|
35574
|
+
"## Peer review and advisor",
|
|
35575
|
+
"",
|
|
35576
|
+
`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
|
+
"",
|
|
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.` : ""}`
|
|
35579
|
+
].join("\n");
|
|
35580
|
+
}
|
|
35175
35581
|
const peersKey = key("peers");
|
|
35176
35582
|
const searchKey = key("search");
|
|
35177
35583
|
const workersKey = key("workers");
|
|
@@ -35225,6 +35631,12 @@ function buildPeerAwarenessSnippet(opts) {
|
|
|
35225
35631
|
*/
|
|
35226
35632
|
function buildPeerAwarenessSummary(opts) {
|
|
35227
35633
|
const key = (g) => opts.groupKeys?.[g] ?? GROUP_META[g].preferredKey;
|
|
35634
|
+
if (opts.profile === "fast") return [
|
|
35635
|
+
"## Injected capabilities (summary)",
|
|
35636
|
+
"",
|
|
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.",
|
|
35638
|
+
`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
|
+
].join("\n");
|
|
35228
35640
|
const renderNative = (name) => {
|
|
35229
35641
|
const modelId = opts.nativeAgentModels?.[name];
|
|
35230
35642
|
if (!modelId) return `\`${name}\``;
|
|
@@ -35259,6 +35671,22 @@ function buildPeerAwarenessSummary(opts) {
|
|
|
35259
35671
|
return lines.join("\n");
|
|
35260
35672
|
}
|
|
35261
35673
|
/**
|
|
35674
|
+
* Translate a `toolNameHttp`-keyed persona allowlist (the currency
|
|
35675
|
+
* `LaunchProfileDescriptor.personaAllowlist` uses, since that is what the MCP
|
|
35676
|
+
* boundary's `tools/call` narrowing filters on) into the `agentName`-keyed
|
|
35677
|
+
* allowlist `personasFor`'s `agentAllowlist` consumes (since that is the key
|
|
35678
|
+
* `buildPeerAgentDefinitions` uses to build subagent `.md` files). The two
|
|
35679
|
+
* identifiers differ (`gemini_critic` vs `gemini-critic`), so a caller wiring
|
|
35680
|
+
* a launch profile's persona restriction into subagent generation needs this
|
|
35681
|
+
* translation rather than assuming the sets are interchangeable.
|
|
35682
|
+
*/
|
|
35683
|
+
function agentNamesForToolAllowlist(toolAllowlist) {
|
|
35684
|
+
const allow = toolAllowlist instanceof Set ? toolAllowlist : new Set(toolAllowlist);
|
|
35685
|
+
const names = /* @__PURE__ */ new Set();
|
|
35686
|
+
for (const p of [...PERSONAS_READ, ...PERSONAS_WRITE]) if (allow.has(p.toolNameHttp)) names.add(p.agentName);
|
|
35687
|
+
return names;
|
|
35688
|
+
}
|
|
35689
|
+
/**
|
|
35262
35690
|
* Applies the resolved Gemini review model to a persona requiring the Gemini
|
|
35263
35691
|
* catalog: swaps `.model` and rewrites every literal occurrence of the
|
|
35264
35692
|
* default id in `.description` so the two never disagree about which model
|
|
@@ -35283,8 +35711,10 @@ function resolveGeminiPersona(p, geminiModel) {
|
|
|
35283
35711
|
}
|
|
35284
35712
|
/** Convenience: every persona that should be registered for the given mode. */
|
|
35285
35713
|
function personasFor(opts) {
|
|
35714
|
+
const allow = opts.agentAllowlist == null ? void 0 : opts.agentAllowlist instanceof Set ? opts.agentAllowlist : new Set(opts.agentAllowlist);
|
|
35286
35715
|
const result = [];
|
|
35287
35716
|
for (const p of PERSONAS_READ) {
|
|
35717
|
+
if (allow && !allow.has(p.agentName)) continue;
|
|
35288
35718
|
if (p.requiresGeminiCatalog) {
|
|
35289
35719
|
if (!opts.geminiAvailable) continue;
|
|
35290
35720
|
result.push(resolveGeminiPersona(p, opts.geminiModel));
|
|
@@ -35292,7 +35722,10 @@ function personasFor(opts) {
|
|
|
35292
35722
|
}
|
|
35293
35723
|
result.push(p);
|
|
35294
35724
|
}
|
|
35295
|
-
if (opts.codexCli) for (const p of PERSONAS_WRITE)
|
|
35725
|
+
if (opts.codexCli) for (const p of PERSONAS_WRITE) {
|
|
35726
|
+
if (allow && !allow.has(p.agentName)) continue;
|
|
35727
|
+
result.push(p);
|
|
35728
|
+
}
|
|
35296
35729
|
return result;
|
|
35297
35730
|
}
|
|
35298
35731
|
const WEB_SEARCH_DESCRIPTION = "Web search via GitHub Copilot's MCP that returns answer text plus source URLs the caller can cite. It accepts a natural-language `query`; the upstream provider rewrites for the search index and the handler formats any references as markdown links. Use for current external information such as API documentation, error-message diagnosis, upstream issue searches, and claims that need web sources. Not for local repository discovery or code navigation, use code, Read, Grep, or Glob for workspace content. Prefer it over the built-in WebSearch when source URLs are needed or the built-in surface is geographically constrained.";
|
|
@@ -36475,6 +36908,6 @@ function enumerateInjectedMcpToolNames(groupKeys, opts = {}) {
|
|
|
36475
36908
|
return [...new Set(names)];
|
|
36476
36909
|
}
|
|
36477
36910
|
//#endregion
|
|
36478
|
-
export {
|
|
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 };
|
|
36479
36912
|
|
|
36480
|
-
//# sourceMappingURL=peer-mcp-personas-
|
|
36913
|
+
//# sourceMappingURL=peer-mcp-personas-DhI7ZPSx.js.map
|