github-router 0.3.177 → 0.3.178

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.
@@ -12076,6 +12076,14 @@ var TreeSitterPool = class {
12076
12076
  * entirely and force the in-process path, rather than churning spawn→crash
12077
12077
  * forever. */
12078
12078
  crashCount = 0;
12079
+ workersSpawned = 0;
12080
+ /** Test-only observability for crash/respawn assertions. */
12081
+ __workerLifecycleForTest() {
12082
+ return {
12083
+ crashes: this.crashCount,
12084
+ spawned: this.workersSpawned
12085
+ };
12086
+ }
12079
12087
  queue = [];
12080
12088
  inflight = /* @__PURE__ */ new Map();
12081
12089
  constructor() {
@@ -12123,6 +12131,7 @@ var TreeSitterPool = class {
12123
12131
  let worker;
12124
12132
  try {
12125
12133
  worker = new Worker(this.workerPath);
12134
+ this.workersSpawned += 1;
12126
12135
  } catch (err) {
12127
12136
  consola.debug(`[code_search] tree-sitter worker spawn failed: ${err.message}`);
12128
12137
  resolve(null);
@@ -12225,8 +12234,14 @@ var TreeSitterPool = class {
12225
12234
  if (!job) break;
12226
12235
  pw.busyJobId = job.id;
12227
12236
  this.inflight.set(job.id, job);
12237
+ const injectCrash = _testCrashOnceArmed;
12238
+ const reqToPost = injectCrash ? {
12239
+ ...job.req,
12240
+ testCrash: true
12241
+ } : job.req;
12228
12242
  try {
12229
- pw.worker.postMessage(job.req);
12243
+ pw.worker.postMessage(reqToPost);
12244
+ if (injectCrash) _testCrashOnceArmed = false;
12230
12245
  } catch (err) {
12231
12246
  consola.debug(`[code_search] tree-sitter worker postMessage failed: ${err.message}`);
12232
12247
  this.inflight.delete(job.id);
@@ -12398,6 +12413,7 @@ function resolveWorkerPath() {
12398
12413
  }
12399
12414
  let _pool = null;
12400
12415
  let _shutdownRegistered = false;
12416
+ let _testCrashOnceArmed = false;
12401
12417
  /**
12402
12418
  * The pool is ON by default for real (non-CI) runs and OFF under CI.
12403
12419
  *
@@ -12650,6 +12666,10 @@ const WALL_TIME_MS = 3e4;
12650
12666
  * comfortable headroom for ~5-10 files even on cold cache.
12651
12667
  */
12652
12668
  const STRUCTURAL_BUDGET_MS = 200;
12669
+ let _structuralBudgetTestOverride = null;
12670
+ function structuralBudgetMs() {
12671
+ return _structuralBudgetTestOverride ?? STRUCTURAL_BUDGET_MS;
12672
+ }
12653
12673
  const STRUCTURAL_TOPN_FULL = 50;
12654
12674
  const STRUCTURAL_TOPN_FAST = 10;
12655
12675
  /**
@@ -13957,7 +13977,7 @@ async function searchCode(rawInput, externalSignal) {
13957
13977
  })).filter((e) => e.index >= 0),
13958
13978
  workspaceRoot: ws.canonical,
13959
13979
  topN,
13960
- budgetMs: STRUCTURAL_BUDGET_MS,
13980
+ budgetMs: structuralBudgetMs(),
13961
13981
  signal: ac.signal
13962
13982
  });
13963
13983
  structuralOutlines = structural.outlinesByFile;
@@ -24084,9 +24104,9 @@ function jsonPathPreflightCap(body, scope) {
24084
24104
  const decision = typeof args.decision === "string" ? args.decision : "";
24085
24105
  const optionsRaw = Array.isArray(args.options) ? args.options : [];
24086
24106
  const standInContext = typeof args.context === "string" ? args.context : "";
24087
- if (!decision || optionsRaw.length === 0) return void 0;
24107
+ if (!decision || optionsRaw.length === 0 || !standInContext.trim()) return void 0;
24088
24108
  const briefBytes$1 = Buffer.byteLength(decision + JSON.stringify(optionsRaw) + standInContext, "utf8");
24089
- const STAND_IN_CAP_BYTES = 6 * 1024;
24109
+ const STAND_IN_CAP_BYTES = 32 * 1024;
24090
24110
  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
24111
  return;
24092
24112
  }
@@ -28039,12 +28059,15 @@ Respond with ONLY a single JSON object — no prose, no markdown fences, no prea
28039
28059
  "choice": "<option.id>" | null,
28040
28060
  "confidence": <number between 0.0 and 1.0>,
28041
28061
  "reasoning": "<one short sentence>",
28042
- "need_more_info": "<what context is missing, if you cannot decide>"
28062
+ "need_more_info": "<what context is missing, if you cannot decide>",
28063
+ "alternative": "<a concrete unlisted option — ONLY if every provided option is inadequate>"
28043
28064
  }
28044
28065
 
28045
28066
  Calibration rules:
28046
28067
  - "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
28068
  - 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.
28069
+ - 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.
28070
+ - 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
28071
  - One sentence of reasoning. Not a paragraph.
28049
28072
  - 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
28073
 
@@ -28057,16 +28080,19 @@ Same JSON schema as round 1:
28057
28080
  "choice": "<option.id>" | null,
28058
28081
  "confidence": <number between 0.0 and 1.0>,
28059
28082
  "reasoning": "<one short sentence>",
28060
- "need_more_info": "<gap, if any>"
28083
+ "need_more_info": "<gap, if any>",
28084
+ "alternative": "<a concrete unlisted option, if every provided option is inadequate>"
28061
28085
  }
28062
28086
 
28063
28087
  Calibration rules:
28064
28088
  - 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
28089
  - 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
28090
  - If round 1 left you genuinely uncertain and peer reasoning hasn't resolved it, "choice": null is still the honest answer.
28091
+ - 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.
28092
+ - 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
28093
 
28068
28094
  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>"}`;
28095
+ 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
28096
  /**
28071
28097
  * Run the two-round stand-in protocol. Returns a structured verdict
28072
28098
  * envelope. Throws only on systemic failure (e.g., all three upstream
@@ -28074,71 +28100,66 @@ const RETRY_PROMPT_SUFFIX = `\n\nYour previous response was not valid JSON match
28074
28100
  * `VoteFailure` entries in the result.
28075
28101
  */
28076
28102
  async function runStandIn(input, signal) {
28103
+ const validIds = new Set(input.options.map((o) => o.id));
28077
28104
  const r1UserText = buildRound1UserText(input);
28078
- const r1 = await Promise.all(STAND_IN_MODELS.map((cfg) => callAndParse(cfg, SYSTEM_PROMPT_R1, r1UserText, signal)));
28105
+ const r1 = await Promise.all(STAND_IN_MODELS.map((cfg) => callAndParse(cfg, SYSTEM_PROMPT_R1, r1UserText, validIds, signal)));
28079
28106
  const successfulR1 = r1.filter((r) => isVote(r.vote));
28080
- if (successfulR1.length === STAND_IN_MODELS.length && successfulR1.every((r) => r.vote.needMoreInfo && r.vote.choice === null)) {
28081
- const gaps = successfulR1.map((r) => `- ${r.key}: ${r.vote.needMoreInfo}`).join("\n");
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
- }
28107
+ const nmiR1 = gapAbstainVerdict(successfulR1, r1, null);
28108
+ if (nmiR1) return nmiR1;
28090
28109
  const r1Decision = aggregateVotes(successfulR1);
28091
- if (r1Decision.verdict === "consensus" && r1Decision.meanConfidence >= .8) return {
28110
+ if (r1Decision.verdict === "consensus" && r1Decision.meanConfidence >= .8) return withDerivedNotes({
28092
28111
  verdict: "consensus",
28093
28112
  recommendation: r1Decision.winner,
28094
28113
  confidence: round2(r1Decision.meanConfidence),
28095
28114
  votes: voteRecord(r1, null),
28096
28115
  notes: `All three models picked ${r1Decision.winner} in round 1 with high confidence (skipped round 2).`
28097
- };
28098
- if (successfulR1.length < 2) return {
28116
+ }, r1, null);
28117
+ if (successfulR1.length < 2) return withDerivedNotes({
28099
28118
  verdict: "no_consensus",
28100
28119
  recommendation: null,
28101
28120
  confidence: 0,
28102
28121
  votes: voteRecord(r1, null),
28103
28122
  notes: `Only ${successfulR1.length} of 3 models returned a parseable round-1 vote; insufficient signal to run round 2.`
28104
- };
28123
+ }, r1, null);
28105
28124
  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)));
28125
+ 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
28126
  const successfulR2 = r2.filter((r) => isVote(r.vote));
28108
- if (successfulR2.length < 2) return {
28127
+ if (successfulR2.length < 2) return withDerivedNotes({
28109
28128
  verdict: "no_consensus",
28110
28129
  recommendation: null,
28111
28130
  confidence: 0,
28112
28131
  votes: voteRecord(r1, r2),
28113
28132
  notes: `Only ${successfulR2.length} of 3 models returned a parseable round-2 vote; deferring to user.`
28114
- };
28133
+ }, r1, r2);
28134
+ const nmiR2 = gapAbstainVerdict(successfulR2, r1, r2);
28135
+ if (nmiR2) return nmiR2;
28115
28136
  const r2Decision = aggregateVotes(successfulR2);
28116
- if (r2Decision.verdict === "consensus") return {
28137
+ if (r2Decision.verdict === "consensus") return withDerivedNotes({
28117
28138
  verdict: "consensus",
28118
28139
  recommendation: r2Decision.winner,
28119
28140
  confidence: round2(r2Decision.meanConfidence),
28120
28141
  votes: voteRecord(r1, r2),
28121
28142
  notes: `All three models picked ${r2Decision.winner} in round 2.`
28122
- };
28143
+ }, r1, r2);
28123
28144
  if (r2Decision.verdict === "majority") {
28124
28145
  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 {
28146
+ return withDerivedNotes({
28126
28147
  verdict: "majority",
28127
28148
  recommendation: r2Decision.winner,
28128
28149
  confidence: round2(r2Decision.meanConfidence),
28129
28150
  votes: voteRecord(r1, r2),
28130
28151
  notes: `Majority (2 of 3) picked ${r2Decision.winner}. Dissent: ${dissenters}.`
28131
- };
28152
+ }, r1, r2);
28132
28153
  }
28133
- return {
28154
+ return withDerivedNotes({
28134
28155
  verdict: "no_consensus",
28135
28156
  recommendation: null,
28136
28157
  confidence: 0,
28137
28158
  votes: voteRecord(r1, r2),
28138
28159
  notes: `Models did not converge in round 2 (votes split). Defer to user.`
28139
- };
28160
+ }, r1, r2);
28140
28161
  }
28141
- async function callAndParse(cfg, instructions, userText, signal) {
28162
+ async function callAndParse(cfg, instructions, userText, validIds, signal) {
28142
28163
  const model = cfg.key === "gpt-5.6-sol" ? resolveOpenAiFrontier() ?? cfg.model : cfg.model;
28143
28164
  let raw;
28144
28165
  try {
@@ -28159,7 +28180,7 @@ async function callAndParse(cfg, instructions, userText, signal) {
28159
28180
  }
28160
28181
  };
28161
28182
  }
28162
- const first = tryParseVote(raw);
28183
+ const first = tryParseVote(raw, validIds);
28163
28184
  if (first.ok) return {
28164
28185
  key: cfg.key,
28165
28186
  vote: first.vote
@@ -28183,7 +28204,7 @@ async function callAndParse(cfg, instructions, userText, signal) {
28183
28204
  }
28184
28205
  };
28185
28206
  }
28186
- const second = tryParseVote(retryRaw);
28207
+ const second = tryParseVote(retryRaw, validIds);
28187
28208
  if (second.ok) return {
28188
28209
  key: cfg.key,
28189
28210
  vote: second.vote
@@ -28197,7 +28218,7 @@ async function callAndParse(cfg, instructions, userText, signal) {
28197
28218
  }
28198
28219
  };
28199
28220
  }
28200
- function tryParseVote(raw) {
28221
+ function tryParseVote(raw, validIds) {
28201
28222
  if (!raw || !raw.trim()) return {
28202
28223
  ok: false,
28203
28224
  error: "empty response"
@@ -28225,11 +28246,16 @@ function tryParseVote(raw) {
28225
28246
  error: "parsed value is not an object"
28226
28247
  };
28227
28248
  const obj = parsed;
28228
- const choice = obj.choice === null ? null : typeof obj.choice === "string" && obj.choice.length > 0 ? obj.choice : void 0;
28229
- if (choice === void 0) return {
28249
+ const rawChoice = obj.choice === null ? null : typeof obj.choice === "string" && obj.choice.length > 0 ? obj.choice : void 0;
28250
+ if (rawChoice === void 0) return {
28230
28251
  ok: false,
28231
28252
  error: "missing or invalid 'choice' field (string or null required)"
28232
28253
  };
28254
+ if (rawChoice !== null && !validIds.has(rawChoice)) return {
28255
+ ok: false,
28256
+ error: "'choice' must be one of the provided option ids or null"
28257
+ };
28258
+ const choice = rawChoice;
28233
28259
  const confidenceRaw = obj.confidence;
28234
28260
  const confidence = typeof confidenceRaw === "number" && Number.isFinite(confidenceRaw) ? Math.max(0, Math.min(1, confidenceRaw)) : void 0;
28235
28261
  if (confidence === void 0) return {
@@ -28241,13 +28267,15 @@ function tryParseVote(raw) {
28241
28267
  ok: false,
28242
28268
  error: "missing or empty 'reasoning' field"
28243
28269
  };
28270
+ 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
28271
  return {
28245
28272
  ok: true,
28246
28273
  vote: {
28247
28274
  choice,
28248
28275
  confidence,
28249
28276
  reasoning,
28250
- needMoreInfo: typeof obj.need_more_info === "string" && obj.need_more_info.length > 0 ? obj.need_more_info : void 0
28277
+ needMoreInfo,
28278
+ alternative: choice === null && !needMoreInfo && typeof obj.alternative === "string" && obj.alternative.trim().length > 0 ? obj.alternative.trim() : void 0
28251
28279
  }
28252
28280
  };
28253
28281
  }
@@ -28310,13 +28338,70 @@ function buildRound2UserTextBase(input, r1) {
28310
28338
  for (const r of r1) if (isVote(r.vote)) {
28311
28339
  const choiceText = r.vote.choice === null ? "abstain" : r.vote.choice;
28312
28340
  const gapText = r.vote.needMoreInfo ? ` (needs: ${r.vote.needMoreInfo})` : "";
28313
- summaries.push(`- ${r.key} picked ${choiceText}, confidence ${r.vote.confidence.toFixed(2)}, reasoning: ${r.vote.reasoning}${gapText}`);
28341
+ const altText = r.vote.alternative ? ` [proposed unlisted alternative: ${r.vote.alternative}]` : "";
28342
+ summaries.push(`- ${r.key} picked ${choiceText}, confidence ${r.vote.confidence.toFixed(2)}, reasoning: ${r.vote.reasoning}${gapText}${altText}`);
28314
28343
  } else summaries.push(`- ${r.key} did not return a valid round-1 vote (${r.vote.error}).`);
28315
28344
  return base + "\n" + summaries.join("\n");
28316
28345
  }
28317
28346
  function isVote(v) {
28318
28347
  return !("error" in v);
28319
28348
  }
28349
+ function gapAbstainVerdict(successful, r1, r2) {
28350
+ const gapVotes = successful.filter((r) => r.vote.choice === null && r.vote.needMoreInfo);
28351
+ if (gapVotes.length < 2) return null;
28352
+ const gaps = gapVotes.map((r) => `- ${r.key}: ${r.vote.needMoreInfo}`).join("\n");
28353
+ 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:`;
28354
+ return withDerivedNotes({
28355
+ verdict: "need_more_info",
28356
+ recommendation: null,
28357
+ confidence: 0,
28358
+ votes: voteRecord(r1, r2),
28359
+ notes: `${header}\n${gaps}`
28360
+ }, r1, r2);
28361
+ }
28362
+ /**
28363
+ * Freshest parsed vote per model (round 2 if it parsed, else round 1) — the
28364
+ * basis for deriving alternative / gap notes without double-counting a model
28365
+ * across rounds.
28366
+ */
28367
+ function freshestVotes(r1, r2) {
28368
+ const out = [];
28369
+ for (const cfg of STAND_IN_MODELS) {
28370
+ const r2Entry = r2?.find((r) => r.key === cfg.key);
28371
+ const r1Entry = r1.find((r) => r.key === cfg.key);
28372
+ const vote = r2Entry && isVote(r2Entry.vote) ? r2Entry.vote : r1Entry && isVote(r1Entry.vote) ? r1Entry.vote : null;
28373
+ if (vote) out.push({
28374
+ key: cfg.key,
28375
+ vote
28376
+ });
28377
+ }
28378
+ return out;
28379
+ }
28380
+ /**
28381
+ * Append derived notes to a verdict WITHOUT touching the verdict / tally:
28382
+ * - panel-proposed unlisted `alternative`s (surfaced on every verdict);
28383
+ * - partial missing-context gaps (surfaced only on no_consensus — the
28384
+ * dedicated need_more_info path already lists its own gaps).
28385
+ * Purely additive to `notes`; never changes verdict / recommendation / isError.
28386
+ * This is what lets the alternative + partial-gap signals ride along while the
28387
+ * abstain and blind-R1 invariants stay untouched.
28388
+ */
28389
+ function withDerivedNotes(result, r1, r2) {
28390
+ const fresh = freshestVotes(r1, r2);
28391
+ const extras = [];
28392
+ const alts = fresh.filter((v) => v.vote.alternative);
28393
+ if (alts.length > 0) extras.push("The panel also flagged unlisted option(s):\n" + alts.map((v) => `- ${v.key}: ${v.vote.alternative}`).join("\n"));
28394
+ if (result.verdict === "no_consensus") {
28395
+ const gaps = fresh.filter((v) => v.vote.choice === null && v.vote.needMoreInfo);
28396
+ if (gaps.length > 0) extras.push("Some models cited missing context:\n" + gaps.map((v) => `- ${v.key}: ${v.vote.needMoreInfo}`).join("\n"));
28397
+ }
28398
+ if (extras.length === 0) return result;
28399
+ const notes = [result.notes, ...extras].filter(Boolean).join("\n\n");
28400
+ return {
28401
+ ...result,
28402
+ notes
28403
+ };
28404
+ }
28320
28405
  function voteRecord(r1, r2) {
28321
28406
  const record = {};
28322
28407
  for (const cfg of STAND_IN_MODELS) {
@@ -31289,10 +31374,14 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
31289
31374
  toolNameHttp: "stand_in",
31290
31375
  group: "decide",
31291
31376
  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.",
31377
+ 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
31378
  inputSchema: {
31294
31379
  type: "object",
31295
- required: ["decision", "options"],
31380
+ required: [
31381
+ "decision",
31382
+ "options",
31383
+ "context"
31384
+ ],
31296
31385
  additionalProperties: false,
31297
31386
  properties: {
31298
31387
  decision: {
@@ -31303,7 +31392,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
31303
31392
  type: "array",
31304
31393
  minItems: 2,
31305
31394
  maxItems: 6,
31306
- description: "2-6 concrete options for the panel to vote on. Caller-provided do NOT ask the panel to generate options. The verdict cites the chosen option by `id`.",
31395
+ 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
31396
  items: {
31308
31397
  type: "object",
31309
31398
  required: ["id", "summary"],
@@ -31326,7 +31415,7 @@ const NON_PERSONA_MCP_TOOLS = Object.freeze([
31326
31415
  },
31327
31416
  context: {
31328
31417
  type: "string",
31329
- description: "Task / code background that informs the decision. Keep tight the input is capped at ~6KB total across decision + options + context."
31418
+ 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
31419
  }
31331
31420
  }
31332
31421
  },
@@ -31663,11 +31752,11 @@ async function runStandInToolCall(args, signal) {
31663
31752
  detail
31664
31753
  });
31665
31754
  }
31666
- const context = args.context === void 0 ? void 0 : typeof args.context === "string" ? args.context : null;
31667
- if (context === null) return {
31755
+ const context = typeof args.context === "string" ? args.context : "";
31756
+ if (!context.trim()) return {
31668
31757
  content: [{
31669
31758
  type: "text",
31670
- text: "stand_in: arguments.context must be a string when provided"
31759
+ 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
31760
  }],
31672
31761
  isError: true
31673
31762
  };
@@ -31684,4 +31773,4 @@ async function runStandInToolCall(args, signal) {
31684
31773
 
31685
31774
  //#endregion
31686
31775
  export { isAdvisorRequested as $, cacheCopilotVersion as $t, liveExec as A, provisionBrowserAssets as At, withNoOutputRetry as B, DEFAULT_CODEX_MODEL as Bt, fileReviewDebounce as C, resolveMcpToolTimeoutMs as Ct, stopGateEnabledForRepo as D, MAX_RESPONSE_BODY_BYTES as Dt, repoRoot as E, createChatCompletions as Et, IMPLEMENT_DEFAULT_MODEL as F, shouldUseInsecureTls as Ft, vscodeRipgrepPath as G, generateRandomPort as Gt, buildToolbeltAwareness as H, DEFAULT_PORT as Ht, PLAN_DEFAULT_MODEL as I, ArtifactClient as It, searchWeb as J, withInstallLock as Jt, TOOLBELT_TOOLS$1 as K, pickClaudeDefault as Kt, REVIEW_DEFAULT_MODEL as L, collapsePathKeys as Lt, BROWSE_DEFAULT_MODEL as M, provisionAndIndexColbert as Mt, DEFAULT_MODEL as N, extractTarGzMember as Nt, stopReviewStateDir as O, readResponseBodyCapped as Ot, EXPLORE_DEFAULT_MODEL as P, extractZipMember as Pt, injectAdvisorTool as Q, tryRefreshAndRetry as Qt, appendPlanReminder as R, toolbeltPathOverride as Rt, fileLastPromptStore as S, assembleResponsesPayload as St, repoFingerprint as T, createResponses as Tt, toolbeltEnabled as U, UPSTREAM_FETCH_TIMEOUT_MS as Ut, availableToolCommands as V, DEFAULT_CODEX_MODEL_FALLBACKS as Vt, toolbeltSkipSet as W, UPSTREAM_INACTIVITY_TIMEOUT_MS as Wt, ADVISOR_TOOL_INSTRUCTIONS as X, setupGitHubAgentToken as Xt, ADVISOR_INTERNAL_TOOL_NAME as Y, setupCopilotToken as Yt, buildAdvisorStream as Z, setupGitHubToken as Zt, stopGateId as _, workerToolsEnabled as _t, buildPeerAwarenessSnippet as a, resolveModel as an, relayAnthropicStream as at, fileBaselineStore as b, createMessages as bt, buildArtifactOpenHookCommand as c, fetchWithTransientRetry as cn, agentToolsEnabled as ct, captureLaunchBaseline as d, GITHUB_API_BASE_URL as dn, browserCompoundToolsEnabled as dt, cacheModels as en, buildAnthropicErrorEvent as et, decideStopHook as f, copilotBaseUrl as fn, browserToolsEnabled as ft, stopGateDisabled as g, standInToolEnabled as gt, launchBaselineKey as h, state as hn, nativeSubagentModel as ht, buildAgentPrompt as i, resolveCodexModel as in, readIteratorWithTimeout as it, resolveSealedGate as j, hasSupportedBrowserInstalled as jt, trustRepo as k, parseJsonOrDiagnose as kt, buildSessionBindHookCommand as l, HTTPError as ln, artifactToolsEnabled as lt, injectStopHookIntoSettingsFile as m, githubHeaders as mn, geminiAvailable as mt, MCP_GROUPS as n, filterBetaHeader as nn, isControllerClosedError as nt, buildPeerAwarenessSummary as o, sleep as on, handleMcpDelete as ot, fileBlockBudget as p, copilotHeaders as pn, fleetToolsEnabled as pt, assetFor as q, getPackageVersion as qt, assertMcpToolSurfaceConsistent as r, isNullish as rn, logStreamError as rt, personasFor as s, getModels as sn, handleMcpPost as st, GROUP_META as t, cacheVSCodeVersion as tn, buildOpenAIErrorEvent as tt, buildStopHookCommand as u, forwardError as un, browseAgentEnabled as ut, stopGatePlanMode as v, shimDefaultsToXhigh as vt, isSubagentContext as w, pickEndpoint as wt, fileFindingsStore as x, getTokenCount as xt, stopReviewEnabled as y, countTokens as yt, runWorkerAgent as z, DEFAULT_CLAUDE_MODEL_FALLBACKS as zt };
31687
- //# sourceMappingURL=peer-mcp-personas-BtQi-dF-.js.map
31776
+ //# sourceMappingURL=peer-mcp-personas-B1-1mVPB.js.map