UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

445 lines (444 loc) • 19.1 kB
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { m as normalizeUniqueSingleOrTrimmedStringList, y as uniqueStrings } from "./string-normalization-CECOqf64.mjs"; import { n as formatConcreteConfigPath } from "./dot-path-CVD5Jolm.mjs"; import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs"; import { r as resolveAgentConfig } from "./agent-scope-config-CDYpzA0r.mjs"; import { E as parseRawSessionConversationRef, M as parseAgentSessionKey, O as parseThreadSessionSuffix } from "./session-key-C09ow4h0.mjs"; import { n as normalizeAccountId } from "./account-id-DbRsdayc.mjs"; import { r as listAgentEntriesWithSource, t as hasAgentRosterProperty } from "./agent-roster-BXD1uMJD.mjs"; import { v as resolveSessionAgentIds } from "./agent-scope-CG17uXFn.mjs"; import { r as resolveProviderToolPolicyEntry } from "./provider-tool-policy-CQtosRma.mjs"; import { s as resolveCoreToolProfilePolicy } from "./tool-catalog-DyHnjiiS.mjs"; import { t as AUTOMATIONS_TOOL_NAME } from "./automations-tool-name-DBMZPbPL.mjs"; import { r as createToolPolicyMatcher } from "./tool-policy-match-Dn8gpf-E.mjs"; import { n as pickSandboxToolPolicy } from "./sandbox-tool-policy-BO1puJQ0.mjs"; import { l as mergeAlsoAllowPolicy } from "./tool-policy-BKvvmcQR.mjs"; import { n as normalizeMessageChannel } from "./message-channel-core-CqBAHB04.mjs"; import "./message-channel-j-k-Nup3.mjs"; import { i as logWarn } from "./logger-DskmrSVa.mjs"; import { n as getLoadedChannelPlugin } from "./registry-bbtfCBEf.mjs"; import "./plugins-9R-VsYQh.mjs"; import { t as resolveSessionConversation } from "./session-conversation-BZFiAP5-.mjs"; import { i as resolveClawToolPolicyConsent, n as markFrozenClawToolAllowPolicy } from "./tool-policy-runtime--SOb7O-O.mjs"; import { r as resolveChannelGroupToolsPolicy } from "./group-policy-BJ7vM7Bm.mjs"; import { r as resolveSandboxToolPolicyForAgent } from "./tool-policy-eD9SQrK2.mjs"; import { a as resolveStoredSubagentInheritedToolDenylist, i as resolveStoredSubagentInheritedToolAllowlist, r as resolveStoredSubagentCapabilities, s as resolveSubagentCapabilityStore } from "./subagent-capabilities-CKvyQ4uQ.mjs"; //#region src/agents/agent-tools.policy.ts /** * Resolves sandbox tool policies for agents, providers, sub-agents, and group * sessions. Keeps runtime tool filtering tied to canonical config, session * provenance, and inherited sub-agent capabilities. */ /** * Tools always denied for sub-agents regardless of depth. * These are system-level or interactive tools that sub-agents should never use. */ const SUBAGENT_TOOL_DENY_ALWAYS = [ "gateway", "agents_list", "openclaw", "session_status", "progress_card", AUTOMATIONS_TOOL_NAME, "message", "sessions_send", "conversations_list", "conversations_send", "conversations_turn" ]; /** Tools that only make sense for orchestrator sub-agents that can spawn children. */ const SUBAGENT_TOOL_DENY_LEAF = [ "subagents", "sessions_list", "sessions_history", "sessions_search", "sessions_spawn" ]; function resolveSubagentDenyListForRole(role) { if (role === "leaf") return [...SUBAGENT_TOOL_DENY_ALWAYS, ...SUBAGENT_TOOL_DENY_LEAF]; return [...SUBAGENT_TOOL_DENY_ALWAYS]; } /** Resolve sub-agent tool policy from stored session capabilities. */ function resolveSubagentToolPolicyForSession(cfg, sessionKey, opts) { const configured = cfg?.tools?.subagents?.tools; const store = resolveSubagentCapabilityStore(sessionKey, { cfg, store: opts?.store }); const capabilities = resolveStoredSubagentCapabilities(sessionKey, { cfg, store }); const allow = Array.isArray(configured?.allow) ? configured.allow : void 0; const alsoAllow = Array.isArray(configured?.alsoAllow) ? configured.alsoAllow : void 0; const deny = [...resolveSubagentDenyListForRole(capabilities.role), ...Array.isArray(configured?.deny) ? configured.deny : []]; return { allow: allow && alsoAllow ? uniqueStrings([...allow, ...alsoAllow]) : allow, deny }; } /** Resolve the tool policy inherited from a parent sub-agent session. */ function resolveInheritedToolPolicyForSession(cfg, sessionKey, opts) { const inheritedToolAllow = resolveStoredSubagentInheritedToolAllowlist(sessionKey, { cfg, store: opts?.store }); const inheritedToolDeny = resolveStoredSubagentInheritedToolDenylist(sessionKey, { cfg, store: opts?.store }); if (inheritedToolAllow.length === 0 && inheritedToolDeny.length === 0) return; return { ...inheritedToolAllow.length > 0 ? { allow: inheritedToolAllow } : {}, ...inheritedToolDeny.length > 0 ? { deny: inheritedToolDeny } : {} }; } /** Resolve the shared profile, scope, extra, and sandbox policy layers. */ function resolveConfiguredToolPolicies(params) { const profile = params.agentTools?.profile ?? params.cfg.tools?.profile; const profileAlsoAllow = resolveExplicitProfileAlsoAllow(params.agentTools) ?? resolveExplicitProfileAlsoAllow(params.cfg.tools); const policies = [ mergeAlsoAllowPolicy(resolveCoreToolProfilePolicy(profile), profileAlsoAllow), pickSandboxToolPolicy(params.cfg.tools ?? void 0), pickSandboxToolPolicy(params.agentTools), ...params.extraPolicies ?? [] ].filter((policy) => Boolean(policy)); if (params.sandboxMode === "all") policies.push(resolveSandboxToolPolicyForAgent(params.cfg, params.agentId ?? void 0)); return policies; } function buildScopedGroupIdCandidates(groupId) { const raw = groupId?.trim(); if (!raw) return []; const topicSenderMatch = raw.match(/^(.+):topic:([^:]+):sender:([^:]+)$/i); if (topicSenderMatch) { const [, chatId, topicId] = topicSenderMatch; return normalizeUniqueSingleOrTrimmedStringList([ raw, `${chatId}:topic:${topicId}`, chatId ]); } const topicMatch = raw.match(/^(.+):topic:([^:]+)$/i); if (topicMatch) { const [, chatId, topicId] = topicMatch; return normalizeUniqueSingleOrTrimmedStringList([`${chatId}:topic:${topicId}`, chatId]); } const senderMatch = raw.match(/^(.+):sender:([^:]+)$/i); if (senderMatch) { const [, chatId] = senderMatch; return normalizeUniqueSingleOrTrimmedStringList([raw, chatId]); } return [raw]; } function resolveGroupContextFromSessionKey(sessionKey) { const raw = (sessionKey ?? "").trim(); if (!raw) return {}; const { baseSessionKey, threadId } = parseThreadSessionSuffix(raw); const conversationKey = threadId ? baseSessionKey : raw; const conversation = parseRawSessionConversationRef(conversationKey); if (conversation) { const resolvedConversation = resolveSessionConversation({ channel: conversation.channel, kind: conversation.kind, rawId: conversation.rawId }); return { channel: conversation.channel, groupIds: normalizeUniqueSingleOrTrimmedStringList([ ...buildScopedGroupIdCandidates(conversation.rawId), resolvedConversation?.id, resolvedConversation?.baseConversationId, ...resolvedConversation?.parentConversationCandidates ?? [] ]) }; } const parts = (conversationKey ?? raw).split(":").filter(Boolean); let body = parts[0] === "agent" ? parts.slice(2) : parts; if (body[0] === "subagent") body = body.slice(1); if (body.length < 3) return {}; const [channel, kind, ...rest] = body; if (kind !== "group" && kind !== "channel") return {}; const groupId = rest.join(":").trim(); if (!groupId) return {}; return { channel: normalizeLowercaseStringOrEmpty(channel), groupIds: buildScopedGroupIdCandidates(groupId) }; } function resolveTrustedGroupIdFromContexts(params) { const callerGroupId = (params.groupId ?? "").trim(); if (!callerGroupId) return { groupId: params.groupId, dropped: false }; if (normalizeUniqueSingleOrTrimmedStringList([...params.sessionContext.groupIds ?? [], ...params.spawnedContext.groupIds ?? []]).includes(callerGroupId)) return { groupId: params.groupId, dropped: false }; return { groupId: null, dropped: true }; } /** Validate caller-supplied group ids against server-derived session context. */ function resolveTrustedGroupId(params) { return resolveTrustedGroupIdFromContexts({ groupId: params.groupId, sessionContext: resolveGroupContextFromSessionKey(params.sessionKey), spawnedContext: resolveGroupContextFromSessionKey(params.spawnedBy) }); } function resolveExplicitProfileAlsoAllow(tools) { return Array.isArray(tools?.alsoAllow) ? tools.alsoAllow : void 0; } function profileAllowsGatewayConfigReads(profile, alsoAllow) { const policy = resolveCoreToolProfilePolicy(profile); if (!policy?.allow || policy.allow.includes("*")) return true; const allow = normalizeUniqueSingleOrTrimmedStringList(alsoAllow); return allow.length > 0 && createToolPolicyMatcher({ allow })("gateway"); } /** Detect removed implicit grants for migration warnings only (#47487). */ function detectImplicitProfileGrants(params) { return [{ section: "exec", grants: ["exec", "process"] }, { section: "fs", grants: [ "read", "write", "edit" ] }].filter(({ section }) => params.agentTools?.[section] != null || params.includeGlobalSections && params.globalTools?.[section] != null).map(({ section, grants }) => ({ section: `tools.${section}`, grants })); } /** Resolve the layered global, provider, agent, and profile tool policies. */ function resolveEffectiveToolPolicy(params) { const explicitAgentId = typeof params.agentId === "string" && params.agentId.trim() ? normalizeAgentId(params.agentId) : void 0; const agentEntries = params.config ? listAgentEntriesWithSource(params.config) : []; const agentId = params.config && (!hasAgentRosterProperty(params.config) || agentEntries.length > 0) ? resolveSessionAgentIds({ config: params.config, agentId: explicitAgentId, sessionKey: params.sessionKey }).sessionAgentId : explicitAgentId ?? parseAgentSessionKey(params.sessionKey)?.agentId; const agentConfig = params.config && agentId ? resolveAgentConfig(params.config, agentId) : void 0; const implicitDefaultTools = params.config ? (params.config.agents?.defaults)?.tools : void 0; const agentTools = agentConfig?.tools ?? (params.config && !hasAgentRosterProperty(params.config) ? implicitDefaultTools : void 0); const globalTools = params.config?.tools; const profile = agentTools?.profile ?? globalTools?.profile; const profileSource = agentTools?.profile ? "agent" : globalTools?.profile ? "global" : void 0; const providerEntry = resolveProviderToolPolicyEntry({ byProvider: globalTools?.byProvider, modelProvider: params.modelProvider, modelId: params.modelId }); const agentProviderEntry = resolveProviderToolPolicyEntry({ byProvider: agentTools?.byProvider, modelProvider: params.modelProvider, modelId: params.modelId }); const providerPolicy = providerEntry?.policy; const agentProviderPolicy = agentProviderEntry?.policy; const agentSource = agentEntries.find(({ entry }) => normalizeAgentId(entry.id) === agentId)?.source; const agentToolsPath = agentSource ? formatConcreteConfigPath([ "agents", agentSource.kind, agentSource.kind === "entries" ? agentSource.key : agentSource.index, "tools" ]) : params.config && !hasAgentRosterProperty(params.config) && !agentConfig?.tools && implicitDefaultTools ? "agents.defaults.tools" : `agents.entries.${agentId}.tools`; const providerPath = providerEntry ? `tools.byProvider[${JSON.stringify(providerEntry.key)}]` : void 0; const agentProviderPath = agentProviderEntry ? `${agentToolsPath}.byProvider[${JSON.stringify(agentProviderEntry.key)}]` : void 0; const providerProfilePath = agentProviderPolicy?.profile ? agentProviderPath : providerPath; const profileAlsoAllowPath = !agentTools?.allow && (agentTools?.alsoAllow || !globalTools?.alsoAllow?.length) ? `${agentToolsPath}.alsoAllow` : void 0; const sources = { profile: { kind: "profile", path: profileSource === "agent" ? `${agentToolsPath}.profile` : "tools.profile", profile, alsoAllowPath: profileAlsoAllowPath }, providerProfile: { kind: "profile", path: providerProfilePath ? `${providerProfilePath}.profile` : void 0, profile: agentProviderPolicy?.profile ?? providerPolicy?.profile }, global: { kind: "config", path: "tools" }, globalProvider: { kind: "config", path: providerPath }, agent: { kind: "config", path: agentToolsPath }, agentProvider: { kind: "config", path: agentProviderPath } }; const profiles = [ ...globalTools?.profile ? [{ profile: globalTools.profile, source: "tools.profile", active: !agentTools?.profile }] : [], ...agentTools?.profile ? [{ profile: agentTools.profile, source: `${agentToolsPath}.profile`, active: true }] : [], ...providerPolicy?.profile && providerPath ? [{ profile: providerPolicy.profile, source: `${providerPath}.profile`, active: !agentProviderPolicy?.profile }] : [], ...agentProviderPolicy?.profile && agentProviderPath ? [{ profile: agentProviderPolicy.profile, source: `${agentProviderPath}.profile`, active: true }] : [] ]; const explicitProfileAlsoAllow = resolveExplicitProfileAlsoAllow(agentTools) ?? resolveExplicitProfileAlsoAllow(globalTools); const agentPolicy = pickSandboxToolPolicy(agentTools); if (resolveClawToolPolicyConsent({ agentTools, agentId, profile, ownsProfile: profileSource === "agent", hasAgentAllowlist: (agentPolicy?.allow?.length ?? 0) > 0 }).frozen) markFrozenClawToolAllowPolicy(agentPolicy); const effectivePolicy = { agentId, sources, profiles, globalPolicy: pickSandboxToolPolicy(globalTools), globalProviderPolicy: pickSandboxToolPolicy(providerPolicy), agentPolicy, agentProviderPolicy: pickSandboxToolPolicy(agentProviderPolicy), profile, providerProfile: agentProviderPolicy?.profile ?? providerPolicy?.profile, profileAlsoAllow: explicitProfileAlsoAllow ? uniqueStrings(explicitProfileAlsoAllow) : void 0, providerProfileAlsoAllow: Array.isArray(agentProviderPolicy?.alsoAllow) ? agentProviderPolicy?.alsoAllow : Array.isArray(providerPolicy?.alsoAllow) ? providerPolicy?.alsoAllow : void 0 }; const gatewayConfigReadAllowed = profileAllowsGatewayConfigReads(profile, effectivePolicy.profileAlsoAllow) && profileAllowsGatewayConfigReads(effectivePolicy.providerProfile, effectivePolicy.providerProfileAlsoAllow); if (profile) { const implicitGrants = detectImplicitProfileGrants({ globalTools, agentTools, includeGlobalSections: profileSource === "global" }); if (implicitGrants.length > 0) { const profilePolicy = mergeAlsoAllowPolicy(resolveCoreToolProfilePolicy(profile), explicitProfileAlsoAllow); const matchesProfile = createToolPolicyMatcher(profilePolicy); const restrictionMatchers = [ effectivePolicy.globalPolicy, effectivePolicy.globalProviderPolicy, effectivePolicy.agentPolicy, effectivePolicy.agentProviderPolicy, mergeAlsoAllowPolicy(resolveCoreToolProfilePolicy(effectivePolicy.providerProfile), effectivePolicy.providerProfileAlsoAllow) ].map((policy) => createToolPolicyMatcher(policy)); const uncoveredEntries = implicitGrants.map((entry) => ({ section: entry.section, grants: entry.grants.filter((toolName) => !matchesProfile(toolName) && restrictionMatchers.every((matches) => matches(toolName))) })).filter((entry) => entry.grants.length > 0); const uncovered = uncoveredEntries.flatMap((entry) => entry.grants); if (uncovered.length > 0) logWarn(`tools policy: profile "${profile}"${agentId ? ` (agent "${agentId}")` : ""} has configured tool sections (${uncoveredEntries.map((entry) => entry.section).join(" / ")}) that no longer implicitly widen the profile. Add alsoAllow: [${uncovered.map((toolName) => `"${toolName}"`).join(", ")}] explicitly if these tools should be available. See #47487.`); } } return { ...effectivePolicy, gatewayConfigReadAllowed }; } function unavailableScheduledAccount(accountId) { return { kind: "account-unavailable", accountId, message: `Scheduled account "${accountId}" is unavailable. Re-add it to the channel configuration, or recreate this automation from the intended account.` }; } /** Resolve policy without conflating unavailable scheduled authority with intentional denial. */ function resolveGroupToolPolicyOutcome(params) { if (!params.config) return { kind: "resolved" }; const sessionContext = resolveGroupContextFromSessionKey(params.sessionKey); const spawnedContext = resolveGroupContextFromSessionKey(params.spawnedBy); const trustedGroup = resolveTrustedGroupIdFromContexts({ groupId: params.groupId, sessionContext, spawnedContext }); const groupIds = normalizeUniqueSingleOrTrimmedStringList([ ...sessionContext.groupIds ?? [], ...spawnedContext.groupIds ?? [], ...buildScopedGroupIdCandidates(trustedGroup.groupId) ]); const channelRaw = sessionContext.channel ?? spawnedContext.channel ?? params.messageProvider; const channel = normalizeMessageChannel(channelRaw); const accountId = normalizeAccountId(params.accountId); if (!channel) return params.requireConfiguredAccount && accountId !== "default" ? unavailableScheduledAccount(accountId) : { kind: "resolved" }; let plugin; try { plugin = getLoadedChannelPlugin(channel); } catch { plugin = void 0; } if (params.requireConfiguredAccount && accountId !== "default") { let configured; try { configured = plugin?.config.listAccountIds(params.config).some((candidate) => normalizeAccountId(candidate) === accountId) === true; } catch { configured = false; } if (!configured) return unavailableScheduledAccount(accountId); } if (groupIds.length === 0) return { kind: "resolved" }; for (const groupId of groupIds) { const toolsConfig = plugin?.groups?.resolveToolPolicy?.({ cfg: params.config, groupId, groupChannel: trustedGroup.dropped ? null : params.groupChannel, groupSpace: trustedGroup.dropped ? null : params.groupSpace, accountId, senderPolicyMode: params.senderPolicyMode, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); const policy = pickSandboxToolPolicy(toolsConfig); if (policy) return { kind: "resolved", policy }; } const configTools = resolveChannelGroupToolsPolicy({ cfg: params.config, channel, messageProvider: channel, groupId: groupIds[0], groupIdCandidates: groupIds.slice(1), accountId, senderPolicyMode: params.senderPolicyMode, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); return { kind: "resolved", policy: pickSandboxToolPolicy(configTools) }; } /** Tool-building callers must stop before exposing a surface with unavailable authority. */ function resolveGroupToolPolicy(params) { const outcome = resolveGroupToolPolicyOutcome(params); if (outcome.kind === "account-unavailable") throw new Error(outcome.message); return outcome.policy; } //#endregion export { resolveSubagentToolPolicyForSession as a, resolveInheritedToolPolicyForSession as i, resolveEffectiveToolPolicy as n, resolveTrustedGroupId as o, resolveGroupToolPolicy as r, resolveConfiguredToolPolicies as t };