UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

231 lines (230 loc) 9.65 kB
import { a as getAdvertisedScopedMcpCatalog, d as retireSessionMcpRuntime, n as acquireSessionMcpRuntime, t as acquireRequesterScopedMcpRuntime, u as rememberAdvertisedScopedMcpCatalog } from "./agent-bundle-mcp-manager-api-DCHQm4w1.js"; import { n as mergeMcpConnectCatalog } from "./agent-bundle-mcp-requester-connect-Blx6kVwa.js"; import { i as resolveProjectedMcpCodexToolApprovalMode, r as requiresMcpCodexToolApproval, t as formatMcpCodexApprovalRemedy } from "./mcp-codex-tool-approval-UMdM31mU.js"; import { a as setPluginToolMeta, r as getPluginToolMeta } from "./tool-metadata-B5aqo73s.js"; import { t as resolveConversationCapabilityProfile } from "./conversation-capability-profile-B6PkMXFD.js"; import { r as materializeBundleMcpToolsForRun, t as buildBundleMcpToolsFromCatalog } from "./agent-bundle-mcp-materialize-CIHEU_uK.js"; import { t as applyFinalEffectiveToolPolicy } from "./effective-tool-policy-Bco8DCTH.js"; import { t as applyEmbeddedAttemptToolsAllow } from "./attempt-tool-construction-plan-CRC43c_k.js"; //#region src/agents/agent-bundle-mcp-harness.ts function formatConfiguredMcpDiagnosticNotice(messages, runLabel) { const bounded = [...new Set(messages)].map((message) => message.replaceAll(/\s+/g, " ").trim().slice(0, 180)).filter(Boolean).slice(0, 4); if (bounded.length === 0) return; return `Configured MCP is incomplete for ${runLabel}: ${bounded.join("; ")}. Do not claim MCP-backed work succeeded; report this blocker to the operator.`; } function applyConfiguredMcpApproval(tools, options) { return tools.flatMap((tool) => { const mcp = getPluginToolMeta(tool)?.mcp; if (mcp?.operation !== "tool") return [tool]; const mode = resolveProjectedMcpCodexToolApprovalMode(mcp.serverName, {}, options.projectedMcpServers?.[mcp.serverName], mcp.toolName) ?? mcp.codexApproval?.mode; if (!requiresMcpCodexToolApproval({ ...mcp.codexApproval, mode, fullPermission: options.fullPermission })) return [tool]; const approvalMode = mode === "prompt" ? "prompt" : "auto"; const requestApproval = options.requestApproval; if (!requestApproval) { options.onOmitted?.(`${mcp.serverName}/${mcp.toolName}: requires interactive Codex approval (${approvalMode}); ${formatMcpCodexApprovalRemedy(mcp.serverName)}`); return []; } const meta = getPluginToolMeta(tool); const execute = tool.execute; const guarded = { ...tool, execute: async (toolCallId, params, signal, onUpdate) => { let active = true; try { await requestApproval({ signal, safeToolName: tool.name, toolCallId, serverName: mcp.serverName, toolName: mcp.toolName, mode: approvalMode, isActive: () => active }); return await execute(toolCallId, params, signal, onUpdate); } finally { active = false; } } }; setPluginToolMeta(guarded, meta); return [guarded]; }); } function notConnectedToolResult(serverName, toolName) { const message = `Requester has not connected MCP server "${serverName}" (tool "${toolName}") for this turn.`; return { content: [{ type: "text", text: message }], details: { status: "error", error: message, mcpServer: serverName, mcpTool: toolName } }; } function applyHarnessToolPolicy(tools, params) { if (tools.length === 0) return tools; const allowed = applyEmbeddedAttemptToolsAllow(tools, params.toolsAllow, { toolMeta: (tool) => getPluginToolMeta(tool) }); const profile = params.conversationCapabilityProfile ?? (params.policyContext ? resolveConversationCapabilityProfile({ ...params.policyContext, runtimeToolAllowlist: params.toolsAllow }) : void 0); if (!profile) return allowed; return applyFinalEffectiveToolPolicy({ bundledTools: allowed, config: params.policyContext?.config ?? params.cfg, conversationCapabilityProfile: profile, warn: params.warn ?? (() => void 0) }); } function buildCatalogTools(catalog, params, requesterConnect) { return buildBundleMcpToolsFromCatalog({ catalog, reservedToolNames: params.reservedToolNames ? Array.from(params.reservedToolNames) : void 0, createExecute: (tool) => { return requesterConnect?.createExecute(tool.serverName) ?? (async () => notConnectedToolResult(tool.serverName, tool.toolName)); } }); } /** * Materialize static configured MCP for a Codex harness turn. * No requester identity is accepted here, so requester resolvers stay unreachable. */ async function materializeStaticMcpToolsForHarnessRunCore(params) { const acquisition = await acquireSessionMcpRuntime({ sessionId: params.sessionId, sessionKey: params.sessionKey, workspaceDir: params.workspaceDir, agentDir: params.agentDir, cfg: params.cfg, manifestRegistry: params.manifestRegistry, toolOverrides: params.toolOverrides }); const retireSnapshotRuntime = params.retireSessionRuntimeAfterDispose ? async () => { await retireSessionMcpRuntime({ sessionId: params.sessionId, reason: "scheduled-authority-snapshot-complete" }); } : void 0; let liveRuntime; try { liveRuntime = await materializeBundleMcpToolsForRun({ ...acquisition, agentId: params.agentId, reservedToolNames: params.reservedToolNames, ...retireSnapshotRuntime ? { disposeRuntime: retireSnapshotRuntime } : {} }); } catch (error) { await retireSnapshotRuntime?.(); throw error; } try { const policyWarnings = []; const policyParams = { ...params, warn: (message) => { policyWarnings.push(message); params.warn?.(message); } }; const fullPermission = params.autoApproveCodexAppServerApprovals === true; const policyTools = applyHarnessToolPolicy(liveRuntime.tools, policyParams); const projectedApproval = params.projectedMcpServers ? { projectedMcpServers: params.projectedMcpServers } : {}; const allowed = applyConfiguredMcpApproval(policyTools, { fullPermission, ...projectedApproval, ...params.requestInteractiveCodexApproval ? { requestApproval: params.requestInteractiveCodexApproval } : {}, onOmitted: (message) => policyWarnings.push(message) }); liveRuntime.restrictAppTools?.(applyConfiguredMcpApproval(applyHarnessToolPolicy(liveRuntime.appTools ?? liveRuntime.tools, policyParams), { fullPermission, ...projectedApproval, ...params.requestInteractiveCodexApproval ? {} : { onOmitted: (message) => policyWarnings.push(message) } })); const diagnosticNotice = formatConfiguredMcpDiagnosticNotice([...(liveRuntime.diagnostics ?? []).map((diagnostic) => `${diagnostic.serverName}: ${diagnostic.message}`), ...policyWarnings], params.requestInteractiveCodexApproval ? "this run" : "this scheduled run"); let disposed = false; return { tools: allowed, ...diagnosticNotice ? { diagnosticNotice } : {}, dispose: async () => { if (disposed) return; disposed = true; await liveRuntime.dispose(); } }; } catch (error) { await liveRuntime.dispose(); throw error; } } /** * Materialize requester-scoped MCP tools for a harness run (e.g. Codex dynamic tools). * Updates the session advertised-catalog cache when a requester resolves a catalog. * Before any requester resolves in the session, returns undefined (nothing to advertise). */ async function materializeRequesterScopedMcpToolsForHarnessRunCore(params) { const scopedRuntimeHandle = await acquireRequesterScopedMcpRuntime({ sessionId: params.sessionId, sessionKey: params.sessionKey, workspaceDir: params.workspaceDir, agentDir: params.agentDir, cfg: params.cfg, manifestRegistry: params.manifestRegistry, toolOverrides: params.toolOverrides, requesterSenderId: params.requesterSenderId, agentAccountId: params.agentAccountId, messageChannel: params.messageChannel }); const scopedRuntime = scopedRuntimeHandle?.runtime; let liveRuntime; let liveCatalog; try { if (scopedRuntime) { liveRuntime = await materializeBundleMcpToolsForRun({ runtime: scopedRuntime, releaseLease: scopedRuntimeHandle?.releaseLease, agentId: params.agentId, reservedToolNames: params.reservedToolNames }); liveCatalog = scopedRuntime.peekCatalog() ?? await scopedRuntime.getCatalog(); if (liveCatalog.tools.length > 0 && scopedRuntimeHandle) rememberAdvertisedScopedMcpCatalog(scopedRuntimeHandle, liveCatalog); } const advertisedCatalog = getAdvertisedScopedMcpCatalog(params.sessionId) ?? (liveCatalog ? mergeMcpConnectCatalog(liveCatalog, scopedRuntime?.requesterConnect) : void 0); if (!advertisedCatalog || advertisedCatalog.tools.length === 0) { await liveRuntime?.dispose(); return; } const reservedToolNames = params.reservedToolNames ? Array.from(params.reservedToolNames) : void 0; const advertisedTools = buildCatalogTools(advertisedCatalog, { ...params, reservedToolNames }, scopedRuntime?.requesterConnect); const liveByName = new Map((liveRuntime?.tools ?? []).map((tool) => [tool.name, tool])); const filteredTools = applyHarnessToolPolicy(advertisedTools.map((tool) => liveByName.get(tool.name) ?? tool), params); const filteredAdvertised = applyHarnessToolPolicy(advertisedTools, params); const allowedNames = new Set(filteredAdvertised.map((tool) => tool.name)); const executableTools = filteredTools.filter((tool) => allowedNames.has(tool.name)); let disposed = false; return { tools: executableTools, advertisedTools: filteredAdvertised, dispose: async () => { if (disposed) return; disposed = true; await liveRuntime?.dispose(); } }; } catch (error) { await liveRuntime?.dispose(); throw error; } } //#endregion export { materializeRequesterScopedMcpToolsForHarnessRunCore, materializeStaticMcpToolsForHarnessRunCore };