openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
242 lines (241 loc) • 11.4 kB
JavaScript
import { c as normalizeOptionalLowercaseString } from "./string-coerce-CIXf7egm.js";
import { d as normalizeStringEntries, f as normalizeStringEntriesLower } from "./string-normalization-DsCfAx8q.js";
import "./session-key-BnWWjqNc.js";
import { n as normalizeAccountId } from "./account-id-CETVCrTz.js";
import { t as normalizeAnyChannelId } from "./registry-normalize-CY8V_nbx.js";
import "./registry-COqNvmCg.js";
import { t as resolveAccountEntry } from "./account-lookup-CvFhSs3G.js";
import "./message-channel-constants-2zSoJXQC.js";
import { t as formatSkillsForPromptBounded } from "./skill-prompt-limits-AF9diYak.js";
import { g as resolveChannelPromptCapabilities } from "./openclaw-tools-CNOZOjlX.js";
import { r as loadSkillLibrarySelection } from "./selection-NYtFFG08.js";
import { a as loadWorkspaceSkills, o as normalizeWorkspaceSkillRoots, r as loadMergedWorkspaceSkills } from "./workspace-skill-loader-_a5Sci8B.js";
import { t as resolveSkillRuntimeConfig } from "./runtime-config-C9Ktnk7G.js";
import { c as resolveThreadBindingSpawnPolicy, u as supportsAutomaticThreadBindingSpawn } from "./thread-bindings-policy-BzrpAQe3.js";
import path from "node:path";
//#region src/agents/embedded-agent-runner/sandbox-skills.ts
/**
* Sandbox skill runtime input selection.
*
* Sandboxed runs must build prompt-facing skill entries from readable in-sandbox
* copies instead of reusing host-path snapshots.
*/
const MATERIALIZED_SKILLS_WORKSPACE_CONTAINER_PARTS = [".openclaw", "sandbox-skills"];
function containerJoin(root, ...parts) {
const normalizedRoot = root.replace(/\\/g, "/").replace(/\/+$/, "") || "/";
const suffix = parts.map((part) => part.replace(/^\/+|\/+$/g, "")).filter(Boolean).join("/");
return suffix ? `${normalizedRoot}/${suffix}` : normalizedRoot;
}
function pathEscapesRoot(relativePath) {
return relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath);
}
function mapPathFromWorkspaceToContainer(params) {
if (!params.filePath || !path.isAbsolute(params.filePath)) return params.filePath;
const relativePath = path.relative(path.resolve(params.sourceWorkspaceDir), path.resolve(params.filePath));
if (pathEscapesRoot(relativePath)) return params.filePath;
if (!relativePath) return params.targetWorkspaceDir.replace(/\\/g, "/");
return containerJoin(params.targetWorkspaceDir, ...relativePath.split(path.sep).filter(Boolean));
}
function mapSandboxSkillEntriesForPrompt(params) {
if (!params.entries || params.skillsWorkspaceDir === params.skillsPromptWorkspaceDir) return params.entries;
return params.entries.map((entry) => {
const filePath = mapPathFromWorkspaceToContainer({
filePath: entry.skill.filePath,
sourceWorkspaceDir: params.skillsWorkspaceDir,
targetWorkspaceDir: params.skillsPromptWorkspaceDir
}) ?? entry.skill.filePath;
const baseDir = mapPathFromWorkspaceToContainer({
filePath: entry.skill.baseDir,
sourceWorkspaceDir: params.skillsWorkspaceDir,
targetWorkspaceDir: params.skillsPromptWorkspaceDir
}) ?? entry.skill.baseDir;
const sourceInfoPath = mapPathFromWorkspaceToContainer({
filePath: entry.skill.sourceInfo.path,
sourceWorkspaceDir: params.skillsWorkspaceDir,
targetWorkspaceDir: params.skillsPromptWorkspaceDir
}) ?? entry.skill.sourceInfo.path;
const sourceInfoBaseDir = mapPathFromWorkspaceToContainer({
filePath: entry.skill.sourceInfo.baseDir,
sourceWorkspaceDir: params.skillsWorkspaceDir,
targetWorkspaceDir: params.skillsPromptWorkspaceDir
});
return {
...entry,
skill: {
...entry.skill,
filePath,
baseDir,
sourceInfo: {
...entry.skill.sourceInfo,
path: sourceInfoPath,
...sourceInfoBaseDir === void 0 ? {} : { baseDir: sourceInfoBaseDir }
}
}
};
});
}
function createSandboxPromptEntryLoader(params) {
return () => mapSandboxSkillEntriesForPrompt({
entries: params.loadEntries(),
skillsWorkspaceDir: params.skillsWorkspaceDir,
skillsPromptWorkspaceDir: params.skillsPromptWorkspaceDir
}) ?? [];
}
function mapSandboxSkillUsagePaths(params) {
if (!params.paths || params.skillsWorkspaceDir === params.skillsPromptWorkspaceDir) return params.paths;
return params.paths.map((entry) => ({
...entry,
readPath: mapPathFromWorkspaceToContainer({
filePath: entry.readPath,
sourceWorkspaceDir: params.skillsWorkspaceDir,
targetWorkspaceDir: params.skillsPromptWorkspaceDir
}) ?? entry.readPath
}));
}
function resolveSandboxSkillRuntimeInputs(params) {
if (params.sandbox?.enabled === true) {
const skillsWorkspaceDir = params.sandbox.skillsWorkspaceDir ?? params.skillsAnchorWorkspace;
const skillsPromptWorkspaceDir = params.sandbox.workspaceAccess === "rw" && params.sandbox.skillsWorkspaceDir && params.sandbox.containerWorkdir ? containerJoin(params.sandbox.containerWorkdir, ...MATERIALIZED_SKILLS_WORKSPACE_CONTAINER_PARTS) : params.sandbox.containerWorkdir ?? skillsWorkspaceDir;
let selectedSnapshot;
if (params.skillsSnapshot?.librarySelections?.length) {
const usage = mapSandboxSkillUsagePaths({
paths: params.sandbox.skillUsagePaths,
skillsWorkspaceDir,
skillsPromptWorkspaceDir
});
const resolvedSkills = params.skillsSnapshot.resolvedSkills?.map((skill) => {
const materialized = usage?.find((item) => item.skillName === skill.name);
if (!materialized) throw new Error(`Selected skill ${skill.name} was not delivered to the sandbox.`);
return {
...skill,
filePath: materialized.readPath,
baseDir: path.posix.dirname(materialized.readPath)
};
});
if (!resolvedSkills) throw new Error("Selected skill snapshot must be hydrated before sandbox delivery.");
selectedSnapshot = {
...params.skillsSnapshot,
resolvedSkills,
prompt: formatSkillsForPromptBounded({
skills: resolvedSkills,
preserveOrder: true
})
};
}
return {
...params.sandbox.skillsEligibility ? { skillsEligibility: params.sandbox.skillsEligibility } : {},
skillsPromptWorkspaceDir,
skillsSnapshot: selectedSnapshot,
skillsWorkspaceDir,
workspaceOnly: true
};
}
return {
skillsPromptWorkspaceDir: params.skillsAnchorWorkspace,
skillsSnapshot: params.skillsSnapshot,
skillsWorkspaceDir: params.skillsAnchorWorkspace,
workspaceOnly: false
};
}
/** Rewrites host-generated explicit skill references to the prepared runtime's exact copies. */
function remapSkillReferencePaths(text, paths) {
return (paths ?? []).reduce((result, item) => result.replaceAll(item.skillFile, item.readPath), text);
}
//#endregion
//#region src/skills/runtime/embedded-run-entries.ts
/** Resolves skill entries embedded into a run payload into runtime-visible entries. */
function resolveEmbeddedRunSkillEntries(params) {
const shouldLoadSkillEntries = !params.skillsSnapshot || !params.skillsSnapshot.resolvedSkills;
const config = resolveSkillRuntimeConfig(params.config);
const skillRoots = params.skillsSnapshot?.skillRoots ?? normalizeWorkspaceSkillRoots({
agentWorkspaceDir: params.workspaceDir,
...params.executionSkillsDir ? { executionSkillsDir: params.executionSkillsDir } : {}
});
let cachedSkillEntries;
const loadSkillEntries = () => {
if (cachedSkillEntries) return cachedSkillEntries;
const options = {
config,
agentId: params.agentId,
...params.eligibility ? { eligibility: params.eligibility } : {},
...params.skillsSnapshot?.skillFilter ? { skillFilter: params.skillsSnapshot.skillFilter } : {},
...params.skillsSnapshot?.skillOverrides ? { skillOverrides: params.skillsSnapshot.skillOverrides } : {},
...params.workspaceOnly === true ? { workspaceOnly: true } : {}
};
cachedSkillEntries = skillRoots.executionSkillsDir ? loadMergedWorkspaceSkills({
...skillRoots,
...options
}) : loadWorkspaceSkills(params.workspaceDir, options);
if (params.skillsSnapshot?.librarySelections?.length && params.workspaceOnly !== true) cachedSkillEntries.push(...loadSkillLibrarySelection(params.skillsSnapshot.librarySelections));
return cachedSkillEntries;
};
return {
shouldLoadSkillEntries,
skillEntries: shouldLoadSkillEntries ? loadSkillEntries() : [],
loadSkillEntries,
preserveEntryOrder: skillRoots.executionSkillsDir !== void 0
};
}
//#endregion
//#region src/config/channel-capabilities.ts
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
function normalizeCapabilities(capabilities) {
if (!isStringArray(capabilities)) return;
const normalized = normalizeStringEntries(capabilities);
return normalized.length > 0 ? normalized : void 0;
}
/** Resolves normalized string capabilities for a channel/account config pair. */
function resolveChannelCapabilities(params) {
const cfg = params.cfg;
const channel = normalizeAnyChannelId(params.channel);
if (!cfg || !channel) return;
const channelConfig = cfg.channels?.[channel];
if (!channelConfig) return;
const normalizedAccountId = normalizeAccountId(params.accountId);
const accounts = channelConfig.accounts;
return normalizeCapabilities((accounts && typeof accounts === "object" ? resolveAccountEntry(accounts, normalizedAccountId) : void 0)?.capabilities) ?? normalizeCapabilities(channelConfig.capabilities);
}
//#endregion
//#region src/agents/runtime-capabilities.ts
/**
* Runtime channel capability collector.
*
* Agent startup uses this to merge configured channel capabilities with prompt
* tools and thread-bound spawn features that depend on channel policy.
*/
const THREAD_BOUND_SUBAGENT_SPAWN_CAPABILITY = "threadbound-subagent-spawn";
const THREAD_BOUND_ACP_SPAWN_CAPABILITY = "threadbound-acp-spawn";
function mergeRuntimeCapabilities(base, additions = []) {
const merged = [...base ?? []];
const seen = new Set(normalizeStringEntriesLower(merged));
for (const capability of additions) {
const normalizedCapability = normalizeOptionalLowercaseString(capability);
if (!normalizedCapability || seen.has(normalizedCapability)) continue;
seen.add(normalizedCapability);
merged.push(capability);
}
return merged.length > 0 ? merged : void 0;
}
/** Collects the effective runtime capabilities for a channel/account pair. */
function collectRuntimeChannelCapabilities(params) {
if (!params.channel) return;
const internalChannelCapabilities = params.channel === "webchat" ? ["markdownDetails"] : [];
const threadSpawnCapabilities = [];
if (params.cfg && supportsAutomaticThreadBindingSpawn(params.channel)) for (const [kind, capability] of [["subagent", THREAD_BOUND_SUBAGENT_SPAWN_CAPABILITY], ["acp", THREAD_BOUND_ACP_SPAWN_CAPABILITY]]) {
const policy = resolveThreadBindingSpawnPolicy({
cfg: params.cfg,
channel: params.channel,
accountId: params.accountId ?? void 0,
kind
});
if (policy.enabled && policy.spawnEnabled) threadSpawnCapabilities.push(capability);
}
const channelPromptCapabilities = params.cfg ? resolveChannelPromptCapabilities(params) : [];
return mergeRuntimeCapabilities(resolveChannelCapabilities(params), [
...channelPromptCapabilities,
...internalChannelCapabilities,
...threadSpawnCapabilities
]);
}
//#endregion
export { mapSandboxSkillUsagePaths as a, mapSandboxSkillEntriesForPrompt as i, resolveEmbeddedRunSkillEntries as n, remapSkillReferencePaths as o, createSandboxPromptEntryLoader as r, resolveSandboxSkillRuntimeInputs as s, collectRuntimeChannelCapabilities as t };