UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

276 lines (275 loc) • 13.1 kB
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { n as filterStringEntries } from "./string-normalization-D6DKCpwE.mjs"; import { n as normalizeAgentId, t as isValidAgentId } from "./agent-id-D1-Q6vR4.mjs"; import "./session-key-DY3MOX4N.mjs"; import { h as resolveSessionAgentId } from "./agent-scope-bJOokm5r.mjs"; import { r as normalizeConfiguredMcpServers } from "./mcp-config-normalize-BxEbywQ8.mjs"; import { n as acquireSessionMcpRuntime } from "./agent-bundle-mcp-manager-api-B24ukpdI.mjs"; import { f as releaseSessionMcpRuntime } from "./mcp-app-model-context-C31IumwY.mjs"; import { u as partitionMcpServersByConnectionScope } from "./bundle-mcp-config-0ZgVgjo8.mjs"; import { r as resolveMcpBearerBundleConfig, t as requiresMcpBearerProjection } from "./mcp-auth-profile-Eg9Zqocr.mjs"; import { r as getPluginToolMeta } from "./tool-metadata-D5DsuLcG.mjs"; import { n as resolveSandboxRuntimeStatus } from "./runtime-status-BBzhhE6k.mjs"; import { t as buildBundleMcpToolsFromCatalog } from "./agent-bundle-mcp-materialize-BZFLfYd6.mjs"; import { t as resolveConversationCapabilityProfile } from "./conversation-capability-profile-Bzbp--xF.mjs"; import { t as applyFinalEffectiveToolPolicy } from "./effective-tool-policy-DwPDbA-1.mjs"; import { t as applyEmbeddedAttemptToolsAllow } from "./attempt-tool-construction-plan-Cf_IxbdX.mjs"; import { t as loadMcpToolGrants } from "./exec-approvals-mcp-NZcNq8Uv.mjs"; import { a as normalizeCodexMcpServerConfig, n as buildCodexMcpServersConfig, t as applyCodexSessionMcpToolDenials } from "./codex-mcp-config-4NP_GYFS.mjs"; import { n as serializeTomlInlineValue } from "./toml-inline-FNO-yGXG.mjs"; //#region src/agents/native-mcp-policy.ts function buildPolicyProjectionTools(catalog) { const callableTools = buildBundleMcpToolsFromCatalog({ catalog }).filter((tool) => getPluginToolMeta(tool)?.mcp?.operation === "tool"); const callableIdentities = new Set(callableTools.flatMap((tool) => { const mcp = getPluginToolMeta(tool)?.mcp; return mcp?.operation === "tool" ? [JSON.stringify([mcp.serverName, mcp.toolName])] : []; })); const hiddenPolicyTools = (catalog.policyTools ?? [...catalog.tools, ...catalog.sessionDeniedTools ?? []]).filter((tool) => !callableIdentities.has(JSON.stringify([tool.serverName, tool.toolName]))); const hiddenTools = buildBundleMcpToolsFromCatalog({ catalog: { ...catalog, tools: hiddenPolicyTools, policyTools: void 0, sessionDeniedTools: void 0 }, reservedToolNames: callableTools.map((tool) => tool.name), includeAppOnlyInventory: true }).filter((tool) => getPluginToolMeta(tool)?.mcp?.operation === "tool"); return [...callableTools, ...hiddenTools]; } async function prepareNativeMcpPolicy(params) { params.runtime.markUsed(); const allTools = buildPolicyProjectionTools(await params.runtime.getCatalog()); const runtimeAllowed = applyEmbeddedAttemptToolsAllow(allTools, params.runtimeToolsAllow, { toolMeta: (tool) => getPluginToolMeta(tool) }); const effectiveAllowed = applyFinalEffectiveToolPolicy({ bundledTools: runtimeAllowed, config: params.config, workspaceDir: params.workspaceDir, conversationCapabilityProfile: params.capabilityProfile, warn: params.warn }); const effectiveAllowedNames = new Set(effectiveAllowed.map((tool) => tool.name)); const servers = {}; for (const tool of allTools) { const mcp = getPluginToolMeta(tool)?.mcp; if (!mcp || mcp.operation !== "tool") continue; const server = servers[mcp.serverName] ??= { serverName: mcp.serverName, safeServerName: mcp.safeServerName, allowedTools: [], deniedTools: [] }; (!mcp.excludedFromOpenClawCatalog && !mcp.deniedBySession && effectiveAllowedNames.has(tool.name) ? server.allowedTools : server.deniedTools).push(mcp.toolName); } for (const server of Object.values(servers)) { server.allowedTools = [...new Set(server.allowedTools)].toSorted(); server.deniedTools = [...new Set(server.deniedTools)].toSorted(); } return { servers: Object.fromEntries(Object.entries(servers).toSorted(([left], [right]) => left.localeCompare(right))) }; } /** Applies one prepared policy to the provider-neutral MCP config shape. */ function applyPreparedNativeMcpPolicy(config, policy) { return { mcpServers: Object.fromEntries(Object.entries(config.mcpServers).flatMap(([serverName, server]) => { const prepared = policy.servers[serverName]; if (!prepared || prepared.allowedTools.length === 0) return []; const toolFilter = isRecord(server.toolFilter) ? server.toolFilter : {}; return [[serverName, { ...server, toolFilter: { ...toolFilter, include: prepared.allowedTools, exclude: prepared.deniedTools } }]]; })) }; } /** Returns raw per-server denials for backends that enforce a deny list. */ function preparedNativeMcpDenials(policy) { const entries = Object.values(policy.servers).filter((server) => server.deniedTools.length > 0).map((server) => [server.serverName, server.deniedTools]); return entries.length > 0 ? Object.fromEntries(entries) : void 0; } //#endregion //#region src/agents/cli-runner/native-mcp-context.ts /** Both native MCP adapters project the same requester facts before selecting runtime policy. */ function projectNativeMcpRunContext(run) { return { sessionId: run.sessionId, runId: run.runId, agentAccountId: run.agentAccountId, messageProvider: run.messageProvider ?? run.messageChannel, messageChannel: run.messageChannel, groupId: run.groupId, groupChannel: run.groupChannel, groupSpace: run.groupSpace, spawnedBy: run.spawnedBy, senderId: run.senderId, senderName: run.senderName, senderUsername: run.senderUsername, senderE164: run.senderE164, senderIsOwner: run.senderIsOwner, conversationToolPolicy: run.conversationToolPolicy, inputProvenance: run.inputProvenance, trustedInternalHandoff: run.trustedInternalHandoff, scheduledToolPolicy: run.scheduledToolPolicy }; } //#endregion //#region src/agents/cli-runner/bundle-mcp-codex.ts /** * Codex CLI and app-server bundle MCP projection helpers. */ function isCodexMcpServerAllowedForAgent(server, options) { const codex = isRecord(server.codex) ? server.codex : {}; if (!Object.hasOwn(codex, "agents")) return true; if (!options?.agentId) return false; const agentId = normalizeAgentId(options.agentId); return filterStringEntries(codex.agents).some((entry) => { const candidate = entry.trim(); return isValidAgentId(candidate) && normalizeAgentId(candidate) === agentId; }); } /** * Applies Codex-only agent scoping before OpenClaw resolves credentials or opens transports. * Session overrides may narrow this result, but cannot widen `codex.agents`. */ function resolveCodexMcpToolOverridesForAgent(cfg, options) { const deniedServerNames = Object.entries(normalizeConfiguredMcpServers(cfg?.mcp?.servers)).filter(([, server]) => !isCodexMcpServerAllowedForAgent(server, options)).map(([name]) => name); if (deniedServerNames.length === 0) return options.toolOverrides; const mcpServers = { ...options.toolOverrides?.mcpServers }; for (const serverName of deniedServerNames) mcpServers[serverName] = false; return { ...options.toolOverrides, mcpServers }; } function readSessionMcpServerOverride(options, name) { const overrides = options?.toolOverrides?.mcpServers; return overrides && Object.hasOwn(overrides, name) ? overrides[name] : void 0; } function selectCodexProjectableMcpServers(cfg, options) { const userServers = normalizeConfiguredMcpServers(cfg?.mcp?.servers); const { staticServers } = partitionMcpServersByConnectionScope(userServers); return Object.fromEntries(Object.entries(staticServers).filter(([serverName, server]) => { const serverOverride = readSessionMcpServerOverride(options, serverName); if (!(serverOverride !== false && (serverOverride === true || server.enabled !== false) && isCodexMcpServerAllowedForAgent(server, options))) return false; if (options?.allowLiteralOAuthProjection === false && requiresMcpBearerProjection(server)) { options.onServerUnavailable?.(serverName, /* @__PURE__ */ new Error(`MCP OAuth bearer projection is only supported for local app-server connections.`)); return false; } return true; })); } /** Returns Codex CLI args with TOML MCP server overrides injected. */ function injectCodexMcpConfigArgs(args, config) { const overrides = serializeTomlInlineValue(buildCodexMcpServersConfig(config)); return [ ...args ?? [], "-c", `mcp_servers=${overrides}` ]; } /** Async runtime projection that resolves OpenClaw-managed MCP bearer tokens. */ async function buildCodexUserMcpServersThreadConfigPatchForRuntime(cfg, options) { let allowedServers = selectCodexProjectableMcpServers(cfg, options); if (options?.preparationOnly && Object.values(allowedServers).some(requiresMcpBearerProjection)) throw new Error("Native fork preparation cannot resolve MCP bearer credentials. Fork an original imported message instead."); if (options?.preparedNativeMcpPolicy) allowedServers = applyPreparedNativeMcpPolicy({ mcpServers: allowedServers }, options.preparedNativeMcpPolicy).mcpServers; if (Object.keys(allowedServers).length === 0) return; const grants = options?.agentId ? await loadMcpToolGrants(options.agentId) : []; const resolvedConfig = await resolveMcpBearerBundleConfig({ config: { mcpServers: allowedServers }, cfg, agentDir: options?.agentDir, tokenProjection: "literal", omitUnavailableOAuthServers: true, onServerUnavailable: options?.onServerUnavailable }); const mcp_servers = Object.fromEntries(Object.entries(resolvedConfig.config.mcpServers).map(([name, server]) => [name, normalizeCodexMcpServerConfig(name, applyCodexSessionMcpToolDenials(name, server, options?.toolOverrides), grants)])); return Object.keys(mcp_servers).length === 0 ? void 0 : { mcp_servers }; } /** Prepares canonical native MCP policy and projects it into Codex before thread creation. */ async function buildCodexUserMcpServersThreadConfigPatchForRun(params) { const run = params.run; const agentId = params.agentId ?? run.agentId; const scopedToolOverrides = resolveCodexMcpToolOverridesForAgent(run.config, { agentId, toolOverrides: run.toolOverrides }); const policySessionKey = run.sandboxSessionKey ?? run.sessionKey; const policyAgentId = resolveSessionAgentId({ config: run.config, sessionKey: policySessionKey, agentId: run.sandboxAgentId, fallbackAgentId: agentId }); const sandboxStatus = resolveSandboxRuntimeStatus({ cfg: run.config, sessionKey: policySessionKey, agentId: policyAgentId }); const capabilityProfile = resolveConversationCapabilityProfile({ ...projectNativeMcpRunContext(run), config: run.config, sessionKey: policySessionKey, runSessionKey: run.sessionKey && run.sessionKey !== policySessionKey ? run.sessionKey : void 0, agentId: policyAgentId, modelProvider: run.provider, modelId: run.modelId, workspaceDir: run.workspaceDir, cwd: params.cwd, sandboxToolPolicy: sandboxStatus.sandboxed ? sandboxStatus.toolPolicy : void 0, runtimeToolAllowlist: run.toolsAllow, inheritRuntimeToolAllowlist: true, runtimePluginToolGrant: run.runtimePluginToolGrant }); const configuredMcpServers = selectCodexProjectableMcpServers(run.config, { agentId, allowLiteralOAuthProjection: params.allowLiteralOAuthProjection, onServerUnavailable: params.onServerUnavailable, toolOverrides: scopedToolOverrides }); if (Object.keys(configuredMcpServers).length === 0) return; const projectionConfig = { ...run.config, mcp: { ...run.config?.mcp, servers: configuredMcpServers } }; const acquisition = await acquireSessionMcpRuntime({ sessionId: run.sessionId, sessionKey: run.sessionKey, workspaceDir: run.workspaceDir, agentDir: run.agentDir, cfg: projectionConfig, requesterSenderId: run.senderId, agentAccountId: run.agentAccountId, messageChannel: run.messageChannel, toolOverrides: scopedToolOverrides, toolDenylist: capabilityProfile.policy.explicitToolDenylist }); let retainedServerNames; try { const preparedNativeMcpPolicy = await prepareNativeMcpPolicy({ runtime: acquisition.runtime, config: run.config, workspaceDir: run.workspaceDir, capabilityProfile, runtimeToolsAllow: run.toolsAllow, warn: params.warn ?? (() => {}) }); const prepared = await buildCodexUserMcpServersThreadConfigPatchForRuntime(projectionConfig, { agentId, agentDir: run.agentDir, allowLiteralOAuthProjection: params.allowLiteralOAuthProjection, onServerUnavailable: params.onServerUnavailable, toolOverrides: scopedToolOverrides, preparedNativeMcpPolicy }); retainedServerNames = new Set(Object.keys(prepared?.mcp_servers ?? {})); return prepared; } finally { await releaseSessionMcpRuntime(acquisition, retainedServerNames); } } //#endregion export { projectNativeMcpRunContext as a, preparedNativeMcpDenials as c, resolveCodexMcpToolOverridesForAgent as i, buildCodexUserMcpServersThreadConfigPatchForRuntime as n, applyPreparedNativeMcpPolicy as o, injectCodexMcpConfigArgs as r, prepareNativeMcpPolicy as s, buildCodexUserMcpServersThreadConfigPatchForRun as t };