UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

508 lines (507 loc) 24.2 kB
import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js"; import { h as writeJson, p as tryReadJson } from "./json-files-Bq1lIlQB.js"; import { t as applyMergePatch } from "./merge-patch-DCMj2JVh.js"; import { c as releaseSessionMcpRuntime, n as acquireSessionMcpRuntime } from "./agent-bundle-mcp-manager-api-DCHQm4w1.js"; import { t as extractMcpServerMap } from "./bundle-mcp-zo_t2klC.js"; import { n as prepareOwnedBundleMcpDataDirs, r as toCliBundleMcpServerConfig, t as loadMergedBundleMcpConfig } from "./bundle-mcp-config-D7dA4odX.js"; import { r as resolveMcpBearerBundleConfig } from "./mcp-auth-profile--_BtN_eA.js"; import { n as OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV, t as OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV } from "./openclaw-tools-serve-config-BpDM9GLM.js"; import { c as normalizeMcpStringRecord, o as decodeHeaderEnvPlaceholder, s as normalizeBundleMcpServerConfig } from "./codex-mcp-config-DulQr1QH.js"; import { c as preparedNativeMcpDenials, i as injectCodexMcpConfigArgs, o as applyPreparedNativeMcpPolicy, s as prepareNativeMcpPolicy } from "./bundle-mcp-codex-Cy9fxv2n.js"; import path from "node:path"; import fs from "node:fs/promises"; import os from "node:os"; import crypto from "node:crypto"; //#region src/agents/cli-runner/bundle-mcp-runtime.ts function injectBundleMcpBackendArgs(backend, inject) { return { ...backend, args: inject(backend.args), resumeArgs: inject(backend.resumeArgs ?? backend.args ?? []) }; } async function writeTemporaryBundleMcpJson(prefix, value, fileName = "settings.json", atomic = true) { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); const filePath = path.join(tempDir, fileName); if (atomic) await writeJson(filePath, value, { trailingNewline: true }); else await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf-8"); return { filePath, cleanup: () => fs.rm(tempDir, { recursive: true, force: true }) }; } function withOpenClawMcpCaptureHeader(config, captureKey, missingServerError) { const mcpServers = isRecord(config.mcpServers) ? config.mcpServers : {}; if (!(isRecord(mcpServers.openclaw) ? mcpServers.openclaw : void 0) && missingServerError) throw new Error(missingServerError); return applyMergePatch(config, { mcpServers: { openclaw: { headers: { "x-openclaw-cli-capture-key": captureKey } } } }); } //#endregion //#region src/agents/cli-runner/bundle-mcp-claude.ts /** * Claude CLI argument helpers for OpenClaw-managed bundle MCP config. */ /** Find existing Claude `--mcp-config` argument values. */ function findClaudeMcpConfigPaths(args) { const paths = []; if (!args?.length) return paths; for (let i = 0; i < args.length; i += 1) { const arg = args[i] ?? ""; if (arg === "--mcp-config") { while (typeof args[i + 1] === "string" && !args[i + 1]?.startsWith("-")) { i += 1; const path = normalizeOptionalString(args[i]); if (path) paths.push(path); } continue; } if (arg.startsWith("--mcp-config=")) { const path = normalizeOptionalString(arg.slice(13)); if (path) paths.push(path); } } return paths; } /** Return Claude args with OpenClaw's strict MCP config path injected. */ function mergeClaudeDisallowedTools(args, deniedTools) { if (deniedTools.length === 0) return args; const next = []; const existingDisallowed = []; for (let i = 0; i < args.length; i += 1) { const arg = args[i] ?? ""; if (arg === "--disallowedTools" || arg === "--disallowed-tools") { while (typeof args[i + 1] === "string" && !args[i + 1]?.startsWith("-")) { i += 1; existingDisallowed.push(args[i] ?? ""); } continue; } if (arg.startsWith("--disallowedTools=") || arg.startsWith("--disallowed-tools=")) { existingDisallowed.push(arg.slice(arg.indexOf("=") + 1)); continue; } next.push(arg); } next.push("--disallowedTools", [.../* @__PURE__ */ new Set([...existingDisallowed, ...deniedTools])].join(",")); return next; } function normalizeClaudeMcpIdentifierPart(value) { return value.replaceAll(/[^a-zA-Z0-9_-]/g, "_"); } function injectClaudeWebSearchDisabledArgs(args) { return mergeClaudeDisallowedTools(args ?? [], ["WebSearch"]); } function injectClaudeMcpConfigArgs(args, mcpConfigPath, mcpToolsDeny, webSearchEnabled) { const next = []; for (let i = 0; i < (args?.length ?? 0); i += 1) { const arg = args?.[i] ?? ""; if (arg === "--strict-mcp-config") continue; if (arg === "--mcp-config") { while (typeof args?.[i + 1] === "string" && !args[i + 1]?.startsWith("-")) i += 1; continue; } if (arg.startsWith("--mcp-config=")) continue; next.push(arg); } next.push("--strict-mcp-config", "--mcp-config", mcpConfigPath); const deniedTools = Object.entries(mcpToolsDeny ?? {}).flatMap(([serverName, toolNames]) => toolNames.map((toolName) => `mcp__${normalizeClaudeMcpIdentifierPart(serverName)}__${normalizeClaudeMcpIdentifierPart(toolName)}`)); if (webSearchEnabled === false) deniedTools.push("WebSearch"); return mergeClaudeDisallowedTools(next, deniedTools.toSorted()); } /** Writes the active per-attempt capture token into OpenClaw's generated Claude MCP config. */ async function writeClaudeMcpCaptureConfig(params) { const raw = JSON.parse(await fs.readFile(params.mcpConfigPath, "utf-8")); if (!isRecord(raw)) throw new Error("Claude MCP capture requires an object config"); await fs.writeFile(params.mcpConfigPath, `${JSON.stringify(withOpenClawMcpCaptureHeader(raw, params.captureKey, "Claude MCP capture requires an openclaw server config"), null, 2)}\n`, "utf-8"); } //#endregion //#region src/agents/cli-runner/bundle-mcp-gemini.ts /** * Gemini CLI bundle MCP adapter that writes temporary system settings files. */ const GEMINI_MCP_SERVER_FIELDS = { strings: ["type"], booleans: ["trust"] }; async function readJsonObject(filePath) { const raw = await tryReadJson(filePath); return raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {}; } async function readGeminiBaseSettings(inheritedEnv) { const settingsPath = inheritedEnv?.GEMINI_CLI_SYSTEM_SETTINGS_PATH ?? process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH; return typeof settingsPath === "string" && settingsPath.trim() ? await readJsonObject(settingsPath) : {}; } function mergeGeminiWebSearchDisabled(base) { const existing = isRecord(base.tools) && Array.isArray(base.tools.exclude) ? base.tools.exclude.filter((name) => typeof name === "string") : []; return applyMergePatch(base, { tools: { exclude: [.../* @__PURE__ */ new Set([...existing, "google_web_search"])] } }); } async function writeGeminiSettings(settings, inheritedEnv) { const temporary = await writeTemporaryBundleMcpJson("openclaw-gemini-mcp-", settings); return { env: { ...inheritedEnv, GEMINI_CLI_SYSTEM_SETTINGS_PATH: temporary.filePath }, cleanup: temporary.cleanup }; } async function writeGeminiWebSearchDisabledSettings(inheritedEnv) { return await writeGeminiSettings(mergeGeminiWebSearchDisabled(await readGeminiBaseSettings(inheritedEnv)), inheritedEnv); } function resolveEnvPlaceholder(value, inheritedEnv) { const decoded = decodeHeaderEnvPlaceholder(value); if (!decoded) return value; const resolved = inheritedEnv?.[decoded.envVar] ?? process.env[decoded.envVar] ?? ""; return decoded.bearer ? `Bearer ${resolved}` : resolved; } function normalizeGeminiServerConfig(server, inheritedEnv, deniedTools) { const next = normalizeBundleMcpServerConfig(server, GEMINI_MCP_SERVER_FIELDS); const headers = normalizeMcpStringRecord(server.headers); if (headers) next.headers = Object.fromEntries(Object.entries(headers).map(([name, value]) => [name, resolveEnvPlaceholder(value, inheritedEnv)])); const toolFilter = isRecord(server.toolFilter) ? server.toolFilter : {}; const included = Array.isArray(toolFilter.include) ? toolFilter.include.filter((name) => typeof name === "string") : []; if (included.length > 0) { const existing = Array.isArray(server.includeTools) ? server.includeTools.filter((name) => typeof name === "string") : []; const finalIncluded = existing.length > 0 ? included.filter((name) => existing.includes(name)).toSorted() : [...new Set(included)].toSorted(); if (finalIncluded.length === 0) return; next.includeTools = finalIncluded; } const filteredDenied = Array.isArray(toolFilter.exclude) ? toolFilter.exclude.filter((name) => typeof name === "string") : []; if (deniedTools?.length || filteredDenied.length > 0) { const existing = Array.isArray(server.excludeTools) ? server.excludeTools.filter((name) => typeof name === "string") : []; next.excludeTools = [.../* @__PURE__ */ new Set([ ...existing, ...filteredDenied, ...deniedTools ?? [] ])].toSorted(); } return next; } /** Writes merged Gemini system settings and returns env plus cleanup hook. */ async function writeGeminiSystemSettings(mergedConfig, inheritedEnv, mcpToolsDeny, webSearchEnabled) { const base = await readGeminiBaseSettings(inheritedEnv); const normalizedConfig = { mcpServers: Object.fromEntries(Object.entries(mergedConfig.mcpServers).flatMap(([name, server]) => { const normalized = normalizeGeminiServerConfig(server, inheritedEnv, mcpToolsDeny && Object.hasOwn(mcpToolsDeny, name) ? mcpToolsDeny[name] : void 0); return normalized ? [[name, normalized]] : []; })) }; const settings = applyMergePatch(webSearchEnabled === false ? mergeGeminiWebSearchDisabled(base) : base, { mcp: { allowed: Object.keys(normalizedConfig.mcpServers) }, mcpServers: normalizedConfig.mcpServers }); if (!isRecord(settings.mcp) || !isRecord(settings.mcpServers)) throw new Error("Gemini MCP settings merge produced an invalid object"); return await writeGeminiSettings(settings, inheritedEnv); } /** Writes per-attempt Gemini settings with the active loopback capture token. */ async function writeGeminiMcpCaptureSettings(params) { const existingSettingsPath = params.inheritedEnv?.GEMINI_CLI_SYSTEM_SETTINGS_PATH; if (!existingSettingsPath) throw new Error("Gemini MCP capture requires prepared system settings"); const temporary = await writeTemporaryBundleMcpJson("openclaw-gemini-mcp-attempt-", withOpenClawMcpCaptureHeader(await readJsonObject(existingSettingsPath), params.captureKey)); return { env: { ...params.inheritedEnv, GEMINI_CLI_SYSTEM_SETTINGS_PATH: temporary.filePath }, cleanup: temporary.cleanup }; } //#endregion //#region src/agents/cli-runner/bundle-mcp.ts /** * Prepares bundled MCP configuration for CLI runner backends. */ async function readExternalMcpConfig(configPath) { return { mcpServers: extractMcpServerMap(await tryReadJson(configPath)) }; } function sortJsonValue(value) { if (Array.isArray(value)) return value.map((entry) => sortJsonValue(entry)); if (!isRecord(value)) return value; return Object.fromEntries(Object.keys(value).toSorted().map((key) => [key, sortJsonValue(value[key])])); } function normalizeOpenClawLoopbackUrl(value) { const match = /^(http:\/\/(?:127\.0\.0\.1|localhost|\[::1\])):\d+(\/mcp)$/.exec(value.trim()) ?? void 0; if (!match) return value; return `${match[1]}:<openclaw-loopback>${match[2]}`; } function canonicalizeSystemAgentTurnStateForResume(server) { if (!isRecord(server.env) || server.env["OPENCLAW_TOOLS_MCP_TOOLS"] !== "openclaw") return server; return { ...server, env: { ...server.env, [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]: "<openclaw-turn-state>", [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV]: "<openclaw-turn-state>" } }; } function canonicalizeBundleMcpConfigForResume(config) { return { mcpServers: sortJsonValue(Object.fromEntries(Object.entries(config.mcpServers).map(([name, server]) => { const canonicalServer = canonicalizeSystemAgentTurnStateForResume(server); if (name !== "openclaw" || typeof canonicalServer.url !== "string") return [name, sortJsonValue(canonicalServer)]; return [name, sortJsonValue({ ...canonicalServer, url: normalizeOpenClawLoopbackUrl(canonicalServer.url) })]; }))) }; } const OPENCLAW_MCP_ENV_TEMPLATE_PATTERN = /\$\{(OPENCLAW_MCP_[A-Z0-9_]+)\}/g; function normalizeMcpToolDenials(value) { const entries = Object.entries(value ?? {}).map(([serverName, toolNames]) => [serverName, [...new Set(toolNames)].toSorted()]).filter(([, toolNames]) => toolNames.length > 0).toSorted(([left], [right]) => left.localeCompare(right)); return entries.length > 0 ? Object.fromEntries(entries) : void 0; } function applyCodexMcpToolDenials(config, denials) { if (!denials) return config; return { mcpServers: Object.fromEntries(Object.entries(config.mcpServers).map(([serverName, server]) => { const denied = Object.hasOwn(denials, serverName) ? denials[serverName] : void 0; if (!denied?.length) return [serverName, server]; const toolFilter = isRecord(server.toolFilter) ? server.toolFilter : {}; const existing = Array.isArray(toolFilter.exclude) ? toolFilter.exclude.filter((name) => typeof name === "string") : []; return [serverName, { ...server, toolFilter: { ...toolFilter, exclude: [.../* @__PURE__ */ new Set([...existing, ...denied])].toSorted() } }]; })) }; } function applyMcpServerOverrides(config, overrides) { return overrides ? { mcpServers: Object.fromEntries(Object.entries(config.mcpServers).filter(([serverName]) => !Object.hasOwn(overrides, serverName) || overrides[serverName] !== false)) } : config; } function selectBundleMcpServers(config, selected) { return { mcpServers: Object.fromEntries(Object.entries(config.mcpServers).filter(([name]) => Object.hasOwn(selected.mcpServers, name))) }; } function resolveOpenClawMcpEnvTemplates(value, env) { if (!env) return value; if (typeof value === "string") return value.replace(OPENCLAW_MCP_ENV_TEMPLATE_PATTERN, (match, name) => { const replacement = env[name]; return Object.hasOwn(env, name) && replacement !== void 0 ? replacement : match; }); if (Array.isArray(value)) return value.map((entry) => resolveOpenClawMcpEnvTemplates(entry, env)); if (!isRecord(value)) return value; return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolveOpenClawMcpEnvTemplates(entry, env)])); } async function prepareModeSpecificBundleMcpConfig(params) { const mcpToolsDeny = normalizeMcpToolDenials(params.mcpToolsDeny); const webSearchDisabled = params.webSearchEnabled === false; const configHashInput = mcpToolsDeny || webSearchDisabled ? { config: params.mergedConfig, mcpToolsDeny, webSearchDisabled } : params.mergedConfig; const serializedConfig = `${JSON.stringify(configHashInput, null, 2)}\n`; const mcpConfigHash = crypto.createHash("sha256").update(serializedConfig).digest("hex"); const serializedResumeConfig = `${JSON.stringify(mcpToolsDeny || webSearchDisabled ? { config: canonicalizeBundleMcpConfigForResume(params.mergedConfig), mcpToolsDeny, webSearchDisabled } : canonicalizeBundleMcpConfigForResume(params.mergedConfig), null, 2)}\n`; const mcpResumeHash = crypto.createHash("sha256").update(serializedResumeConfig).digest("hex"); if (params.mode === "codex-config-overrides") { const codexConfig = applyCodexMcpToolDenials(params.mergedConfig, mcpToolsDeny); return { backend: injectBundleMcpBackendArgs(params.backend, (args) => webSearchDisabled ? [ ...injectCodexMcpConfigArgs(args, codexConfig), "-c", "web_search=\"disabled\"" ] : injectCodexMcpConfigArgs(args, codexConfig)), mcpConfigHash, mcpResumeHash, env: params.env }; } if (params.mode === "gemini-system-settings") { const settings = await writeGeminiSystemSettings(params.mergedConfig, params.env, mcpToolsDeny, params.webSearchEnabled); return { backend: params.backend, mcpConfigHash, mcpResumeHash, env: settings.env, cleanup: settings.cleanup }; } const runtimeConfig = resolveOpenClawMcpEnvTemplates(params.mergedConfig, params.env); const temporary = await writeTemporaryBundleMcpJson("openclaw-cli-mcp-", { mcpServers: Object.fromEntries(Object.entries(runtimeConfig.mcpServers).map(([name, server]) => { const { toolFilter: _toolFilter, ...nativeServer } = server; return [name, nativeServer]; })) }, "mcp.json", false); return { backend: injectBundleMcpBackendArgs(params.backend, (args) => injectClaudeMcpConfigArgs(args, temporary.filePath, mcpToolsDeny, params.webSearchEnabled)), mcpConfigHash, mcpResumeHash, env: params.env, cleanup: temporary.cleanup }; } async function prepareCliWebSearchDisabled(params) { const fingerprint = crypto.createHash("sha256").update("web-search-disabled-v1").digest("hex"); if (params.mode === "gemini-system-settings") { const settings = await writeGeminiWebSearchDisabledSettings(params.env); return { backend: params.backend, env: settings.env, cleanup: settings.cleanup, mcpConfigHash: fingerprint, mcpResumeHash: fingerprint }; } return { backend: injectBundleMcpBackendArgs(params.backend, (args) => params.mode === "codex-config-overrides" ? [ ...args ?? [], "-c", "web_search=\"disabled\"" ] : injectClaudeWebSearchDisabledArgs(args)), env: params.env, mcpConfigHash: fingerprint, mcpResumeHash: fingerprint }; } /** Prepare backend args/env/cleanup for bundle MCP injection into a CLI run. */ async function prepareCliBundleMcpConfig(params) { if (!params.enabled) return params.toolOverrides?.webSearch === false ? await prepareCliWebSearchDisabled({ mode: params.mode ?? "claude-config-file", backend: params.backend, env: params.env }) : { backend: params.backend, env: params.env }; const mode = params.mode ?? "claude-config-file"; if (params.exclusiveConfig) return await prepareModeSpecificBundleMcpConfig({ mode, backend: params.backend, mergedConfig: applyMcpServerOverrides(params.exclusiveConfig, params.toolOverrides?.mcpServers), env: params.env, mcpToolsDeny: params.toolOverrides?.mcpToolsDeny, webSearchEnabled: params.toolOverrides?.webSearch }); const resumeMcpConfigPaths = mode === "claude-config-file" ? findClaudeMcpConfigPaths(params.backend.resumeArgs) : []; const existingMcpConfigPaths = mode === "claude-config-file" && resumeMcpConfigPaths.length > 0 ? resumeMcpConfigPaths : mode === "claude-config-file" ? findClaudeMcpConfigPaths(params.backend.args) : []; let mergedConfig = { mcpServers: {} }; for (const existingMcpConfigPath of existingMcpConfigPaths) { const resolvedExistingPath = path.isAbsolute(existingMcpConfigPath) ? existingMcpConfigPath : path.resolve(params.workspaceDir, existingMcpConfigPath); mergedConfig = applyMergePatch(mergedConfig, await readExternalMcpConfig(resolvedExistingPath)); } const bundleConfig = loadMergedBundleMcpConfig({ workspaceDir: params.workspaceDir, cfg: params.config, mapConfiguredServer: toCliBundleMcpServerConfig, toolOverrides: params.toolOverrides }); for (const diagnostic of bundleConfig.diagnostics) params.warn?.(`bundle MCP skipped for ${diagnostic.pluginId}: ${diagnostic.message}`); mergedConfig = applyMergePatch(mergedConfig, bundleConfig.config); const prepareDataDirsByServer = { ...bundleConfig.prepareDataDirsByServer }; const additionalServerNames = new Set(Object.keys(params.additionalConfig?.mcpServers ?? {})); if (params.additionalConfig) { mergedConfig = applyMergePatch(mergedConfig, params.additionalConfig); for (const serverName of Object.keys(params.additionalConfig.mcpServers)) delete prepareDataDirsByServer[serverName]; } const warnUnavailableOAuthServer = (serverName, error) => params.warn?.(`bundle MCP skipped unavailable OAuth server ${serverName}: ${formatErrorMessage(error)}`); const resolvedBearerConfig = await resolveMcpBearerBundleConfig({ config: mergedConfig, cfg: params.config, agentDir: params.agentDir, env: params.env, omitUnavailableOAuthServers: true, onServerUnavailable: warnUnavailableOAuthServer }); const preparedDataDirs = prepareOwnedBundleMcpDataDirs({ config: applyMcpServerOverrides(resolvedBearerConfig.config, params.toolOverrides?.mcpServers), prepareDataDirsByServer }); for (const diagnostic of preparedDataDirs.diagnostics) params.warn?.(`bundle MCP skipped for ${diagnostic.pluginId}: ${diagnostic.message}`); let effectiveConfig = preparedDataDirs.config; let effectiveEnv = resolvedBearerConfig.env; let effectiveDenials = params.toolOverrides?.mcpToolsDeny; const policyConfig = { mcpServers: Object.fromEntries(Object.entries(effectiveConfig.mcpServers).filter(([serverName]) => !additionalServerNames.has(serverName))) }; if (params.nativeMcpPolicy && Object.keys(policyConfig.mcpServers).length > 0) { const nativePolicyConfig = selectBundleMcpServers(mergedConfig, policyConfig); const runtimeConfig = { ...params.config, mcp: { ...params.config?.mcp, servers: nativePolicyConfig.mcpServers } }; const acquisition = await acquireSessionMcpRuntime({ sessionId: params.nativeMcpPolicy.sessionId, sessionKey: params.nativeMcpPolicy.sessionKey, workspaceDir: params.workspaceDir, agentDir: params.agentDir, cfg: runtimeConfig, toolOverrides: params.toolOverrides }); let policy; try { policy = await prepareNativeMcpPolicy({ runtime: acquisition.runtime, config: params.config, workspaceDir: params.workspaceDir, capabilityProfile: params.nativeMcpPolicy.capabilityProfile, runtimeToolsAllow: params.nativeMcpPolicy.runtimeToolsAllow, warn: params.warn ?? (() => {}) }); } finally { await releaseSessionMcpRuntime(acquisition); } effectiveConfig = { mcpServers: { ...applyPreparedNativeMcpPolicy(policyConfig, policy).mcpServers, ...Object.fromEntries(Object.entries(effectiveConfig.mcpServers).filter(([serverName]) => additionalServerNames.has(serverName))) } }; const preservedAdditionalDenials = Object.fromEntries(Object.entries(params.toolOverrides?.mcpToolsDeny ?? {}).filter(([serverName]) => additionalServerNames.has(serverName))); const combinedDenials = { ...preparedNativeMcpDenials(policy), ...preservedAdditionalDenials }; effectiveDenials = Object.keys(combinedDenials).length > 0 ? combinedDenials : void 0; const refreshedBearerConfig = await resolveMcpBearerBundleConfig({ config: selectBundleMcpServers(mergedConfig, effectiveConfig), cfg: params.config, agentDir: params.agentDir, env: params.env, omitUnavailableOAuthServers: true, onServerUnavailable: warnUnavailableOAuthServer }); effectiveConfig = selectBundleMcpServers(effectiveConfig, refreshedBearerConfig.config); effectiveEnv = refreshedBearerConfig.env; } return await prepareModeSpecificBundleMcpConfig({ mode, backend: params.backend, mergedConfig: effectiveConfig, env: effectiveEnv, mcpToolsDeny: effectiveDenials, webSearchEnabled: params.toolOverrides?.webSearch }); } /** Prepares a per-attempt capture token without changing resume compatibility hashes. */ async function prepareCliBundleMcpCaptureAttempt(params) { if (!params.captureKey) return { env: params.env }; if ((params.mode ?? "claude-config-file") === "gemini-system-settings") return await writeGeminiMcpCaptureSettings({ inheritedEnv: params.env, captureKey: params.captureKey }); if ((params.mode ?? "claude-config-file") === "claude-config-file") { const mcpConfigPath = findClaudeMcpConfigPaths(params.backend?.args)[0] ?? findClaudeMcpConfigPaths(params.backend?.resumeArgs)[0]; if (mcpConfigPath) await writeClaudeMcpCaptureConfig({ mcpConfigPath, captureKey: params.captureKey }); } return { env: { ...params.env, OPENCLAW_MCP_CLI_CAPTURE_KEY: params.captureKey } }; } //#endregion //#region src/agents/cli-runner/prompt-context.ts /** Logical input for raw transports, policy hooks, and bounded diagnostics. */ function composeCliPromptContext(prompt, context) { const prepended = context?.prependContext ? `${context.prependContext}\n\n${prompt}` : prompt; return context?.appendContext ? `${prepended}\n\n${context.appendContext}` : prepended; } //#endregion export { prepareCliBundleMcpCaptureAttempt as n, prepareCliBundleMcpConfig as r, composeCliPromptContext as t };