UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

356 lines (355 loc) 15 kB
import { a as normalizeLowercaseStringOrEmpty, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js"; import { _ as uniqueStrings, f as normalizeUniqueSingleOrTrimmedStringList } from "./string-normalization-WNUDCpXX.js"; import { i as normalizeProviderId } from "./provider-id-Dq06Bcx6.js"; import "./agent-scope-MrLta7Pq.js"; import { l as parseRawSessionConversationRef, u as parseThreadSessionSuffix } from "./session-key-utils-Bx3apsJ3.js"; import { f as resolveAgentIdFromSessionKey, u as normalizeAgentId } from "./session-key-B_NoIfpX.js"; import { r as resolveAgentConfig } from "./agent-scope-config-CgCYpZfK.js"; import { a as logWarn } from "./logger-lqqYRtFw.js"; import { n as getLoadedChannelPlugin } from "./registry-MkxNn1Ue.js"; import { t as resolveSessionConversation } from "./session-conversation-Bg8yraqi.js"; import "./plugins-t2ejWcVy.js"; import { i as normalizeMessageChannel } from "./message-channel-normalize-BLLf0ubu.js"; import "./message-channel-BiOeMu0l.js"; import { n as pickSandboxToolPolicy } from "./sandbox-tool-policy-BRuLtPlA.js"; import { g as resolveToolProfilePolicy, h as normalizeToolName, l as mergeAlsoAllowPolicy } from "./tool-policy-CpBMaMTY.js"; import { n as isToolAllowedByPolicyName } from "./tool-policy-match-Dj5a1jle.js"; import { r as resolveChannelGroupToolsPolicy } from "./group-policy-CShy7eWv.js"; import { i as resolveStoredSubagentInheritedToolDenylist, n as resolveStoredSubagentCapabilities, o as resolveSubagentCapabilityStore, r as resolveStoredSubagentInheritedToolAllowlist } from "./subagent-capabilities-Cva3gXJL.js"; //#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", "session_status", "cron", "sessions_send" ]; /** * Additional tools denied for leaf sub-agents (depth >= maxSpawnDepth). * These are tools that only make sense for orchestrator sub-agents that can spawn children. */ const SUBAGENT_TOOL_DENY_LEAF = [ "subagents", "sessions_list", "sessions_history", "sessions_spawn" ]; function resolveSubagentDenyListForRole(role) { if (role === "leaf") return [...SUBAGENT_TOOL_DENY_ALWAYS, ...SUBAGENT_TOOL_DENY_LEAF]; return [...SUBAGENT_TOOL_DENY_ALWAYS]; } function mergeConfiguredSubagentAllow(allow, alsoAllow) { return allow && alsoAllow ? uniqueStrings([...allow, ...alsoAllow]) : allow; } /** Resolve sub-agent tool policy from stored session capabilities. */ function resolveSubagentToolPolicyForSession(cfg, sessionKey, opts) { const configured = cfg?.tools?.subagents?.tools; const capabilities = resolveStoredSubagentCapabilities(sessionKey, { cfg, store: resolveSubagentCapabilityStore(sessionKey, { cfg, store: opts?.store }) }); const allow = Array.isArray(configured?.allow) ? configured.allow : void 0; const alsoAllow = Array.isArray(configured?.alsoAllow) ? configured.alsoAllow : void 0; const explicitAllow = new Set([...allow ?? [], ...alsoAllow ?? []].map((toolName) => normalizeToolName(toolName))); const deny = [...resolveSubagentDenyListForRole(capabilities.role).filter((toolName) => !explicitAllow.has(normalizeToolName(toolName))), ...Array.isArray(configured?.deny) ? configured.deny : []]; return { allow: mergeConfiguredSubagentAllow(allow, alsoAllow), 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 } : {} }; } /** Filter runtime tools by sandbox allow/deny policy. */ function filterToolsByPolicy(tools, policy) { if (!policy) return tools; return tools.filter((tool) => isToolAllowedByPolicyName(tool.name, policy)); } function normalizeProviderKey(value) { const normalized = normalizeLowercaseStringOrEmpty(value); const slashIndex = normalized.indexOf("/"); if (slashIndex <= 0) return normalizeProviderId(normalized); const provider = normalizeProviderId(normalized.slice(0, slashIndex)); const modelId = normalized.slice(slashIndex + 1); return modelId ? `${provider}/${modelId}` : provider; } function isCanonicalProviderKey(value) { return normalizeLowercaseStringOrEmpty(value) === normalizeProviderKey(value); } function buildProviderToolPolicyLookup(entries) { const lookup = /* @__PURE__ */ new Map(); for (const [key, value] of entries) { const normalized = normalizeProviderKey(key); if (!normalized) continue; const canonical = isCanonicalProviderKey(key); const existing = lookup.get(normalized); if (!existing || canonical && !existing.canonical) lookup.set(normalized, { canonical, value }); } const resolved = /* @__PURE__ */ new Map(); for (const [key, entry] of lookup) resolved.set(key, entry.value); return resolved; } function collectUniqueStrings(values) { return normalizeUniqueSingleOrTrimmedStringList(values); } function buildScopedGroupIdCandidates(groupId) { const raw = groupId?.trim(); if (!raw) return []; const topicSenderMatch = raw.match(/^(.+):topic:([^:]+):sender:([^:]+)$/i); if (topicSenderMatch) { const [, chatId, topicId] = topicSenderMatch; return collectUniqueStrings([ raw, `${chatId}:topic:${topicId}`, chatId ]); } const topicMatch = raw.match(/^(.+):topic:([^:]+)$/i); if (topicMatch) { const [, chatId, topicId] = topicMatch; return collectUniqueStrings([`${chatId}:topic:${topicId}`, chatId]); } const senderMatch = raw.match(/^(.+):sender:([^:]+)$/i); if (senderMatch) { const [, chatId] = senderMatch; return collectUniqueStrings([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: collectUniqueStrings([ ...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 }; const trustedGroupIds = collectUniqueStrings([...params.sessionContext.groupIds ?? [], ...params.spawnedContext.groupIds ?? []]); if (trustedGroupIds.length === 0) return { groupId: null, dropped: true }; if (trustedGroupIds.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) }); } /** Resolve model/provider-scoped tool policy from canonical provider keys. */ function resolveProviderToolPolicy(params) { const provider = params.modelProvider?.trim(); if (!provider || !params.byProvider) return; const entries = Object.entries(params.byProvider); if (entries.length === 0) return; const lookup = buildProviderToolPolicyLookup(entries); const normalizedProvider = normalizeProviderKey(provider); const rawModelId = normalizeOptionalLowercaseString(params.modelId); const fullModelId = rawModelId ? `${normalizedProvider}/${rawModelId}` : void 0; const candidates = [...fullModelId ? [fullModelId] : [], normalizedProvider]; for (const key of candidates) { const match = lookup.get(key); if (match) return match; } } function resolveExplicitProfileAlsoAllow(tools) { return Array.isArray(tools?.alsoAllow) ? tools.alsoAllow : void 0; } function hasExplicitToolSection(section) { return section !== void 0 && section !== null; } function detectImplicitProfileGrants(params) { const entries = []; if (hasExplicitToolSection(params.agentTools?.exec) || params.includeGlobalSections && hasExplicitToolSection(params.globalTools?.exec)) entries.push({ section: "tools.exec", grants: ["exec", "process"] }); if (hasExplicitToolSection(params.agentTools?.fs) || params.includeGlobalSections && hasExplicitToolSection(params.globalTools?.fs)) entries.push({ section: "tools.fs", grants: [ "read", "write", "edit" ] }); if (entries.length === 0) return; return { entries }; } function formatImplicitToolSections(sections) { return sections.join(" / "); } function formatToolListForWarning(toolNames) { return toolNames.map((toolName) => `"${toolName}"`).join(", "); } /** Resolve the layered global, provider, agent, and profile tool policies. */ function resolveEffectiveToolPolicy(params) { const agentId = (typeof params.agentId === "string" && params.agentId.trim() ? normalizeAgentId(params.agentId) : void 0) ?? (params.sessionKey ? resolveAgentIdFromSessionKey(params.sessionKey) : void 0); const agentTools = (params.config && agentId ? resolveAgentConfig(params.config, agentId) : void 0)?.tools; const globalTools = params.config?.tools; const profile = agentTools?.profile ?? globalTools?.profile; const profileSource = agentTools?.profile ? "agent" : globalTools?.profile ? "global" : void 0; const providerPolicy = resolveProviderToolPolicy({ byProvider: globalTools?.byProvider, modelProvider: params.modelProvider, modelId: params.modelId }); const agentProviderPolicy = resolveProviderToolPolicy({ byProvider: agentTools?.byProvider, modelProvider: params.modelProvider, modelId: params.modelId }); const explicitProfileAlsoAllow = resolveExplicitProfileAlsoAllow(agentTools) ?? resolveExplicitProfileAlsoAllow(globalTools); if (profile) { const implicitGrants = detectImplicitProfileGrants({ globalTools, agentTools, includeGlobalSections: profileSource === "global" }); if (implicitGrants) { const profilePolicy = mergeAlsoAllowPolicy(resolveToolProfilePolicy(profile), explicitProfileAlsoAllow); const uncoveredEntries = implicitGrants.entries.map((entry) => ({ section: entry.section, grants: entry.grants.filter((toolName) => !isToolAllowedByPolicyName(toolName, profilePolicy)) })).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 (${formatImplicitToolSections(uncoveredEntries.map((entry) => entry.section))}) that no longer implicitly widen the profile. Add alsoAllow: [${formatToolListForWarning(uncovered)}] explicitly if these tools should be available. See #47487.`); } } const profileAlsoAllow = explicitProfileAlsoAllow ? uniqueStrings(explicitProfileAlsoAllow) : void 0; return { agentId, globalPolicy: pickSandboxToolPolicy(globalTools), globalProviderPolicy: pickSandboxToolPolicy(providerPolicy), agentPolicy: pickSandboxToolPolicy(agentTools), agentProviderPolicy: pickSandboxToolPolicy(agentProviderPolicy), profile, providerProfile: agentProviderPolicy?.profile ?? providerPolicy?.profile, profileAlsoAllow, providerProfileAlsoAllow: Array.isArray(agentProviderPolicy?.alsoAllow) ? agentProviderPolicy?.alsoAllow : Array.isArray(providerPolicy?.alsoAllow) ? providerPolicy?.alsoAllow : void 0 }; } /** Resolve group-scoped tool policy after validating session provenance. */ function resolveGroupToolPolicy(params) { if (!params.config) return; const sessionContext = resolveGroupContextFromSessionKey(params.sessionKey); const spawnedContext = resolveGroupContextFromSessionKey(params.spawnedBy); const trustedGroup = resolveTrustedGroupIdFromContexts({ groupId: params.groupId, sessionContext, spawnedContext }); const groupIds = collectUniqueStrings([ ...sessionContext.groupIds ?? [], ...spawnedContext.groupIds ?? [], ...buildScopedGroupIdCandidates(trustedGroup.groupId) ]); if (groupIds.length === 0) return; const channel = normalizeMessageChannel(sessionContext.channel ?? spawnedContext.channel ?? params.messageProvider); if (!channel) return; let plugin; try { plugin = getLoadedChannelPlugin(channel); } catch { plugin = void 0; } 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: params.accountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 }); const policy = pickSandboxToolPolicy(toolsConfig); if (policy) return policy; } return pickSandboxToolPolicy(resolveChannelGroupToolsPolicy({ cfg: params.config, channel, messageProvider: channel, groupId: groupIds[0], groupIdCandidates: groupIds.slice(1), accountId: params.accountId, senderId: params.senderId, senderName: params.senderName, senderUsername: params.senderUsername, senderE164: params.senderE164 })); } //#endregion export { resolveProviderToolPolicy as a, resolveInheritedToolPolicyForSession as i, resolveEffectiveToolPolicy as n, resolveSubagentToolPolicyForSession as o, resolveGroupToolPolicy as r, resolveTrustedGroupId as s, filterToolsByPolicy as t };