github-router 0.3.248 → 0.3.250

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.
@@ -183,7 +183,14 @@ async function toolScreenshot(args) {
183
183
  // reason. Real Chrome with a visible window has a surface and works
184
184
  // fine. If you're driving Chrome-for-Testing programmatically and
185
185
  // need screenshots, launch with `--headless=new`.
186
- const dataUrl = await chrome.tabs.captureVisibleTab(windowId, { format })
186
+ // `quality` (1-100) applies to JPEG only; Chrome ignores it for PNG. It is the
187
+ // model's lever for shrinking a capture when a full-size one is refused for
188
+ // exceeding a model's image-size limit — without it, "retake it smaller" is
189
+ // advice the caller cannot act on. An older extension build that predates this
190
+ // simply ignores the extra key and captures at default quality.
191
+ const captureOpts = { format }
192
+ if (typeof args.quality === "number") captureOpts.quality = args.quality
193
+ const dataUrl = await chrome.tabs.captureVisibleTab(windowId, captureOpts)
187
194
  // dataUrl: "data:image/png;base64,...."
188
195
  const m = /^data:([^;]+);base64,(.*)$/.exec(dataUrl)
189
196
  if (!m) throw new Error("browser_screenshot: captureVisibleTab returned unexpected shape")
@@ -2,7 +2,7 @@
2
2
  "manifest_version": 3,
3
3
  "name": "github-router browser bridge",
4
4
  "short_name": "gh-router-browser",
5
- "version": "0.3.248",
5
+ "version": "0.3.250",
6
6
  "description": "Bridge between Claude (via github-router /mcp) and the browser. Implements tab control, navigation, clicks, form fill, downloads, screenshots, devtools eval. Blocks navigation to chrome://settings.",
7
7
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqJElxuBlonBS3TVW9FJN0mGTtShB3L1hoaYf6k39SOr1ogGYmF90EjRxy1i21k9wQQjPf26bcBu/9X67KrQjQV0uB38CaNukgiSeoLjfptN811u+PJHx6BP+jx3Qa6/3VenNPxHC8WEU0GXql8QSjIHEyCwKb6fMASXOK94JyB5Ywov2x8mt/+9ncqBBBMVzf6r5Sagy4PL1XnryLsuADD/vOEkPet8wXgH/Oj7v5tTsQQZ7U1JT51PoDs2BFnXc5v3TkVgZwd32k3ONh+nkDw1Hof+4zwUGOyJE6eMrlYzRlKM4Qxdf9JpavQvqfieAbTRWcyKeclnHeoIfE7cDBQIDAQAB",
8
8
  "background": {
@@ -0,0 +1,2 @@
1
+ import { W as runWorkerAgent } from "./peer-mcp-personas-BvommiSI.js";
2
+ export { runWorkerAgent };
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as searchWeb, $t as DEFAULT_CLAUDE_MODEL_FALLBACKS, A as trustRepo, An as state, At as getTokenCount, Bt as hasSupportedBrowserInstalled, C as fileLastPromptStore, Cn as fetchWithTransientRetry, Ct as scoutModel, D as repoRoot, Dn as copilotBaseUrl, Dt as shimDefaultsToXhigh, E as repoFingerprint, En as GITHUB_API_BASE_URL, Et as workerToolsEnabled, Ft as createChatCompletions, G as buildEnv, Gt as CONDENSED_OPERATING_SEQUENCE, Ht as extractTarGzMember, It as MAX_RESPONSE_BODY_BYTES, J as toolbeltEnabled, Jt as ArtifactClient, K as availableToolCommands, Kt as DEFINITION_OF_GREATNESS, Lt as readResponseBodyCapped, M as resolveSealedGate, Mt as resolveMcpToolTimeoutMs, Nt as pickEndpoint, O as stopGateEnabledForRepo, On as copilotHeaders, Ot as countTokens, Pt as createResponses, Q as assetFor, Qt as toolbeltPathOverride, Rt as parseJsonOrDiagnose, S as fileFindingsStore, Sn as getGitHubUser, St as reviewerModel, T as isSubagentContext, Tn as forwardError, Tt as standInToolEnabled, Ut as extractZipMember, Vt as provisionAndIndexColbert, Wt as warmTreeSitterPool, X as vscodeRipgrepPath, Xt as buildWorkspaceHeaderJson, Y as toolbeltSkipSet, Yt as buildWorkspaceHeaderHelperCommand, Z as TOOLBELT_TOOLS, Zt as collapsePathKeys, _ as stopGateDisabled, _n as isNullish, _t as browserCompoundToolsEnabled, a as buildPeerAwarenessSnippet, an as generateRandomPort, at as buildAnthropicErrorEvent, b as stopReviewEnabled, bn as sleep$1, bt as geminiAvailable, c as personasFor, cn as withInstallLock, ct as logStreamError, d as buildStopHookCommand, dn as setupGitHubToken, dt as handleMcpDelete, en as DEFAULT_CODEX_MODEL, et as ADVISOR_INTERNAL_TOOL_NAME, f as captureLaunchBaseline, fn as tryRefreshAndRetry, ft as handleMcpPost, g as launchBaselineKey, gn as filterBetaHeader, gt as browseAgentEnabled, h as injectStopHookIntoSettingsFile, hn as cacheVSCodeVersion, ht as brainstormModel, i as buildAgentPrompt, in as UPSTREAM_INACTIVITY_TIMEOUT_MS, it as isAdvisorRequested, j as liveExec, jt as assembleResponsesPayload, k as stopReviewStateDir, kn as githubHeaders, kt as createMessages, l as buildArtifactOpenHookCommand, ln as setupCopilotToken, lt as readIteratorWithTimeout, m as fileBlockBudget, mn as cacheModels, mt as artifactToolsEnabled, n as MCP_GROUPS, nt as buildAdvisorStream, o as buildPeerAwarenessSummary, on as pickClaudeDefault, ot as buildOpenAIErrorEvent, p as decideStopHook, pn as cacheCopilotVersion, pt as agentToolsEnabled, q as buildToolbeltAwareness, qt as shouldUseInsecureTls, r as assertMcpToolSurfaceConsistent, rn as UPSTREAM_FETCH_TIMEOUT_MS, rt as injectAdvisorTool, s as enumerateInjectedMcpToolNames, sn as getPackageVersion$1, st as isControllerClosedError, t as GROUP_META, tn as DEFAULT_CODEX_MODEL_FALLBACKS, tt as ADVISOR_TOOL_INSTRUCTIONS, u as buildSessionBindHookCommand, un as setupGitHubAgentToken, ut as relayAnthropicStream, v as stopGateId, vn as resolveCodexModel, vt as browserToolsEnabled, w as fileReviewDebounce, wn as HTTPError, wt as scribeModel, x as fileBaselineStore, xn as getModels, xt as nativeSubagentModel, y as stopGatePlanMode, yn as resolveModel, yt as fleetToolsEnabled, zt as provisionBrowserAssets } from "./peer-mcp-personas-HWhxZUvf.js";
2
+ import { $ as searchWeb, $t as DEFAULT_CLAUDE_MODEL_FALLBACKS, A as trustRepo, An as state, At as getTokenCount, Bt as hasSupportedBrowserInstalled, C as fileLastPromptStore, Cn as fetchWithTransientRetry, Ct as scoutModel, D as repoRoot, Dn as copilotBaseUrl, Dt as shimDefaultsToXhigh, E as repoFingerprint, En as GITHUB_API_BASE_URL, Et as workerToolsEnabled, Ft as createChatCompletions, G as buildEnv, Gt as CONDENSED_OPERATING_SEQUENCE, Ht as extractTarGzMember, It as MAX_RESPONSE_BODY_BYTES, J as toolbeltEnabled, Jt as ArtifactClient, K as availableToolCommands, Kt as DEFINITION_OF_GREATNESS, Lt as readResponseBodyCapped, M as resolveSealedGate, Mt as resolveMcpToolTimeoutMs, Nt as pickEndpoint, O as stopGateEnabledForRepo, On as copilotHeaders, Ot as countTokens, Pt as createResponses, Q as assetFor, Qt as toolbeltPathOverride, Rt as parseJsonOrDiagnose, S as fileFindingsStore, Sn as getGitHubUser, St as reviewerModel, T as isSubagentContext, Tn as forwardError, Tt as standInToolEnabled, Ut as extractZipMember, Vt as provisionAndIndexColbert, Wt as warmTreeSitterPool, X as vscodeRipgrepPath, Xt as buildWorkspaceHeaderJson, Y as toolbeltSkipSet, Yt as buildWorkspaceHeaderHelperCommand, Z as TOOLBELT_TOOLS, Zt as collapsePathKeys, _ as stopGateDisabled, _n as isNullish, _t as browserCompoundToolsEnabled, a as buildPeerAwarenessSnippet, an as generateRandomPort, at as buildAnthropicErrorEvent, b as stopReviewEnabled, bn as sleep$1, bt as geminiAvailable, c as personasFor, cn as withInstallLock, ct as logStreamError, d as buildStopHookCommand, dn as setupGitHubToken, dt as handleMcpDelete, en as DEFAULT_CODEX_MODEL, et as ADVISOR_INTERNAL_TOOL_NAME, f as captureLaunchBaseline, fn as tryRefreshAndRetry, ft as handleMcpPost, g as launchBaselineKey, gn as filterBetaHeader, gt as browseAgentEnabled, h as injectStopHookIntoSettingsFile, hn as cacheVSCodeVersion, ht as brainstormModel, i as buildAgentPrompt, in as UPSTREAM_INACTIVITY_TIMEOUT_MS, it as isAdvisorRequested, j as liveExec, jt as assembleResponsesPayload, k as stopReviewStateDir, kn as githubHeaders, kt as createMessages, l as buildArtifactOpenHookCommand, ln as setupCopilotToken, lt as readIteratorWithTimeout, m as fileBlockBudget, mn as cacheModels, mt as artifactToolsEnabled, n as MCP_GROUPS, nt as buildAdvisorStream, o as buildPeerAwarenessSummary, on as pickClaudeDefault, ot as buildOpenAIErrorEvent, p as decideStopHook, pn as cacheCopilotVersion, pt as agentToolsEnabled, q as buildToolbeltAwareness, qt as shouldUseInsecureTls, r as assertMcpToolSurfaceConsistent, rn as UPSTREAM_FETCH_TIMEOUT_MS, rt as injectAdvisorTool, s as enumerateInjectedMcpToolNames, sn as getPackageVersion$1, st as isControllerClosedError, t as GROUP_META, tn as DEFAULT_CODEX_MODEL_FALLBACKS, tt as ADVISOR_TOOL_INSTRUCTIONS, u as buildSessionBindHookCommand, un as setupGitHubAgentToken, ut as relayAnthropicStream, v as stopGateId, vn as resolveCodexModel, vt as browserToolsEnabled, w as fileReviewDebounce, wn as HTTPError, wt as scribeModel, x as fileBaselineStore, xn as getModels, xt as nativeSubagentModel, y as stopGatePlanMode, yn as resolveModel, yt as fleetToolsEnabled, zt as provisionBrowserAssets } from "./peer-mcp-personas-BvommiSI.js";
3
3
  import { a as isUnderClaudeConfigMirror, d as writeRuntimeFileSecure, i as ensurePaths, o as removeOwnClaudeConfigMirror, r as ensureClaudeConfigMirror, t as PATHS, u as writeArtifactCredsToMirror } from "./paths-BjTMI_xK.js";
4
4
  import { c as killManagedTree, d as resolveExecutable, f as runCommandCapture, l as parseBoolEnv, p as runCommandVoid, s as killChildProcessTree, u as parseIntEnv } from "./lifecycle-DHZsVN8j.js";
5
5
  import { a as sweepRegistry } from "./lifecycle-CUf1UDrH.js";
@@ -18,6 +18,7 @@ import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
18
18
  import { Writable } from "node:stream";
19
19
  import { clearTimeout as clearTimeout$1, setTimeout as setTimeout$1 } from "node:timers";
20
20
  import { serve } from "srvx";
21
+ import { createBodyTooLargeError, limitRequestBody } from "srvx/body-limit";
21
22
  import { getProxyForUrl } from "proxy-from-env";
22
23
  import { Hono } from "hono";
23
24
  import { cors } from "hono/cors";
@@ -1396,7 +1397,7 @@ function buildPeerAgentDefinitions(opts) {
1396
1397
  ...modelField
1397
1398
  };
1398
1399
  out.reviewer = {
1399
- description: reviewerModel ? `Feedback subagent running ${reviewerModel}, a DIFFERENT lab from both the lead and the implementer, so its blind spots are decorrelated from whoever produced the work. Use proactively when something already exists and you want it assessed: a diff, a plan, a document, a failing test. Unlike the stateless peer critics, it reads the repo and can RUN things, so prefer it whenever the assessment needs execution or repo context (reproduce a failure, run the suite, bisect); prefer a peer critic when you already hold the artifact and want a fresh-context opinion on it. Model is overridable at spawn.` : `Feedback subagent (native tools, runs on the lead's model in its own context). Use proactively when something already exists and you want it assessed: a diff, a plan, a document, a failing test. Unlike the stateless peer critics, it reads the repo and can RUN things, so prefer it whenever the assessment needs execution or repo context; prefer a peer critic when you already hold the artifact and want a fresh-context opinion on it. Model is overridable at spawn.`,
1400
+ description: reviewerModel ? `Feedback subagent running ${reviewerModel}, a DIFFERENT lab from both the lead and the implementer, so its blind spots are decorrelated from whoever produced the work. Use proactively when something already exists and you want it assessed: a diff, a plan, a document, a failing test. Unlike the stateless peer critics, it reads the repo and can RUN things, so prefer it whenever the assessment needs execution or repo context (reproduce a failure, run the suite, bisect); prefer a peer critic when you already hold the artifact and want a fresh-context opinion on it. It can also REVIEW SCREENSHOTS and other images: just point it at the file and it will look at them (its model accepts up to 10 images per request, more than any other lane here). Model is overridable at spawn.` : `Feedback subagent (native tools, runs on the lead's model in its own context). Use proactively when something already exists and you want it assessed: a diff, a plan, a document, a failing test. Unlike the stateless peer critics, it reads the repo and can RUN things, so prefer it whenever the assessment needs execution or repo context; prefer a peer critic when you already hold the artifact and want a fresh-context opinion on it. Model is overridable at spawn.`,
1400
1401
  prompt: "You are a feedback subagent. Your job is to tell the caller what is actually true about the artifact you are given — code, a plan, a document, a failure report — and what is wrong with it. Verify against the ACTUAL code by reading it; never assume. Do whatever the assessment requires: reproduce a failure end to end as close to how a real user hits it as you can, form hypotheses and test them against the code and runtime, and isolate the true root cause rather than a symptom. Where the change warrants it, author tests that try to BREAK the implementation (edge cases, error paths, and the acceptance criteria as executable checks), run them, and report which pass and which fail; do NOT modify production code just to make tests pass. " + fileToolSteer("builds") + " Do the work yourself — do not spawn further subagents. Report severity-ranked findings with `file:line` citations, the evidence behind each, and end with a clear go/no-go.",
1401
1402
  ...reviewerModel ? { model: reviewerModel } : {}
1402
1403
  };
@@ -3087,7 +3088,7 @@ async function discoverGateCommands(cwd, opts) {
3087
3088
  if (files.length === 0) return null;
3088
3089
  let result;
3089
3090
  try {
3090
- const { runWorkerAgent } = await import("./engine-C1nJ6ue1.js");
3091
+ const { runWorkerAgent } = await import("./engine-dOYoMQis.js");
3091
3092
  result = await runWorkerAgent({
3092
3093
  mode: "explore",
3093
3094
  workspace: root,
@@ -4281,14 +4282,27 @@ const STYLE_DIRECTIVE = "Write concisely without losing detail. Use a natural hu
4281
4282
  * main context free to reason and collaborate with the user, while still
4282
4283
  * doing trivial / surgical / last-mile work directly (delegating that
4283
4284
  * would only add relay-fidelity loss + latency).
4284
- * 2. Excellence lens: the principles stated plainly and concretely (radical
4285
+ * 2. Adversarial review: WHEN a peer critic earns its keep and, equally
4286
+ * important, when reaching for one is ritual rather than review. Same
4287
+ * failure shape the delegation default had: "consult a critic for
4288
+ * non-trivial changes" is unfalsifiable in advance, so it collapses into
4289
+ * either never (four of four unprimed agents) or always (worse than
4290
+ * never). The discriminator is whether the conclusion still turns on
4291
+ * judgment once the direct evidence is in: a consequential recommendation
4292
+ * cannot be run, which is exactly where confabulation hides, while a
4293
+ * tracing question a search already proved gains nothing from a second
4294
+ * model re-deriving it. The roster, the lens-to-artifact match, the
4295
+ * advisor-complements-rather-than-substitutes distinction, and the
4296
+ * do-not-anchor-the-critic rule live here; the digest carries only the
4297
+ * trigger and the ritual exclusion.
4298
+ * 3. Excellence lens: the principles stated plainly and concretely (radical
4285
4299
  * simplicity + real-user focus; whole-system first-principles thinking that
4286
4300
  * anticipates scale; work back from the customer outcome). Named exemplars
4287
4301
  * were dropped per the injected-surface review: a named entity is a dense,
4288
4302
  * high-variance vector that pulls in persona mannerisms at top salience, and
4289
4303
  * the guidance favors specific functional framing over comparison, so
4290
4304
  * specificity carries the vividness instead.
4291
- * 3. Engineering excellence: quality / robustness / maintainability over
4305
+ * 4. Engineering excellence: quality / robustness / maintainability over
4292
4306
  * development cost; reproduce a bug end-to-end (as a real user hits it)
4293
4307
  * before fixing so the fix targets the real cause; a pixel-perfect UI bar;
4294
4308
  * and fix any lint error / test failure / flake on sight, whoever caused it,
@@ -4300,15 +4314,24 @@ const STYLE_DIRECTIVE = "Write concisely without losing detail. Use a natural hu
4300
4314
  * Self-referentially compliant with the style directive: no em dashes, no
4301
4315
  * Claude / Anthropic attribution.
4302
4316
  */
4303
- const OPERATING_DEFAULTS_DIRECTIVE = "## Operating defaults (apply when the user has not specified otherwise; the user's explicit direction and the domain's own standards always override)\n\nOrchestrate. Delegate research, implementation, review, and large reads to the right subagent, worker, or model. Reach for `implementer` when you know what to build, `reviewer` when something exists and you want it assessed (including reproducing and root-causing a failure), `brainstorm` when you do not yet know which approach to take, `scout` to find or understand something in the repo, and `scribe` for docs and ADRs that trail the code; worker-* agents for background non-blocking runs; Task subagents for parallel work; peer critics for review. That keeps your own context free to reason and collaborate with the user. Prefer parallel delegation for independent work. Delegation pays when the work is WIDE (many files or sources to sweep) or SLOW, and you need only the conclusion: the main thread is where you think with and respond to the user, and its context window is a finite shared resource. It does NOT pay merely because a sub-question is separable. A narrow, deep question whose whole value is file:line fidelity loses exactly that through a summarization layer, and a sub-question you could answer in one command is cheaper done directly than paying a subagent's startup. Do trivial, surgical, and last-mile work yourself.\n\nAim high. Default to radical simplicity and a relentless focus on the user's real experience: design for the person and the job to be done, not the demo. Reason about the whole system from first principles, anticipating scale and the long arc rather than patching the surface. Work backwards from the outcome the user actually needs. Question every assumption and prefer what you can derive, reproduce, or test.\n\nEngineering excellence. When making technical decisions, give little weight to development cost; prefer quality, simplicity, robustness, scalability, and long-term maintainability. Fix a bug by first reproducing it end to end, as close to how a real user hits it as you can, so you solve the real problem and not a symptom. When testing a product end to end, be picky about the UI and obsessed with pixel perfection: if something clearly looks off, even when it is unrelated to your task, get it fixed along the way. Hold that same bar for the codebase itself: a lint error, a failing test, or a flaky test is worth fixing the moment you see it, whoever introduced it. Fold it into your current work rather than letting it derail the task the user actually asked for.";
4317
+ const OPERATING_DEFAULTS_DIRECTIVE = "## Operating defaults (apply when the user has not specified otherwise; the user's explicit direction and the domain's own standards always override)\n\nOrchestrate. Delegate research, implementation, review, and large reads to the right subagent, worker, or model. Reach for `implementer` when you know what to build, `reviewer` when something exists and you want it assessed (including reproducing and root-causing a failure), `brainstorm` when you do not yet know which approach to take, `scout` to find or understand something in the repo, and `scribe` for docs and ADRs that trail the code; worker-* agents for background non-blocking runs; Task subagents for parallel work; peer critics for review. That keeps your own context free to reason and collaborate with the user. Prefer parallel delegation for independent work. Delegation pays when the work is WIDE (many files or sources to sweep) or SLOW, and you need only the conclusion: the main thread is where you think with and respond to the user, and its context window is a finite shared resource. It does NOT pay merely because a sub-question is separable. A narrow, deep question whose whole value is file:line fidelity loses exactly that through a summarization layer, and a sub-question you could answer in one command is cheaper done directly than paying a subagent's startup. Do trivial, surgical, and last-mile work yourself.\n\nAdversarial review. The peer critics (`codex_critic` and `codex_reviewer`, `gemini_critic` and `gemini_reviewer`, `opus_critic`, and the `peer-review-coordinator` that fans out to several of them) are fresh-context models, so what they add is a blind spot that whoever produced the work cannot reach by thinking harder about it; prefer a critic from a different lab than the producer, since blind spots correlate within a lab. The `advisor` is a complement and not a substitute: it sees your transcript, so it catches your own drift and momentum, but it inherits your framing, which is exactly what a fresh-context critic does not. They earn their keep on consequential design choices, recommendations, and hard-to-reverse decisions: the cases where plausible alternatives remain and the conclusion rests on judgment rather than on something you can verify directly. That is where confabulation hides, so budget the wait even under delivery pressure. Always consult one when the change touches auth, user input, database queries, crypto, or serialization. They do NOT pay for read-only tracing, ordinary repository lookup, or a conclusion that a focused test, a direct reproduction, or unambiguous code evidence already settles. Asking a critic to re-derive a proven fact returns a confident answer either way, which is ritual skepticism rather than review, and skipping them there is the right call and not a shortcut. Match the lens to the artifact: a strategic critic for plans and trade-offs, a code reviewer for a concrete diff, the coordinator only when the risk warrants several independent lenses. Give whichever you pick the artifact and the constraints and not your rationale, since justification anchors the review and dulls it.\n\nAim high. Default to radical simplicity and a relentless focus on the user's real experience: design for the person and the job to be done, not the demo. Reason about the whole system from first principles, anticipating scale and the long arc rather than patching the surface. Work backwards from the outcome the user actually needs. Question every assumption and prefer what you can derive, reproduce, or test.\n\nEngineering excellence. When making technical decisions, give little weight to development cost; prefer quality, simplicity, robustness, scalability, and long-term maintainability. Fix a bug by first reproducing it end to end, as close to how a real user hits it as you can, so you solve the real problem and not a symptom. When testing a product end to end, be picky about the UI and obsessed with pixel perfection: if something clearly looks off, even when it is unrelated to your task, get it fixed along the way. Hold that same bar for the codebase itself: a lint error, a failing test, or a flaky test is worth fixing the moment you see it, whoever introduced it. Fold it into your current work rather than letting it derail the task the user actually asked for.";
4304
4318
  /**
4305
4319
  * Condensed digest of OPERATING_DEFAULTS_DIRECTIVE for the spawned session's
4306
4320
  * system prompt (--append-system-prompt). The FULL directive is prepended to
4307
4321
  * the mirrored CLAUDE.md (read by the main agent and descendants); this digest
4308
4322
  * keeps both behavioral directives at top salience without duplicating the full
4309
4323
  * ~310-token block in the context window every turn. Points to the full copy.
4324
+ *
4325
+ * The unverifiable-claim rule is here rather than in CLAUDE.md alone because it
4326
+ * fires at a moment that suppresses lookups: an agent racing to deliver a
4327
+ * recommendation is precisely the one that will not stop to consult its project
4328
+ * instructions, which is how the check got crowded out in the first place. It
4329
+ * also closes a hole in the rule above it, which only covers claims that direct
4330
+ * evidence CAN settle. The roster of critics, the lens-to-artifact match, and
4331
+ * the reasoning stay in the directive; the always-resident copy carries only
4332
+ * the trigger and the ritual exclusion.
4310
4333
  */
4311
- const OPERATING_DEFAULTS_DIGEST = "## Operating defaults (the user's explicit direction and the domain's standards always override)\n\nDelegate when the work is WIDE (many files or sources to sweep) or SLOW and you need only the conclusion, to protect the main thread's finite context and keep it free for reasoning and interacting with the user; prefer parallel delegation for independent work. Do NOT delegate merely because a sub-question is separable: a narrow, deep question whose value is file:line fidelity loses exactly that through a summarization layer, and one answerable in a single command is cheaper done directly. Do trivial, surgical, and last-mile work yourself.\n\nVerify, do not assert. Run the code, read the file, check the exit code. A claim in prose is worth nothing against state you did not check, and a check that cannot fail proves nothing. Reproduce a bug end to end, the way a real user hits it, before fixing it. Fix a lint error, failing test, or flake the moment you see it, whoever introduced it, without letting it derail the task at hand. Prefer quality and long-term maintainability over development cost.\n\nThe agent roster, the tool surface, and the reasoning behind these defaults are in your CLAUDE.md project instructions. Read them when choosing HOW to work; the rules above apply without a lookup.";
4334
+ const OPERATING_DEFAULTS_DIGEST = "## Operating defaults (the user's explicit direction and the domain's standards always override)\n\nDelegate when the work is WIDE (many files or sources to sweep) or SLOW and you need only the conclusion, to protect the main thread's finite context and keep it free for reasoning and interacting with the user; prefer parallel delegation for independent work. Do NOT delegate merely because a sub-question is separable: a narrow, deep question whose value is file:line fidelity loses exactly that through a summarization layer, and one answerable in a single command is cheaper done directly. Do trivial, surgical, and last-mile work yourself.\n\nVerify, do not assert. Run the code, read the file, check the exit code. A claim in prose is worth nothing against state you did not check, and a check that cannot fail proves nothing. Reproduce a bug end to end, the way a real user hits it, before fixing it. Fix a lint error, failing test, or flake the moment you see it, whoever introduced it, without letting it derail the task at hand. Prefer quality and long-term maintainability over development cost.\n\nVerification has a blind spot: a consequential recommendation, a design or trade-off call, or a hard-to-reverse decision that still turns on judgment among plausible alternatives once the direct evidence is in. That is where confabulation hides, so put it past a peer critic before you ship it and budget the wait even under delivery pressure. When a test, a run, a reproduction, or a search would settle the claim, settle it that way instead; a critic asked to re-derive what you can already prove is ritual, not review.\n\nThe agent roster, the tool surface, and the reasoning behind these defaults are in your CLAUDE.md project instructions. Read them when choosing HOW to work; the rules above apply without a lookup.";
4312
4335
  /**
4313
4336
  * Skip the helper if the user's `~/.claude/CLAUDE.md` (or, equivalently,
4314
4337
  * the would-be post-write file) has grown past this size.
@@ -5538,7 +5561,7 @@ function initProxyFromEnv() {
5538
5561
  //#endregion
5539
5562
  //#region package.json
5540
5563
  var name = "github-router";
5541
- var version$1 = "0.3.248";
5564
+ var version$1 = "0.3.250";
5542
5565
  //#endregion
5543
5566
  //#region src/lib/approval.ts
5544
5567
  const awaitApproval = async () => {
@@ -6945,12 +6968,18 @@ function anthropicUsageFromChat(u) {
6945
6968
  * Map a chat/completions `finish_reason` to an Anthropic stop_reason. A
6946
6969
  * truncated (`length`) response is `max_tokens` even when a partial tool call
6947
6970
  * is present — the response was cut — mirroring the Responses egress precedence.
6948
- * `tool_calls` (or any buffered tool) → `tool_use`; everything else (`stop`,
6949
- * `content_filter`, null) → `end_turn`.
6971
+ * `tool_calls` (or any buffered tool) → `tool_use`.
6972
+ *
6973
+ * `content_filter` maps to Anthropic's `refusal`, NOT `end_turn`. It used to
6974
+ * collapse into `end_turn`, which made an upstream safety block indistinguishable
6975
+ * from a normal completion: the client saw a successful, usually empty, message
6976
+ * and had no way to tell that content had been withheld. `refusal` is the
6977
+ * documented Anthropic stop_reason for exactly this case.
6950
6978
  */
6951
6979
  function chatStopReason(finishReason, sawTool) {
6952
6980
  if (finishReason === "length") return "max_tokens";
6953
6981
  if (finishReason === "tool_calls" || sawTool) return "tool_use";
6982
+ if (finishReason === "content_filter") return "refusal";
6954
6983
  return "end_turn";
6955
6984
  }
6956
6985
  /**
@@ -6968,6 +6997,10 @@ function chatResponseToAnthropicMessage(resp, modelId) {
6968
6997
  type: "text",
6969
6998
  text: message.content
6970
6999
  });
7000
+ if (typeof message.refusal === "string" && message.refusal.length > 0) content.push({
7001
+ type: "text",
7002
+ text: message.refusal
7003
+ });
6971
7004
  if (Array.isArray(message.tool_calls)) for (const tc of message.tool_calls) {
6972
7005
  sawTool = true;
6973
7006
  const rawId = typeof tc.id === "string" ? tc.id : "";
@@ -7050,6 +7083,16 @@ async function* synthAnthropicFromChat(upstream, opts) {
7050
7083
  }
7051
7084
  yield makeTextDelta(activeTextIndex, delta.content);
7052
7085
  }
7086
+ if (delta && typeof delta.refusal === "string" && delta.refusal.length > 0) {
7087
+ if (activeTextIndex == null) {
7088
+ activeTextIndex = nextIndex++;
7089
+ yield makeContentBlockStart(activeTextIndex, {
7090
+ type: "text",
7091
+ text: ""
7092
+ });
7093
+ }
7094
+ yield makeTextDelta(activeTextIndex, delta.refusal);
7095
+ }
7053
7096
  if (delta && Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
7054
7097
  if (activeTextIndex != null) {
7055
7098
  yield makeContentBlockStop(activeTextIndex);
@@ -7512,6 +7555,29 @@ function isClaudeModel(modelId, model, originalModelId) {
7512
7555
  * any chat-default model) → "chat-shim".
7513
7556
  * - A non-Claude model absent from the catalog (so we can't confirm an endpoint)
7514
7557
  * → "claude-passthrough" (unchanged; we don't divert what we can't classify).
7558
+ * - A non-Claude model that IS in the catalog and serves NEITHER of the two shim
7559
+ * endpoints (`pickEndpoint` → undefined) → "claude-passthrough" as well.
7560
+ *
7561
+ * Those last two land on the same route but are NOT the same answer, and the
7562
+ * coincidence is deliberate rather than a collapsed default (contrast
7563
+ * `resolveEndpointForModelId`, whose callers must tell them apart because
7564
+ * guessing there produces an opaque upstream 400). Here neither shim is even a
7565
+ * candidate: a shim can only speak `/responses` or `/chat/completions`, so
7566
+ * diverting a model that serves neither would 400 just as surely. Passthrough
7567
+ * is the better default because it is sometimes RIGHT — a non-Claude catalog
7568
+ * model advertising `/v1/messages` is served by exactly the endpoint
7569
+ * passthrough uses. It also preserves this module's fail-CLOSED-toward-Claude
7570
+ * invariant: an unclassifiable model is never diverted.
7571
+ *
7572
+ * KNOWN GAP (audited, deliberately not fixed here): a non-Claude model serving
7573
+ * only something we cannot speak at all (say `/embeddings`) also lands on
7574
+ * passthrough and will still 400 upstream — `logEndpointMismatch(modelId,
7575
+ * "/v1/messages")` logs it at the passthrough seam, but no local error is
7576
+ * raised. Closing that needs a change in `src/routes/messages/handler.ts`,
7577
+ * which this seam does not own. It is strictly narrower than the defect fixed
7578
+ * in `resolveEndpointForModelId`: no such model is reachable as a Claude Code
7579
+ * `/v1/messages` target today, whereas the plan worker's `claude-opus-5`
7580
+ * default is.
7515
7581
  *
7516
7582
  * `originalModelId` is the optional pre-resolution request id; when supplied it
7517
7583
  * is checked for Claude-likeness alongside the resolved id so an alias that
@@ -8783,21 +8849,177 @@ server.notFound((c) => c.json({
8783
8849
  //#region src/lib/server-setup.ts
8784
8850
  const MAX_PORT_RETRIES = 10;
8785
8851
  /**
8852
+ * Maximum request body the proxy will accept, in bytes.
8853
+ *
8854
+ * Pinned EXPLICITLY because the runtimes disagree by default: bun's `serve`
8855
+ * defaults `maxRequestBodySize` to 128 MB and rejects past it, while
8856
+ * `node:http` (the srvx node adapter) has no body limit at all. Left
8857
+ * unset, the same request succeeds under node and 413s under bun, with
8858
+ * nothing in the response explaining that the runtime is the variable.
8859
+ * Same class of defect as the `idleTimeout` divergence below.
8860
+ *
8861
+ * Note which side that leaves exposed: `dist/main.js` ships
8862
+ * `#!/usr/bin/env node`, so npm-installed users are on the runtime with NO
8863
+ * limit, and bun is mostly the dev/`bun run start` path. The uncapped case
8864
+ * was the shipped one.
8865
+ *
8866
+ * 128 MB — i.e. bun's default, adopted as the intended value on BOTH
8867
+ * runtimes rather than inherited on one:
8868
+ *
8869
+ * - It sits above the traffic this proxy is built for, with margin. A
8870
+ * full 1M-token Claude Code context is on the order of 4-5 MB of JSON
8871
+ * text; base64 inline images inflate 4/3 and are re-sent every turn, so
8872
+ * an image-heavy session runs to tens of MB. That is an estimate, not a
8873
+ * measurement, which is part of why the cap is set well clear of it
8874
+ * rather than snugly above it.
8875
+ * - Removing the limit to "match node" is not free even on loopback. The
8876
+ * app mounts permissive `cors()`, so any web page the user visits can
8877
+ * POST to 127.0.0.1:<port> (CORS gates reading the response, not sending
8878
+ * the request); so can any local process. An unbounded body lets either
8879
+ * stream the proxy to death.
8880
+ * - Raising it above 128 MB buys nothing and costs safety: the proxy
8881
+ * BUFFERS the whole body (`c.req.json()`), parses it into a JS object
8882
+ * (multiples of the text size in heap), then re-serializes it upstream.
8883
+ * Past ~512 MB V8's max string length makes `JSON.stringify` fail
8884
+ * opaquely — a cap well under that keeps the failure explainable.
8885
+ *
8886
+ * A body over this ceiling means something is wrong (runaway loop,
8887
+ * corrupted context), not a workload that needs a bigger buffer, so
8888
+ * there is deliberately no env override to raise it.
8889
+ */
8890
+ const MAX_REQUEST_BODY_BYTES = 134217728;
8891
+ /**
8892
+ * Where the RUNTIME's own body check sits. Not the policy.
8893
+ *
8894
+ * The policy is `MAX_REQUEST_BODY_BYTES`, enforced by `withBodyLimit` in
8895
+ * this process, identically on every runtime. This value exists only to
8896
+ * displace the 128 MB default bun's `serve` applies, which would otherwise pre-empt
8897
+ * that gate — bun enforces its limit at header-parse time by replying and
8898
+ * closing while the client is still uploading, so a client that has not
8899
+ * finished writing never reads the reply. Measured with a real client
8900
+ * posting 64 MB over a limit: `The socket connection was closed
8901
+ * unexpectedly`, i.e. exactly the opaque failure this change removes, and
8902
+ * exactly what bun does TODAY at its default. An early response from our
8903
+ * own handler, by contrast, is delivered cleanly (413 with the JSON body,
8904
+ * ~50ms, no memory growth; draining the body first was strictly worse at
8905
+ * 155ms and +50 MB, so we do not drain).
8906
+ *
8907
+ * Set as ONE top-level srvx option, so bun and node get the same number.
8908
+ * A bun-only override would put the two runtimes back on different
8909
+ * thresholds, which is the defect being fixed, not a fix for it.
8910
+ *
8911
+ * Deliberately far above the policy so the policy always decides first:
8912
+ * everything a client can realistically send is answered by the explained
8913
+ * 413, and this is reached only by a body that is both undeclared and
8914
+ * absurd. It stays finite so "no limit at all" is never the state. It
8915
+ * bounds nothing the gate does not already bound — an unread body is
8916
+ * discarded by both runtimes rather than buffered — so a high value costs
8917
+ * no memory, and buys message quality across the whole realistic range.
8918
+ */
8919
+ const TRANSPORT_BODY_CEILING_BYTES = 1073741824;
8920
+ /**
8921
+ * The 413 the user sees when a request body is over the ceiling.
8922
+ *
8923
+ * Anthropic error envelope (`request_too_large` is Anthropic's own 413
8924
+ * category), so a client that parses our error shape on every other path
8925
+ * parses this one too, and the message says what to actually do about it.
8926
+ *
8927
+ * `declaredBytes` is present only when the request declared a length. A
8928
+ * body caught mid-stream has no known size, and saying so beats inventing
8929
+ * one. Byte counts are exact, with the MB figure only as a gloss:
8930
+ * rounding alone reads as a contradiction at the boundary, where a body
8931
+ * one byte over renders as "128.0 MB, over the 128.0 MB limit".
8932
+ */
8933
+ function bodyTooLargeResponse(declaredBytes) {
8934
+ const mb = (n) => `${(n / 1048576).toFixed(1)} MB`;
8935
+ const actual = declaredBytes === void 0 ? `Request body exceeds the size github-router accepts` : `Request body is ${declaredBytes} bytes (${mb(declaredBytes)}), over the size github-router accepts`;
8936
+ return new Response(JSON.stringify({
8937
+ type: "error",
8938
+ error: {
8939
+ type: "request_too_large",
8940
+ message: `${actual}: at most ${MAX_REQUEST_BODY_BYTES} bytes (${mb(MAX_REQUEST_BODY_BYTES)}), the same limit under bun and node. This is usually an accumulated conversation or inline images (base64 attachments are re-sent every turn) rather than one large message — start a new session or drop the attachments.`
8941
+ }
8942
+ }), {
8943
+ status: 413,
8944
+ headers: { "content-type": "application/json" }
8945
+ });
8946
+ }
8947
+ /** srvx's canonical over-limit error, however it reaches us. */
8948
+ function isBodyTooLarge(error) {
8949
+ return typeof error === "object" && error !== null && error.code === "ERR_BODY_TOO_LARGE";
8950
+ }
8951
+ /**
8952
+ * Enforce `MAX_REQUEST_BODY_BYTES` in-process, so the limit and the
8953
+ * rejection are the same on every runtime.
8954
+ *
8955
+ * Leaving it to the runtimes is what produced the divergence in the first
8956
+ * place, and their rejections are not interchangeable:
8957
+ *
8958
+ * - bun rejects at header-parse time and closes the connection while the
8959
+ * client is still uploading, so the client sees a dead socket rather
8960
+ * than the 413 (measured; see `TRANSPORT_BODY_CEILING_BYTES`).
8961
+ * - node lets the handler run and rejects the body READ with an
8962
+ * `ERR_BODY_TOO_LARGE` error. The app installs no Hono `onError`, so
8963
+ * that becomes `500 Internal Server Error` — indistinguishable from a
8964
+ * real proxy fault.
8965
+ *
8966
+ * Two layers, one threshold, applied uniformly:
8967
+ *
8968
+ * 1. A declared `Content-Length` over the limit is answered before the
8969
+ * body is read at all. This is the path every real client takes
8970
+ * (Claude Code / undici / curl all declare a length on a JSON body).
8971
+ * 2. Anything else — chunked, or a length that understates the body — is
8972
+ * caught mid-stream by srvx's own `limitRequestBody`, the same helper
8973
+ * its node and deno adapters use. Overflow is surfaced as the SAME
8974
+ * explained 413 rather than leaking out as a 500.
8975
+ *
8976
+ * So a body that declares no length is no longer a hole in the message
8977
+ * quality OR in the bound, and neither depends on which runtime is
8978
+ * serving. Verified end to end against both adapters with a real client:
8979
+ * declared-over and chunked-over each return an identical 413.
8980
+ */
8981
+ function withBodyLimit(fetchHandler) {
8982
+ return async (request) => {
8983
+ const declared = request.headers.get("content-length");
8984
+ if (declared !== null && /^\d+$/.test(declared)) {
8985
+ const bytes = Number(declared);
8986
+ if (bytes > 134217728) return bodyTooLargeResponse(bytes);
8987
+ }
8988
+ let overflowed = false;
8989
+ const limited = limitRequestBody(request, MAX_REQUEST_BODY_BYTES, { createError: (max) => {
8990
+ overflowed = true;
8991
+ return createBodyTooLargeError(max);
8992
+ } });
8993
+ try {
8994
+ const response = await fetchHandler(limited);
8995
+ if (overflowed) {
8996
+ response.body?.cancel().catch(() => {});
8997
+ return bodyTooLargeResponse();
8998
+ }
8999
+ return response;
9000
+ } catch (error) {
9001
+ if (overflowed || isBodyTooLarge(error)) return bodyTooLargeResponse();
9002
+ throw error;
9003
+ }
9004
+ };
9005
+ }
9006
+ /**
8786
9007
  * Build the srvx `serve()` options shared by the explicit-port and
8787
9008
  * random-port paths.
8788
9009
  *
8789
- * Extracted and exported so the Bun idle-reaper override below is
9010
+ * Extracted and exported so the per-runtime overrides below are
8790
9011
  * assertable. `setupAndServe` itself performs auth and network I/O, so the
8791
9012
  * only way to pin this behaviour in a test is to make the options object
8792
- * reachable on its own — and it needs pinning, because deleting the override
8793
- * reintroduces a failure that looks like an upstream/network problem rather
8794
- * than a config one.
9013
+ * reachable on its own — and it needs pinning, because deleting either
9014
+ * override reintroduces a failure that looks like an upstream/network
9015
+ * problem rather than a config one.
8795
9016
  */
8796
9017
  function buildServeOptions(fetchHandler, silent) {
8797
9018
  return {
8798
- fetch: fetchHandler,
9019
+ fetch: withBodyLimit(fetchHandler),
8799
9020
  hostname: "127.0.0.1",
8800
9021
  silent,
9022
+ maxRequestBodySize: TRANSPORT_BODY_CEILING_BYTES,
8801
9023
  bun: { idleTimeout: 0 }
8802
9024
  };
8803
9025
  }