github-router 0.3.177 → 0.3.202
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/browser-ext/manifest.json +1 -1
- package/dist/engine-CIt6z9K3.js +6 -0
- package/dist/lib/tree-sitter-pool/worker.js +1 -1
- package/dist/{lifecycle-ClliSC-c.js → lifecycle-CJV1XBFP.js} +2 -2
- package/dist/{lifecycle-D0n3hXUT.js → lifecycle-Ci81s6eL.js} +2 -2
- package/dist/{lifecycle-DrDEjx_c.js → lifecycle-D-1CYr1Y.js} +2 -2
- package/dist/{lifecycle-DrDEjx_c.js.map → lifecycle-D-1CYr1Y.js.map} +1 -1
- package/dist/{lifecycle-Dvr3pGGG.js → lifecycle-bPdiXjYB.js} +2 -2
- package/dist/{lifecycle-Dvr3pGGG.js.map → lifecycle-bPdiXjYB.js.map} +1 -1
- package/dist/main.js +1765 -33
- package/dist/main.js.map +1 -1
- package/dist/{paths-CDYvyApX.js → paths-B5k78n0d.js} +1 -1
- package/dist/{paths-BZPgxKUl.js → paths-BO22pMUb.js} +6 -3
- package/dist/paths-BO22pMUb.js.map +1 -0
- package/dist/{peer-mcp-personas-BtQi-dF-.js → peer-mcp-personas-rik0SIrg.js} +226 -64
- package/dist/peer-mcp-personas-rik0SIrg.js.map +1 -0
- package/package.json +4 -2
- package/dist/engine-DOOYh35O.js +0 -6
- package/dist/paths-BZPgxKUl.js.map +0 -1
- package/dist/peer-mcp-personas-BtQi-dF-.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as PATHS } from "./paths-
|
|
2
|
-
import { d as runCommandCapture, l as parseBoolEnv, n as isPidAlive, o as trackChild, p as runManagedExeCapture, r as registerColbertExitHandlers, t as getColbertInstanceUuid, u as resolveExecutable } from "./lifecycle-
|
|
3
|
-
import { i as registerExitHandlers, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-
|
|
1
|
+
import { t as PATHS } from "./paths-BO22pMUb.js";
|
|
2
|
+
import { d as runCommandCapture, l as parseBoolEnv, n as isPidAlive, o as trackChild, p as runManagedExeCapture, r as registerColbertExitHandlers, t as getColbertInstanceUuid, u as resolveExecutable } from "./lifecycle-D-1CYr1Y.js";
|
|
3
|
+
import { i as registerExitHandlers, n as getInstanceUuid, r as recordWorkerRepo, t as WorktreeRegistry } from "./lifecycle-bPdiXjYB.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import consola from "consola";
|
|
6
6
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
@@ -623,6 +623,13 @@ function normalizeModelId$1(id) {
|
|
|
623
623
|
* version pinning (a dated catalog id matched at Step 1) always wins.
|
|
624
624
|
* 6. Return as-is with a warning
|
|
625
625
|
*/
|
|
626
|
+
/**
|
|
627
|
+
* `[1m]`-downgrade warnings we've already emitted this process. `resolveModel`
|
|
628
|
+
* is called many times per launch (env setup, model validation, the banner,
|
|
629
|
+
* and — under `serve` — on every model-picker `/models` poll), so the downgrade
|
|
630
|
+
* notice is deduped to once per model id to avoid log spam.
|
|
631
|
+
*/
|
|
632
|
+
const warnedOneMDowngrade = /* @__PURE__ */ new Set();
|
|
626
633
|
function resolveModel(modelId) {
|
|
627
634
|
const models = state.models?.data;
|
|
628
635
|
if (!models) return modelId;
|
|
@@ -630,7 +637,10 @@ function resolveModel(modelId) {
|
|
|
630
637
|
if (oneMMatch) {
|
|
631
638
|
const stripped = oneMMatch[1];
|
|
632
639
|
const resolved = resolveModel(stripped);
|
|
633
|
-
if (!/-1m(?:$|-)/.test(resolved)
|
|
640
|
+
if (!/-1m(?:$|-)/.test(resolved) && !warnedOneMDowngrade.has(modelId)) {
|
|
641
|
+
warnedOneMDowngrade.add(modelId);
|
|
642
|
+
consola.warn(`Model "${modelId}" requested 1M context but no -1m backend is in Copilot's catalog for this tier/family; downgrading upstream to "${resolved}" (200K). Claude Code's local context accounting will still assume 1M — expect premature auto-compact. Drop the [1m] suffix (or unset CLAUDE_CODE_DISABLE_1M_CONTEXT if you set it) to silence.`);
|
|
643
|
+
}
|
|
634
644
|
return resolved;
|
|
635
645
|
}
|
|
636
646
|
if (models.some((m) => m.id === modelId)) return modelId;
|
|
@@ -1145,6 +1155,25 @@ function collapsePathKeys(env) {
|
|
|
1145
1155
|
return env;
|
|
1146
1156
|
}
|
|
1147
1157
|
|
|
1158
|
+
//#endregion
|
|
1159
|
+
//#region src/lib/mcp-workspace-header.ts
|
|
1160
|
+
/** Header the per-session MCP headersHelper emits, carrying the calling Claude
|
|
1161
|
+
* session's working directory. The /mcp handler uses it as the default
|
|
1162
|
+
* `workspace` for repo-scoped tools so a machine-wide `serve` targets the
|
|
1163
|
+
* active repo, not the proxy launch cwd. */
|
|
1164
|
+
const MCP_WORKSPACE_HEADER = "X-GH-Workspace";
|
|
1165
|
+
/** stdout JSON the headersHelper prints; Claude merges it into the connection
|
|
1166
|
+
* headers. `cwd` is the helper's own process cwd = the session's project dir. */
|
|
1167
|
+
function buildWorkspaceHeaderJson(cwd) {
|
|
1168
|
+
return JSON.stringify({ [MCP_WORKSPACE_HEADER]: cwd });
|
|
1169
|
+
}
|
|
1170
|
+
/** Command string Claude runs as the headersHelper. Mirrors
|
|
1171
|
+
* buildPromptSubmitHookCommand's quoting (src/lib/orchestration/prompt-submit-hook.ts). */
|
|
1172
|
+
function buildWorkspaceHeaderHelperCommand(execPath, scriptPath) {
|
|
1173
|
+
const q = (s) => `"${s}"`;
|
|
1174
|
+
return scriptPath && scriptPath !== execPath ? `${q(execPath)} ${q(scriptPath)} internal-workspace-header` : `${q(execPath)} internal-workspace-header`;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1148
1177
|
//#endregion
|
|
1149
1178
|
//#region src/lib/insecure-tls.ts
|
|
1150
1179
|
const IS_BUN$1 = typeof globalThis.Bun !== "undefined";
|
|
@@ -12076,6 +12105,14 @@ var TreeSitterPool = class {
|
|
|
12076
12105
|
* entirely and force the in-process path, rather than churning spawn→crash
|
|
12077
12106
|
* forever. */
|
|
12078
12107
|
crashCount = 0;
|
|
12108
|
+
workersSpawned = 0;
|
|
12109
|
+
/** Test-only observability for crash/respawn assertions. */
|
|
12110
|
+
__workerLifecycleForTest() {
|
|
12111
|
+
return {
|
|
12112
|
+
crashes: this.crashCount,
|
|
12113
|
+
spawned: this.workersSpawned
|
|
12114
|
+
};
|
|
12115
|
+
}
|
|
12079
12116
|
queue = [];
|
|
12080
12117
|
inflight = /* @__PURE__ */ new Map();
|
|
12081
12118
|
constructor() {
|
|
@@ -12123,6 +12160,7 @@ var TreeSitterPool = class {
|
|
|
12123
12160
|
let worker;
|
|
12124
12161
|
try {
|
|
12125
12162
|
worker = new Worker(this.workerPath);
|
|
12163
|
+
this.workersSpawned += 1;
|
|
12126
12164
|
} catch (err) {
|
|
12127
12165
|
consola.debug(`[code_search] tree-sitter worker spawn failed: ${err.message}`);
|
|
12128
12166
|
resolve(null);
|
|
@@ -12225,8 +12263,14 @@ var TreeSitterPool = class {
|
|
|
12225
12263
|
if (!job) break;
|
|
12226
12264
|
pw.busyJobId = job.id;
|
|
12227
12265
|
this.inflight.set(job.id, job);
|
|
12266
|
+
const injectCrash = _testCrashOnceArmed;
|
|
12267
|
+
const reqToPost = injectCrash ? {
|
|
12268
|
+
...job.req,
|
|
12269
|
+
testCrash: true
|
|
12270
|
+
} : job.req;
|
|
12228
12271
|
try {
|
|
12229
|
-
pw.worker.postMessage(
|
|
12272
|
+
pw.worker.postMessage(reqToPost);
|
|
12273
|
+
if (injectCrash) _testCrashOnceArmed = false;
|
|
12230
12274
|
} catch (err) {
|
|
12231
12275
|
consola.debug(`[code_search] tree-sitter worker postMessage failed: ${err.message}`);
|
|
12232
12276
|
this.inflight.delete(job.id);
|
|
@@ -12398,6 +12442,7 @@ function resolveWorkerPath() {
|
|
|
12398
12442
|
}
|
|
12399
12443
|
let _pool = null;
|
|
12400
12444
|
let _shutdownRegistered = false;
|
|
12445
|
+
let _testCrashOnceArmed = false;
|
|
12401
12446
|
/**
|
|
12402
12447
|
* The pool is ON by default for real (non-CI) runs and OFF under CI.
|
|
12403
12448
|
*
|
|
@@ -12650,6 +12695,10 @@ const WALL_TIME_MS = 3e4;
|
|
|
12650
12695
|
* comfortable headroom for ~5-10 files even on cold cache.
|
|
12651
12696
|
*/
|
|
12652
12697
|
const STRUCTURAL_BUDGET_MS = 200;
|
|
12698
|
+
let _structuralBudgetTestOverride = null;
|
|
12699
|
+
function structuralBudgetMs() {
|
|
12700
|
+
return _structuralBudgetTestOverride ?? STRUCTURAL_BUDGET_MS;
|
|
12701
|
+
}
|
|
12653
12702
|
const STRUCTURAL_TOPN_FULL = 50;
|
|
12654
12703
|
const STRUCTURAL_TOPN_FAST = 10;
|
|
12655
12704
|
/**
|
|
@@ -13957,7 +14006,7 @@ async function searchCode(rawInput, externalSignal) {
|
|
|
13957
14006
|
})).filter((e) => e.index >= 0),
|
|
13958
14007
|
workspaceRoot: ws.canonical,
|
|
13959
14008
|
topN,
|
|
13960
|
-
budgetMs:
|
|
14009
|
+
budgetMs: structuralBudgetMs(),
|
|
13961
14010
|
signal: ac.signal
|
|
13962
14011
|
});
|
|
13963
14012
|
structuralOutlines = structural.outlinesByFile;
|
|
@@ -15562,6 +15611,7 @@ async function provisionAndIndexColbert(opts = {}) {
|
|
|
15562
15611
|
return;
|
|
15563
15612
|
}
|
|
15564
15613
|
if (!provisioned) return;
|
|
15614
|
+
if (opts.skipCwdIndex) return;
|
|
15565
15615
|
const cwd = opts.cwd ?? process$1.cwd();
|
|
15566
15616
|
try {
|
|
15567
15617
|
if ((await gitState(cwd)).isRepo && await startupKickAllowed(cwd)) kickBackgroundInit(cwd);
|
|
@@ -16881,7 +16931,7 @@ function logAudit$1(record) {
|
|
|
16881
16931
|
try {
|
|
16882
16932
|
const fs$2 = await import("node:fs/promises");
|
|
16883
16933
|
const path$1 = await import("node:path");
|
|
16884
|
-
const { PATHS: PATHS$1 } = await import("./paths-
|
|
16934
|
+
const { PATHS: PATHS$1 } = await import("./paths-B5k78n0d.js");
|
|
16885
16935
|
const dir = path$1.join(PATHS$1.APP_DIR, "browser-mcp");
|
|
16886
16936
|
await fs$2.mkdir(dir, { recursive: true });
|
|
16887
16937
|
const line = JSON.stringify({
|
|
@@ -24084,9 +24134,9 @@ function jsonPathPreflightCap(body, scope) {
|
|
|
24084
24134
|
const decision = typeof args.decision === "string" ? args.decision : "";
|
|
24085
24135
|
const optionsRaw = Array.isArray(args.options) ? args.options : [];
|
|
24086
24136
|
const standInContext = typeof args.context === "string" ? args.context : "";
|
|
24087
|
-
if (!decision || optionsRaw.length === 0) return void 0;
|
|
24137
|
+
if (!decision || optionsRaw.length === 0 || !standInContext.trim()) return void 0;
|
|
24088
24138
|
const briefBytes$1 = Buffer.byteLength(decision + JSON.stringify(optionsRaw) + standInContext, "utf8");
|
|
24089
|
-
const STAND_IN_CAP_BYTES =
|
|
24139
|
+
const STAND_IN_CAP_BYTES = 32 * 1024;
|
|
24090
24140
|
if (briefBytes$1 > STAND_IN_CAP_BYTES) return rpcResult(body.id, toolError(`pre-flight rejected: stand_in on a ${briefBytes$1}-byte input is predicted to exceed the JSON tools/call timeout (cap=${STAND_IN_CAP_BYTES} bytes). stand_in runs two sequential voting rounds across three frontier models — wall-clock is typically 2-3 minutes regardless of input size. Send Accept: text/event-stream to use the SSE path which bypasses this cap, or trim the decision/options/context.`));
|
|
24091
24141
|
return;
|
|
24092
24142
|
}
|
|
@@ -24213,7 +24263,13 @@ function logTelemetry(t) {
|
|
|
24213
24263
|
if (t.errorMessage) parts.push(`error=${JSON.stringify(t.errorMessage)}`);
|
|
24214
24264
|
process.stderr.write(parts.join(" ") + "\n");
|
|
24215
24265
|
}
|
|
24216
|
-
|
|
24266
|
+
function toolAcceptsWorkspace(tool$1) {
|
|
24267
|
+
return tool$1.capability === "worker" || tool$1.toolNameHttp === "code" || tool$1.toolNameHttp === "run_workflow";
|
|
24268
|
+
}
|
|
24269
|
+
function applySessionWorkspace(args, sessionWorkspace, tool$1) {
|
|
24270
|
+
if ((!tool$1 || toolAcceptsWorkspace(tool$1)) && typeof sessionWorkspace === "string" && sessionWorkspace.length > 0 && nodePath.isAbsolute(sessionWorkspace) && (args.workspace === void 0 || args.workspace === "")) args.workspace = sessionWorkspace;
|
|
24271
|
+
}
|
|
24272
|
+
async function handleToolsCall(body, scope, sessionWorkspace) {
|
|
24217
24273
|
const params = body.params ?? {};
|
|
24218
24274
|
const name = typeof params.name === "string" ? params.name : "";
|
|
24219
24275
|
const args = params.arguments ?? {};
|
|
@@ -24276,6 +24332,7 @@ async function handleToolsCall(body, scope) {
|
|
|
24276
24332
|
const telemetryName = persona ? persona.agentName : nonPersonaTool.toolNameHttp;
|
|
24277
24333
|
const telemetryModel = persona ? persona.model : "(non-persona)";
|
|
24278
24334
|
try {
|
|
24335
|
+
if (nonPersonaTool) applySessionWorkspace(args, sessionWorkspace, nonPersonaTool);
|
|
24279
24336
|
const result = persona ? await callPersona(persona, personaPrompt, personaContext, personaEffort, aborter?.signal) : await nonPersonaTool.handler(args, aborter?.signal);
|
|
24280
24337
|
logTelemetry({
|
|
24281
24338
|
name: telemetryName,
|
|
@@ -24321,7 +24378,7 @@ function handleCancelledNotification(body) {
|
|
|
24321
24378
|
}
|
|
24322
24379
|
cancelInflight(requestId, "client requested cancellation");
|
|
24323
24380
|
}
|
|
24324
|
-
async function handleRpc(_c, body, scope) {
|
|
24381
|
+
async function handleRpc(_c, body, scope, sessionWorkspace) {
|
|
24325
24382
|
if (body === null || typeof body !== "object" || Array.isArray(body)) return {
|
|
24326
24383
|
status: 200,
|
|
24327
24384
|
body: rpcError(null, RPC_INVALID_REQUEST, "jsonrpc 2.0 envelope required")
|
|
@@ -24372,7 +24429,7 @@ async function handleRpc(_c, body, scope) {
|
|
|
24372
24429
|
};
|
|
24373
24430
|
return {
|
|
24374
24431
|
status: 200,
|
|
24375
|
-
body: await handleToolsCall(body, scope)
|
|
24432
|
+
body: await handleToolsCall(body, scope, sessionWorkspace)
|
|
24376
24433
|
};
|
|
24377
24434
|
case "resources/list":
|
|
24378
24435
|
if (isNotification) return {
|
|
@@ -24463,17 +24520,18 @@ async function handleMcpPost(c, scopeArg = "all") {
|
|
|
24463
24520
|
consola.debug("/mcp parse error:", err);
|
|
24464
24521
|
return c.json(rpcError(null, RPC_PARSE_ERROR, "request body is not valid JSON"), 200);
|
|
24465
24522
|
}
|
|
24523
|
+
const sessionWorkspace = c.req.header(MCP_WORKSPACE_HEADER);
|
|
24466
24524
|
if (process.env.GH_ROUTER_LOG_PEER_MCP === "1" && typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call") {
|
|
24467
24525
|
const nm = typeof body.params?.name === "string" ? body.params.name : "?";
|
|
24468
24526
|
process.stderr.write(`[peer-mcp] recv t=${Date.now()} name=${nm} scope=${scope} inflight=${currentInFlight()}\n`);
|
|
24469
24527
|
}
|
|
24470
|
-
if (typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call" && acceptsEventStream(c.req.header("accept"))) return handleToolsCallSSE(body, scope);
|
|
24528
|
+
if (typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call" && acceptsEventStream(c.req.header("accept"))) return handleToolsCallSSE(body, scope, sessionWorkspace);
|
|
24471
24529
|
if (typeof body === "object" && body !== null && !Array.isArray(body) && body.method === "tools/call") {
|
|
24472
24530
|
const preflight = jsonPathPreflightCap(body, scope);
|
|
24473
24531
|
if (preflight) return c.json(preflight, 200);
|
|
24474
24532
|
}
|
|
24475
24533
|
try {
|
|
24476
|
-
const { status, body: respBody } = await handleRpc(c, body, scope);
|
|
24534
|
+
const { status, body: respBody } = await handleRpc(c, body, scope, sessionWorkspace);
|
|
24477
24535
|
if (respBody === null) return c.body(null, status);
|
|
24478
24536
|
return c.json(respBody, status);
|
|
24479
24537
|
} catch (err) {
|
|
@@ -24526,9 +24584,9 @@ function acceptsEventStream(accept) {
|
|
|
24526
24584
|
* "Invalid state: Controller is already closed" race without warning.
|
|
24527
24585
|
*/
|
|
24528
24586
|
const SSE_HEARTBEAT_INTERVAL_MS = 5e3;
|
|
24529
|
-
async function handleToolsCallSSE(body, scope) {
|
|
24587
|
+
async function handleToolsCallSSE(body, scope, sessionWorkspace) {
|
|
24530
24588
|
const encoder = new TextEncoder();
|
|
24531
|
-
const callPromise = handleToolsCall(body, scope);
|
|
24589
|
+
const callPromise = handleToolsCall(body, scope, sessionWorkspace);
|
|
24532
24590
|
let heartbeatHandle;
|
|
24533
24591
|
const stream = new ReadableStream({
|
|
24534
24592
|
async start(controller) {
|
|
@@ -28039,12 +28097,15 @@ Respond with ONLY a single JSON object — no prose, no markdown fences, no prea
|
|
|
28039
28097
|
"choice": "<option.id>" | null,
|
|
28040
28098
|
"confidence": <number between 0.0 and 1.0>,
|
|
28041
28099
|
"reasoning": "<one short sentence>",
|
|
28042
|
-
"need_more_info": "<what context is missing, if you cannot decide>"
|
|
28100
|
+
"need_more_info": "<what context is missing, if you cannot decide>",
|
|
28101
|
+
"alternative": "<a concrete unlisted option — ONLY if every provided option is inadequate>"
|
|
28043
28102
|
}
|
|
28044
28103
|
|
|
28045
28104
|
Calibration rules:
|
|
28046
28105
|
- "confidence" reflects how sure you are this is the better option (not how confident you are in your prose). 0.5 = coin flip. 0.9 = clear winner. Be honestly calibrated; the orchestrator weighs your number directly.
|
|
28047
28106
|
- If the question is genuinely under-specified — you'd need information you don't have to choose well — set "choice": null AND populate "need_more_info" with the specific gap. Do NOT guess.
|
|
28107
|
+
- The caller curated these options; default to choosing among them. Only if a provided option is actively harmful, or clearly dominated by an obvious unlisted option, set "choice": null AND put that concrete option in "alternative" (one sentence). This is distinct from "need_more_info" (which is about missing context, not a better option). Prefer choosing over proposing — do not invent an alternative to avoid committing.
|
|
28108
|
+
- On an abstention, populate at most ONE escape channel: "need_more_info" OR "alternative", never both. If both seem to apply, use "need_more_info" — missing context takes precedence, because you can't reliably judge the options inadequate without it.
|
|
28048
28109
|
- One sentence of reasoning. Not a paragraph.
|
|
28049
28110
|
- The other two models will vote independently and you will see their votes in round 2. There is no benefit to anticipating what they'll pick; vote on the merits.
|
|
28050
28111
|
|
|
@@ -28057,16 +28118,19 @@ Same JSON schema as round 1:
|
|
|
28057
28118
|
"choice": "<option.id>" | null,
|
|
28058
28119
|
"confidence": <number between 0.0 and 1.0>,
|
|
28059
28120
|
"reasoning": "<one short sentence>",
|
|
28060
|
-
"need_more_info": "<gap, if any>"
|
|
28121
|
+
"need_more_info": "<gap, if any>",
|
|
28122
|
+
"alternative": "<a concrete unlisted option, if every provided option is inadequate>"
|
|
28061
28123
|
}
|
|
28062
28124
|
|
|
28063
28125
|
Calibration rules:
|
|
28064
28126
|
- You may keep your round-1 vote OR change it. Do NOT change just to agree — agreement is not the goal, the right answer is. Capitulating to peer pressure when you still believe your original choice is better is a failure mode, not a success.
|
|
28065
28127
|
- If a peer's reasoning identifies a consideration you missed or weighed wrong, update freely. The blind round was the anti-anchor mechanism; this round is where genuine evidence can move you.
|
|
28066
28128
|
- If round 1 left you genuinely uncertain and peer reasoning hasn't resolved it, "choice": null is still the honest answer.
|
|
28129
|
+
- Keep using "alternative" only when every provided option is inadequate (choice null); it is not a way to dodge a decision the options already support.
|
|
28130
|
+
- On an abstention, populate at most ONE escape channel: "need_more_info" OR "alternative", never both. If both seem to apply, use "need_more_info" (missing context takes precedence).
|
|
28067
28131
|
|
|
28068
28132
|
Output ONLY the JSON object.`;
|
|
28069
|
-
const RETRY_PROMPT_SUFFIX = `\n\nYour previous response was not valid JSON matching the schema. Respond with ONLY the JSON object — no preamble, no markdown fences, no closing remarks. Schema reminder: {"choice": "<id>" | null, "confidence": 0.0-1.0, "reasoning": "<one sentence>", "need_more_info": "<gap, if any>"}`;
|
|
28133
|
+
const RETRY_PROMPT_SUFFIX = `\n\nYour previous response was not valid JSON matching the schema. Respond with ONLY the JSON object — no preamble, no markdown fences, no closing remarks. Schema reminder: {"choice": "<id>" | null, "confidence": 0.0-1.0, "reasoning": "<one sentence>", "need_more_info": "<gap, if any>", "alternative": "<unlisted option, if any>"}`;
|
|
28070
28134
|
/**
|
|
28071
28135
|
* Run the two-round stand-in protocol. Returns a structured verdict
|
|
28072
28136
|
* envelope. Throws only on systemic failure (e.g., all three upstream
|
|
@@ -28074,71 +28138,66 @@ const RETRY_PROMPT_SUFFIX = `\n\nYour previous response was not valid JSON match
|
|
|
28074
28138
|
* `VoteFailure` entries in the result.
|
|
28075
28139
|
*/
|
|
28076
28140
|
async function runStandIn(input, signal) {
|
|
28141
|
+
const validIds = new Set(input.options.map((o) => o.id));
|
|
28077
28142
|
const r1UserText = buildRound1UserText(input);
|
|
28078
|
-
const r1 = await Promise.all(STAND_IN_MODELS.map((cfg) => callAndParse(cfg, SYSTEM_PROMPT_R1, r1UserText, signal)));
|
|
28143
|
+
const r1 = await Promise.all(STAND_IN_MODELS.map((cfg) => callAndParse(cfg, SYSTEM_PROMPT_R1, r1UserText, validIds, signal)));
|
|
28079
28144
|
const successfulR1 = r1.filter((r) => isVote(r.vote));
|
|
28080
|
-
|
|
28081
|
-
|
|
28082
|
-
return {
|
|
28083
|
-
verdict: "need_more_info",
|
|
28084
|
-
recommendation: null,
|
|
28085
|
-
confidence: 0,
|
|
28086
|
-
votes: voteRecord(r1, null),
|
|
28087
|
-
notes: `All three models reported they need more context to decide:\n${gaps}`
|
|
28088
|
-
};
|
|
28089
|
-
}
|
|
28145
|
+
const nmiR1 = gapAbstainVerdict(successfulR1, r1, null);
|
|
28146
|
+
if (nmiR1) return nmiR1;
|
|
28090
28147
|
const r1Decision = aggregateVotes(successfulR1);
|
|
28091
|
-
if (r1Decision.verdict === "consensus" && r1Decision.meanConfidence >= .8) return {
|
|
28148
|
+
if (r1Decision.verdict === "consensus" && r1Decision.meanConfidence >= .8) return withDerivedNotes({
|
|
28092
28149
|
verdict: "consensus",
|
|
28093
28150
|
recommendation: r1Decision.winner,
|
|
28094
28151
|
confidence: round2(r1Decision.meanConfidence),
|
|
28095
28152
|
votes: voteRecord(r1, null),
|
|
28096
28153
|
notes: `All three models picked ${r1Decision.winner} in round 1 with high confidence (skipped round 2).`
|
|
28097
|
-
};
|
|
28098
|
-
if (successfulR1.length < 2) return {
|
|
28154
|
+
}, r1, null);
|
|
28155
|
+
if (successfulR1.length < 2) return withDerivedNotes({
|
|
28099
28156
|
verdict: "no_consensus",
|
|
28100
28157
|
recommendation: null,
|
|
28101
28158
|
confidence: 0,
|
|
28102
28159
|
votes: voteRecord(r1, null),
|
|
28103
28160
|
notes: `Only ${successfulR1.length} of 3 models returned a parseable round-1 vote; insufficient signal to run round 2.`
|
|
28104
|
-
};
|
|
28161
|
+
}, r1, null);
|
|
28105
28162
|
const r2UserTextBase = buildRound2UserTextBase(input, r1);
|
|
28106
|
-
const r2 = await Promise.all(STAND_IN_MODELS.map((cfg) => callAndParse(cfg, SYSTEM_PROMPT_R2, r2UserTextBase + `\n\nYou are ${cfg.key}. Reconsider and vote.`, signal)));
|
|
28163
|
+
const r2 = await Promise.all(STAND_IN_MODELS.map((cfg) => callAndParse(cfg, SYSTEM_PROMPT_R2, r2UserTextBase + `\n\nYou are ${cfg.key}. Reconsider and vote.`, validIds, signal)));
|
|
28107
28164
|
const successfulR2 = r2.filter((r) => isVote(r.vote));
|
|
28108
|
-
if (successfulR2.length < 2) return {
|
|
28165
|
+
if (successfulR2.length < 2) return withDerivedNotes({
|
|
28109
28166
|
verdict: "no_consensus",
|
|
28110
28167
|
recommendation: null,
|
|
28111
28168
|
confidence: 0,
|
|
28112
28169
|
votes: voteRecord(r1, r2),
|
|
28113
28170
|
notes: `Only ${successfulR2.length} of 3 models returned a parseable round-2 vote; deferring to user.`
|
|
28114
|
-
};
|
|
28171
|
+
}, r1, r2);
|
|
28172
|
+
const nmiR2 = gapAbstainVerdict(successfulR2, r1, r2);
|
|
28173
|
+
if (nmiR2) return nmiR2;
|
|
28115
28174
|
const r2Decision = aggregateVotes(successfulR2);
|
|
28116
|
-
if (r2Decision.verdict === "consensus") return {
|
|
28175
|
+
if (r2Decision.verdict === "consensus") return withDerivedNotes({
|
|
28117
28176
|
verdict: "consensus",
|
|
28118
28177
|
recommendation: r2Decision.winner,
|
|
28119
28178
|
confidence: round2(r2Decision.meanConfidence),
|
|
28120
28179
|
votes: voteRecord(r1, r2),
|
|
28121
28180
|
notes: `All three models picked ${r2Decision.winner} in round 2.`
|
|
28122
|
-
};
|
|
28181
|
+
}, r1, r2);
|
|
28123
28182
|
if (r2Decision.verdict === "majority") {
|
|
28124
28183
|
const dissenters = successfulR2.filter((r) => r.vote.choice !== r2Decision.winner).map((r) => `${r.key} picked ${r.vote.choice ?? "abstain"} (${r.vote.reasoning})`).join("; ");
|
|
28125
|
-
return {
|
|
28184
|
+
return withDerivedNotes({
|
|
28126
28185
|
verdict: "majority",
|
|
28127
28186
|
recommendation: r2Decision.winner,
|
|
28128
28187
|
confidence: round2(r2Decision.meanConfidence),
|
|
28129
28188
|
votes: voteRecord(r1, r2),
|
|
28130
28189
|
notes: `Majority (2 of 3) picked ${r2Decision.winner}. Dissent: ${dissenters}.`
|
|
28131
|
-
};
|
|
28190
|
+
}, r1, r2);
|
|
28132
28191
|
}
|
|
28133
|
-
return {
|
|
28192
|
+
return withDerivedNotes({
|
|
28134
28193
|
verdict: "no_consensus",
|
|
28135
28194
|
recommendation: null,
|
|
28136
28195
|
confidence: 0,
|
|
28137
28196
|
votes: voteRecord(r1, r2),
|
|
28138
28197
|
notes: `Models did not converge in round 2 (votes split). Defer to user.`
|
|
28139
|
-
};
|
|
28198
|
+
}, r1, r2);
|
|
28140
28199
|
}
|
|
28141
|
-
async function callAndParse(cfg, instructions, userText, signal) {
|
|
28200
|
+
async function callAndParse(cfg, instructions, userText, validIds, signal) {
|
|
28142
28201
|
const model = cfg.key === "gpt-5.6-sol" ? resolveOpenAiFrontier() ?? cfg.model : cfg.model;
|
|
28143
28202
|
let raw;
|
|
28144
28203
|
try {
|
|
@@ -28159,7 +28218,7 @@ async function callAndParse(cfg, instructions, userText, signal) {
|
|
|
28159
28218
|
}
|
|
28160
28219
|
};
|
|
28161
28220
|
}
|
|
28162
|
-
const first = tryParseVote(raw);
|
|
28221
|
+
const first = tryParseVote(raw, validIds);
|
|
28163
28222
|
if (first.ok) return {
|
|
28164
28223
|
key: cfg.key,
|
|
28165
28224
|
vote: first.vote
|
|
@@ -28183,7 +28242,7 @@ async function callAndParse(cfg, instructions, userText, signal) {
|
|
|
28183
28242
|
}
|
|
28184
28243
|
};
|
|
28185
28244
|
}
|
|
28186
|
-
const second = tryParseVote(retryRaw);
|
|
28245
|
+
const second = tryParseVote(retryRaw, validIds);
|
|
28187
28246
|
if (second.ok) return {
|
|
28188
28247
|
key: cfg.key,
|
|
28189
28248
|
vote: second.vote
|
|
@@ -28197,7 +28256,7 @@ async function callAndParse(cfg, instructions, userText, signal) {
|
|
|
28197
28256
|
}
|
|
28198
28257
|
};
|
|
28199
28258
|
}
|
|
28200
|
-
function tryParseVote(raw) {
|
|
28259
|
+
function tryParseVote(raw, validIds) {
|
|
28201
28260
|
if (!raw || !raw.trim()) return {
|
|
28202
28261
|
ok: false,
|
|
28203
28262
|
error: "empty response"
|
|
@@ -28225,11 +28284,16 @@ function tryParseVote(raw) {
|
|
|
28225
28284
|
error: "parsed value is not an object"
|
|
28226
28285
|
};
|
|
28227
28286
|
const obj = parsed;
|
|
28228
|
-
const
|
|
28229
|
-
if (
|
|
28287
|
+
const rawChoice = obj.choice === null ? null : typeof obj.choice === "string" && obj.choice.length > 0 ? obj.choice : void 0;
|
|
28288
|
+
if (rawChoice === void 0) return {
|
|
28230
28289
|
ok: false,
|
|
28231
28290
|
error: "missing or invalid 'choice' field (string or null required)"
|
|
28232
28291
|
};
|
|
28292
|
+
if (rawChoice !== null && !validIds.has(rawChoice)) return {
|
|
28293
|
+
ok: false,
|
|
28294
|
+
error: "'choice' must be one of the provided option ids or null"
|
|
28295
|
+
};
|
|
28296
|
+
const choice = rawChoice;
|
|
28233
28297
|
const confidenceRaw = obj.confidence;
|
|
28234
28298
|
const confidence = typeof confidenceRaw === "number" && Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : void 0;
|
|
28235
28299
|
if (confidence === void 0) return {
|
|
@@ -28241,13 +28305,15 @@ function tryParseVote(raw) {
|
|
|
28241
28305
|
ok: false,
|
|
28242
28306
|
error: "missing or empty 'reasoning' field"
|
|
28243
28307
|
};
|
|
28308
|
+
const needMoreInfo = choice === null && typeof obj.need_more_info === "string" && obj.need_more_info.trim().length > 0 ? obj.need_more_info.trim() : void 0;
|
|
28244
28309
|
return {
|
|
28245
28310
|
ok: true,
|
|
28246
28311
|
vote: {
|
|
28247
28312
|
choice,
|
|
28248
28313
|
confidence,
|
|
28249
28314
|
reasoning,
|
|
28250
|
-
needMoreInfo
|
|
28315
|
+
needMoreInfo,
|
|
28316
|
+
alternative: choice === null && !needMoreInfo && typeof obj.alternative === "string" && obj.alternative.trim().length > 0 ? obj.alternative.trim() : void 0
|
|
28251
28317
|
}
|
|
28252
28318
|
};
|
|
28253
28319
|
}
|
|
@@ -28310,13 +28376,70 @@ function buildRound2UserTextBase(input, r1) {
|
|
|
28310
28376
|
for (const r of r1) if (isVote(r.vote)) {
|
|
28311
28377
|
const choiceText = r.vote.choice === null ? "abstain" : r.vote.choice;
|
|
28312
28378
|
const gapText = r.vote.needMoreInfo ? ` (needs: ${r.vote.needMoreInfo})` : "";
|
|
28313
|
-
|
|
28379
|
+
const altText = r.vote.alternative ? ` [proposed unlisted alternative: ${r.vote.alternative}]` : "";
|
|
28380
|
+
summaries.push(`- ${r.key} picked ${choiceText}, confidence ${r.vote.confidence.toFixed(2)}, reasoning: ${r.vote.reasoning}${gapText}${altText}`);
|
|
28314
28381
|
} else summaries.push(`- ${r.key} did not return a valid round-1 vote (${r.vote.error}).`);
|
|
28315
28382
|
return base + "\n" + summaries.join("\n");
|
|
28316
28383
|
}
|
|
28317
28384
|
function isVote(v) {
|
|
28318
28385
|
return !("error" in v);
|
|
28319
28386
|
}
|
|
28387
|
+
function gapAbstainVerdict(successful, r1, r2) {
|
|
28388
|
+
const gapVotes = successful.filter((r) => r.vote.choice === null && r.vote.needMoreInfo);
|
|
28389
|
+
if (gapVotes.length < 2) return null;
|
|
28390
|
+
const gaps = gapVotes.map((r) => `- ${r.key}: ${r.vote.needMoreInfo}`).join("\n");
|
|
28391
|
+
const header = gapVotes.length === STAND_IN_MODELS.length ? "All three models reported they need more context to decide:" : `${gapVotes.length} of 3 models reported they need more context to decide:`;
|
|
28392
|
+
return withDerivedNotes({
|
|
28393
|
+
verdict: "need_more_info",
|
|
28394
|
+
recommendation: null,
|
|
28395
|
+
confidence: 0,
|
|
28396
|
+
votes: voteRecord(r1, r2),
|
|
28397
|
+
notes: `${header}\n${gaps}`
|
|
28398
|
+
}, r1, r2);
|
|
28399
|
+
}
|
|
28400
|
+
/**
|
|
28401
|
+
* Freshest parsed vote per model (round 2 if it parsed, else round 1) — the
|
|
28402
|
+
* basis for deriving alternative / gap notes without double-counting a model
|
|
28403
|
+
* across rounds.
|
|
28404
|
+
*/
|
|
28405
|
+
function freshestVotes(r1, r2) {
|
|
28406
|
+
const out = [];
|
|
28407
|
+
for (const cfg of STAND_IN_MODELS) {
|
|
28408
|
+
const r2Entry = r2?.find((r) => r.key === cfg.key);
|
|
28409
|
+
const r1Entry = r1.find((r) => r.key === cfg.key);
|
|
28410
|
+
const vote = r2Entry && isVote(r2Entry.vote) ? r2Entry.vote : r1Entry && isVote(r1Entry.vote) ? r1Entry.vote : null;
|
|
28411
|
+
if (vote) out.push({
|
|
28412
|
+
key: cfg.key,
|
|
28413
|
+
vote
|
|
28414
|
+
});
|
|
28415
|
+
}
|
|
28416
|
+
return out;
|
|
28417
|
+
}
|
|
28418
|
+
/**
|
|
28419
|
+
* Append derived notes to a verdict WITHOUT touching the verdict / tally:
|
|
28420
|
+
* - panel-proposed unlisted `alternative`s (surfaced on every verdict);
|
|
28421
|
+
* - partial missing-context gaps (surfaced only on no_consensus — the
|
|
28422
|
+
* dedicated need_more_info path already lists its own gaps).
|
|
28423
|
+
* Purely additive to `notes`; never changes verdict / recommendation / isError.
|
|
28424
|
+
* This is what lets the alternative + partial-gap signals ride along while the
|
|
28425
|
+
* abstain and blind-R1 invariants stay untouched.
|
|
28426
|
+
*/
|
|
28427
|
+
function withDerivedNotes(result, r1, r2) {
|
|
28428
|
+
const fresh = freshestVotes(r1, r2);
|
|
28429
|
+
const extras = [];
|
|
28430
|
+
const alts = fresh.filter((v) => v.vote.alternative);
|
|
28431
|
+
if (alts.length > 0) extras.push("The panel also flagged unlisted option(s):\n" + alts.map((v) => `- ${v.key}: ${v.vote.alternative}`).join("\n"));
|
|
28432
|
+
if (result.verdict === "no_consensus") {
|
|
28433
|
+
const gaps = fresh.filter((v) => v.vote.choice === null && v.vote.needMoreInfo);
|
|
28434
|
+
if (gaps.length > 0) extras.push("Some models cited missing context:\n" + gaps.map((v) => `- ${v.key}: ${v.vote.needMoreInfo}`).join("\n"));
|
|
28435
|
+
}
|
|
28436
|
+
if (extras.length === 0) return result;
|
|
28437
|
+
const notes = [result.notes, ...extras].filter(Boolean).join("\n\n");
|
|
28438
|
+
return {
|
|
28439
|
+
...result,
|
|
28440
|
+
notes
|
|
28441
|
+
};
|
|
28442
|
+
}
|
|
28320
28443
|
function voteRecord(r1, r2) {
|
|
28321
28444
|
const record = {};
|
|
28322
28445
|
for (const cfg of STAND_IN_MODELS) {
|
|
@@ -31289,10 +31412,14 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31289
31412
|
toolNameHttp: "stand_in",
|
|
31290
31413
|
group: "decide",
|
|
31291
31414
|
capability: "stand_in",
|
|
31292
|
-
description: "Three-lab away-mode decision tiebreak advisor for moments when the user is unavailable and the agent is stuck between two or more concrete options. It polls gpt-5.6-sol, Opus 4.7, and gemini-3.1-pro-preview across blind and informed voting rounds, then returns a ranked-choice verdict such as consensus, majority, no_consensus, or need_more_info. Use when work would otherwise halt on a bounded choice the user would normally make. Not for code review, open-ended exploration, single-model second opinions, or bypassing confirmation on irreversible actions such as push, delete, drop, or deploy; use peer-review-coordinator or the individual critics for review and still ask the user for destructive actions.",
|
|
31415
|
+
description: "Three-lab away-mode decision tiebreak advisor for moments when the user is unavailable and the agent is stuck between two or more concrete options. It polls gpt-5.6-sol, Opus 4.7, and gemini-3.1-pro-preview across blind and informed voting rounds, then returns a ranked-choice verdict such as consensus, majority, no_consensus, or need_more_info. Use when work would otherwise halt on a bounded choice the user would normally make. If every provided option is inadequate, the panel may flag a concrete better unlisted option in `notes` so you can re-invoke with a revised set. The three panel models are cold-start — no repo, transcript, or memory access — and see only your decision, options, and context, so the `context` argument must carry all the background they need to judge. Not for code review, open-ended exploration, single-model second opinions, or bypassing confirmation on irreversible actions such as push, delete, drop, or deploy; use peer-review-coordinator or the individual critics for review and still ask the user for destructive actions.",
|
|
31293
31416
|
inputSchema: {
|
|
31294
31417
|
type: "object",
|
|
31295
|
-
required: [
|
|
31418
|
+
required: [
|
|
31419
|
+
"decision",
|
|
31420
|
+
"options",
|
|
31421
|
+
"context"
|
|
31422
|
+
],
|
|
31296
31423
|
additionalProperties: false,
|
|
31297
31424
|
properties: {
|
|
31298
31425
|
decision: {
|
|
@@ -31303,7 +31430,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31303
31430
|
type: "array",
|
|
31304
31431
|
minItems: 2,
|
|
31305
31432
|
maxItems: 6,
|
|
31306
|
-
description: "2-6 concrete options for the panel to vote on.
|
|
31433
|
+
description: "2-6 concrete options curated by the caller for the panel to vote on. The panel may surface a gated unlisted alternative in `notes`, but does not replace the caller's option set. The verdict cites the chosen option by `id`.",
|
|
31307
31434
|
items: {
|
|
31308
31435
|
type: "object",
|
|
31309
31436
|
required: ["id", "summary"],
|
|
@@ -31326,7 +31453,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
|
|
|
31326
31453
|
},
|
|
31327
31454
|
context: {
|
|
31328
31455
|
type: "string",
|
|
31329
|
-
description: "
|
|
31456
|
+
description: "REQUIRED. The three panel models are cold-start: no access to your repository, prior transcript, or memory — they see ONLY this decision + options + context. Include everything needed to decide well: the constraints that matter, the relevant code or excerpts, prior decisions not to relitigate, and what a good outcome looks like. Thin context yields a weak verdict. (On the JSON transport a ~32KB size guard applies; the SSE transport has no such limit.)"
|
|
31330
31457
|
}
|
|
31331
31458
|
}
|
|
31332
31459
|
},
|
|
@@ -31370,8 +31497,9 @@ function assertMcpToolSurfaceConsistent() {
|
|
|
31370
31497
|
/**
|
|
31371
31498
|
* Shared closure body for the two worker MCP tools. Validates the
|
|
31372
31499
|
* minimal arg shape (prompt required + optional knobs typed), then
|
|
31373
|
-
* forwards to `runWorkerAgent`. `workspace` defaults
|
|
31374
|
-
* launch cwd;
|
|
31500
|
+
* forwards to `runWorkerAgent`. Outside serve mode, `workspace` defaults
|
|
31501
|
+
* to the proxy's launch cwd; serve mode requires an explicit/header-derived
|
|
31502
|
+
* workspace. Callers can override via the optional `workspace` arg
|
|
31375
31503
|
* (absolute paths only — enforced here). The engine performs every
|
|
31376
31504
|
* deeper validation (model existence, thinking clamp, worktree
|
|
31377
31505
|
* provisioning, semaphore acquisition, workspace realpath +
|
|
@@ -31433,7 +31561,7 @@ async function runWorkerToolCall(call) {
|
|
|
31433
31561
|
};
|
|
31434
31562
|
worktree = args.worktree;
|
|
31435
31563
|
}
|
|
31436
|
-
let workspace
|
|
31564
|
+
let workspace;
|
|
31437
31565
|
if (args.workspace !== void 0) {
|
|
31438
31566
|
if (typeof args.workspace !== "string" || args.workspace.length === 0) return {
|
|
31439
31567
|
content: [{
|
|
@@ -31450,7 +31578,14 @@ async function runWorkerToolCall(call) {
|
|
|
31450
31578
|
isError: true
|
|
31451
31579
|
};
|
|
31452
31580
|
workspace = args.workspace;
|
|
31453
|
-
}
|
|
31581
|
+
} else if (state.serveMode) return {
|
|
31582
|
+
content: [{
|
|
31583
|
+
type: "text",
|
|
31584
|
+
text: `worker_${mode}: a workspace is required. This is a machine-wide github-router serve; pass the absolute path of the project you are working in as \`workspace\`.`
|
|
31585
|
+
}],
|
|
31586
|
+
isError: true
|
|
31587
|
+
};
|
|
31588
|
+
else workspace = process.cwd();
|
|
31454
31589
|
let maxWallClockMs;
|
|
31455
31590
|
let clampNote = "";
|
|
31456
31591
|
if (args.maxWallClockMs !== void 0) {
|
|
@@ -31663,11 +31798,11 @@ async function runStandInToolCall(args, signal) {
|
|
|
31663
31798
|
detail
|
|
31664
31799
|
});
|
|
31665
31800
|
}
|
|
31666
|
-
const context =
|
|
31667
|
-
if (context
|
|
31801
|
+
const context = typeof args.context === "string" ? args.context : "";
|
|
31802
|
+
if (!context.trim()) return {
|
|
31668
31803
|
content: [{
|
|
31669
31804
|
type: "text",
|
|
31670
|
-
text: "stand_in: arguments.context
|
|
31805
|
+
text: "stand_in: arguments.context is required (non-empty string). The panel is cold-start and sees only decision + options + context; include the constraints, relevant code, and success criteria needed to decide."
|
|
31671
31806
|
}],
|
|
31672
31807
|
isError: true
|
|
31673
31808
|
};
|
|
@@ -31681,7 +31816,34 @@ async function runStandInToolCall(args, signal) {
|
|
|
31681
31816
|
text: JSON.stringify(result)
|
|
31682
31817
|
}] };
|
|
31683
31818
|
}
|
|
31819
|
+
/**
|
|
31820
|
+
* Every exact `mcp__<key>__<tool>` name github-router injects, for the given
|
|
31821
|
+
* resolved group keys (`peers`/`search`/… → their collision-resolved mcpServers
|
|
31822
|
+
* key). A SUPERSET — it ignores per-tool capability gates because an allow-list
|
|
31823
|
+
* entry for a tool that isn't actually served is inert, which keeps it correct
|
|
31824
|
+
* as gates change and as tools are added.
|
|
31825
|
+
*
|
|
31826
|
+
* Used to seed CloudCLI's `localStorage['claude-settings'].allowedTools` so its
|
|
31827
|
+
* Agent-SDK `canUseTool` auto-approves our MCP tools in PLAN mode. `canUseTool`
|
|
31828
|
+
* does EXACT tool-name matching (no `mcp__<server>` wildcard — see
|
|
31829
|
+
* `matchesToolPermission` in CloudCLI's `claude-sdk.js`), so bare `mcp__peers`
|
|
31830
|
+
* would NOT cover `mcp__peers__gemini_critic`; the exact names are required.
|
|
31831
|
+
* This is the ONLY lever for plan mode: bypass mode skips `canUseTool`, and the
|
|
31832
|
+
* mirror `settings.json permissions.allow` is NOT consulted by `canUseTool`
|
|
31833
|
+
* (which reads `sdkOptions.allowedTools`, seeded from this localStorage key).
|
|
31834
|
+
*/
|
|
31835
|
+
function enumerateInjectedMcpToolNames(groupKeys, opts = {}) {
|
|
31836
|
+
const names = [];
|
|
31837
|
+
const peersKey = groupKeys.peers;
|
|
31838
|
+
if (peersKey) for (const p of [...PERSONAS_READ, ...PERSONAS_WRITE]) names.push(`mcp__${peersKey}__${p.toolNameHttp}`);
|
|
31839
|
+
for (const t of NON_PERSONA_MCP_TOOLS) {
|
|
31840
|
+
const key = groupKeys[t.group];
|
|
31841
|
+
if (key) names.push(`mcp__${key}__${t.toolNameHttp}`);
|
|
31842
|
+
}
|
|
31843
|
+
if (opts.codexCli) names.push("mcp__codex-cli__codex");
|
|
31844
|
+
return [...new Set(names)];
|
|
31845
|
+
}
|
|
31684
31846
|
|
|
31685
31847
|
//#endregion
|
|
31686
|
-
export {
|
|
31687
|
-
//# sourceMappingURL=peer-mcp-personas-
|
|
31848
|
+
export { buildAdvisorStream as $, setupCopilotToken as $t, trustRepo as A, readResponseBodyCapped as At, runWorkerAgent as B, buildWorkspaceHeaderJson as Bt, fileLastPromptStore as C, getTokenCount as Ct, repoRoot as D, createResponses as Dt, repoFingerprint as E, pickEndpoint as Et, EXPLORE_DEFAULT_MODEL as F, extractTarGzMember as Ft, toolbeltEnabled as G, DEFAULT_CODEX_MODEL_FALLBACKS as Gt, buildEnv as H, toolbeltPathOverride as Ht, IMPLEMENT_DEFAULT_MODEL as I, extractZipMember as It, TOOLBELT_TOOLS$1 as J, UPSTREAM_INACTIVITY_TIMEOUT_MS as Jt, toolbeltSkipSet as K, DEFAULT_PORT as Kt, PLAN_DEFAULT_MODEL as L, shouldUseInsecureTls as Lt, resolveSealedGate as M, provisionBrowserAssets as Mt, BROWSE_DEFAULT_MODEL as N, hasSupportedBrowserInstalled as Nt, stopGateEnabledForRepo as O, createChatCompletions as Ot, DEFAULT_MODEL as P, provisionAndIndexColbert as Pt, ADVISOR_TOOL_INSTRUCTIONS as Q, withInstallLock as Qt, REVIEW_DEFAULT_MODEL as R, ArtifactClient as Rt, fileFindingsStore as S, createMessages as St, isSubagentContext as T, resolveMcpToolTimeoutMs as Tt, availableToolCommands as U, DEFAULT_CLAUDE_MODEL_FALLBACKS as Ut, withNoOutputRetry as V, collapsePathKeys as Vt, buildToolbeltAwareness as W, DEFAULT_CODEX_MODEL as Wt, searchWeb as X, pickClaudeDefault as Xt, assetFor as Y, generateRandomPort as Yt, ADVISOR_INTERNAL_TOOL_NAME as Z, getPackageVersion as Zt, stopGateDisabled as _, copilotBaseUrl as _n, nativeSubagentModel as _t, buildPeerAwarenessSnippet as a, cacheVSCodeVersion as an, logStreamError as at, stopReviewEnabled as b, state as bn, shimDefaultsToXhigh as bt, personasFor as c, resolveCodexModel as cn, handleMcpDelete as ct, buildStopHookCommand as d, getModels as dn, artifactToolsEnabled as dt, setupGitHubAgentToken as en, injectAdvisorTool as et, captureLaunchBaseline as f, getGitHubUser as fn, browseAgentEnabled as ft, launchBaselineKey as g, GITHUB_API_BASE_URL as gn, geminiAvailable as gt, injectStopHookIntoSettingsFile as h, forwardError as hn, fleetToolsEnabled as ht, buildAgentPrompt as i, cacheModels as in, isControllerClosedError as it, liveExec as j, parseJsonOrDiagnose as jt, stopReviewStateDir as k, MAX_RESPONSE_BODY_BYTES as kt, buildArtifactOpenHookCommand as l, resolveModel as ln, handleMcpPost as lt, fileBlockBudget as m, HTTPError as mn, browserToolsEnabled as mt, MCP_GROUPS as n, tryRefreshAndRetry as nn, buildAnthropicErrorEvent as nt, buildPeerAwarenessSummary as o, filterBetaHeader as on, readIteratorWithTimeout as ot, decideStopHook as p, fetchWithTransientRetry as pn, browserCompoundToolsEnabled as pt, vscodeRipgrepPath as q, UPSTREAM_FETCH_TIMEOUT_MS as qt, assertMcpToolSurfaceConsistent as r, cacheCopilotVersion as rn, buildOpenAIErrorEvent as rt, enumerateInjectedMcpToolNames as s, isNullish as sn, relayAnthropicStream as st, GROUP_META as t, setupGitHubToken as tn, isAdvisorRequested as tt, buildSessionBindHookCommand as u, sleep as un, agentToolsEnabled as ut, stopGateId as v, copilotHeaders as vn, standInToolEnabled as vt, fileReviewDebounce as w, assembleResponsesPayload as wt, fileBaselineStore as x, countTokens as xt, stopGatePlanMode as y, githubHeaders as yn, workerToolsEnabled as yt, appendPlanReminder as z, buildWorkspaceHeaderHelperCommand as zt };
|
|
31849
|
+
//# sourceMappingURL=peer-mcp-personas-rik0SIrg.js.map
|