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.
- package/dist/browser-ext/background.js +8 -1
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/engine-dOYoMQis.js +2 -0
- package/dist/main.js +237 -15
- package/dist/main.js.map +1 -1
- package/dist/paths-BjTMI_xK.js.map +1 -1
- package/dist/{peer-mcp-personas-HWhxZUvf.js → peer-mcp-personas-BvommiSI.js} +974 -115
- package/dist/peer-mcp-personas-BvommiSI.js.map +1 -0
- package/package.json +1 -1
- package/dist/engine-C1nJ6ue1.js +0 -2
- package/dist/peer-mcp-personas-HWhxZUvf.js.map +0 -1
|
@@ -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
|
-
|
|
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.
|
|
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": {
|
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-
|
|
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-
|
|
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.
|
|
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
|
-
*
|
|
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.
|
|
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
|
|
6949
|
-
*
|
|
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
|
|
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
|
|
8793
|
-
* reintroduces a failure that looks like an upstream/network
|
|
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
|
}
|