github-router 0.3.202 → 0.3.206
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/{engine-CIt6z9K3.js → engine-DZjN7BuD.js} +1 -1
- package/dist/main.js +166 -60
- package/dist/main.js.map +1 -1
- package/dist/paths-BO22pMUb.js.map +1 -1
- package/dist/{peer-mcp-personas-rik0SIrg.js → peer-mcp-personas-C3ii7ZqP.js} +241 -56
- package/dist/peer-mcp-personas-C3ii7ZqP.js.map +1 -0
- package/package.json +1 -1
- package/dist/peer-mcp-personas-rik0SIrg.js.map +0 -1
|
@@ -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.206",
|
|
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": {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as runWorkerAgent, F as EXPLORE_DEFAULT_MODEL, I as IMPLEMENT_DEFAULT_MODEL, L as PLAN_DEFAULT_MODEL, N as BROWSE_DEFAULT_MODEL, P as DEFAULT_MODEL, R as REVIEW_DEFAULT_MODEL, V as withNoOutputRetry, z as appendPlanReminder } from "./peer-mcp-personas-
|
|
1
|
+
import { B as runWorkerAgent, F as EXPLORE_DEFAULT_MODEL, I as IMPLEMENT_DEFAULT_MODEL, L as PLAN_DEFAULT_MODEL, N as BROWSE_DEFAULT_MODEL, P as DEFAULT_MODEL, R as REVIEW_DEFAULT_MODEL, V as withNoOutputRetry, z as appendPlanReminder } from "./peer-mcp-personas-C3ii7ZqP.js";
|
|
2
2
|
import "./paths-BO22pMUb.js";
|
|
3
3
|
import "./lifecycle-D-1CYr1Y.js";
|
|
4
4
|
import "./lifecycle-bPdiXjYB.js";
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as buildAdvisorStream, $t as setupCopilotToken, A as trustRepo, At as readResponseBodyCapped, Bt as buildWorkspaceHeaderJson, C as fileLastPromptStore, Ct as getTokenCount, D as repoRoot, Dt as createResponses, E as repoFingerprint, Et as pickEndpoint, Ft as extractTarGzMember, G as toolbeltEnabled, Gt as DEFAULT_CODEX_MODEL_FALLBACKS, H as buildEnv, Ht as toolbeltPathOverride, It as extractZipMember, J as TOOLBELT_TOOLS, Jt as UPSTREAM_INACTIVITY_TIMEOUT_MS, K as toolbeltSkipSet, Kt as DEFAULT_PORT, Lt as shouldUseInsecureTls, M as resolveSealedGate, Mt as provisionBrowserAssets, Nt as hasSupportedBrowserInstalled, O as stopGateEnabledForRepo, Ot as createChatCompletions, Pt as provisionAndIndexColbert, Q as ADVISOR_TOOL_INSTRUCTIONS, Qt as withInstallLock, Rt as ArtifactClient, S as fileFindingsStore, St as createMessages, T as isSubagentContext, Tt as resolveMcpToolTimeoutMs, U as availableToolCommands, Ut as DEFAULT_CLAUDE_MODEL_FALLBACKS, Vt as collapsePathKeys, W as buildToolbeltAwareness, Wt as DEFAULT_CODEX_MODEL, X as searchWeb, Xt as pickClaudeDefault, Y as assetFor, Yt as generateRandomPort, Z as ADVISOR_INTERNAL_TOOL_NAME, Zt as getPackageVersion, _ as stopGateDisabled, _n as copilotBaseUrl, _t as nativeSubagentModel, a as buildPeerAwarenessSnippet, an as cacheVSCodeVersion, at as logStreamError, b as stopReviewEnabled, bn as state, bt as shimDefaultsToXhigh, c as personasFor, cn as resolveCodexModel, ct as handleMcpDelete, d as buildStopHookCommand, dn as getModels, dt as artifactToolsEnabled, en as setupGitHubAgentToken, et as injectAdvisorTool, f as captureLaunchBaseline, fn as getGitHubUser, ft as browseAgentEnabled, g as launchBaselineKey, gn as GITHUB_API_BASE_URL, gt as geminiAvailable, h as injectStopHookIntoSettingsFile, hn as forwardError, ht as fleetToolsEnabled, i as buildAgentPrompt, in as cacheModels, it as isControllerClosedError, j as liveExec, jt as parseJsonOrDiagnose, k as stopReviewStateDir, kt as MAX_RESPONSE_BODY_BYTES, l as buildArtifactOpenHookCommand, ln as resolveModel, lt as handleMcpPost, m as fileBlockBudget, mn as HTTPError, mt as browserToolsEnabled, n as MCP_GROUPS, nn as tryRefreshAndRetry, nt as buildAnthropicErrorEvent, o as buildPeerAwarenessSummary, on as filterBetaHeader, ot as readIteratorWithTimeout, p as decideStopHook, pn as fetchWithTransientRetry, pt as browserCompoundToolsEnabled, q as vscodeRipgrepPath, qt as UPSTREAM_FETCH_TIMEOUT_MS, r as assertMcpToolSurfaceConsistent, rn as cacheCopilotVersion, rt as buildOpenAIErrorEvent, s as enumerateInjectedMcpToolNames, sn as isNullish, st as relayAnthropicStream, t as GROUP_META, tn as setupGitHubToken, tt as isAdvisorRequested, u as buildSessionBindHookCommand, un as sleep$1, ut as agentToolsEnabled, v as stopGateId, vn as copilotHeaders, vt as standInToolEnabled, w as fileReviewDebounce, wt as assembleResponsesPayload, x as fileBaselineStore, xt as countTokens, y as stopGatePlanMode, yn as githubHeaders, yt as workerToolsEnabled, zt as buildWorkspaceHeaderHelperCommand } from "./peer-mcp-personas-
|
|
2
|
+
import { $ as buildAdvisorStream, $t as setupCopilotToken, A as trustRepo, At as readResponseBodyCapped, Bt as buildWorkspaceHeaderJson, C as fileLastPromptStore, Ct as getTokenCount, D as repoRoot, Dt as createResponses, E as repoFingerprint, Et as pickEndpoint, Ft as extractTarGzMember, G as toolbeltEnabled, Gt as DEFAULT_CODEX_MODEL_FALLBACKS, H as buildEnv, Ht as toolbeltPathOverride, It as extractZipMember, J as TOOLBELT_TOOLS, Jt as UPSTREAM_INACTIVITY_TIMEOUT_MS, K as toolbeltSkipSet, Kt as DEFAULT_PORT, Lt as shouldUseInsecureTls, M as resolveSealedGate, Mt as provisionBrowserAssets, Nt as hasSupportedBrowserInstalled, O as stopGateEnabledForRepo, Ot as createChatCompletions, Pt as provisionAndIndexColbert, Q as ADVISOR_TOOL_INSTRUCTIONS, Qt as withInstallLock, Rt as ArtifactClient, S as fileFindingsStore, St as createMessages, T as isSubagentContext, Tt as resolveMcpToolTimeoutMs, U as availableToolCommands, Ut as DEFAULT_CLAUDE_MODEL_FALLBACKS, Vt as collapsePathKeys, W as buildToolbeltAwareness, Wt as DEFAULT_CODEX_MODEL, X as searchWeb, Xt as pickClaudeDefault, Y as assetFor, Yt as generateRandomPort, Z as ADVISOR_INTERNAL_TOOL_NAME, Zt as getPackageVersion, _ as stopGateDisabled, _n as copilotBaseUrl, _t as nativeSubagentModel, a as buildPeerAwarenessSnippet, an as cacheVSCodeVersion, at as logStreamError, b as stopReviewEnabled, bn as state, bt as shimDefaultsToXhigh, c as personasFor, cn as resolveCodexModel, ct as handleMcpDelete, d as buildStopHookCommand, dn as getModels, dt as artifactToolsEnabled, en as setupGitHubAgentToken, et as injectAdvisorTool, f as captureLaunchBaseline, fn as getGitHubUser, ft as browseAgentEnabled, g as launchBaselineKey, gn as GITHUB_API_BASE_URL, gt as geminiAvailable, h as injectStopHookIntoSettingsFile, hn as forwardError, ht as fleetToolsEnabled, i as buildAgentPrompt, in as cacheModels, it as isControllerClosedError, j as liveExec, jt as parseJsonOrDiagnose, k as stopReviewStateDir, kt as MAX_RESPONSE_BODY_BYTES, l as buildArtifactOpenHookCommand, ln as resolveModel, lt as handleMcpPost, m as fileBlockBudget, mn as HTTPError, mt as browserToolsEnabled, n as MCP_GROUPS, nn as tryRefreshAndRetry, nt as buildAnthropicErrorEvent, o as buildPeerAwarenessSummary, on as filterBetaHeader, ot as readIteratorWithTimeout, p as decideStopHook, pn as fetchWithTransientRetry, pt as browserCompoundToolsEnabled, q as vscodeRipgrepPath, qt as UPSTREAM_FETCH_TIMEOUT_MS, r as assertMcpToolSurfaceConsistent, rn as cacheCopilotVersion, rt as buildOpenAIErrorEvent, s as enumerateInjectedMcpToolNames, sn as isNullish, st as relayAnthropicStream, t as GROUP_META, tn as setupGitHubToken, tt as isAdvisorRequested, u as buildSessionBindHookCommand, un as sleep$1, ut as agentToolsEnabled, v as stopGateId, vn as copilotHeaders, vt as standInToolEnabled, w as fileReviewDebounce, wt as assembleResponsesPayload, x as fileBaselineStore, xt as countTokens, y as stopGatePlanMode, yn as githubHeaders, yt as workerToolsEnabled, zt as buildWorkspaceHeaderHelperCommand } from "./peer-mcp-personas-C3ii7ZqP.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-BO22pMUb.js";
|
|
4
4
|
import { c as killManagedTree, d as runCommandCapture, f as runCommandVoid, l as parseBoolEnv, s as killChildProcessTree, u as resolveExecutable } from "./lifecycle-D-1CYr1Y.js";
|
|
5
5
|
import { a as sweepRegistry } from "./lifecycle-bPdiXjYB.js";
|
|
@@ -978,10 +978,10 @@ function decideWorkerGuard(input) {
|
|
|
978
978
|
function dispatcherDescription(mode) {
|
|
979
979
|
return `${{
|
|
980
980
|
explore: "Non-blocking `explore` worker: dispatches a read-only autonomous worker (its own context) in the background and delivers its summary as a completion notification.",
|
|
981
|
-
implement: "Non-blocking `implement` worker: dispatches an autonomous coding worker (read/write/bash
|
|
981
|
+
implement: "Non-blocking `implement` worker: dispatches an autonomous coding worker (read/write/bash) that ALWAYS runs in an isolated git worktree and returns the diff (for in-place edits use the `implementer` subagent), in the background, and delivers its result as a completion notification.",
|
|
982
982
|
review: "Non-blocking `review` worker: dispatches a read-only reviewer that reads the code itself to verify a change or claim, in the background, and delivers findings as a completion notification.",
|
|
983
983
|
plan: "Non-blocking `plan` worker: dispatches a read-only planner that returns an ordered implementation plan, in the background, and delivers it as a completion notification.",
|
|
984
|
-
test: "Non-blocking `test` worker: dispatches an independent test author that writes tests trying to break the implementation, in the background, and delivers pass/fail as a completion notification.",
|
|
984
|
+
test: "Non-blocking `test` worker: dispatches an independent test author (in an isolated git worktree) that writes tests trying to break the implementation, in the background, and delivers pass/fail as a completion notification.",
|
|
985
985
|
browse: "Non-blocking `browse` worker: dispatches an autonomous browser agent in the background and delivers its result as a completion notification."
|
|
986
986
|
}[mode]} Use proactively for any ${mode}-mode worker task so a long run never blocks your turn: it returns immediately and notifies you when done.`;
|
|
987
987
|
}
|
|
@@ -991,7 +991,7 @@ function dispatcherPrompt(mode, workersKey) {
|
|
|
991
991
|
const tool = workerToolName(workersKey, mode);
|
|
992
992
|
const briefField = mode === "browse" ? "task" : "prompt";
|
|
993
993
|
const briefDescription = mode === "browse" ? "the lead's browse task, copied verbatim" : "the lead's worker brief, copied verbatim";
|
|
994
|
-
const modeSpecificPassThrough = mode === "
|
|
994
|
+
const modeSpecificPassThrough = mode === "browse" ? "\n - `sessionId` (optional): pass through if the lead specified one" : "";
|
|
995
995
|
return [
|
|
996
996
|
`# Subagent: ${dispatcherAgentName(mode)}`,
|
|
997
997
|
"",
|
|
@@ -1089,6 +1089,27 @@ function resolveCodexCliBackend(opts) {
|
|
|
1089
1089
|
return "cli";
|
|
1090
1090
|
}
|
|
1091
1091
|
/**
|
|
1092
|
+
* Build one scoped HTTP `mcpServers` entry for a group: `type: http`, the
|
|
1093
|
+
* `/mcp/<urlSuffix>` URL, the Bearer-nonce Authorization header, and the
|
|
1094
|
+
* per-session workspace `headersHelper` when supplied. Shared by
|
|
1095
|
+
* `buildPeerMcpConfig` (the session `.claude.json` entries) AND
|
|
1096
|
+
* `buildPeerAgentDefinitions` (the per-subagent inline `mcpServers` frontmatter
|
|
1097
|
+
* that sidesteps Claude Code's Agent-tool MCP-inheritance bug, anthropics/
|
|
1098
|
+
* claude-code#30280 — a bare name-reference "shares the parent connection" and
|
|
1099
|
+
* re-triggers it, so subagents MUST inline the full HTTP config to connect
|
|
1100
|
+
* independently). One source of truth so the two can't drift.
|
|
1101
|
+
*/
|
|
1102
|
+
function httpEntryFor(serverUrl, group, nonce, workspaceHeaderCmd) {
|
|
1103
|
+
const entry = {
|
|
1104
|
+
type: "http",
|
|
1105
|
+
url: `${serverUrl}/mcp/${GROUP_META[group].urlSuffix}`,
|
|
1106
|
+
headers: { Authorization: `Bearer ${nonce}` }
|
|
1107
|
+
};
|
|
1108
|
+
const ws = workspaceHeaderCmd?.trim();
|
|
1109
|
+
if (ws) entry.headersHelper = ws;
|
|
1110
|
+
return entry;
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1092
1113
|
* Build the JSON payload for `claude --mcp-config <path>` (and the same
|
|
1093
1114
|
* entries that get merged into the mirrored `.claude.json`).
|
|
1094
1115
|
*
|
|
@@ -1106,13 +1127,7 @@ function buildPeerMcpConfig(serverUrl, opts) {
|
|
|
1106
1127
|
for (const group of MCP_GROUPS) {
|
|
1107
1128
|
const key = opts.groupKeys[group];
|
|
1108
1129
|
if (!key) continue;
|
|
1109
|
-
|
|
1110
|
-
type: "http",
|
|
1111
|
-
url: `${serverUrl}/mcp/${GROUP_META[group].urlSuffix}`,
|
|
1112
|
-
headers: { Authorization: `Bearer ${opts.nonce}` }
|
|
1113
|
-
};
|
|
1114
|
-
if (workspaceHeaderCmd) entry.headersHelper = workspaceHeaderCmd;
|
|
1115
|
-
mcpServers[key] = entry;
|
|
1130
|
+
mcpServers[key] = httpEntryFor(serverUrl, group, opts.nonce, workspaceHeaderCmd);
|
|
1116
1131
|
}
|
|
1117
1132
|
if (opts.codexCli) mcpServers["codex-cli"] = {
|
|
1118
1133
|
command: "codex",
|
|
@@ -1244,17 +1259,22 @@ function buildPeerAgentDefinitions(opts) {
|
|
|
1244
1259
|
geminiAvailable: opts.geminiAvailable
|
|
1245
1260
|
});
|
|
1246
1261
|
const peersKey = peersKeyOf(opts.groupKeys);
|
|
1262
|
+
const peersMcp = opts.serverUrl ? { mcpServers: { [peersKey]: httpEntryFor(opts.serverUrl, "peers", opts.nonce, opts.workspaceHeaderCmd) } } : {};
|
|
1247
1263
|
for (const persona of personas) out[persona.agentName] = {
|
|
1248
1264
|
description: persona.description,
|
|
1249
1265
|
prompt: buildAgentPrompt(persona, {
|
|
1250
1266
|
codexCli: opts.codexCli,
|
|
1251
1267
|
peersKey
|
|
1252
|
-
})
|
|
1268
|
+
}),
|
|
1269
|
+
...peersMcp
|
|
1270
|
+
};
|
|
1271
|
+
out["peer-review-coordinator"] = {
|
|
1272
|
+
...buildCoordinatorAgent({
|
|
1273
|
+
codexCli: opts.codexCli,
|
|
1274
|
+
geminiAvailable: opts.geminiAvailable
|
|
1275
|
+
}),
|
|
1276
|
+
...peersMcp
|
|
1253
1277
|
};
|
|
1254
|
-
out["peer-review-coordinator"] = buildCoordinatorAgent({
|
|
1255
|
-
codexCli: opts.codexCli,
|
|
1256
|
-
geminiAvailable: opts.geminiAvailable
|
|
1257
|
-
});
|
|
1258
1278
|
const nativeModel = opts.nativeSubagentModel && opts.nativeSubagentModel.length > 0 ? opts.nativeSubagentModel : void 0;
|
|
1259
1279
|
const modelField = nativeModel ? { model: nativeModel } : {};
|
|
1260
1280
|
out.implementer = {
|
|
@@ -1274,10 +1294,12 @@ function buildPeerAgentDefinitions(opts) {
|
|
|
1274
1294
|
};
|
|
1275
1295
|
if (opts.workerToolsAvailable) {
|
|
1276
1296
|
const workersKey = workersKeyOf(opts.groupKeys);
|
|
1297
|
+
const workersMcp = opts.serverUrl ? { mcpServers: { [workersKey]: httpEntryFor(opts.serverUrl, "workers", opts.nonce, opts.workspaceHeaderCmd) } } : {};
|
|
1277
1298
|
for (const mode of activeDispatchModes({ browse: opts.browseAvailable === true })) out[dispatcherAgentName(mode)] = {
|
|
1278
1299
|
description: dispatcherDescription(mode),
|
|
1279
1300
|
prompt: dispatcherPrompt(mode, workersKey),
|
|
1280
|
-
tools: dispatcherTools(mode, workersKey)
|
|
1301
|
+
tools: dispatcherTools(mode, workersKey),
|
|
1302
|
+
...workersMcp
|
|
1281
1303
|
};
|
|
1282
1304
|
}
|
|
1283
1305
|
return out;
|
|
@@ -1323,15 +1345,48 @@ function escapeYamlString(s) {
|
|
|
1323
1345
|
* component.
|
|
1324
1346
|
*/
|
|
1325
1347
|
const VALID_AGENT_NAME = /^[A-Za-z][A-Za-z0-9-]*$/;
|
|
1348
|
+
/**
|
|
1349
|
+
* Emit the subagent-frontmatter `mcpServers` block as a YAML sequence of
|
|
1350
|
+
* single-key maps (the INLINE form that connects on subagent start — a bare
|
|
1351
|
+
* name-reference would re-trigger claude-code#30280). Scalars are
|
|
1352
|
+
* JSON.stringify-quoted, which is valid YAML double-quoted syntax (handles the
|
|
1353
|
+
* Windows-path/backslash headersHelper command safely).
|
|
1354
|
+
*
|
|
1355
|
+
* ```yaml
|
|
1356
|
+
* mcpServers:
|
|
1357
|
+
* - workers:
|
|
1358
|
+
* type: http
|
|
1359
|
+
* url: "http://127.0.0.1:PORT/mcp/workers"
|
|
1360
|
+
* headers:
|
|
1361
|
+
* Authorization: "Bearer <nonce>"
|
|
1362
|
+
* headersHelper: "<cmd>"
|
|
1363
|
+
* ```
|
|
1364
|
+
*/
|
|
1365
|
+
function emitMcpServersYaml(mcpServers) {
|
|
1366
|
+
const lines = ["mcpServers:"];
|
|
1367
|
+
for (const [key, entry] of Object.entries(mcpServers)) {
|
|
1368
|
+
lines.push(` - ${JSON.stringify(key)}:`);
|
|
1369
|
+
lines.push(` type: ${JSON.stringify(entry.type)}`);
|
|
1370
|
+
lines.push(` url: ${JSON.stringify(entry.url)}`);
|
|
1371
|
+
lines.push(` headers:`);
|
|
1372
|
+
for (const [hk, hv] of Object.entries(entry.headers)) lines.push(` ${JSON.stringify(hk)}: ${JSON.stringify(hv)}`);
|
|
1373
|
+
if (entry.headersHelper !== void 0) lines.push(` headersHelper: ${JSON.stringify(entry.headersHelper)}`);
|
|
1374
|
+
}
|
|
1375
|
+
return lines;
|
|
1376
|
+
}
|
|
1326
1377
|
/** Build a single subagent .md file body (frontmatter + system prompt).
|
|
1327
1378
|
*
|
|
1328
1379
|
* `tools` (optional) becomes a `tools:` frontmatter allowlist RESTRICTING the
|
|
1329
1380
|
* subagent to exactly those tools (omission inherits the parent's full toolset,
|
|
1330
1381
|
* per Claude Code semantics). Used by the `worker-*` dispatchers to pin each to
|
|
1331
1382
|
* its single `mcp__<workersKey>__<mode>` tool — which physically prevents them
|
|
1332
|
-
* from spawning other agents or doing extra work.
|
|
1333
|
-
*
|
|
1334
|
-
*
|
|
1383
|
+
* from spawning other agents or doing extra work. `mcpServers` (optional) is
|
|
1384
|
+
* emitted as an inline `mcpServers` frontmatter list so the subagent connects
|
|
1385
|
+
* directly to its scoped MCP server(s) — a workaround for claude-code#30280
|
|
1386
|
+
* (Agent-tool subagents don't reliably inherit the session's HTTP MCP servers).
|
|
1387
|
+
* Names are validated by the caller (`writePeerAgentMdFiles`) / are
|
|
1388
|
+
* proxy-generated, so no escaping needed beyond the comma-join Claude Code's
|
|
1389
|
+
* frontmatter parser expects. */
|
|
1335
1390
|
function buildAgentMd(spec) {
|
|
1336
1391
|
const lines = [
|
|
1337
1392
|
"---",
|
|
@@ -1340,6 +1395,7 @@ function buildAgentMd(spec) {
|
|
|
1340
1395
|
];
|
|
1341
1396
|
if (spec.model) lines.push(`model: ${escapeYamlString(spec.model)}`);
|
|
1342
1397
|
if (spec.tools && spec.tools.length > 0) lines.push(`tools: [${spec.tools.map((t) => JSON.stringify(t)).join(", ")}]`);
|
|
1398
|
+
if (spec.mcpServers && Object.keys(spec.mcpServers).length > 0) lines.push(...emitMcpServersYaml(spec.mcpServers));
|
|
1343
1399
|
lines.push("---", "", spec.prompt, "");
|
|
1344
1400
|
return lines.join("\n");
|
|
1345
1401
|
}
|
|
@@ -1374,7 +1430,8 @@ async function writePeerAgentMdFiles(agents, opts) {
|
|
|
1374
1430
|
description: def.description,
|
|
1375
1431
|
prompt: def.prompt,
|
|
1376
1432
|
model: def.model,
|
|
1377
|
-
tools: def.tools
|
|
1433
|
+
tools: def.tools,
|
|
1434
|
+
mcpServers: def.mcpServers
|
|
1378
1435
|
}));
|
|
1379
1436
|
paths.push(filePath);
|
|
1380
1437
|
}
|
|
@@ -1572,7 +1629,9 @@ async function writePeerMcpRuntimeFiles(serverUrl, opts) {
|
|
|
1572
1629
|
browseAvailable: opts.browseAvailable,
|
|
1573
1630
|
nativeSubagentModel: opts.nativeSubagentModel,
|
|
1574
1631
|
nonce,
|
|
1575
|
-
codexHome
|
|
1632
|
+
codexHome,
|
|
1633
|
+
serverUrl,
|
|
1634
|
+
workspaceHeaderCmd: buildWorkspaceHeaderHelperCommand(process.execPath, process.argv[1])
|
|
1576
1635
|
});
|
|
1577
1636
|
await fs.unlink(mcpConfigPath).catch(() => {});
|
|
1578
1637
|
await fs.unlink(agentsPath).catch(() => {});
|
|
@@ -1852,6 +1911,44 @@ async function sanitizeServeSettingsEnv(settingsPath) {
|
|
|
1852
1911
|
*/
|
|
1853
1912
|
const NATIVE_RESEARCH_ALLOW_RULES = ["WebSearch", "WebFetch"];
|
|
1854
1913
|
/**
|
|
1914
|
+
* The routine native Claude Code tools serve auto-approves so ordinary agent
|
|
1915
|
+
* work stays seamless (no per-call prompt) — the exact surface a coding agent
|
|
1916
|
+
* uses turn to turn. Seeded into CloudCLI's `localStorage['claude-settings']`
|
|
1917
|
+
* `allowedTools` (feeds the Agent SDK `canUseTool` fast-path + `sdkOptions.allowedTools`).
|
|
1918
|
+
*
|
|
1919
|
+
* DELIBERATELY EXCLUDES the two interaction tools `AskUserQuestion` and
|
|
1920
|
+
* `ExitPlanMode`. CloudCLI hard-codes both into its `TOOLS_REQUIRING_INTERACTION`
|
|
1921
|
+
* set (claude-sdk.js) and renders a real question widget / plan Approve-Reject
|
|
1922
|
+
* card for them — but ONLY when `canUseTool` runs (non-bypass) AND they are NOT
|
|
1923
|
+
* pre-approved. Leaving them off this list is what routes them to the human
|
|
1924
|
+
* instead of letting the model auto-answer its own question. This is the same
|
|
1925
|
+
* line the interactive Claude CLI draws: routine tools seamless, decisions to
|
|
1926
|
+
* the user. Never add either name here.
|
|
1927
|
+
*
|
|
1928
|
+
* A tool omitted from this list degrades to a one-off Allow/Deny prompt in
|
|
1929
|
+
* CloudCLI — safe, not broken — so the list is a superset of the common surface
|
|
1930
|
+
* rather than an exact registry match (extra names for tools this Claude build
|
|
1931
|
+
* lacks are harmless no-ops).
|
|
1932
|
+
*/
|
|
1933
|
+
const SEAMLESS_BUILTIN_TOOLS = [
|
|
1934
|
+
"Task",
|
|
1935
|
+
"Bash",
|
|
1936
|
+
"BashOutput",
|
|
1937
|
+
"KillShell",
|
|
1938
|
+
"Glob",
|
|
1939
|
+
"Grep",
|
|
1940
|
+
"Read",
|
|
1941
|
+
"Edit",
|
|
1942
|
+
"Write",
|
|
1943
|
+
"NotebookEdit",
|
|
1944
|
+
"WebFetch",
|
|
1945
|
+
"WebSearch",
|
|
1946
|
+
"TodoWrite",
|
|
1947
|
+
"Skill",
|
|
1948
|
+
"SlashCommand",
|
|
1949
|
+
"EnterPlanMode"
|
|
1950
|
+
];
|
|
1951
|
+
/**
|
|
1855
1952
|
* Merge arbitrary allow rules into a mirror `settings.json` `permissions.allow`
|
|
1856
1953
|
* so the listed tools auto-run WITHOUT a permission prompt in every mode —
|
|
1857
1954
|
* including plan mode, where a matched allow rule bypasses the prompt for any
|
|
@@ -1893,42 +1990,42 @@ function planModeAllowRules(serverKeys) {
|
|
|
1893
1990
|
return [...[...new Set(serverKeys.filter(Boolean))].map((k) => `mcp__${k}`), ...NATIVE_RESEARCH_ALLOW_RULES];
|
|
1894
1991
|
}
|
|
1895
1992
|
/**
|
|
1896
|
-
*
|
|
1897
|
-
* `bypassPermissions` — the
|
|
1898
|
-
*
|
|
1899
|
-
* Agent-SDK `canUseTool` callback posts a browser `permission_request` and AWAITS
|
|
1900
|
-
* approval for every not-yet-allowed tool, stalling the chat on each injected
|
|
1901
|
-
* `mcp__*` and native call.
|
|
1993
|
+
* Ensure the serve mirror's `settings.json` `permissions.defaultMode` is NOT
|
|
1994
|
+
* `bypassPermissions` — the load-bearing half of serve's "seamless routine +
|
|
1995
|
+
* decisions to the user" model.
|
|
1902
1996
|
*
|
|
1903
|
-
*
|
|
1904
|
-
*
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
1907
|
-
*
|
|
1908
|
-
*
|
|
1909
|
-
*
|
|
1910
|
-
*
|
|
1911
|
-
*
|
|
1912
|
-
*
|
|
1997
|
+
* Under `bypassPermissions` the Agent SDK resolves approval at the permission-mode
|
|
1998
|
+
* step and SKIPS the `canUseTool` callback entirely (claude-sdk.js) — the only
|
|
1999
|
+
* channel CloudCLI's interactive widgets ride on, so `AskUserQuestion` and
|
|
2000
|
+
* `ExitPlanMode` auto-resolve on a model-generated answer and never reach the human.
|
|
2001
|
+
* CloudCLI leaves `sdkOptions.permissionMode` unset when the composer is in default
|
|
2002
|
+
* mode, so the binary falls back to THIS `defaultMode` — a stale `bypassPermissions`
|
|
2003
|
+
* here would silently re-defeat the fix through the settings vector.
|
|
2004
|
+
*
|
|
2005
|
+
* MINIMAL override: only rewrite when the mode is absent or an explicit
|
|
2006
|
+
* `bypassPermissions` (→ `"default"`, keeping `canUseTool` live). A user's
|
|
2007
|
+
* deliberate NON-bypass posture (`plan` / `acceptEdits` / `default`) is preserved
|
|
2008
|
+
* verbatim — all three already keep `canUseTool` live, so there is nothing to fix
|
|
2009
|
+
* and no reason to override their choice. `allow`/`deny`/`ask` are always untouched.
|
|
1913
2010
|
*
|
|
1914
2011
|
* Only ever rewrites the per-launch serve mirror, never the operator's real
|
|
1915
2012
|
* `~/.claude/settings.json`. Atomic temp+rename, mode 0o600. A non-object
|
|
1916
|
-
* settings.json throws
|
|
1917
|
-
* wraps this in warn-and-continue so a hiccup never blocks launch.
|
|
2013
|
+
* settings.json throws; the caller wraps this in warn-and-continue.
|
|
1918
2014
|
*
|
|
1919
|
-
* Opt out with `GH_ROUTER_SERVE_NO_AUTO_APPROVE=1` (the caller skips this
|
|
1920
|
-
* mirrored `defaultMode` is then left exactly
|
|
2015
|
+
* Opt out with `GH_ROUTER_SERVE_NO_AUTO_APPROVE=1` (the caller skips this AND the
|
|
2016
|
+
* allow-list/localStorage seed) — the mirrored `defaultMode` is then left exactly
|
|
2017
|
+
* as snapshotted and CloudCLI prompts on every not-yet-approved tool.
|
|
1921
2018
|
*/
|
|
1922
|
-
async function
|
|
2019
|
+
async function configureServeDefaultPermissionMode(settingsPath) {
|
|
1923
2020
|
const existing = await readSettingsObject(settingsPath) ?? {};
|
|
1924
2021
|
const permsRaw = existing.permissions;
|
|
1925
2022
|
const perms = permsRaw && typeof permsRaw === "object" && !Array.isArray(permsRaw) ? { ...permsRaw } : {};
|
|
1926
|
-
if (perms.defaultMode
|
|
1927
|
-
perms.defaultMode = "
|
|
2023
|
+
if (perms.defaultMode !== void 0 && perms.defaultMode !== "bypassPermissions") return { written: false };
|
|
2024
|
+
perms.defaultMode = "default";
|
|
1928
2025
|
await writeSettingsObject(settingsPath, {
|
|
1929
2026
|
...existing,
|
|
1930
2027
|
permissions: perms
|
|
1931
|
-
}, "
|
|
2028
|
+
}, "servemode");
|
|
1932
2029
|
return { written: true };
|
|
1933
2030
|
}
|
|
1934
2031
|
|
|
@@ -2797,7 +2894,7 @@ async function discoverGateCommands(cwd, opts) {
|
|
|
2797
2894
|
if (files.length === 0) return null;
|
|
2798
2895
|
let result;
|
|
2799
2896
|
try {
|
|
2800
|
-
const { runWorkerAgent } = await import("./engine-
|
|
2897
|
+
const { runWorkerAgent } = await import("./engine-DZjN7BuD.js");
|
|
2801
2898
|
result = await runWorkerAgent({
|
|
2802
2899
|
mode: "explore",
|
|
2803
2900
|
workspace: root,
|
|
@@ -4957,7 +5054,7 @@ function initProxyFromEnv() {
|
|
|
4957
5054
|
//#endregion
|
|
4958
5055
|
//#region package.json
|
|
4959
5056
|
var name = "github-router";
|
|
4960
|
-
var version$1 = "0.3.
|
|
5057
|
+
var version$1 = "0.3.206";
|
|
4961
5058
|
|
|
4962
5059
|
//#endregion
|
|
4963
5060
|
//#region src/lib/approval.ts
|
|
@@ -10778,7 +10875,7 @@ async function provisionServeEnhancements(serverUrl, opts = {}) {
|
|
|
10778
10875
|
return { removed: [] };
|
|
10779
10876
|
});
|
|
10780
10877
|
if (strip.removed.length) consola.info(`Serve: removed ${strip.removed.join(", ")} from the mirror settings (single-agent surface).`);
|
|
10781
|
-
if (process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== "1") await
|
|
10878
|
+
if (process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== "1") await configureServeDefaultPermissionMode(settingsPath).catch((err) => consola.warn(`Could not set serve permission defaults: ${String(err)}`));
|
|
10782
10879
|
await injectAllowRules(settingsPath, planModeAllowRules(injected.ok ? injected.serversAdded : [...Object.values(groupKeys).filter((k) => Boolean(k)), ...opts.codexCli === true ? ["codex-cli"] : []])).catch((err) => consola.warn(`Could not auto-approve injected tools: ${String(err)}`));
|
|
10783
10880
|
if (workerToolsEnabled()) {
|
|
10784
10881
|
await injectStopHookIntoSettingsFile(settingsPath, buildPromptSubmitHookCommand(process.execPath, process.argv[1]), "UserPromptSubmit", 45).catch((err) => consola.warn(`Could not register the UserPromptSubmit hook: ${String(err)}`));
|
|
@@ -11093,27 +11190,35 @@ const HEAD_CLOSE = "</head>";
|
|
|
11093
11190
|
/** CloudCLI's localStorage key for per-tool chat permission settings. */
|
|
11094
11191
|
const CLAUDE_SETTINGS_KEY = "claude-settings";
|
|
11095
11192
|
/**
|
|
11193
|
+
* The two interaction tools CloudCLI routes to the human (its
|
|
11194
|
+
* `TOOLS_REQUIRING_INTERACTION`). They must NEVER appear in the seeded
|
|
11195
|
+
* `allowedTools` — an exact-name match there would auto-approve them and defeat
|
|
11196
|
+
* the question widget / plan card. Stripped defensively at the seed chokepoint
|
|
11197
|
+
* so no upstream list (mcp names, a future caller) can reintroduce them.
|
|
11198
|
+
*/
|
|
11199
|
+
const NEVER_AUTO_APPROVE = new Set(["AskUserQuestion", "ExitPlanMode"]);
|
|
11200
|
+
/**
|
|
11096
11201
|
* Embed the token as a JS string literal. JSON.stringify escapes quotes and
|
|
11097
11202
|
* backslashes; we additionally neutralize `<` so the value can never break out
|
|
11098
11203
|
* of the surrounding <script> element regardless of contents.
|
|
11099
11204
|
*
|
|
11100
11205
|
* When `seedToolSettings` is set, also seed CloudCLI's `claude-settings`
|
|
11101
|
-
* localStorage with `{allowedTools
|
|
11102
|
-
*
|
|
11103
|
-
*
|
|
11104
|
-
*
|
|
11105
|
-
*
|
|
11106
|
-
*
|
|
11107
|
-
*
|
|
11108
|
-
*
|
|
11206
|
+
* localStorage with `{allowedTools:<routine+mcp>, disallowedTools:[], skipPermissions:false}`.
|
|
11207
|
+
* This is serve's "seamless routine + decisions to the user" model: `canUseTool`
|
|
11208
|
+
* stays LIVE (skipPermissions:false, NOT bypass), routine tools in `allowedTools`
|
|
11209
|
+
* auto-run with no prompt, and `AskUserQuestion`/`ExitPlanMode` — deliberately
|
|
11210
|
+
* NOT in the list and hard-cased by CloudCLI into `TOOLS_REQUIRING_INTERACTION` —
|
|
11211
|
+
* always reach the human as a real question widget / plan Approve-Reject card.
|
|
11212
|
+
* `skipPermissions:true` (bypass) would skip `canUseTool` entirely and silently
|
|
11213
|
+
* auto-answer those two, which is exactly what we are fixing.
|
|
11109
11214
|
*/
|
|
11110
11215
|
function buildInjection(token, seedToolSettings = false, seedAllowedTools = []) {
|
|
11111
11216
|
let body = `localStorage.setItem('auth-token',${JSON.stringify(token).replace(/</g, "\\u003c")});`;
|
|
11112
11217
|
if (seedToolSettings) {
|
|
11113
11218
|
const settings = JSON.stringify({
|
|
11114
|
-
allowedTools: seedAllowedTools,
|
|
11219
|
+
allowedTools: seedAllowedTools.filter((t) => !NEVER_AUTO_APPROVE.has(t)),
|
|
11115
11220
|
disallowedTools: [],
|
|
11116
|
-
skipPermissions:
|
|
11221
|
+
skipPermissions: false
|
|
11117
11222
|
});
|
|
11118
11223
|
const safeSettings = JSON.stringify(settings).replace(/</g, "\\u003c");
|
|
11119
11224
|
body += `localStorage.setItem('${CLAUDE_SETTINGS_KEY}',${safeSettings});`;
|
|
@@ -11542,6 +11647,7 @@ const serve$1 = defineCommand({
|
|
|
11542
11647
|
fleetOverTunnel: args["fleet-over-tunnel"] === true
|
|
11543
11648
|
});
|
|
11544
11649
|
const anthropicVars = getClaudeCodeEnvVars(serverUrl, chosenSlug);
|
|
11650
|
+
delete anthropicVars.CLAUDE_CODE_FORK_SUBAGENT;
|
|
11545
11651
|
if (enhancements.nonce) {
|
|
11546
11652
|
anthropicVars.GH_ROUTER_HOOK_MCP_URL = serverUrl;
|
|
11547
11653
|
anthropicVars.GH_ROUTER_HOOK_NONCE = enhancements.nonce;
|
|
@@ -11650,7 +11756,7 @@ const serve$1 = defineCommand({
|
|
|
11650
11756
|
bindPort: servePort,
|
|
11651
11757
|
authToken: token,
|
|
11652
11758
|
seedToolSettings: process.env.GH_ROUTER_SERVE_NO_AUTO_APPROVE !== "1",
|
|
11653
|
-
seedAllowedTools: enhancements.mcpToolNames ?? [],
|
|
11759
|
+
seedAllowedTools: [...SEAMLESS_BUILTIN_TOOLS, ...enhancements.mcpToolNames ?? []],
|
|
11654
11760
|
extraAllowedHosts,
|
|
11655
11761
|
extraAllowedOrigins,
|
|
11656
11762
|
allowDevtunnelHosts: tunnelMode,
|