pi-subagents 0.80.0 → 0.82.0
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/CHANGELOG.md +66 -0
- package/bundle/agents.js +18 -0
- package/bundle/background-work.js +16 -0
- package/bundle/capability-ceiling.js +22 -0
- package/bundle/child-tool-plan.js +11 -0
- package/bundle/chunk-43SCHIMD.js +57 -0
- package/bundle/chunk-4QFZZV3O.js +57 -0
- package/bundle/chunk-4SDU2BZX.js +1190 -0
- package/bundle/chunk-52UBALJL.js +753 -0
- package/bundle/chunk-5LFLVSTM.js +260 -0
- package/bundle/chunk-5TYMSZX3.js +235 -0
- package/bundle/chunk-62OCY2S4.js +14 -0
- package/bundle/chunk-7F6RKT6W.js +132 -0
- package/bundle/chunk-7IVSUMQQ.js +1337 -0
- package/bundle/chunk-7J4ZXMY7.js +18 -0
- package/bundle/chunk-7PCKZHR5.js +1234 -0
- package/bundle/chunk-7YMXOYEU.js +1373 -0
- package/bundle/chunk-B5EKXDHE.js +16870 -0
- package/bundle/chunk-CIZQXJ5Q.js +8619 -0
- package/bundle/chunk-CTPAE36Y.js +94 -0
- package/bundle/chunk-D4EHT5SJ.js +14 -0
- package/bundle/chunk-GC4DSPMH.js +946 -0
- package/bundle/chunk-GJECHSAR.js +179 -0
- package/bundle/chunk-M2CTFYT3.js +4164 -0
- package/bundle/chunk-MHZ4G23A.js +1296 -0
- package/bundle/chunk-N2BN7BF5.js +159 -0
- package/bundle/chunk-NF7IEKBH.js +490 -0
- package/bundle/chunk-NGGAFDL6.js +742 -0
- package/bundle/chunk-NIXWDZZV.js +9 -0
- package/bundle/chunk-NRQ75MGH.js +8327 -0
- package/bundle/chunk-OYF6AFDY.js +458 -0
- package/bundle/chunk-PLCCBLPY.js +526 -0
- package/bundle/chunk-RMQFMMAT.js +416 -0
- package/bundle/chunk-SIJ4S3EN.js +276 -0
- package/bundle/chunk-UKG4V45O.js +228 -0
- package/bundle/chunk-VH5OAWVG.js +4096 -0
- package/bundle/chunk-W2SZ22EM.js +5462 -0
- package/bundle/chunk-WVD4UEL4.js +9367 -0
- package/bundle/chunk-XSWOTIA3.js +175 -0
- package/bundle/chunk-YFCK7H2S.js +129 -0
- package/bundle/chunk-YKEWIB4M.js +380 -0
- package/bundle/chunk-YYREO77V.js +137 -0
- package/bundle/chunk-Z7KVHQHL.js +173 -0
- package/bundle/chunk-ZLINIYTP.js +8613 -0
- package/bundle/chunk-ZQGSDHL6.js +128 -0
- package/bundle/control-channel.js +11 -0
- package/bundle/delegation.js +14 -0
- package/bundle/external-job-provider.js +20 -0
- package/bundle/external-runs.js +21 -0
- package/bundle/index.js +30 -0
- package/bundle/inspectors.js +8 -0
- package/bundle/intercom-bridge.js +11 -0
- package/bundle/preflight.js +425 -0
- package/bundle/project-panes.js +28 -0
- package/bundle/required-child-extensions.js +6 -0
- package/bundle/shared-types.js +8 -0
- package/bundle/workflow-resources.js +7 -0
- package/docs/agents.md +31 -0
- package/docs/configuration.md +9 -1
- package/docs/extension-api.md +27 -0
- package/docs/observability.md +6 -2
- package/docs/tool-reference.md +3 -1
- package/docs/workflows.md +16 -0
- package/package.json +19 -19
- package/skills/pi-subagents/references/execution-controls.md +5 -4
- package/src/agents/agent-management.js +3 -3
- package/src/agents/agent-memory.js +3 -1
- package/src/agents/agents.js +6 -6
- package/src/agents/frontmatter.d.ts +4 -0
- package/src/agents/frontmatter.js +24 -3
- package/src/agents/skills.js +2 -1
- package/src/api/delegation.d.ts +20 -0
- package/src/extension/config.js +3 -0
- package/src/extension/herdr-pi-bridge.js +17 -10
- package/src/extension/index.js +109 -9
- package/src/extension/schemas.js +3 -3
- package/src/intercom/native-supervisor-channel.js +20 -0
- package/src/missions/lifecycle.js +7 -1
- package/src/runs/background/active-async-capacity.d.ts +1 -0
- package/src/runs/background/active-async-capacity.js +9 -3
- package/src/runs/background/async-execution.js +20 -10
- package/src/runs/background/async-job-tracker.d.ts +6 -1
- package/src/runs/background/async-job-tracker.js +9 -4
- package/src/runs/background/async-status.d.ts +2 -0
- package/src/runs/background/async-status.js +21 -2
- package/src/runs/background/await-async-run.js +3 -7
- package/src/runs/background/boot-id.d.ts +12 -0
- package/src/runs/background/boot-id.js +47 -0
- package/src/runs/background/interrupted-elsewhere.d.ts +22 -0
- package/src/runs/background/interrupted-elsewhere.js +68 -0
- package/src/runs/background/notify.d.ts +2 -0
- package/src/runs/background/notify.js +13 -0
- package/src/runs/background/result-delivery-ownership.d.ts +6 -0
- package/src/runs/background/result-delivery-ownership.js +19 -1
- package/src/runs/background/result-watcher.d.ts +3 -1
- package/src/runs/background/result-watcher.js +24 -8
- package/src/runs/background/run-child-session.d.ts +1 -6
- package/src/runs/background/run-child-session.js +19 -1
- package/src/runs/background/run-status.js +2 -1
- package/src/runs/background/stale-run-reconciler.d.ts +25 -0
- package/src/runs/background/stale-run-reconciler.js +54 -13
- package/src/runs/background/subagent-runner-bootstrap.js +3 -0
- package/src/runs/background/subagent-runner.d.ts +4 -0
- package/src/runs/background/subagent-runner.js +13 -5
- package/src/runs/background/subagent-wait.d.ts +1 -1
- package/src/runs/background/subagent-wait.js +6 -1
- package/src/runs/background/terminal-run-index.d.ts +1 -0
- package/src/runs/background/terminal-run-index.js +4 -2
- package/src/runs/foreground/async-stop-action.js +3 -1
- package/src/runs/foreground/execution.js +45 -5
- package/src/runs/foreground/foreground-history.d.ts +16 -2
- package/src/runs/foreground/foreground-history.js +133 -31
- package/src/runs/foreground/subagent-executor.d.ts +11 -0
- package/src/runs/foreground/subagent-executor.js +277 -74
- package/src/runs/shared/child-launch.d.ts +2 -2
- package/src/runs/shared/child-session.d.ts +3 -2
- package/src/runs/shared/child-session.js +51 -8
- package/src/runs/shared/child-tool-plan.d.ts +7 -0
- package/src/runs/shared/child-tool-plan.js +21 -6
- package/src/runs/shared/herdr-connection.d.ts +17 -8
- package/src/runs/shared/herdr-connection.js +19 -7
- package/src/runs/shared/herdr-external-adapters.d.ts +3 -2
- package/src/runs/shared/herdr-external-adapters.js +1 -1
- package/src/runs/shared/herdr-machine.d.ts +12 -2
- package/src/runs/shared/herdr-machine.js +55 -9
- package/src/runs/shared/herdr-pane-host.d.ts +88 -0
- package/src/runs/shared/herdr-pane-host.js +155 -0
- package/src/runs/shared/herdr-placed-run.d.ts +27 -150
- package/src/runs/shared/herdr-placed-run.js +48 -395
- package/src/runs/shared/local-machine-access.d.ts +5 -0
- package/src/runs/shared/local-machine-access.js +28 -0
- package/src/runs/shared/orca-progress-tabs.d.ts +0 -1
- package/src/runs/shared/orca-progress-tabs.js +1 -24
- package/src/runs/shared/parallel-utils.d.ts +1 -1
- package/src/runs/shared/placed-run-owner.d.ts +49 -0
- package/src/runs/shared/placed-run-owner.js +93 -0
- package/src/runs/shared/placement-host.d.ts +99 -0
- package/src/runs/shared/placement-host.js +157 -0
- package/src/runs/shared/rex-machine.d.ts +25 -0
- package/src/runs/shared/rex-machine.js +83 -0
- package/src/runs/shared/rex-pane-host.d.ts +62 -0
- package/src/runs/shared/rex-pane-host.js +327 -0
- package/src/runs/shared/structured-output.d.ts +1 -0
- package/src/runs/shared/structured-output.js +1 -0
- package/src/shared/accessible-dir.js +4 -1
- package/src/shared/completion-owner.d.ts +5 -0
- package/src/shared/completion-owner.js +5 -0
- package/src/shared/provider-diagnostics.d.ts +9 -0
- package/src/shared/provider-diagnostics.js +75 -0
- package/src/shared/session-identity.d.ts +2 -0
- package/src/shared/session-identity.js +26 -0
- package/src/shared/types.d.ts +48 -6
- package/src/slash/delegation-adapters.d.ts +3 -1
- package/src/slash/delegation-adapters.js +7 -0
- package/src/slash/prompt-template-bridge.js +3 -1
- package/src/slash/subagents-admin.d.ts +1 -0
- package/src/slash/subagents-admin.js +1 -1
- package/src/tui/fleet-layout.d.ts +20 -0
- package/src/tui/fleet-layout.js +17 -0
- package/src/tui/fleet-viewport.d.ts +12 -0
- package/src/tui/fleet-viewport.js +35 -0
- package/src/tui/fleet.d.ts +6 -6
- package/src/tui/fleet.js +93 -66
- package/src/tui/render.d.ts +6 -0
- package/src/tui/render.js +3 -3
- package/src/tui/subagent-messages.js +1 -0
- package/src/workflows/scripted-workflow.d.ts +1 -1
- package/src/workflows/structured-workflow-scripts.d.ts +1 -0
- package/src/workflows/structured-workflow-scripts.js +5 -4
- package/src/workflows/workflow-auto-resume.d.ts +32 -0
- package/src/workflows/workflow-auto-resume.js +102 -0
- package/src/workflows/workflow-launch-record.d.ts +27 -0
- package/src/workflows/workflow-launch-record.js +48 -0
- package/src/workflows/workflow-resources.js +2 -2
- package/src/workflows/workflow-resume-claim.d.ts +82 -0
- package/src/workflows/workflow-resume-claim.js +210 -0
- package/src/workflows/workflow-resume.d.ts +56 -0
- package/src/workflows/workflow-resume.js +106 -0
- package/src/workflows/workflow-reuse.d.ts +15 -9
- package/src/workflows/workflow-reuse.js +77 -14
- package/src/workflows/workflow-revival.d.ts +2 -0
- package/src/workflows/workflow-revival.js +2 -1
- package/src/workflows/workflow-stop-cause.d.ts +6 -0
- package/src/workflows/workflow-stop-cause.js +8 -0
|
@@ -0,0 +1,1190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
snapshotRequiredChildExtensions
|
|
3
|
+
} from "./chunk-CTPAE36Y.js";
|
|
4
|
+
import {
|
|
5
|
+
THINKING_LEVELS,
|
|
6
|
+
findConfiguredProjectRoot,
|
|
7
|
+
getAgentDir,
|
|
8
|
+
getConfigDirName,
|
|
9
|
+
getProjectConfigDir
|
|
10
|
+
} from "./chunk-CIZQXJ5Q.js";
|
|
11
|
+
import {
|
|
12
|
+
TEMP_ROOT_DIR
|
|
13
|
+
} from "./chunk-5LFLVSTM.js";
|
|
14
|
+
import {
|
|
15
|
+
capabilityCeilingAgentRestrictionSources,
|
|
16
|
+
intersectSubagentCapabilityCeilings,
|
|
17
|
+
isAgentAllowedByCapabilityCeiling
|
|
18
|
+
} from "./chunk-Z7KVHQHL.js";
|
|
19
|
+
|
|
20
|
+
// src/runs/shared/child-tool-plan.ts
|
|
21
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
22
|
+
import * as fs4 from "node:fs";
|
|
23
|
+
import * as path4 from "node:path";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
|
|
26
|
+
// src/runs/shared/mcp-direct-tool-allowlist.ts
|
|
27
|
+
import { createHash } from "node:crypto";
|
|
28
|
+
import * as fs2 from "node:fs";
|
|
29
|
+
import * as os2 from "node:os";
|
|
30
|
+
import * as path2 from "node:path";
|
|
31
|
+
|
|
32
|
+
// src/runs/shared/mcp-config-sources.ts
|
|
33
|
+
import * as fs from "node:fs";
|
|
34
|
+
import * as os from "node:os";
|
|
35
|
+
import * as path from "node:path";
|
|
36
|
+
var PACKAGE_CONFIG_ROOT = "npm";
|
|
37
|
+
var PACKAGE_GIT_ROOT = "git";
|
|
38
|
+
var PLUGIN_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json";
|
|
39
|
+
var PLUGIN_MCP_SCHEMA = "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json";
|
|
40
|
+
var PLUGIN_NAME_PATTERN = /^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
41
|
+
var PLUGIN_STDIO_FIELDS = /* @__PURE__ */ new Set(["type", "command", "args", "env", "cwd"]);
|
|
42
|
+
var PLUGIN_HTTP_FIELDS = /* @__PURE__ */ new Set(["type", "url", "headers"]);
|
|
43
|
+
function isMcpServerDefinition(value) {
|
|
44
|
+
if (!isRecord(value)) return false;
|
|
45
|
+
for (const field of ["command", "socket", "cwd", "url", "bearerToken", "bearerTokenEnv", "protocolVersion", "httpTransport", "pluginDataDir"]) {
|
|
46
|
+
if (value[field] !== void 0 && typeof value[field] !== "string") return false;
|
|
47
|
+
}
|
|
48
|
+
for (const field of ["args", "includeTools", "excludeTools"]) {
|
|
49
|
+
if (value[field] !== void 0 && !isStringArray(value[field])) return false;
|
|
50
|
+
}
|
|
51
|
+
for (const field of ["env", "headers"]) {
|
|
52
|
+
if (value[field] !== void 0 && !isStringRecord(value[field])) return false;
|
|
53
|
+
}
|
|
54
|
+
for (const field of ["exposeResources", "literalEnv", "inheritEnv"]) {
|
|
55
|
+
if (value[field] !== void 0 && typeof value[field] !== "boolean") return false;
|
|
56
|
+
}
|
|
57
|
+
if (value.requestHeadersCommand !== void 0 && !isRequestHeadersCommand(value.requestHeadersCommand)) return false;
|
|
58
|
+
if (value.auth !== void 0 && value.auth !== "oauth" && value.auth !== "bearer" && value.auth !== false) return false;
|
|
59
|
+
return value.directTools === void 0 || typeof value.directTools === "boolean" || isStringArray(value.directTools);
|
|
60
|
+
}
|
|
61
|
+
function isRecord(value) {
|
|
62
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
63
|
+
}
|
|
64
|
+
function isStringArray(value) {
|
|
65
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
66
|
+
}
|
|
67
|
+
function isStringRecord(value) {
|
|
68
|
+
return isRecord(value) && Object.values(value).every((entry) => typeof entry === "string");
|
|
69
|
+
}
|
|
70
|
+
function isRequestHeadersCommand(value) {
|
|
71
|
+
if (!isRecord(value) || typeof value.command !== "string") return false;
|
|
72
|
+
if (value.args !== void 0 && !isStringArray(value.args)) return false;
|
|
73
|
+
if (value.env !== void 0 && !isStringRecord(value.env)) return false;
|
|
74
|
+
return value.timeoutMs === void 0 || typeof value.timeoutMs === "number" && Number.isFinite(value.timeoutMs);
|
|
75
|
+
}
|
|
76
|
+
function loadPackageMcpServers(cwd) {
|
|
77
|
+
const servers = {};
|
|
78
|
+
const seen = /* @__PURE__ */ new Set();
|
|
79
|
+
for (const packageRoot of getConfiguredPackageRoots(cwd)) {
|
|
80
|
+
const manifest = readJson(path.join(packageRoot, "package.json"));
|
|
81
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) continue;
|
|
82
|
+
const packageName = manifest.name;
|
|
83
|
+
if (typeof packageName !== "string" || !packageName) continue;
|
|
84
|
+
const mcpValue = manifest.pi?.mcp;
|
|
85
|
+
const configPaths = typeof mcpValue === "string" ? [mcpValue] : Array.isArray(mcpValue) && mcpValue.every((value) => typeof value === "string") ? mcpValue : [];
|
|
86
|
+
const packagePrefix = formatName(packageName, "package");
|
|
87
|
+
for (const configPath of configPaths) {
|
|
88
|
+
const resolvedPath = resolvePackageConfigPath(packageRoot, configPath);
|
|
89
|
+
if (!resolvedPath) continue;
|
|
90
|
+
const config = readJson(resolvedPath);
|
|
91
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) continue;
|
|
92
|
+
const rawServers = config.mcpServers;
|
|
93
|
+
if (!rawServers || typeof rawServers !== "object" || Array.isArray(rawServers)) continue;
|
|
94
|
+
for (const [serverName, definition] of Object.entries(rawServers)) {
|
|
95
|
+
if (!isMcpServerDefinition(definition)) continue;
|
|
96
|
+
const normalizedName = `${packagePrefix}__${formatName(serverName, "server")}`;
|
|
97
|
+
if (seen.has(normalizedName)) continue;
|
|
98
|
+
seen.add(normalizedName);
|
|
99
|
+
servers[normalizedName] = definition;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return servers;
|
|
104
|
+
}
|
|
105
|
+
function loadAgentPluginMcpServers(paths, cwd) {
|
|
106
|
+
const servers = {};
|
|
107
|
+
if (!Array.isArray(paths)) return servers;
|
|
108
|
+
for (const configuredPath of paths) {
|
|
109
|
+
if (typeof configuredPath !== "string") continue;
|
|
110
|
+
const pluginRoot = resolvePluginPath(configuredPath, cwd);
|
|
111
|
+
const manifest = readJson(path.join(pluginRoot, "plugin.json"));
|
|
112
|
+
if (!isValidPluginManifest(manifest)) continue;
|
|
113
|
+
const config = readJson(path.join(pluginRoot, "mcp.json"));
|
|
114
|
+
if (!isValidPluginConfig(config)) continue;
|
|
115
|
+
const rawServers = config.mcpServers;
|
|
116
|
+
for (const [serverName, rawDefinition] of Object.entries(rawServers)) {
|
|
117
|
+
const definition = translatePluginServer(manifest.name, pluginRoot, rawDefinition);
|
|
118
|
+
if (!definition) continue;
|
|
119
|
+
const normalizedName = `${formatName(manifest.name, "plugin")}__${formatName(serverName, "server")}`;
|
|
120
|
+
if (Object.hasOwn(servers, normalizedName)) continue;
|
|
121
|
+
servers[normalizedName] = definition;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return servers;
|
|
125
|
+
}
|
|
126
|
+
function getConfiguredPackageRoots(cwd) {
|
|
127
|
+
const roots = [];
|
|
128
|
+
const projectConfigDir = findProjectConfigDir(cwd);
|
|
129
|
+
const sources = [
|
|
130
|
+
{ path: path.join(projectConfigDir, "settings.json"), baseDir: projectConfigDir },
|
|
131
|
+
{ path: path.join(getAgentDir(), "settings.json"), baseDir: getAgentDir() }
|
|
132
|
+
];
|
|
133
|
+
for (const source of sources) {
|
|
134
|
+
const settings = readJson(source.path);
|
|
135
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings)) continue;
|
|
136
|
+
const packages = settings.packages;
|
|
137
|
+
if (!Array.isArray(packages)) continue;
|
|
138
|
+
for (const entry of packages) {
|
|
139
|
+
const packageSource = typeof entry === "string" ? entry : entry && typeof entry === "object" && !Array.isArray(entry) && typeof entry.source === "string" ? entry.source : void 0;
|
|
140
|
+
if (!packageSource) continue;
|
|
141
|
+
const root = resolvePackageRoot(packageSource, source.baseDir);
|
|
142
|
+
if (root && !roots.includes(root)) roots.push(root);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return roots;
|
|
146
|
+
}
|
|
147
|
+
function findProjectConfigDir(cwd) {
|
|
148
|
+
const projectRoot = findConfiguredProjectRoot(path.resolve(cwd));
|
|
149
|
+
return projectRoot ? getProjectConfigDir(projectRoot) : path.join(path.resolve(cwd), getConfigDirName());
|
|
150
|
+
}
|
|
151
|
+
function resolvePackageRoot(source, baseDir) {
|
|
152
|
+
const trimmed = source.trim();
|
|
153
|
+
if (!trimmed) return void 0;
|
|
154
|
+
if (trimmed.startsWith("npm:")) {
|
|
155
|
+
const packageName = parseNpmPackageName(trimmed);
|
|
156
|
+
return packageName ? resolveContainedPath(path.join(baseDir, PACKAGE_CONFIG_ROOT, "node_modules"), packageName) ?? void 0 : void 0;
|
|
157
|
+
}
|
|
158
|
+
if (trimmed.startsWith("git:") || /^(?:https?:\/\/|ssh:\/\/|git@[^:]+:)/.test(trimmed)) {
|
|
159
|
+
const parsed = parseGitPackagePath(trimmed.startsWith("git:") ? trimmed : `git:${trimmed}`);
|
|
160
|
+
return parsed ? path.join(baseDir, PACKAGE_GIT_ROOT, parsed.host, parsed.repoPath) : void 0;
|
|
161
|
+
}
|
|
162
|
+
const localPath = trimmed.startsWith("file:") ? trimmed.slice(5) : trimmed;
|
|
163
|
+
if (localPath === "~") return os.homedir();
|
|
164
|
+
if (localPath.startsWith("~/")) return path.join(os.homedir(), localPath.slice(2));
|
|
165
|
+
return path.isAbsolute(localPath) ? path.resolve(localPath) : path.resolve(baseDir, localPath);
|
|
166
|
+
}
|
|
167
|
+
function parseNpmPackageName(source) {
|
|
168
|
+
const spec = source.slice(4).trim();
|
|
169
|
+
if (!spec) return void 0;
|
|
170
|
+
const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/);
|
|
171
|
+
const packageName = match?.[1] ?? spec;
|
|
172
|
+
return resolveContainedPath("/", packageName) ? packageName : void 0;
|
|
173
|
+
}
|
|
174
|
+
function parseGitPackagePath(source) {
|
|
175
|
+
const spec = source.slice(4).trim();
|
|
176
|
+
if (!spec) return void 0;
|
|
177
|
+
let host = "";
|
|
178
|
+
let repoPath = "";
|
|
179
|
+
const scpLike = spec.match(/^git@([^:]+):(.+)$/);
|
|
180
|
+
if (scpLike) {
|
|
181
|
+
host = scpLike[1] ?? "";
|
|
182
|
+
repoPath = scpLike[2] ?? "";
|
|
183
|
+
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(spec)) {
|
|
184
|
+
try {
|
|
185
|
+
const url = new URL(spec);
|
|
186
|
+
host = url.hostname;
|
|
187
|
+
repoPath = url.pathname.replace(/^\/+/, "");
|
|
188
|
+
} catch {
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
const slashIndex = spec.indexOf("/");
|
|
193
|
+
if (slashIndex < 0) return void 0;
|
|
194
|
+
host = spec.slice(0, slashIndex);
|
|
195
|
+
repoPath = spec.slice(slashIndex + 1);
|
|
196
|
+
}
|
|
197
|
+
const normalizedPath = stripGitRef(repoPath).replace(/\.git$/, "").replace(/^\/+/, "");
|
|
198
|
+
if (!host || !isSafePackagePath(host) || !isSafePackagePath(normalizedPath) || normalizedPath.split(/[\\/]/).length < 2) {
|
|
199
|
+
return void 0;
|
|
200
|
+
}
|
|
201
|
+
return { host, repoPath: normalizedPath };
|
|
202
|
+
}
|
|
203
|
+
function stripGitRef(repoPath) {
|
|
204
|
+
const atIndex = repoPath.indexOf("@");
|
|
205
|
+
const hashIndex = repoPath.indexOf("#");
|
|
206
|
+
const refIndex = [atIndex, hashIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
207
|
+
return refIndex === void 0 ? repoPath : repoPath.slice(0, refIndex);
|
|
208
|
+
}
|
|
209
|
+
function isSafePackagePath(value) {
|
|
210
|
+
return value.length > 0 && !path.isAbsolute(value) && value.split(/[\\/]/).every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
211
|
+
}
|
|
212
|
+
function resolvePackageConfigPath(packageRoot, configuredPath) {
|
|
213
|
+
const lexicalPath = resolveContainedPath(packageRoot, configuredPath);
|
|
214
|
+
if (!lexicalPath || !fs.existsSync(lexicalPath)) return void 0;
|
|
215
|
+
try {
|
|
216
|
+
if (!fs.statSync(lexicalPath).isFile()) return void 0;
|
|
217
|
+
const packageRealPath = fs.realpathSync(packageRoot);
|
|
218
|
+
const configRealPath = fs.realpathSync(lexicalPath);
|
|
219
|
+
return resolveContainedPath(packageRealPath, configRealPath) ?? void 0;
|
|
220
|
+
} catch {
|
|
221
|
+
return void 0;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function resolveContainedPath(root, value) {
|
|
225
|
+
const resolved = path.resolve(root, value);
|
|
226
|
+
const relative2 = path.relative(root, resolved);
|
|
227
|
+
return relative2 === "" || !relative2.startsWith("..") && !relative2.startsWith(path.sep) && !path.isAbsolute(relative2) ? resolved : void 0;
|
|
228
|
+
}
|
|
229
|
+
function resolvePluginPath(configuredPath, cwd) {
|
|
230
|
+
if (configuredPath === "~") return path.resolve(process.env.HOME ?? "", ".");
|
|
231
|
+
if (configuredPath.startsWith("~/")) return path.resolve(process.env.HOME ?? "", configuredPath.slice(2));
|
|
232
|
+
return path.isAbsolute(configuredPath) ? path.resolve(configuredPath) : path.resolve(cwd, configuredPath);
|
|
233
|
+
}
|
|
234
|
+
function isValidPluginManifest(value) {
|
|
235
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
236
|
+
const manifest = value;
|
|
237
|
+
return manifest.$schema === PLUGIN_SCHEMA && typeof manifest.name === "string" && manifest.name.length >= 1 && manifest.name.length <= 64 && PLUGIN_NAME_PATTERN.test(manifest.name);
|
|
238
|
+
}
|
|
239
|
+
function isValidPluginConfig(value) {
|
|
240
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
241
|
+
const config = value;
|
|
242
|
+
return config.$schema === PLUGIN_MCP_SCHEMA && Object.keys(config).every((key) => key === "$schema" || key === "mcpServers") && !!config.mcpServers && typeof config.mcpServers === "object" && !Array.isArray(config.mcpServers);
|
|
243
|
+
}
|
|
244
|
+
function translatePluginServer(pluginName, pluginRoot, value) {
|
|
245
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
246
|
+
const raw = value;
|
|
247
|
+
if (raw.type === "stdio") return translatePluginStdioServer(pluginName, pluginRoot, raw);
|
|
248
|
+
if (raw.type === "streamable-http" || raw.type === "sse") return translatePluginHttpServer(raw);
|
|
249
|
+
return void 0;
|
|
250
|
+
}
|
|
251
|
+
function translatePluginStdioServer(pluginName, pluginRoot, raw) {
|
|
252
|
+
if ([...Object.keys(raw)].some((key) => !PLUGIN_STDIO_FIELDS.has(key))) return void 0;
|
|
253
|
+
if (typeof raw.command !== "string" || !raw.command) return void 0;
|
|
254
|
+
if (!isBareCommand(raw.command) && !raw.command.startsWith("./")) return void 0;
|
|
255
|
+
const args = stringArray(raw.args);
|
|
256
|
+
if (raw.args !== void 0 && !args) return void 0;
|
|
257
|
+
const env = stringRecord(raw.env);
|
|
258
|
+
if (raw.env !== void 0 && !env) return void 0;
|
|
259
|
+
if (env && (Object.hasOwn(env, "PLUGIN_ROOT") || Object.hasOwn(env, "PLUGIN_DATA"))) return void 0;
|
|
260
|
+
const pluginDataDir = path.join(getAgentDir(), "agent-plugin-data", pluginName);
|
|
261
|
+
const command = raw.command.startsWith("./") ? resolveContainedPath(pluginRoot, raw.command) : raw.command;
|
|
262
|
+
if (!command) return void 0;
|
|
263
|
+
const cwd = resolvePluginCwd(raw.cwd, pluginRoot, pluginDataDir);
|
|
264
|
+
if (!cwd) return void 0;
|
|
265
|
+
return {
|
|
266
|
+
command,
|
|
267
|
+
args: (args ?? []).map((value) => expandPluginPlaceholders(value, pluginRoot, pluginDataDir)),
|
|
268
|
+
env: {
|
|
269
|
+
...Object.fromEntries(Object.entries(env ?? {}).map(([key, value]) => [key, expandPluginPlaceholders(value, pluginRoot, pluginDataDir)])),
|
|
270
|
+
PLUGIN_ROOT: pluginRoot,
|
|
271
|
+
PLUGIN_DATA: pluginDataDir
|
|
272
|
+
},
|
|
273
|
+
cwd,
|
|
274
|
+
pluginDataDir,
|
|
275
|
+
literalEnv: true
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function translatePluginHttpServer(raw) {
|
|
279
|
+
if ([...Object.keys(raw)].some((key) => !PLUGIN_HTTP_FIELDS.has(key))) return void 0;
|
|
280
|
+
if (typeof raw.url !== "string" || !isValidPluginUrl(raw.url)) return void 0;
|
|
281
|
+
const headers = stringRecord(raw.headers);
|
|
282
|
+
if (raw.headers !== void 0 && !headers) return void 0;
|
|
283
|
+
if (headers) {
|
|
284
|
+
const normalized = /* @__PURE__ */ new Set();
|
|
285
|
+
for (const key of Object.keys(headers)) {
|
|
286
|
+
const lower = key.toLowerCase();
|
|
287
|
+
if (normalized.has(lower)) return void 0;
|
|
288
|
+
normalized.add(lower);
|
|
289
|
+
}
|
|
290
|
+
try {
|
|
291
|
+
new Headers(headers);
|
|
292
|
+
} catch {
|
|
293
|
+
return void 0;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return { url: raw.url, ...headers ? { headers } : {}, httpTransport: raw.type };
|
|
297
|
+
}
|
|
298
|
+
function resolvePluginCwd(value, pluginRoot, pluginDataDir) {
|
|
299
|
+
if (value === void 0) return pluginRoot;
|
|
300
|
+
if (typeof value !== "string") return void 0;
|
|
301
|
+
if (value.startsWith("./")) return resolveContainedPath(pluginRoot, value);
|
|
302
|
+
if (value === "${PLUGIN_ROOT}" || value.startsWith("${PLUGIN_ROOT}/")) return resolveContainedPath(pluginRoot, value.replace("${PLUGIN_ROOT}", "."));
|
|
303
|
+
if (value === "${PLUGIN_DATA}" || value.startsWith("${PLUGIN_DATA}/")) return resolveContainedPath(pluginDataDir, value.replace("${PLUGIN_DATA}", "."));
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
function expandPluginPlaceholders(value, pluginRoot, pluginDataDir) {
|
|
307
|
+
return value.replaceAll("${PLUGIN_ROOT}", pluginRoot).replaceAll("${PLUGIN_DATA}", pluginDataDir);
|
|
308
|
+
}
|
|
309
|
+
function isBareCommand(command) {
|
|
310
|
+
return !command.includes("/") && !command.includes("\\") && !command.includes("${PLUGIN_ROOT}") && !command.includes("${PLUGIN_DATA}");
|
|
311
|
+
}
|
|
312
|
+
function isValidPluginUrl(value) {
|
|
313
|
+
if (value.includes("${") || value.includes("$env:") || value.includes("{env:")) return false;
|
|
314
|
+
let url;
|
|
315
|
+
try {
|
|
316
|
+
url = new URL(value);
|
|
317
|
+
} catch {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
321
|
+
if (url.username || url.password || url.hash) return false;
|
|
322
|
+
if (url.protocol === "https:") return true;
|
|
323
|
+
const host = url.hostname.toLowerCase();
|
|
324
|
+
return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(host);
|
|
325
|
+
}
|
|
326
|
+
function stringArray(value) {
|
|
327
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
328
|
+
}
|
|
329
|
+
function stringRecord(value) {
|
|
330
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
331
|
+
const entries = Object.entries(value);
|
|
332
|
+
if (entries.some(([, entry]) => typeof entry !== "string")) return void 0;
|
|
333
|
+
return Object.fromEntries(entries);
|
|
334
|
+
}
|
|
335
|
+
function formatName(value, fallback) {
|
|
336
|
+
return value.replace(/[^A-Za-z0-9_-]+/g, "_").replace(/^[_-]+|[_-]+$/g, "") || fallback;
|
|
337
|
+
}
|
|
338
|
+
function readJson(filePath) {
|
|
339
|
+
let content;
|
|
340
|
+
try {
|
|
341
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (error.code === "ENOENT") return void 0;
|
|
344
|
+
throw new Error(`Failed to read JSON file '${filePath}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
return JSON.parse(content);
|
|
348
|
+
} catch (error) {
|
|
349
|
+
throw new Error(`Invalid JSON in '${filePath}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/runs/shared/mcp-direct-tool-grant.ts
|
|
354
|
+
var BUILTIN_TOOL_NAMES = /* @__PURE__ */ new Set(["read", "bash", "edit", "write", "grep", "find", "ls", "mcp"]);
|
|
355
|
+
function normalizeMcpDirectToolSelectors(selectors) {
|
|
356
|
+
return [...new Set((selectors ?? []).map((selector) => selector.replace(/\/+$/, "")).filter(Boolean))];
|
|
357
|
+
}
|
|
358
|
+
function parseMcpDirectToolSelectors(selectors) {
|
|
359
|
+
const servers = /* @__PURE__ */ new Set();
|
|
360
|
+
const tools = /* @__PURE__ */ new Map();
|
|
361
|
+
for (const item of selectors) {
|
|
362
|
+
if (item.includes("/")) {
|
|
363
|
+
const [server, tool] = item.split("/", 2);
|
|
364
|
+
if (server && tool) {
|
|
365
|
+
if (!tools.has(server)) tools.set(server, /* @__PURE__ */ new Set());
|
|
366
|
+
tools.get(server).add(tool);
|
|
367
|
+
} else if (server) {
|
|
368
|
+
servers.add(server);
|
|
369
|
+
}
|
|
370
|
+
} else if (item) {
|
|
371
|
+
servers.add(item);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return { servers, tools };
|
|
375
|
+
}
|
|
376
|
+
function planMcpDirectToolGrant(input) {
|
|
377
|
+
const selectors = normalizeMcpDirectToolSelectors(input.selectors);
|
|
378
|
+
if (selectors.length === 0) return { selections: [], unresolvedSelectors: [] };
|
|
379
|
+
const { servers: selectedServers, tools: selectedTools } = parseMcpDirectToolSelectors(selectors);
|
|
380
|
+
const selections = [];
|
|
381
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
382
|
+
for (const [serverName, server] of Object.entries(input.servers)) {
|
|
383
|
+
const metadata = input.metadata[serverName];
|
|
384
|
+
if (!metadata) continue;
|
|
385
|
+
const toolFilter = selectedServers.has(serverName) ? true : selectedTools.get(serverName);
|
|
386
|
+
if (!toolFilter) continue;
|
|
387
|
+
const prefix = normalizeMcpToolPrefix(server.toolPrefix ?? input.toolPrefix);
|
|
388
|
+
for (const tool of Array.isArray(metadata.tools) ? metadata.tools : []) {
|
|
389
|
+
if (typeof tool?.name !== "string" || !tool.name) continue;
|
|
390
|
+
if (toolFilter !== true && !toolFilter.has(tool.name)) continue;
|
|
391
|
+
if (!isToolAllowed(tool.name, serverName, prefix, server.includeTools, server.excludeTools)) continue;
|
|
392
|
+
const name = formatToolName(tool.name, serverName, prefix);
|
|
393
|
+
if (BUILTIN_TOOL_NAMES.has(name) || seenNames.has(name)) continue;
|
|
394
|
+
seenNames.add(name);
|
|
395
|
+
selections.push({ name, selector: `${serverName}/${tool.name}` });
|
|
396
|
+
}
|
|
397
|
+
if (server.exposeResources === false) continue;
|
|
398
|
+
for (const resource of Array.isArray(metadata.resources) ? metadata.resources : []) {
|
|
399
|
+
if (typeof resource?.name !== "string" || !resource.name || typeof resource.uri !== "string" || !resource.uri) continue;
|
|
400
|
+
const baseName = `get_${resourceNameToToolName(resource.name)}`;
|
|
401
|
+
if (toolFilter !== true && !toolFilter.has(baseName)) continue;
|
|
402
|
+
if (!isToolAllowed(baseName, serverName, prefix, server.includeTools, server.excludeTools)) continue;
|
|
403
|
+
const name = formatToolName(baseName, serverName, prefix);
|
|
404
|
+
if (BUILTIN_TOOL_NAMES.has(name) || seenNames.has(name)) continue;
|
|
405
|
+
seenNames.add(name);
|
|
406
|
+
selections.push({ name, selector: `${serverName}/${baseName}` });
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const unresolvedSelectors = selectors.filter(
|
|
410
|
+
(selector) => selector.includes("/") ? !selections.some((selection) => selection.selector === selector) : !selections.some((selection) => selection.selector.startsWith(`${selector}/`))
|
|
411
|
+
);
|
|
412
|
+
return { selections, unresolvedSelectors };
|
|
413
|
+
}
|
|
414
|
+
function normalizeMcpToolPrefix(value) {
|
|
415
|
+
return value === "none" || value === "short" || value === "mcp" || value === "server" ? value : "server";
|
|
416
|
+
}
|
|
417
|
+
function formatUnresolvedMcpDirectToolSelectors(selectors) {
|
|
418
|
+
return `Unresolved MCP direct-tool selectors: ${selectors.join(", ")}. Direct MCP tools require a matching configured server and fresh metadata cache; runtime-registered servers require a host/pi-mcp-adapter handoff before child launch.`;
|
|
419
|
+
}
|
|
420
|
+
function getServerPrefix(serverName, mode) {
|
|
421
|
+
if (mode === "none") return "";
|
|
422
|
+
if (mode === "short") return sanitizeServerPrefix(serverName.replace(/-?mcp$/i, "")) || "mcp";
|
|
423
|
+
if (mode === "mcp") return `mcp__${sanitizeServerPrefix(serverName)}`;
|
|
424
|
+
return sanitizeServerPrefix(serverName);
|
|
425
|
+
}
|
|
426
|
+
function sanitizeServerPrefix(serverName) {
|
|
427
|
+
return Array.from(serverName, (char) => /^[A-Za-z0-9_-]$/.test(char) ? char : `_${char.codePointAt(0).toString(16)}_`).join("");
|
|
428
|
+
}
|
|
429
|
+
function formatToolName(toolName, serverName, prefix) {
|
|
430
|
+
const serverPrefix = getServerPrefix(serverName, prefix);
|
|
431
|
+
const sanitized = toolName.replace(/\./g, "_");
|
|
432
|
+
if (serverPrefix && sanitized.startsWith(`${serverPrefix}_`) && sanitized.length > serverPrefix.length + 1) {
|
|
433
|
+
return sanitized;
|
|
434
|
+
}
|
|
435
|
+
return serverPrefix ? `${serverPrefix}_${sanitized}` : sanitized;
|
|
436
|
+
}
|
|
437
|
+
function isToolAllowed(toolName, serverName, prefix, includeTools, excludeTools) {
|
|
438
|
+
const candidates = toolNameCandidates(toolName, serverName, prefix);
|
|
439
|
+
return (!Array.isArray(includeTools) || includeTools.length === 0 || matchesToolPatterns(candidates, includeTools)) && !matchesToolPatterns(candidates, excludeTools);
|
|
440
|
+
}
|
|
441
|
+
function toolNameCandidates(toolName, serverName, prefix) {
|
|
442
|
+
return /* @__PURE__ */ new Set([
|
|
443
|
+
toolName,
|
|
444
|
+
`mcp_${toolName}`,
|
|
445
|
+
formatToolName(toolName, serverName, prefix),
|
|
446
|
+
formatToolName(toolName, serverName, "server"),
|
|
447
|
+
formatToolName(toolName, serverName, "short"),
|
|
448
|
+
formatToolName(toolName, serverName, "mcp"),
|
|
449
|
+
formatToolName(toolName, serverName, "none")
|
|
450
|
+
]);
|
|
451
|
+
}
|
|
452
|
+
function matchesToolPatterns(candidates, patterns) {
|
|
453
|
+
if (!Array.isArray(patterns) || patterns.length === 0) return false;
|
|
454
|
+
for (const pattern of patterns) {
|
|
455
|
+
if (typeof pattern !== "string") continue;
|
|
456
|
+
const normalizedPattern = normalizeToolName(pattern);
|
|
457
|
+
const matcher = normalizedPattern.includes("*") || normalizedPattern.includes("?") ? globToRegExp(normalizedPattern) : void 0;
|
|
458
|
+
for (const candidate of candidates) {
|
|
459
|
+
const normalizedCandidate = normalizeToolName(candidate);
|
|
460
|
+
if (matcher ? matcher.test(normalizedCandidate) : normalizedCandidate === normalizedPattern) return true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
function globToRegExp(pattern) {
|
|
466
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
467
|
+
return new RegExp(`^${escaped}$`);
|
|
468
|
+
}
|
|
469
|
+
function normalizeToolName(value) {
|
|
470
|
+
return value.replace(/-/g, "_");
|
|
471
|
+
}
|
|
472
|
+
function resourceNameToToolName(name) {
|
|
473
|
+
let result = name.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_+/, "").replace(/_+$/, "").toLowerCase();
|
|
474
|
+
if (!result || /^\d/.test(result)) result = `resource${result ? `_${result}` : ""}`;
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// src/runs/shared/mcp-direct-tool-allowlist.ts
|
|
479
|
+
var CACHE_VERSION = 1;
|
|
480
|
+
var CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
481
|
+
var GENERIC_GLOBAL_CONFIG_PATH = path2.join(os2.homedir(), ".config", "mcp", "mcp.json");
|
|
482
|
+
var IMPORT_PATHS = {
|
|
483
|
+
cursor: [path2.join(os2.homedir(), ".cursor", "mcp.json")],
|
|
484
|
+
"claude-code": [
|
|
485
|
+
path2.join(os2.homedir(), ".claude", "mcp.json"),
|
|
486
|
+
path2.join(os2.homedir(), ".claude.json"),
|
|
487
|
+
path2.join(os2.homedir(), ".claude", "claude_desktop_config.json")
|
|
488
|
+
],
|
|
489
|
+
"claude-desktop": [path2.join(os2.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json")],
|
|
490
|
+
codex: [path2.join(os2.homedir(), ".codex", "config.json")],
|
|
491
|
+
windsurf: [path2.join(os2.homedir(), ".windsurf", "mcp.json")],
|
|
492
|
+
vscode: [".vscode/mcp.json"]
|
|
493
|
+
};
|
|
494
|
+
var MCP_RUNTIME_SNAPSHOT_EVENT = "pi-mcp-adapter:runtime-snapshot:v1";
|
|
495
|
+
var MCP_RUNTIME_SNAPSHOT_VERSION = 1;
|
|
496
|
+
function resolveMcpDirectToolResolution(mcpDirectTools, cwd = process.cwd(), runtimeSnapshotHost, configOverride) {
|
|
497
|
+
const selectors = normalizeMcpDirectToolSelectors(mcpDirectTools);
|
|
498
|
+
if (selectors.length === 0) return { selections: [], unresolvedSelectors: [] };
|
|
499
|
+
if (usesBuiltinMcp(runtimeSnapshotHost)) {
|
|
500
|
+
return resolveBuiltinMcpSelections(selectors, runtimeSnapshotHost.getAllTools?.() ?? []);
|
|
501
|
+
}
|
|
502
|
+
const config = configOverride ?? loadMcpConfig(cwd);
|
|
503
|
+
const { servers: selectedServers, tools: selectedTools } = parseMcpDirectToolSelectors(selectors);
|
|
504
|
+
const runtimeSelectionServers = /* @__PURE__ */ new Set([...selectedServers, ...selectedTools.keys()]);
|
|
505
|
+
const runtimeServers = resolveRuntimeMcpServers(config, runtimeSelectionServers, runtimeSnapshotHost);
|
|
506
|
+
const resolvedConfig = Object.keys(runtimeServers).length > 0 ? mergeConfigs(config, { mcpServers: runtimeServers }) : config;
|
|
507
|
+
validateSelectedServerDefinitions(resolvedConfig, selectors);
|
|
508
|
+
const runtime = Object.keys(runtimeServers).length > 0 ? { mcpConfig: resolvedConfig, runtimeServerNames: Object.keys(runtimeServers) } : {};
|
|
509
|
+
const cache = loadMetadataCache();
|
|
510
|
+
if (!cache) return { selections: [], unresolvedSelectors: selectors, ...runtime };
|
|
511
|
+
const validMetadata = {};
|
|
512
|
+
for (const [serverName, definition] of Object.entries(resolvedConfig.mcpServers)) {
|
|
513
|
+
const serverCache = cache.servers[serverName];
|
|
514
|
+
if (isServerCacheValid(serverCache, definition)) validMetadata[serverName] = serverCache;
|
|
515
|
+
}
|
|
516
|
+
const grant = planMcpDirectToolGrant({
|
|
517
|
+
selectors,
|
|
518
|
+
servers: resolvedConfig.mcpServers,
|
|
519
|
+
metadata: validMetadata,
|
|
520
|
+
toolPrefix: resolvedConfig.settings?.toolPrefix
|
|
521
|
+
});
|
|
522
|
+
return {
|
|
523
|
+
selections: grant.selections,
|
|
524
|
+
unresolvedSelectors: grant.unresolvedSelectors,
|
|
525
|
+
...runtime
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function usesBuiltinMcp(host) {
|
|
529
|
+
const commands = host?.getCommands?.() ?? [];
|
|
530
|
+
return commands.find((command) => command.name === "mcp")?.sourceInfo.path === "builtin:mcp" && !commands.some((command) => /^mcp-adapter(?::\d+)?$/.test(command.name));
|
|
531
|
+
}
|
|
532
|
+
function resolveBuiltinMcpSelections(selectors, tools) {
|
|
533
|
+
const selections = [];
|
|
534
|
+
const unresolvedSelectors = [];
|
|
535
|
+
for (const selector of selectors) {
|
|
536
|
+
const slash = selector.indexOf("/");
|
|
537
|
+
const server = slash === -1 ? selector : selector.slice(0, slash);
|
|
538
|
+
const inServer = tools.filter((tool) => tool.namespace?.name === `mcp__${server.replace(/-/g, "_")}`);
|
|
539
|
+
const toolName = slash === -1 ? void 0 : builtinMcpToolNames(server, selector.slice(slash + 1)).find((name) => inServer.some((tool) => tool.name === name));
|
|
540
|
+
const matches = inServer.filter((tool) => tool.exposure !== "hidden" && (slash === -1 || tool.name === toolName));
|
|
541
|
+
if (matches.length === 0) unresolvedSelectors.push(selector);
|
|
542
|
+
for (const tool of matches) {
|
|
543
|
+
if (!selections.some((selection) => selection.name === tool.name)) selections.push({ name: tool.name, selector });
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return { selections, unresolvedSelectors, builtin: true };
|
|
547
|
+
}
|
|
548
|
+
function builtinMcpToolNames(server, tool) {
|
|
549
|
+
const name = `mcp__${server}__${tool}`.replace(/[^A-Za-z0-9_]/g, "_");
|
|
550
|
+
const hash = createHash("sha256").update(`${server}\0${tool}`).digest("hex").slice(0, 8);
|
|
551
|
+
const suffixed = `${name.slice(0, 64 - hash.length - 1)}_${hash}`;
|
|
552
|
+
return name.length <= 64 ? [suffixed, name] : [suffixed];
|
|
553
|
+
}
|
|
554
|
+
function formatUnresolvedBuiltinMcpSelectors(agentName, selectors) {
|
|
555
|
+
const subject = agentName ? `Agent '${agentName}'` : "Subagent";
|
|
556
|
+
return `${subject} selects MCP tools that Pi's built-in MCP does not offer: ${selectors.map((selector) => `mcp:${selector}`).join(", ")}. The server may be missing, disconnected, still connecting, or its tools hidden; check /mcp.`;
|
|
557
|
+
}
|
|
558
|
+
function extensionOnlyMcpServers(selections, host, cwd, projectTrusted) {
|
|
559
|
+
const registered = new Set(host.getMcpServers?.().map(({ name }) => name));
|
|
560
|
+
const servers = [...new Set(selections.map(({ selector }) => selector.split("/")[0]))].filter((server) => registered.has(server));
|
|
561
|
+
if (servers.length === 0) return [];
|
|
562
|
+
const files = [path2.join(getAgentDir(), "mcp.json"), ...projectTrusted ? [path2.join(cwd, ".pi", "mcp.json")] : []];
|
|
563
|
+
const configured = new Set(files.flatMap((file) => {
|
|
564
|
+
let parsed;
|
|
565
|
+
try {
|
|
566
|
+
parsed = JSON.parse(fs2.readFileSync(file, "utf-8"));
|
|
567
|
+
} catch {
|
|
568
|
+
return [];
|
|
569
|
+
}
|
|
570
|
+
return isRecord2(parsed) && isRecord2(parsed.mcpServers) ? Object.keys(parsed.mcpServers) : [];
|
|
571
|
+
}));
|
|
572
|
+
return servers.filter((server) => !configured.has(server));
|
|
573
|
+
}
|
|
574
|
+
function loadMetadataCache() {
|
|
575
|
+
const cachePath = path2.join(getAgentDir(), "mcp-cache.json");
|
|
576
|
+
let parsed;
|
|
577
|
+
try {
|
|
578
|
+
parsed = JSON.parse(fs2.readFileSync(cachePath, "utf-8"));
|
|
579
|
+
} catch {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
if (!isRecord2(parsed) || parsed.version !== CACHE_VERSION || !isRecord2(parsed.servers)) return null;
|
|
583
|
+
const servers = {};
|
|
584
|
+
for (const [name, value] of Object.entries(parsed.servers)) {
|
|
585
|
+
const entry = parseServerCacheEntry(value);
|
|
586
|
+
if (entry) servers[name] = entry;
|
|
587
|
+
}
|
|
588
|
+
return { version: CACHE_VERSION, servers };
|
|
589
|
+
}
|
|
590
|
+
function parseServerCacheEntry(value) {
|
|
591
|
+
if (!isRecord2(value)) return void 0;
|
|
592
|
+
if (value.configHash !== void 0 && typeof value.configHash !== "string") return void 0;
|
|
593
|
+
if (value.cachedAt !== void 0 && (typeof value.cachedAt !== "number" || !Number.isFinite(value.cachedAt))) return void 0;
|
|
594
|
+
if (value.tools !== void 0 && !Array.isArray(value.tools)) return void 0;
|
|
595
|
+
if (value.resources !== void 0 && !Array.isArray(value.resources)) return void 0;
|
|
596
|
+
const tools = value.tools?.map(parseCachedTool);
|
|
597
|
+
const resources = value.resources?.map(parseCachedResource);
|
|
598
|
+
if (tools && !tools.every((entry) => entry !== void 0)) return void 0;
|
|
599
|
+
if (resources && !resources.every((entry) => entry !== void 0)) return void 0;
|
|
600
|
+
return {
|
|
601
|
+
...value.configHash !== void 0 ? { configHash: value.configHash } : {},
|
|
602
|
+
...value.cachedAt !== void 0 ? { cachedAt: value.cachedAt } : {},
|
|
603
|
+
...tools ? { tools } : {},
|
|
604
|
+
...resources ? { resources } : {}
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function parseCachedTool(value) {
|
|
608
|
+
if (!isRecord2(value) || value.name !== void 0 && typeof value.name !== "string") return void 0;
|
|
609
|
+
return value.name === void 0 ? {} : { name: value.name };
|
|
610
|
+
}
|
|
611
|
+
function parseCachedResource(value) {
|
|
612
|
+
if (!isRecord2(value)) return void 0;
|
|
613
|
+
if (value.uri !== void 0 && typeof value.uri !== "string") return void 0;
|
|
614
|
+
if (value.name !== void 0 && typeof value.name !== "string") return void 0;
|
|
615
|
+
return {
|
|
616
|
+
...value.uri !== void 0 ? { uri: value.uri } : {},
|
|
617
|
+
...value.name !== void 0 ? { name: value.name } : {}
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
function resolveRuntimeMcpServers(config, selectedServers, runtimeSnapshotHost) {
|
|
621
|
+
if (!runtimeSnapshotHost) return {};
|
|
622
|
+
const runtimeServers = {};
|
|
623
|
+
for (const serverName of selectedServers) {
|
|
624
|
+
if (Object.hasOwn(config.mcpServers, serverName)) continue;
|
|
625
|
+
try {
|
|
626
|
+
const snapshot = getRuntimeMcpServerSnapshot(runtimeSnapshotHost, serverName);
|
|
627
|
+
runtimeServers[serverName] = snapshot.definition;
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return runtimeServers;
|
|
632
|
+
}
|
|
633
|
+
function getRuntimeMcpServerSnapshot(host, name) {
|
|
634
|
+
const request = {
|
|
635
|
+
version: MCP_RUNTIME_SNAPSHOT_VERSION,
|
|
636
|
+
name
|
|
637
|
+
};
|
|
638
|
+
host.events.emit(MCP_RUNTIME_SNAPSHOT_EVENT, request);
|
|
639
|
+
if (!request.result) throw new Error("pi-mcp-adapter is not installed for this Pi instance");
|
|
640
|
+
if (!request.result.ok) throw request.result.error;
|
|
641
|
+
const snapshot = request.result.snapshot;
|
|
642
|
+
if (!snapshot || snapshot.name !== name || snapshot.runtime !== true || snapshot.persisted !== false || !isMcpServerDefinition(snapshot.definition)) {
|
|
643
|
+
throw new Error(`Invalid MCP runtime snapshot for server "${name}"`);
|
|
644
|
+
}
|
|
645
|
+
return snapshot;
|
|
646
|
+
}
|
|
647
|
+
function loadMcpConfig(cwd) {
|
|
648
|
+
const resolvedCwd = path2.resolve(cwd);
|
|
649
|
+
const projectRoot = findConfiguredProjectRoot(resolvedCwd) ?? resolvedCwd;
|
|
650
|
+
let config = { mcpServers: {} };
|
|
651
|
+
for (const sourcePath of getConfigPaths(projectRoot)) {
|
|
652
|
+
const loaded = readConfig(sourcePath);
|
|
653
|
+
if (!loaded) continue;
|
|
654
|
+
config = mergeConfigs(config, expandImports(loaded, projectRoot));
|
|
655
|
+
}
|
|
656
|
+
const packageServers = loadPackageMcpServers(projectRoot);
|
|
657
|
+
const pluginServers = loadAgentPluginMcpServers(config.settings?.agentPluginPaths, projectRoot);
|
|
658
|
+
const packageOnlyServers = Object.fromEntries(
|
|
659
|
+
Object.entries(packageServers).filter(([name]) => !Object.hasOwn(pluginServers, name))
|
|
660
|
+
);
|
|
661
|
+
return mergeConfigs(
|
|
662
|
+
{ mcpServers: packageOnlyServers },
|
|
663
|
+
mergeConfigs({ mcpServers: pluginServers }, config)
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
function getConfigPaths(projectRoot) {
|
|
667
|
+
const agentDir = getAgentDir();
|
|
668
|
+
const projectDir = getProjectConfigDir(projectRoot);
|
|
669
|
+
const candidates = [
|
|
670
|
+
GENERIC_GLOBAL_CONFIG_PATH,
|
|
671
|
+
path2.join(agentDir, "mcp-adapter.json"),
|
|
672
|
+
path2.resolve(projectRoot, ".mcp.json"),
|
|
673
|
+
path2.join(projectDir, "mcp-adapter.json")
|
|
674
|
+
];
|
|
675
|
+
return [...new Set(candidates)];
|
|
676
|
+
}
|
|
677
|
+
function readConfig(configPath) {
|
|
678
|
+
let parsed;
|
|
679
|
+
try {
|
|
680
|
+
parsed = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
|
|
681
|
+
} catch {
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
return validateConfig(parsed);
|
|
685
|
+
}
|
|
686
|
+
function validateConfig(raw) {
|
|
687
|
+
if (!isRecord2(raw)) return { mcpServers: {} };
|
|
688
|
+
const obj = raw;
|
|
689
|
+
const servers = obj.mcpServers ?? obj["mcp-servers"] ?? {};
|
|
690
|
+
return {
|
|
691
|
+
mcpServers: parseServerEntries(servers),
|
|
692
|
+
imports: Array.isArray(obj.imports) ? obj.imports.filter((value) => isImportKind(value)) : void 0,
|
|
693
|
+
settings: parseSettings(obj.settings)
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function parseServerEntries(value) {
|
|
697
|
+
if (!isRecord2(value)) return {};
|
|
698
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => isMcpServerDefinition(entry[1])));
|
|
699
|
+
}
|
|
700
|
+
function parseSettings(value) {
|
|
701
|
+
if (!isRecord2(value)) return void 0;
|
|
702
|
+
const toolPrefix = value.toolPrefix === "server" || value.toolPrefix === "short" || value.toolPrefix === "none" || value.toolPrefix === "mcp" ? value.toolPrefix : void 0;
|
|
703
|
+
const directTools = typeof value.directTools === "boolean" ? value.directTools : void 0;
|
|
704
|
+
const settings = {
|
|
705
|
+
...toolPrefix ? { toolPrefix } : {},
|
|
706
|
+
...directTools !== void 0 ? { directTools } : {},
|
|
707
|
+
...value.agentPluginPaths !== void 0 ? { agentPluginPaths: value.agentPluginPaths } : {}
|
|
708
|
+
};
|
|
709
|
+
return Object.keys(settings).length ? settings : void 0;
|
|
710
|
+
}
|
|
711
|
+
function mergeConfigs(base, next) {
|
|
712
|
+
const imports = [...base.imports ?? [], ...next.imports ?? []];
|
|
713
|
+
return {
|
|
714
|
+
mcpServers: { ...base.mcpServers, ...next.mcpServers },
|
|
715
|
+
imports: imports.length ? [...new Set(imports)] : void 0,
|
|
716
|
+
settings: next.settings ? { ...base.settings, ...next.settings } : base.settings
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function expandImports(config, cwd) {
|
|
720
|
+
if (!config.imports?.length) return config;
|
|
721
|
+
const importedServers = {};
|
|
722
|
+
for (const importKind of config.imports) {
|
|
723
|
+
const importPath = resolveImportPath(importKind, cwd);
|
|
724
|
+
if (!importPath) continue;
|
|
725
|
+
let imported;
|
|
726
|
+
try {
|
|
727
|
+
imported = JSON.parse(fs2.readFileSync(importPath, "utf-8"));
|
|
728
|
+
} catch {
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
for (const [name, definition] of Object.entries(extractServers(imported, importKind))) {
|
|
732
|
+
if (!importedServers[name]) importedServers[name] = definition;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
imports: config.imports,
|
|
737
|
+
settings: config.settings,
|
|
738
|
+
mcpServers: { ...importedServers, ...config.mcpServers }
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function resolveImportPath(importKind, cwd) {
|
|
742
|
+
for (const candidate of IMPORT_PATHS[importKind]) {
|
|
743
|
+
const fullPath = candidate.startsWith(".") ? path2.resolve(cwd, candidate) : candidate;
|
|
744
|
+
if (fs2.existsSync(fullPath)) return fullPath;
|
|
745
|
+
}
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
function extractServers(config, kind) {
|
|
749
|
+
if (!isRecord2(config)) return {};
|
|
750
|
+
const obj = config;
|
|
751
|
+
const servers = kind === "cursor" || kind === "windsurf" || kind === "vscode" ? obj.mcpServers ?? obj["mcp-servers"] : obj.mcpServers;
|
|
752
|
+
return parseServerEntries(servers);
|
|
753
|
+
}
|
|
754
|
+
function validateSelectedServerDefinitions(config, selectors) {
|
|
755
|
+
const { servers, tools } = parseMcpDirectToolSelectors(selectors);
|
|
756
|
+
for (const serverName of /* @__PURE__ */ new Set([...servers, ...tools.keys()])) {
|
|
757
|
+
const definition = config.mcpServers[serverName];
|
|
758
|
+
if (definition) computeMcpServerHash(definition);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function isServerCacheValid(entry, definition) {
|
|
762
|
+
if (!entry || entry.configHash !== computeMcpServerHash(definition)) return false;
|
|
763
|
+
if (!entry.cachedAt || typeof entry.cachedAt !== "number") return false;
|
|
764
|
+
return Date.now() - entry.cachedAt <= CACHE_MAX_AGE_MS;
|
|
765
|
+
}
|
|
766
|
+
function computeMcpServerHash(definition) {
|
|
767
|
+
const isStdio = definition.command !== void 0;
|
|
768
|
+
const literalEnv = isStdio && definition.literalEnv === true;
|
|
769
|
+
const identity = {
|
|
770
|
+
command: resolveConfigPath(definition.command),
|
|
771
|
+
args: definition.args,
|
|
772
|
+
socket: resolveConfigPath(definition.socket),
|
|
773
|
+
env: literalEnv ? definition.env : interpolateEnvRecord(definition.env),
|
|
774
|
+
...isStdio ? { inheritEnv: definition.inheritEnv !== false, literalEnv } : {},
|
|
775
|
+
cwd: resolveConfigPath(definition.cwd),
|
|
776
|
+
url: resolveServerUrl(definition),
|
|
777
|
+
headers: interpolateEnvRecord(definition.headers),
|
|
778
|
+
requestHeadersCommand: definition.requestHeadersCommand ? {
|
|
779
|
+
command: interpolateEnvVars(definition.requestHeadersCommand.command),
|
|
780
|
+
args: definition.requestHeadersCommand.args?.map(interpolateEnvVars),
|
|
781
|
+
env: interpolateEnvRecord(definition.requestHeadersCommand.env),
|
|
782
|
+
timeoutMs: definition.requestHeadersCommand.timeoutMs
|
|
783
|
+
} : void 0,
|
|
784
|
+
auth: definition.auth,
|
|
785
|
+
bearerToken: resolveBearerToken(definition),
|
|
786
|
+
bearerTokenEnv: definition.bearerTokenEnv,
|
|
787
|
+
exposeResources: definition.exposeResources,
|
|
788
|
+
includeTools: definition.includeTools,
|
|
789
|
+
excludeTools: definition.excludeTools,
|
|
790
|
+
protocolVersion: definition.protocolVersion
|
|
791
|
+
};
|
|
792
|
+
return createHash("sha256").update(stableStringify(identity)).digest("hex");
|
|
793
|
+
}
|
|
794
|
+
function isImportKind(value) {
|
|
795
|
+
return typeof value === "string" && Object.hasOwn(IMPORT_PATHS, value);
|
|
796
|
+
}
|
|
797
|
+
function isRecord2(value) {
|
|
798
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
799
|
+
}
|
|
800
|
+
function interpolateEnvRecord(values) {
|
|
801
|
+
if (!values) return void 0;
|
|
802
|
+
return Object.fromEntries(Object.entries(values).map(([key, value]) => [key, interpolateSecretExpression(value)]));
|
|
803
|
+
}
|
|
804
|
+
function interpolateEnvVars(value) {
|
|
805
|
+
return value.replace(/\$\{(\w+)\}/g, (_, name) => process.env[name] ?? "").replace(/\$env:(\w+)/g, (_, name) => process.env[name] ?? "").replace(/\{env:(\w+)\}/g, (_, name) => process.env[name] ?? "");
|
|
806
|
+
}
|
|
807
|
+
function interpolateSecretExpression(value) {
|
|
808
|
+
if (value.startsWith("!!")) return interpolateEnvVars(value.slice(1));
|
|
809
|
+
return value.startsWith("!") ? value : interpolateEnvVars(value);
|
|
810
|
+
}
|
|
811
|
+
function getMissingEnvVars(value) {
|
|
812
|
+
const missing = /* @__PURE__ */ new Set();
|
|
813
|
+
for (const match of value.matchAll(/\$\{(\w+)\}|\$env:(\w+)|\{env:(\w+)\}/g)) {
|
|
814
|
+
const name = match[1] ?? match[2] ?? match[3];
|
|
815
|
+
if (name && process.env[name] === void 0) missing.add(name);
|
|
816
|
+
}
|
|
817
|
+
return [...missing];
|
|
818
|
+
}
|
|
819
|
+
function resolveServerUrl(definition) {
|
|
820
|
+
if (definition.url == null) return void 0;
|
|
821
|
+
if (typeof definition.url !== "string") throw new Error("MCP server URL must be a string");
|
|
822
|
+
const missing = getMissingEnvVars(definition.url);
|
|
823
|
+
if (missing.length > 0) {
|
|
824
|
+
throw new Error(`Missing environment variable${missing.length === 1 ? "" : "s"} in MCP server URL: ${missing.join(", ")}`);
|
|
825
|
+
}
|
|
826
|
+
const resolved = interpolateEnvVars(definition.url);
|
|
827
|
+
try {
|
|
828
|
+
new URL(resolved);
|
|
829
|
+
} catch (error) {
|
|
830
|
+
throw new Error(`Invalid MCP server URL after environment interpolation: ${resolved}`, { cause: error });
|
|
831
|
+
}
|
|
832
|
+
return resolved;
|
|
833
|
+
}
|
|
834
|
+
function resolveConfigPath(value) {
|
|
835
|
+
if (value === void 0) return void 0;
|
|
836
|
+
const resolved = interpolateEnvVars(value);
|
|
837
|
+
if (resolved === "~") return os2.homedir();
|
|
838
|
+
if (resolved.startsWith("~/") || resolved.startsWith("~\\")) return path2.join(os2.homedir(), resolved.slice(2));
|
|
839
|
+
return resolved;
|
|
840
|
+
}
|
|
841
|
+
function resolveBearerToken(definition) {
|
|
842
|
+
if (definition.bearerToken !== void 0) return interpolateSecretExpression(definition.bearerToken);
|
|
843
|
+
return definition.bearerTokenEnv ? process.env[definition.bearerTokenEnv] : void 0;
|
|
844
|
+
}
|
|
845
|
+
function stableStringify(value) {
|
|
846
|
+
if (value === null || value === void 0 || typeof value !== "object") {
|
|
847
|
+
const serialized = JSON.stringify(value);
|
|
848
|
+
return serialized === void 0 ? "undefined" : serialized;
|
|
849
|
+
}
|
|
850
|
+
if (Array.isArray(value)) return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
|
851
|
+
const obj = value;
|
|
852
|
+
return `{${Object.keys(obj).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`).join(",")}}`;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// src/shared/runtime-module-path.ts
|
|
856
|
+
import * as fs3 from "node:fs";
|
|
857
|
+
import * as path3 from "node:path";
|
|
858
|
+
var RUNTIME_MODULE_EXTENSIONS = [".js", ".ts"];
|
|
859
|
+
function resolveRuntimeModuleExtension(dir, basename) {
|
|
860
|
+
return RUNTIME_MODULE_EXTENSIONS.find((extension) => fs3.existsSync(path3.join(dir, `${basename}${extension}`))) ?? ".js";
|
|
861
|
+
}
|
|
862
|
+
function resolveRuntimeModulePath(dir, basename) {
|
|
863
|
+
return path3.normalize(path3.join(dir, `${basename}${resolveRuntimeModuleExtension(dir, basename)}`));
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/runs/shared/child-tool-plan.ts
|
|
867
|
+
var MAX_LAUNCH_RESOLVED_EXTENSION_IDS = 32;
|
|
868
|
+
var RUNTIME_EXTENSION_DIR = path4.dirname(fileURLToPath(new URL("../src/runs/shared/child-tool-plan.js", import.meta.url).href));
|
|
869
|
+
var FANOUT_CHILD_EXTENSION_DIR = path4.join(RUNTIME_EXTENSION_DIR, "..", "..", "extension");
|
|
870
|
+
var SUBAGENT_RUNTIME_EXTENSION_PATHS = new Set([
|
|
871
|
+
path4.join(RUNTIME_EXTENSION_DIR, "subagent-prompt-runtime"),
|
|
872
|
+
path4.join(RUNTIME_EXTENSION_DIR, "fast-mode-extension"),
|
|
873
|
+
path4.join(FANOUT_CHILD_EXTENSION_DIR, "fanout-child")
|
|
874
|
+
].flatMap((base) => RUNTIME_MODULE_EXTENSIONS.map((extension) => path4.normalize(`${base}${extension}`))));
|
|
875
|
+
function isSubagentRuntimeExtensionPath(extensionPath) {
|
|
876
|
+
return SUBAGENT_RUNTIME_EXTENSION_PATHS.has(path4.normalize(extensionPath));
|
|
877
|
+
}
|
|
878
|
+
var FAST_MODE_PROVIDER_PREFIX = "openai-codex/";
|
|
879
|
+
var OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
880
|
+
function deriveForkPromptCacheKey(parentSessionId) {
|
|
881
|
+
const parent = parentSessionId?.trim();
|
|
882
|
+
if (!parent) return void 0;
|
|
883
|
+
const digest = createHash2("sha256").update(parent).digest("hex").slice(0, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH - "pi-fork:".length);
|
|
884
|
+
return `pi-fork:${digest}`;
|
|
885
|
+
}
|
|
886
|
+
function sanitizeSupervisorChannelSegment(value) {
|
|
887
|
+
return value.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
888
|
+
}
|
|
889
|
+
function supervisorChannelDir(runId, agent, childIndex) {
|
|
890
|
+
return path4.join(
|
|
891
|
+
TEMP_ROOT_DIR,
|
|
892
|
+
"supervisor-channels",
|
|
893
|
+
`${sanitizeSupervisorChannelSegment(runId)}-${sanitizeSupervisorChannelSegment(agent)}-${childIndex}`
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
function applyThinkingSuffix(model, thinking, replaceExisting = false) {
|
|
897
|
+
if (!model || !thinking) return model;
|
|
898
|
+
const colonIdx = model.lastIndexOf(":");
|
|
899
|
+
if (colonIdx !== -1 && THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1))) {
|
|
900
|
+
return replaceExisting ? `${model.slice(0, colonIdx)}:${thinking}` : model;
|
|
901
|
+
}
|
|
902
|
+
return `${model}:${thinking}`;
|
|
903
|
+
}
|
|
904
|
+
function stripThinkingSuffix(model) {
|
|
905
|
+
const colonIdx = model.lastIndexOf(":");
|
|
906
|
+
if (colonIdx === -1) return model;
|
|
907
|
+
return THINKING_LEVELS.some((level) => level === model.substring(colonIdx + 1)) ? model.slice(0, colonIdx) : model;
|
|
908
|
+
}
|
|
909
|
+
function resolveFastModeExtension(input) {
|
|
910
|
+
if (!input.fast) return [];
|
|
911
|
+
const candidates = (input.model ? [input.model] : []).map(stripThinkingSuffix);
|
|
912
|
+
if (candidates.length === 0) {
|
|
913
|
+
throw new Error(`fast mode requires an explicit supported native OpenAI-Codex model${input.agentName ? ` for agent '${input.agentName}'` : ""}.`);
|
|
914
|
+
}
|
|
915
|
+
const unsupported = candidates.filter((model) => !model.startsWith(FAST_MODE_PROVIDER_PREFIX));
|
|
916
|
+
if (unsupported.length > 0) {
|
|
917
|
+
throw new Error(`fast mode supports only native ${FAST_MODE_PROVIDER_PREFIX}* models; unsupported model${unsupported.length === 1 ? "" : "s"}: ${unsupported.join(", ")}.`);
|
|
918
|
+
}
|
|
919
|
+
return [resolveRuntimeModulePath(RUNTIME_EXTENSION_DIR, "fast-mode-extension")];
|
|
920
|
+
}
|
|
921
|
+
function extensionIdentifier(value) {
|
|
922
|
+
return `sha256:${createHash2("sha256").update(path4.normalize(value.trim())).digest("hex").slice(0, 16)}`;
|
|
923
|
+
}
|
|
924
|
+
function boundedExtensionIdentifiers(values) {
|
|
925
|
+
const ids = [...new Set(values.map(extensionIdentifier))];
|
|
926
|
+
return {
|
|
927
|
+
ids: ids.slice(0, MAX_LAUNCH_RESOLVED_EXTENSION_IDS),
|
|
928
|
+
omitted: Math.max(0, ids.length - MAX_LAUNCH_RESOLVED_EXTENSION_IDS)
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function hasPermissionRules(rules) {
|
|
932
|
+
return rules !== void 0 && Object.keys(rules).length > 0;
|
|
933
|
+
}
|
|
934
|
+
function formatRuntimeSnapshotMcpServersError(agentName, serverNames) {
|
|
935
|
+
const subject = agentName ? `Agent '${agentName}'` : "Subagent";
|
|
936
|
+
return `${subject} selects MCP tools from servers that exist only in pi-mcp-adapter's runtime snapshot (${serverNames.join(", ")}). MCP servers from the runtime snapshot cannot be provided to in-process children; MCP tools must come from an ambient adapter extension in a background child (\`async: true\`), so add the server to the adapter's configuration file instead.`;
|
|
937
|
+
}
|
|
938
|
+
function isToolExtensionPath(entry) {
|
|
939
|
+
return entry.includes("/") || entry.endsWith(".ts") || entry.endsWith(".js");
|
|
940
|
+
}
|
|
941
|
+
function assertNoToolModifierEntries(agentName, tools) {
|
|
942
|
+
const entries = tools.filter((entry) => /^[+-]/u.test(entry) && !isToolExtensionPath(entry));
|
|
943
|
+
if (entries.length === 0) return;
|
|
944
|
+
const subject = agentName ? `Agent '${agentName}'` : "Subagent";
|
|
945
|
+
throw new Error(`${subject} lists Pi tool modifier ${entries.length === 1 ? "entry" : "entries"} in \`tools\`: ${entries.join(", ")}. A child's \`tools\` list is the complete set of tools it gets, so +name and -name entries are not supported; name every tool the child needs, or omit \`tools\` and use \`excludeTools\` to drop one.`);
|
|
946
|
+
}
|
|
947
|
+
function projectLaunchResolvedChildExtensions(toolPlan) {
|
|
948
|
+
const runtime = boundedExtensionIdentifiers(toolPlan.runtimeExtensions);
|
|
949
|
+
const configured = boundedExtensionIdentifiers(toolPlan.configuredExtensions);
|
|
950
|
+
const effective = boundedExtensionIdentifiers(toolPlan.extensionArgs);
|
|
951
|
+
return {
|
|
952
|
+
version: 1,
|
|
953
|
+
source: "launch-resolved",
|
|
954
|
+
disableAmbientExtensions: toolPlan.disableAmbientExtensions,
|
|
955
|
+
runtime: runtime.ids,
|
|
956
|
+
configured: configured.ids,
|
|
957
|
+
required: toolPlan.requiredExtensions.map(({ id }) => id),
|
|
958
|
+
effective: effective.ids,
|
|
959
|
+
omitted: {
|
|
960
|
+
runtime: runtime.omitted,
|
|
961
|
+
configured: configured.omitted,
|
|
962
|
+
required: 0,
|
|
963
|
+
effective: effective.omitted
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
function resolvePermissionSystemExtension() {
|
|
968
|
+
const agentDir = getAgentDir();
|
|
969
|
+
const candidates = [
|
|
970
|
+
// npm-scoped package (most common)
|
|
971
|
+
path4.join(
|
|
972
|
+
agentDir,
|
|
973
|
+
"npm",
|
|
974
|
+
"node_modules",
|
|
975
|
+
"@gotgenes",
|
|
976
|
+
"pi-permission-system"
|
|
977
|
+
),
|
|
978
|
+
// direct extension directory (some layouts)
|
|
979
|
+
path4.join(agentDir, "extensions", "pi-permission-system")
|
|
980
|
+
];
|
|
981
|
+
const errors = [];
|
|
982
|
+
for (const extDir of candidates) {
|
|
983
|
+
if (!fs4.existsSync(extDir)) continue;
|
|
984
|
+
const pkgPath = path4.join(extDir, "package.json");
|
|
985
|
+
if (!fs4.existsSync(pkgPath)) {
|
|
986
|
+
errors.push(new Error(`Permission-system package manifest is missing at ${pkgPath}.`));
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
try {
|
|
990
|
+
let pkg;
|
|
991
|
+
const parsed = JSON.parse(fs4.readFileSync(pkgPath, "utf-8"));
|
|
992
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
993
|
+
throw new Error("manifest root must be an object");
|
|
994
|
+
}
|
|
995
|
+
pkg = parsed;
|
|
996
|
+
const extensions = pkg.pi?.extensions;
|
|
997
|
+
const entry = Array.isArray(extensions) ? extensions[0] : void 0;
|
|
998
|
+
if (typeof entry !== "string" || !entry.trim()) {
|
|
999
|
+
throw new Error(
|
|
1000
|
+
`Permission-system package manifest at ${pkgPath} must declare pi.extensions[0] as a non-empty string.`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
const resolved = path4.resolve(extDir, entry);
|
|
1004
|
+
if (fs4.existsSync(resolved)) return resolved;
|
|
1005
|
+
throw new Error(
|
|
1006
|
+
`Permission-system extension entry ${JSON.stringify(entry)} in ${pkgPath} does not exist at ${resolved}.`
|
|
1007
|
+
);
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1010
|
+
errors.push(message.startsWith("Permission-system") ? new Error(message) : new Error(`Cannot read permission-system package manifest at ${pkgPath}: ${message}`));
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (errors.length > 0) throw errors[0];
|
|
1014
|
+
return void 0;
|
|
1015
|
+
}
|
|
1016
|
+
function resolvePiLaunchToolPlan(input) {
|
|
1017
|
+
const capabilityCeiling = intersectSubagentCapabilityCeilings(
|
|
1018
|
+
input.capabilityCeiling,
|
|
1019
|
+
input.inheritedCapabilityCeiling
|
|
1020
|
+
);
|
|
1021
|
+
const requiredExtensions = snapshotRequiredChildExtensions(input.requiredExtensions ?? []);
|
|
1022
|
+
if (requiredExtensions.length > 0 && capabilityCeiling?.denyExtensions) {
|
|
1023
|
+
throw new Error(`Capability ceiling from ${capabilityCeiling.sources.join(", ") || "unknown source"} denies extensions but this host requires: ${requiredExtensions.map(({ id }) => id).join(", ")}.`);
|
|
1024
|
+
}
|
|
1025
|
+
const allowedToolSet = capabilityCeiling?.allowedTools === void 0 ? void 0 : new Set(capabilityCeiling.allowedTools);
|
|
1026
|
+
const requestedBuiltinTools = input.tools?.filter((tool) => !isToolExtensionPath(tool)) ?? [];
|
|
1027
|
+
assertNoToolModifierEntries(input.agentName, input.tools ?? []);
|
|
1028
|
+
if (input.requireReadTool && allowedToolSet && !allowedToolSet.has("read")) {
|
|
1029
|
+
throw new Error(
|
|
1030
|
+
`Capability ceiling from ${capabilityCeiling?.sources.join(", ") || "unknown source"} excludes required tool 'read' for lazy skill loading.`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
const ceilingFilteredBuiltinTools = input.tools === void 0 ? allowedToolSet ? [...allowedToolSet].filter((tool) => tool !== "subagent_command") : [] : (input.requireReadTool && requestedBuiltinTools.length > 0 && !requestedBuiltinTools.includes("read") && !allowedToolSet ? ["read", ...requestedBuiltinTools] : requestedBuiltinTools).filter((tool) => !allowedToolSet || allowedToolSet.has(tool));
|
|
1034
|
+
const declaredBuiltinTools = ceilingFilteredBuiltinTools;
|
|
1035
|
+
const excludeTools = [...new Set((input.excludeTools ?? []).map((tool) => tool.trim()).filter(Boolean))];
|
|
1036
|
+
const excludedToolSet = new Set(excludeTools);
|
|
1037
|
+
const effectiveDeclaredBuiltinTools = declaredBuiltinTools.filter((tool) => !excludedToolSet.has(tool));
|
|
1038
|
+
const fanoutAuthorized = effectiveDeclaredBuiltinTools.includes("subagent") || input.allowNestedSubagents === true && !excludedToolSet.has("subagent") && (!allowedToolSet || allowedToolSet.has("subagent"));
|
|
1039
|
+
if (effectiveDeclaredBuiltinTools.includes("subagent_supervisor") && !fanoutAuthorized) {
|
|
1040
|
+
throw new Error("Tool 'subagent_supervisor' requires fanout authorization: include 'subagent' in the effective tools allowlist or enable allowNestedSubagents.");
|
|
1041
|
+
}
|
|
1042
|
+
const toolExtensionPaths = capabilityCeiling?.denyExtensions ? [] : (input.tools ?? []).filter(isToolExtensionPath);
|
|
1043
|
+
const mcpResolution = capabilityCeiling?.denyExtensions ? { selections: [], unresolvedSelectors: [] } : input.builtinMcpTools ? { selections: input.builtinMcpTools, unresolvedSelectors: [], builtin: true } : resolveMcpDirectToolResolution(input.mcpDirectTools, input.cwd, input.runtimeSnapshotHost);
|
|
1044
|
+
if (mcpResolution.runtimeServerNames?.length) {
|
|
1045
|
+
throw new Error(formatRuntimeSnapshotMcpServersError(input.agentName, mcpResolution.runtimeServerNames));
|
|
1046
|
+
}
|
|
1047
|
+
if (mcpResolution.unresolvedSelectors.length > 0) {
|
|
1048
|
+
throw new Error(mcpResolution.builtin ? formatUnresolvedBuiltinMcpSelectors(input.agentName, mcpResolution.unresolvedSelectors) : formatUnresolvedMcpDirectToolSelectors(mcpResolution.unresolvedSelectors));
|
|
1049
|
+
}
|
|
1050
|
+
const resolvedMcpSelections = mcpResolution.selections;
|
|
1051
|
+
const resolvedMcpNames = new Set(resolvedMcpSelections.map((selection) => selection.name));
|
|
1052
|
+
const legacyMcpNameCounts = mcpResolution.builtin ? /* @__PURE__ */ new Map() : countLegacyUnderscoreMcpToolNames(resolvedMcpSelections);
|
|
1053
|
+
const effectiveMcpSelections = resolvedMcpSelections.filter(
|
|
1054
|
+
(selection) => !allowedToolSet || allowedToolSet.has(selection.name) || isLegacyUnderscoreMcpToolAllowed(selection, allowedToolSet, resolvedMcpNames, legacyMcpNameCounts)
|
|
1055
|
+
).filter((selection) => !excludedToolSet.has(selection.name));
|
|
1056
|
+
const effectiveMcpTools = effectiveMcpSelections.map(
|
|
1057
|
+
(selection) => selection.name
|
|
1058
|
+
);
|
|
1059
|
+
const builtinMcpTools = mcpResolution.builtin ? effectiveMcpSelections : void 0;
|
|
1060
|
+
const explicitToolAllowlist = input.tools !== void 0 || (input.mcpDirectTools?.length ?? 0) > 0 || allowedToolSet !== void 0;
|
|
1061
|
+
const internalTools = (input.structuredOutput ? ["structured_output"] : []).filter((tool) => !excludedToolSet.has(tool));
|
|
1062
|
+
const effectiveToolAllowlist = [
|
|
1063
|
+
.../* @__PURE__ */ new Set([
|
|
1064
|
+
...effectiveDeclaredBuiltinTools,
|
|
1065
|
+
...effectiveMcpTools,
|
|
1066
|
+
...internalTools
|
|
1067
|
+
])
|
|
1068
|
+
];
|
|
1069
|
+
const legacySupervisorPairing = effectiveDeclaredBuiltinTools.includes("contact_supervisor");
|
|
1070
|
+
const requiredChildTools = explicitToolAllowlist ? [
|
|
1071
|
+
...new Set([
|
|
1072
|
+
...input.tools !== void 0 ? effectiveDeclaredBuiltinTools : [],
|
|
1073
|
+
...input.mcpDirectTools?.length ? effectiveMcpTools : [],
|
|
1074
|
+
...internalTools
|
|
1075
|
+
].filter((tool) => tool !== "contact_supervisor" && (!legacySupervisorPairing || tool !== "intercom")))
|
|
1076
|
+
] : [];
|
|
1077
|
+
const permSystemExt = capabilityCeiling?.denyExtensions ? void 0 : hasPermissionRules(input.permissionRules) ? resolvePermissionSystemExtension() : void 0;
|
|
1078
|
+
if (input.fast && capabilityCeiling?.denyExtensions) throw new Error("fast mode requires a child runtime extension, but this launch denies extensions.");
|
|
1079
|
+
const fastModeExtensions = resolveFastModeExtension({ fast: input.fast, model: input.model, agentName: input.agentName });
|
|
1080
|
+
const runtimeExtensions = [
|
|
1081
|
+
resolveRuntimeModulePath(RUNTIME_EXTENSION_DIR, "subagent-prompt-runtime"),
|
|
1082
|
+
...fastModeExtensions,
|
|
1083
|
+
...fanoutAuthorized ? [resolveRuntimeModulePath(FANOUT_CHILD_EXTENSION_DIR, "fanout-child")] : [],
|
|
1084
|
+
...permSystemExt ? [permSystemExt] : []
|
|
1085
|
+
];
|
|
1086
|
+
const disableAmbientExtensions = capabilityCeiling?.denyExtensions === true || input.extensions !== void 0;
|
|
1087
|
+
const warnings = [];
|
|
1088
|
+
if (capabilityCeiling?.denyExtensions !== true && Array.isArray(input.extensions) && input.extensions.length === 0) {
|
|
1089
|
+
const agentLabel = input.agentName ? ` for agent '${input.agentName}'` : "";
|
|
1090
|
+
warnings.push(
|
|
1091
|
+
`extensions: [] override${agentLabel} disables ALL ambient extensions for this child (not just "adds nothing"), including any model-provider extension needed to resolve a provider-qualified model. List the extensions this child actually needs instead of an empty array.`
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
const configuredExtensions = capabilityCeiling?.denyExtensions ? [] : [
|
|
1095
|
+
...toolExtensionPaths,
|
|
1096
|
+
...input.extensions ?? [],
|
|
1097
|
+
...input.subagentOnlyExtensions ?? []
|
|
1098
|
+
];
|
|
1099
|
+
const ordinaryExtensionArgs = disableAmbientExtensions ? [.../* @__PURE__ */ new Set([...runtimeExtensions, ...configuredExtensions])] : [
|
|
1100
|
+
.../* @__PURE__ */ new Set([
|
|
1101
|
+
...runtimeExtensions,
|
|
1102
|
+
...toolExtensionPaths,
|
|
1103
|
+
...input.subagentOnlyExtensions ?? []
|
|
1104
|
+
])
|
|
1105
|
+
];
|
|
1106
|
+
const extensionArgs = [.../* @__PURE__ */ new Set([...ordinaryExtensionArgs, ...requiredExtensions.map(({ path: path5 }) => path5)])];
|
|
1107
|
+
const requestedToolNames = input.tools !== void 0 ? [
|
|
1108
|
+
.../* @__PURE__ */ new Set([
|
|
1109
|
+
...requestedBuiltinTools,
|
|
1110
|
+
...resolvedMcpSelections.map((selection) => selection.name)
|
|
1111
|
+
])
|
|
1112
|
+
] : void 0;
|
|
1113
|
+
const capabilityAudit = capabilityCeiling ? {
|
|
1114
|
+
ceiling: capabilityCeiling,
|
|
1115
|
+
...requestedToolNames ? { requestedTools: requestedToolNames } : {},
|
|
1116
|
+
effectiveTools: effectiveToolAllowlist,
|
|
1117
|
+
...excludeTools.length > 0 ? { excludeTools } : {},
|
|
1118
|
+
removedTools: requestedToolNames?.filter(
|
|
1119
|
+
(tool) => !effectiveToolAllowlist.includes(tool)
|
|
1120
|
+
) ?? [],
|
|
1121
|
+
internalTools,
|
|
1122
|
+
extensionsDenied: capabilityCeiling.denyExtensions,
|
|
1123
|
+
removedExtensionCount: capabilityCeiling.denyExtensions ? (input.extensions?.length ?? 0) + (input.subagentOnlyExtensions?.length ?? 0) + (input.tools ?? []).filter(isToolExtensionPath).length : 0,
|
|
1124
|
+
requestedMcpToolCount: input.mcpDirectTools?.length ?? 0,
|
|
1125
|
+
effectiveMcpTools,
|
|
1126
|
+
agentAllowed: input.agentName === void 0 ? true : isAgentAllowedByCapabilityCeiling(
|
|
1127
|
+
input.agentName,
|
|
1128
|
+
capabilityCeiling
|
|
1129
|
+
),
|
|
1130
|
+
...capabilityCeilingAgentRestrictionSources(capabilityCeiling) ? {
|
|
1131
|
+
agentRestrictionSources: capabilityCeilingAgentRestrictionSources(capabilityCeiling)
|
|
1132
|
+
} : {}
|
|
1133
|
+
} : void 0;
|
|
1134
|
+
return {
|
|
1135
|
+
...capabilityCeiling ? { capabilityCeiling } : {},
|
|
1136
|
+
requestedBuiltinTools,
|
|
1137
|
+
declaredBuiltinTools,
|
|
1138
|
+
excludeTools,
|
|
1139
|
+
toolExtensionPaths,
|
|
1140
|
+
resolvedMcpSelections,
|
|
1141
|
+
effectiveMcpSelections,
|
|
1142
|
+
effectiveMcpTools,
|
|
1143
|
+
builtinMcpTools,
|
|
1144
|
+
explicitToolAllowlist,
|
|
1145
|
+
internalTools,
|
|
1146
|
+
effectiveToolAllowlist,
|
|
1147
|
+
requiredChildTools,
|
|
1148
|
+
fanoutAuthorized,
|
|
1149
|
+
runtimeExtensions,
|
|
1150
|
+
configuredExtensions,
|
|
1151
|
+
requiredExtensions,
|
|
1152
|
+
extensionArgs,
|
|
1153
|
+
disableAmbientExtensions,
|
|
1154
|
+
warnings,
|
|
1155
|
+
...capabilityAudit ? { capabilityAudit } : {}
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
function countLegacyUnderscoreMcpToolNames(selections) {
|
|
1159
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1160
|
+
for (const selection of selections) {
|
|
1161
|
+
const legacyName = legacyUnderscoreMcpToolName(selection);
|
|
1162
|
+
if (legacyName !== selection.name) counts.set(legacyName, (counts.get(legacyName) ?? 0) + 1);
|
|
1163
|
+
}
|
|
1164
|
+
return counts;
|
|
1165
|
+
}
|
|
1166
|
+
function isLegacyUnderscoreMcpToolAllowed(selection, allowedToolSet, resolvedMcpNames, legacyMcpNameCounts) {
|
|
1167
|
+
const legacyName = legacyUnderscoreMcpToolName(selection);
|
|
1168
|
+
return legacyMcpNameCounts.get(legacyName) === 1 && !resolvedMcpNames.has(legacyName) && allowedToolSet.has(legacyName);
|
|
1169
|
+
}
|
|
1170
|
+
function legacyUnderscoreMcpToolName(selection) {
|
|
1171
|
+
const slash = selection.selector.indexOf("/");
|
|
1172
|
+
if (slash < 1) return selection.name;
|
|
1173
|
+
const toolName = selection.selector.slice(slash + 1);
|
|
1174
|
+
const suffix = `_${toolName}`;
|
|
1175
|
+
if (!selection.name.endsWith(suffix)) return selection.name;
|
|
1176
|
+
const prefix = selection.name.slice(0, -suffix.length);
|
|
1177
|
+
return `${prefix.replace(/-/g, "_")}${suffix}`;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
export {
|
|
1181
|
+
extensionOnlyMcpServers,
|
|
1182
|
+
resolveRuntimeModulePath,
|
|
1183
|
+
isSubagentRuntimeExtensionPath,
|
|
1184
|
+
deriveForkPromptCacheKey,
|
|
1185
|
+
supervisorChannelDir,
|
|
1186
|
+
applyThinkingSuffix,
|
|
1187
|
+
assertNoToolModifierEntries,
|
|
1188
|
+
projectLaunchResolvedChildExtensions,
|
|
1189
|
+
resolvePiLaunchToolPlan
|
|
1190
|
+
};
|