openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
209 lines (208 loc) • 10 kB
JavaScript
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 };