UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

209 lines (208 loc) • 10 kB
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { n as normalizeAgentId, t as isValidAgentId } from "./agent-id-D1-Q6vR4.mjs"; import { n as buildAgentMainSessionKey } from "./session-key-CmZ4o9-l.mjs"; import "./session-key-DY3MOX4N.mjs"; import { a as isCronSessionKey, r as isAcpSessionKey, s as isSubagentSessionKey } from "./session-key-utils-CDyBC9Gn.mjs"; import { i as listAgentIds } from "./agent-roster-DH6r_8f4.mjs"; import { g as resolveDefaultAgentId, t as AgentSelectionRequiredError } from "./agent-scope-config-BEk6-ThS.mjs"; import { n as normalizeMessageChannel } from "./message-channel-core-D9WI2Y8r.mjs"; import { i as getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot-DpeEPkbV.mjs"; import "./agent-scope-bJOokm5r.mjs"; import { f as getActivePluginRegistryWorkspaceDirFromStateCore } from "./model-ref-shared-Df4xc5Gg.mjs"; import { t as modelKey } from "./model-key-5zORvMDY.mjs"; import { n as parseModelRef } from "./model-selection-normalize-B-Anaau2.mjs"; import { t as resolveDefaultModelForAgent } from "./model-selection-config-BC0CegJ7.mjs"; import { t as ADMIN_SCOPE } from "./operator-scopes-BO7_XQCg.mjs"; import { r as getRuntimeConfig } from "./io.runtime-B134MOMz.mjs"; import "./io-BGddrCVX.mjs"; import "./message-channel-AvA3r_Xp.mjs"; import { a as isAgentHarnessSessionKey, s as isAgentHarnessSessionStoreEntryProtected } from "./agent-harness-session-key-Dx0vv6oG.mjs"; import "./model-selection-Cpoq0B2R.mjs"; import { r as getHeader } from "./http-header-value-BM79-5V8.mjs"; import "./method-scopes-CRSVCw9J.mjs"; import "./session-accessor-C3ljJ974.mjs"; import { t as canonicalizeSessionKeyForAgent } from "./session-store-key-Dvf3CKvN.mjs"; import { s as resolveSessionEntryAccessTarget } from "./session-accessor.entry-xtqpYxWo.mjs"; import { n as createModelVisibilityPolicy } from "./model-visibility-policy-JItm7p_F.mjs"; import { t as createSyntheticPluginRuntimeClient } from "./server-plugin-runtime-client-CjNl2Aeh.mjs"; import { a as authorizeResolvedSessionMutation, p as isResolvedIncognitoSession } from "./session-sharing-policy-lVDdZ20H.mjs"; import "./session-sharing-BFM0s33P.mjs"; import { t as loadGatewayModelCatalog } from "./server-model-catalog-V6QOVB-l.mjs"; import "./http-auth-utils-xEG2Jlr0.mjs"; import { randomUUID } from "node:crypto"; //#region src/gateway/http-utils.ts const OPENCLAW_MODEL_ID = "openclaw"; /** Default OpenAI-compatible model alias that targets the default OpenClaw agent. */ const OPENCLAW_DEFAULT_MODEL_ID = "openclaw/default"; const AGENT_MODEL_PATTERN = /^(?:openclaw[:/]|agent:)(?<agentId>[a-z0-9][a-z0-9_-]{0,63})$/i; var UnknownGatewayAgentError = class extends Error { constructor(agentId) { super(`Unknown agent '${agentId}'.`); this.agentId = agentId; this.name = "UnknownGatewayAgentError"; } }; var GatewaySessionKeyOverrideError = class extends Error { constructor() { super("`x-openclaw-session-key` cannot use reserved internal session namespaces."); this.name = "GatewaySessionKeyOverrideError"; } }; var InvalidGatewayModelError = class extends Error { constructor() { super("Invalid `model`. Use `openclaw` or `openclaw/<agentId>`."); this.name = "InvalidGatewayModelError"; } }; function isUnknownGatewayAgentError(err) { return err instanceof UnknownGatewayAgentError; } function isAgentSelectionRequiredError(err) { return err instanceof AgentSelectionRequiredError; } function isGatewayAgentRequestError(err) { return isAgentSelectionRequiredError(err) || err instanceof InvalidGatewayModelError || isUnknownGatewayAgentError(err); } function isGatewayRequestContextError(err) { return isGatewayAgentRequestError(err) || err instanceof GatewaySessionKeyOverrideError; } function assertKnownAgentId(agentId, cfg = getRuntimeConfig()) { if (!listAgentIds(cfg).includes(agentId)) throw new UnknownGatewayAgentError(agentId); } function resolveAgentIdFromModel(model, cfg = getRuntimeConfig()) { const raw = model?.trim(); if (!raw) return; const lowered = normalizeLowercaseStringOrEmpty(raw); if (lowered === "openclaw" || lowered === "openclaw/default") return resolveDefaultAgentId(cfg); const agentId = raw.match(AGENT_MODEL_PATTERN)?.groups?.agentId; if (!agentId) return; return normalizeAgentId(agentId); } /** Checks OpenClaw routing-model syntax without resolving fleet ownership. */ function isOpenClawAgentModelId(model) { const raw = model?.trim(); if (!raw) return false; const lowered = normalizeLowercaseStringOrEmpty(raw); if (lowered === "openclaw" || lowered === "openclaw/default") return true; return AGENT_MODEL_PATTERN.test(raw); } /** Validates and resolves the `x-openclaw-model` override for OpenAI-compatible requests. */ async function resolveOpenAiCompatModelOverride(params) { const requestModel = params.model?.trim(); if (requestModel && !isOpenClawAgentModelId(requestModel)) return { errorMessage: "Invalid `model`. Use `openclaw` or `openclaw/<agentId>`." }; const raw = getHeader(params.req, "x-openclaw-model")?.trim(); if (!raw) return {}; const cfg = getRuntimeConfig(); const defaultProvider = resolveDefaultModelForAgent({ cfg, agentId: params.agentId }).provider; const workspaceDir = getActivePluginRegistryWorkspaceDirFromStateCore(); const modelManifestContext = { manifestPlugins: getCurrentPluginMetadataSnapshot({ config: cfg, env: process.env, ...workspaceDir ? { workspaceDir } : {} }) }; const parsed = parseModelRef(raw, defaultProvider, { allowManifestNormalization: true, allowPluginNormalization: true, ...modelManifestContext }); if (!parsed) return { errorMessage: "Invalid `x-openclaw-model`." }; const catalog = await loadGatewayModelCatalog({ agentId: params.agentId }); const policy = createModelVisibilityPolicy({ cfg, catalog, defaultProvider, agentId: params.agentId, allowManifestNormalization: true, allowPluginNormalization: true, ...modelManifestContext }); const normalized = modelKey(parsed.provider, parsed.model); if (!policy.allows(parsed)) return { errorMessage: `Model '${normalized}' is not allowed for agent '${params.agentId}'.` }; return { modelOverride: raw }; } /** Resolves the request agent from headers, model alias, or the configured default. */ function resolveAgentIdForRequest(params) { const cfg = getRuntimeConfig(); if (params.model?.trim() && !isOpenClawAgentModelId(params.model)) throw new InvalidGatewayModelError(); const headerAgent = normalizeOptionalString(getHeader(params.req, "x-openclaw-agent-id")) || normalizeOptionalString(getHeader(params.req, "x-openclaw-agent")); if (headerAgent) { if (!isValidAgentId(headerAgent)) throw new UnknownGatewayAgentError(headerAgent); const agentId = normalizeAgentId(headerAgent); assertKnownAgentId(agentId, cfg); return agentId; } const fromModel = resolveAgentIdFromModel(params.model, cfg); if (fromModel) { assertKnownAgentId(fromModel, cfg); return fromModel; } return resolveDefaultAgentId(cfg); } function isReservedSessionKeyOverride(sessionKey, agentId) { const lowered = normalizeLowercaseStringOrEmpty(sessionKey); const harnessLookupKey = sessionKey.startsWith("agent:") ? sessionKey : canonicalizeSessionKeyForAgent(agentId, sessionKey); const harnessEntry = isAgentHarnessSessionKey(sessionKey) ? resolveSessionEntryAccessTarget({ cfg: getRuntimeConfig(), sessionKey: harnessLookupKey }).entry : void 0; const harnessKeyReserved = isAgentHarnessSessionKey(sessionKey) && (!harnessEntry || isAgentHarnessSessionStoreEntryProtected(sessionKey, harnessEntry)); return lowered.startsWith("subagent:") || lowered.startsWith("cron:") || lowered.startsWith("acp:") || harnessKeyReserved || isSubagentSessionKey(sessionKey) || isCronSessionKey(sessionKey) || isAcpSessionKey(sessionKey); } function resolveGatewayRequestContext(params) { const agentId = resolveAgentIdForRequest({ req: params.req, model: params.model }); const explicit = getHeader(params.req, "x-openclaw-session-key")?.trim(); let sessionKey; if (explicit) { if (isReservedSessionKeyOverride(explicit, agentId)) throw new GatewaySessionKeyOverrideError(); sessionKey = explicit; } else { const user = params.user?.trim(); const mainKey = user ? `${params.sessionPrefix}-user:${user}` : `${params.sessionPrefix}:${randomUUID()}`; sessionKey = buildAgentMainSessionKey({ agentId, mainKey }); } const messageChannel = normalizeMessageChannel(getHeader(params.req, "x-openclaw-message-channel")) ?? "webchat"; return { agentId, sessionKey, messageChannel }; } function authorizeOpenAiCompatibleHttpSession(params) { const cfg = getRuntimeConfig(); const authenticatedUserProfile = params.requestAuth.authenticatedUserProfile; const authorizationError = authorizeResolvedSessionMutation({ cfg, client: createSyntheticPluginRuntimeClient({ ...authenticatedUserProfile ? { authenticatedUserProfile } : {}, operatorRoleActor: params.requestAuth.operatorRoleActor, operatorAccessAuthority: params.requestAuth.operatorAccessAuthority, scopes: params.senderIsOwner ? [ADMIN_SCOPE] : [] }), sessionKey: params.sessionKey, agentId: params.agentId }); if (authorizationError) return { allowed: false, message: authorizationError.message }; if (!params.senderIsOwner && !authenticatedUserProfile && isResolvedIncognitoSession({ cfg, sessionKey: params.sessionKey, agentId: params.agentId })) return { allowed: false, message: `missing scope: ${ADMIN_SCOPE}` }; return { allowed: true }; } //#endregion export { isGatewayAgentRequestError as a, isUnknownGatewayAgentError as c, resolveGatewayRequestContext as d, resolveOpenAiCompatModelOverride as f, isAgentSelectionRequiredError as i, resolveAgentIdForRequest as l, OPENCLAW_MODEL_ID as n, isGatewayRequestContextError as o, authorizeOpenAiCompatibleHttpSession as r, isOpenClawAgentModelId as s, OPENCLAW_DEFAULT_MODEL_ID as t, resolveAgentIdFromModel as u };