openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
602 lines (601 loc) • 28.9 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { c as isRecord, s as filterStringRecord } from "./record-coerce-TIsquoVR.mjs";
import { n as filterStringEntries } from "./string-normalization-CECOqf64.mjs";
import { t as formatErrorMessage } from "./errors-O33D01XW.mjs";
import { n as sha256Hex } from "./node-crypto-BG4u3UnX.mjs";
import "./crypto-digest-y6oZN-_F.mjs";
import { d as writeJson, l as tryReadJson } from "./json-files-C5mNTScg.mjs";
import { t as applyMergePatch } from "./merge-patch-C4XTCka4.mjs";
import { n as acquireSessionMcpRuntime } from "./agent-bundle-mcp-manager-api-IlYicdWC.mjs";
import { f as releaseSessionMcpRuntime } from "./mcp-app-model-context-XPQPOrco.mjs";
import { t as extractMcpServerMap } from "./bundle-mcp-DKUhPwsU.mjs";
import { i as toCliBundleMcpServerConfig, r as prepareOwnedBundleMcpDataDirs, t as loadMergedBundleMcpConfig } from "./bundle-mcp-config-hq2bcXOw.mjs";
import { h as labelRuntimeContextText } from "./types-CQc6JNG4.mjs";
import { u as projectRuntimeContextFragments } from "./internal-runtime-context-CAWYwg4i.mjs";
import { r as resolveMcpBearerBundleConfig } from "./mcp-auth-profile-CEl_bToU.mjs";
import { r as buildCliSessionDriftNote } from "./cli-session-CYn0MI9z.mjs";
import { a as resolveQuestionTimeoutMs } from "./ask-user-tool-normalization-F7IQTaTx.mjs";
import { a as prepareActiveNodeContext, t as buildActiveNodeContextText } from "./active-node-context-DpQMef3G.mjs";
import { c as buildMediaTaskRuntimeContext } from "./media-generation-task-status-2HywbS4t.mjs";
import { i as buildRuntimeContextCustomMessage } from "./runtime-context-prompt-CKrto4c6.mjs";
import { t as buildProactiveSubagentOrchestrationSection } from "./ultra-orchestration-KhJuvQWv.mjs";
import { n as composeSystemPromptWithHookContext } from "./attempt-thread-helpers-dYlH76mO.mjs";
import { t as resolveSessionGitCoauthorPrompt } from "./git-coauthor-prompt-DDWeemrO.mjs";
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-BTwSHtye.mjs";
import { n as normalizeBundleMcpServerConfig, t as decodeHeaderEnvPlaceholder } from "./bundle-mcp-adapter-DfH5O5-o.mjs";
import { a as applyPreparedNativeMcpPolicy, o as prepareNativeMcpPolicy, r as injectCodexMcpConfigArgs, s as preparedNativeMcpDenials } from "./bundle-mcp-codex-YJz18H3A.mjs";
import { n as cliBackendLog } from "./log-C-xabBWt.mjs";
import path from "node:path";
import fs from "node:fs/promises";
import os from "node:os";
//#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
const CLAUDE_MANAGED_MCP_TIMEOUT_MS = resolveQuestionTimeoutMs(3600);
function applyClaudeManagedMcpTimeout(config) {
return {
...config,
mcpServers: {
...config.mcpServers,
openclaw: {
...config.mcpServers.openclaw,
timeout: CLAUDE_MANAGED_MCP_TIMEOUT_MS
}
}
};
}
function extractClaudeVariadicArgs(args, names) {
const next = [];
const values = [];
for (let i = 0; i < args.length; i += 1) {
const arg = args[i] ?? "";
const equalsIndex = arg.indexOf("=");
const name = equalsIndex < 0 ? arg : arg.slice(0, equalsIndex);
if (!names.includes(name)) next.push(arg);
else if (equalsIndex >= 0) values.push(arg.slice(equalsIndex + 1));
else while (typeof args[i + 1] === "string" && !args[i + 1]?.startsWith("-")) {
i += 1;
values.push(args[i] ?? "");
}
}
return {
args: next,
values
};
}
function findClaudeMcpConfigPaths(args) {
return extractClaudeVariadicArgs(args ?? [], ["--mcp-config"]).values.map(normalizeOptionalString).filter((value) => value !== void 0);
}
function mergeClaudeDisallowedTools(args, deniedTools) {
if (deniedTools.length === 0) return args;
const { args: next, values } = extractClaudeVariadicArgs(args, ["--disallowedTools", "--disallowed-tools"]);
next.push("--disallowedTools", [.../* @__PURE__ */ new Set([...values, ...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 = extractClaudeVariadicArgs(args ?? [], ["--mcp-config"]).args.filter((arg) => arg !== "--strict-mcp-config");
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
const GEMINI_MCP_SERVER_FIELDS = {
strings: ["type"],
booleans: ["trust"]
};
async function readJsonObject(filePath) {
const raw = await tryReadJson(filePath);
return isRecord(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 = filterStringEntries(isRecord(base.tools) ? base.tools.exclude : void 0);
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 = filterStringRecord(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 = filterStringEntries(toolFilter.include);
if (included.length > 0) {
const existing = filterStringEntries(server.includeTools);
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 = filterStringEntries(toolFilter.exclude);
if (deniedTools?.length || filteredDenied.length > 0) {
const existing = filterStringEntries(server.excludeTools);
next.excludeTools = [.../* @__PURE__ */ new Set([
...existing,
...filteredDenied,
...deniedTools ?? []
])].toSorted();
}
return next;
}
async function writeGeminiSystemSettings(mergedConfig, inheritedEnv, mcpToolsDeny, webSearchEnabled) {
const base = await readGeminiBaseSettings(inheritedEnv);
const 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]] : [];
}));
return await writeGeminiSettings(applyMergePatch(webSearchEnabled === false ? mergeGeminiWebSearchDisabled(base) : base, {
mcp: { allowed: Object.keys(mcpServers) },
mcpServers
}), 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
/** A managed provider pin replaces native search without disabling OpenClaw's MCP tool. */
function resolveCliNativeWebSearchEnabled(params, backend) {
if (params.toolOverrides?.webSearch === false) return false;
if (!backend.bundleMcp && !backend.bundleMcpMode) return true;
const search = params.config?.tools?.web?.search;
return search?.enabled !== false && !search?.provider?.trim();
}
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, canonicalServer];
return [name, {
...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 = filterStringEntries(toolFilter.exclude);
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 cliConfig = { mcpServers: Object.fromEntries(Object.entries(params.mergedConfig.mcpServers).map(([name, server]) => [name, toCliBundleMcpServerConfig(server)])) };
const hashConfig = (config) => sha256Hex(`${JSON.stringify(mcpToolsDeny || webSearchDisabled ? {
config,
mcpToolsDeny,
webSearchDisabled
} : config, null, 2)}\n`);
const fingerprints = {
mcpConfigHash: hashConfig(cliConfig),
mcpResumeHash: hashConfig(canonicalizeBundleMcpConfigForResume(cliConfig))
};
if (params.mode === "codex-config-overrides") {
const codexConfig = applyCodexMcpToolDenials(cliConfig, mcpToolsDeny);
return {
backend: injectBundleMcpBackendArgs(params.backend, (args) => webSearchDisabled ? [
...injectCodexMcpConfigArgs(args, codexConfig),
"-c",
"web_search=\"disabled\""
] : injectCodexMcpConfigArgs(args, codexConfig)),
...fingerprints,
env: params.env
};
}
if (params.mode === "gemini-system-settings") {
const settings = await writeGeminiSystemSettings(cliConfig, params.env, mcpToolsDeny, params.webSearchEnabled);
return {
backend: params.backend,
...fingerprints,
env: settings.env,
cleanup: settings.cleanup
};
}
const runtimeConfig = resolveOpenClawMcpEnvTemplates(cliConfig, 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)),
...fingerprints,
env: params.env,
cleanup: temporary.cleanup
};
}
async function prepareCliWebSearchDisabled(params) {
const fingerprint = sha256Hex("web-search-disabled-v1");
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) {
const nativeWebSearchEnabled = resolveCliNativeWebSearchEnabled(params, {
bundleMcp: params.enabled,
bundleMcpMode: params.mode
});
if (!params.enabled) return !nativeWebSearchEnabled ? 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: nativeWebSearchEnabled
});
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, { mcpServers: extractMcpServerMap(await tryReadJson(resolvedExistingPath)) });
}
const bundleConfig = loadMergedBundleMcpConfig({
workspaceDir: params.workspaceDir,
cfg: params.config,
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,
toolDenylist: params.nativeMcpPolicy.capabilityProfile.policy.explicitToolDenylist
});
let retainedServerNames;
try {
const policy = await prepareNativeMcpPolicy({
runtime: acquisition.runtime,
config: params.config,
workspaceDir: params.workspaceDir,
capabilityProfile: params.nativeMcpPolicy.capabilityProfile,
runtimeToolsAllow: params.nativeMcpPolicy.runtimeToolsAllow,
warn: params.warn ?? (() => {})
});
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;
retainedServerNames = new Set(Object.keys(effectiveConfig.mcpServers));
} finally {
await releaseSessionMcpRuntime(acquisition, retainedServerNames);
}
}
return await prepareModeSpecificBundleMcpConfig({
mode,
backend: params.backend,
mergedConfig: effectiveConfig,
env: effectiveEnv,
mcpToolsDeny: effectiveDenials,
webSearchEnabled: nativeWebSearchEnabled
});
}
/** 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
/** Current-turn facts stay outside native prompts that are retained across CLI turns. */
async function buildCliTurnAppendContext(params) {
const { resolveSystemPromptUsage } = await import("./helpers-BzxxLrBQ.mjs");
const mediaTaskContext = await buildMediaTaskRuntimeContext({
capabilityToolNames: params.capabilityToolNames,
sessionKey: params.sessionKey,
agentId: params.agentId,
includeEmptySnapshots: true
});
const mediaTaskMessage = buildRuntimeContextCustomMessage(mediaTaskContext);
await prepareActiveNodeContext(params.requesterProfileId);
return [
...params.context,
params.runtimeContextFragments?.length ? labelRuntimeContextText(projectRuntimeContextFragments(params.runtimeContextFragments)) : void 0,
buildProactiveSubagentOrchestrationSection({
enabled: params.thinkLevel === "ultra",
hasSessionsSpawn: params.capabilityToolNames.has("sessions_spawn")
}).join("\n"),
mediaTaskMessage ? labelRuntimeContextText(mediaTaskMessage.content) : void 0,
resolveSystemPromptUsage(params) ? void 0 : buildActiveNodeContextText(params.requesterProfileId)
].filter((value) => Boolean(value?.trim())).join("\n\n");
}
async function prepareCliTurnPromptContext(params) {
let systemPrompt = params.systemPrompt;
let prependContext = "";
let appendContext = params.deliveryGuidance ?? "";
try {
const preparedPrependContext = params.prependContext.filter((value) => Boolean(value?.trim())).join("\n\n");
const preparedAppendContext = await buildCliTurnAppendContext({
...params,
context: [...params.context, params.deliveryGuidance]
});
prependContext = preparedPrependContext;
appendContext = preparedAppendContext;
const hookSystemPrompt = params.hookResult?.systemPrompt?.trim();
if (hookSystemPrompt) systemPrompt = hookSystemPrompt;
systemPrompt = composeSystemPromptWithHookContext({
baseSystemPrompt: systemPrompt,
prependSystemContext: params.hookResult?.prependSystemContext,
appendSystemContext: params.hookResult?.appendSystemContext
}) ?? systemPrompt;
} catch (error) {
cliBackendLog.warn(`cli prompt-build hook preparation failed: ${String(error)}`);
}
const logicalPrompt = composeCliPromptContext(params.prompt, {
prependContext,
appendContext
});
if ((prependContext || appendContext) && params.privateContext) return {
prompt: params.prompt,
systemPrompt,
promptContext: {
...prependContext ? { prependContext } : {},
...appendContext ? { appendContext } : {}
},
promptForHooks: logicalPrompt
};
return {
prompt: logicalPrompt,
systemPrompt
};
}
/** 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;
}
async function prepareCliSystemPrompt(params) {
const { buildCliAgentSystemPrompt } = await import("./helpers-BzxxLrBQ.mjs");
let preparedModelRuntime;
if (params.config) {
const { getPreparedModelRuntimeBorrowedSnapshot, getPreparedModelRuntimePluginGeneration } = await import("./prepared-model-runtime-generation-scope-BE7GAnv9.mjs");
const generation = getPreparedModelRuntimePluginGeneration();
const borrowed = generation ? getPreparedModelRuntimeBorrowedSnapshot(generation) : void 0;
if (borrowed?.config === params.config && borrowed.agentId === params.agentId && borrowed.workspaceDir === params.workspaceDir) preparedModelRuntime = borrowed;
else {
const { getPreparedModelCatalogOwnerSnapshot } = await import("./prepared-model-catalog-Y2c6s1Zg.mjs");
preparedModelRuntime = getPreparedModelCatalogOwnerSnapshot({
config: params.config,
agentId: params.agentId,
workspaceDir: params.workspaceDir
});
}
}
await prepareActiveNodeContext(params.requesterProfileId);
const preparedGitCoauthorPrompt = await resolveSessionGitCoauthorPrompt({
config: params.config,
agentId: params.agentId,
sessionKey: params.sessionKey,
...params.sessionId ? { sessionId: params.sessionId } : {}
});
return buildCliAgentSystemPrompt({
...params,
preparedModelRuntime,
preparedGitCoauthorPrompt
});
}
function prependCliSessionDriftUserContext(context, reusableCliSession) {
if (reusableCliSession.mode !== "reuse-with-drift") return context;
const note = buildCliSessionDriftNote(reusableCliSession.drift.reasons);
if (!context) return { text: note };
return {
...context,
text: [note, context.text].join("\n\n"),
...context.resumableText ? { resumableText: [note, context.resumableText].join("\n\n") } : {}
};
}
//#endregion
export { prepareCliBundleMcpCaptureAttempt as a, CLAUDE_MANAGED_MCP_TIMEOUT_MS as c, prependCliSessionDriftUserContext as i, applyClaudeManagedMcpTimeout as l, prepareCliSystemPrompt as n, prepareCliBundleMcpConfig as o, prepareCliTurnPromptContext as r, resolveCliNativeWebSearchEnabled as s, composeCliPromptContext as t };