openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
405 lines (404 loc) • 17.8 kB
JavaScript
import { c as normalizeOptionalString, i as normalizeFastMode, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import "./agent-scope-MrLta7Pq.js";
import { a as isSubagentSessionKey, c as parseAgentSessionKey, n as isAcpSessionKey } from "./session-key-utils-Bx3apsJ3.js";
import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js";
import { c as resolveDefaultAgentId } from "./agent-scope-config-CgCYpZfK.js";
import { r as resolveProviderIdForAuth } from "./provider-auth-aliases-BNZrcvHv.js";
import { t as splitTrailingAuthProfile } from "./model-ref-profile-BIKs-96s.js";
import { d as normalizeElevatedLevel, f as normalizeReasoningLevel, h as normalizeUsageDisplay, n as isThinkingLevelSupported, o as resolveSupportedThinkingLevel, p as normalizeThinkLevel, t as formatThinkingLevels } from "./thinking-OG7pb4lf.js";
import { a as resolveAllowedModelRef, c as resolveDefaultModelForAgent, p as resolveSubagentConfiguredModelSelection } from "./model-selection-CA_65iXi.js";
import { t as normalizeSendPolicy } from "./send-policy-CfC5ATxp.js";
import { h as normalizeInheritedToolDenylist, m as normalizeInheritedToolAllowlist } from "./subagent-capabilities-Cva3gXJL.js";
import { r as parseSessionLabel } from "./openclaw-tools-C0nKaVVY.js";
import { x as normalizeExecTarget } from "./exec-approvals-C6M6SGY3.js";
import { in as errorShape, rn as ErrorCodes } from "./schema-BwaBORnA.js";
import "./src-oj0IwW6K.js";
import { t as applyModelOverrideToSessionEntry } from "./model-overrides-D5WaLvcp.js";
import { t as normalizeGroupActivation } from "./group-activation-qzpxwTmf.js";
import { i as parseVerboseOverride, n as applyVerboseOverride, r as parseTraceOverride, t as applyTraceOverride } from "./level-overrides-D6cD7r8A.js";
import { randomUUID } from "node:crypto";
//#region src/gateway/sessions-patch.ts
function invalid(message) {
return {
ok: false,
error: errorShape(ErrorCodes.INVALID_REQUEST, message)
};
}
function normalizeExecSecurity(raw) {
const normalized = normalizeOptionalLowercaseString(raw);
if (normalized === "deny" || normalized === "allowlist" || normalized === "full") return normalized;
}
function normalizeExecAsk(raw) {
const normalized = normalizeOptionalLowercaseString(raw);
if (normalized === "off" || normalized === "on-miss" || normalized === "always") return normalized;
}
function shouldPreserveSessionAuthProfileOverride(params) {
const profileOverride = normalizeOptionalString(params.entry.authProfileOverride);
if (!profileOverride) return false;
const provider = normalizeOptionalLowercaseString(params.provider);
if (!provider) return false;
const resolvesToTargetProvider = (rawProvider) => {
const candidate = normalizeOptionalLowercaseString(rawProvider);
if (!candidate) return false;
return resolveProviderIdForAuth(candidate, { config: params.cfg }) === resolveProviderIdForAuth(provider, { config: params.cfg });
};
const delimiterIndex = profileOverride.indexOf(":");
if (delimiterIndex < 0) return resolvesToTargetProvider(params.currentProvider);
const profileProvider = normalizeOptionalLowercaseString(profileOverride.slice(0, delimiterIndex));
if (!profileProvider) return false;
return resolvesToTargetProvider(profileProvider);
}
function supportsSpawnLineage(storeKey) {
return isSubagentSessionKey(storeKey) || isAcpSessionKey(storeKey);
}
function normalizeSubagentRole(raw) {
const normalized = normalizeOptionalLowercaseString(raw);
if (normalized === "orchestrator" || normalized === "leaf") return normalized;
}
function normalizeSubagentControlScope(raw) {
const normalized = normalizeOptionalLowercaseString(raw);
if (normalized === "children" || normalized === "none") return normalized;
}
/** Apply a validated gateway session patch to an in-memory session store entry. */
async function applySessionsPatchToStore(params) {
const { cfg, store, storeKey, patch } = params;
const now = Date.now();
const parsedAgent = parseAgentSessionKey(storeKey);
const sessionAgentId = normalizeAgentId(params.agentId ?? parsedAgent?.agentId ?? resolveDefaultAgentId(cfg));
const resolvedDefault = resolveDefaultModelForAgent({
cfg,
agentId: sessionAgentId
});
const subagentModelHint = isSubagentSessionKey(storeKey) ? resolveSubagentConfiguredModelSelection({
cfg,
agentId: sessionAgentId
}) : void 0;
let loadedModelCatalog;
const loadModelCatalogForPatch = async () => {
if (loadedModelCatalog) return loadedModelCatalog;
if (!params.loadGatewayModelCatalog) return;
const catalog = await params.loadGatewayModelCatalog();
loadedModelCatalog = Array.isArray(catalog) ? catalog : [];
return loadedModelCatalog;
};
const existing = store[storeKey];
const next = existing?.sessionId ? {
...existing,
updatedAt: Math.max(existing.updatedAt ?? 0, now)
} : {
...existing,
sessionId: randomUUID(),
sessionFile: void 0,
updatedAt: Math.max(existing?.updatedAt ?? 0, now)
};
if (existing && !existing.sessionId) {
delete next.label;
delete next.displayName;
}
if ("spawnedBy" in patch) {
const raw = patch.spawnedBy;
if (raw === null) {
if (existing?.spawnedBy) return invalid("spawnedBy cannot be cleared once set");
} else if (raw !== void 0) {
const trimmed = normalizeOptionalString(raw) ?? "";
if (!trimmed) return invalid("invalid spawnedBy: empty");
if (!supportsSpawnLineage(storeKey)) return invalid("spawnedBy is only supported for subagent:* or acp:* sessions");
if (existing?.spawnedBy && existing.spawnedBy !== trimmed) return invalid("spawnedBy cannot be changed once set");
next.spawnedBy = trimmed;
}
}
if ("spawnedWorkspaceDir" in patch) {
const raw = patch.spawnedWorkspaceDir;
if (raw === null) {
if (existing?.spawnedWorkspaceDir) return invalid("spawnedWorkspaceDir cannot be cleared once set");
} else if (raw !== void 0) {
if (!supportsSpawnLineage(storeKey)) return invalid("spawnedWorkspaceDir is only supported for subagent:* or acp:* sessions");
const trimmed = normalizeOptionalString(raw) ?? "";
if (!trimmed) return invalid("invalid spawnedWorkspaceDir: empty");
if (existing?.spawnedWorkspaceDir && existing.spawnedWorkspaceDir !== trimmed) return invalid("spawnedWorkspaceDir cannot be changed once set");
next.spawnedWorkspaceDir = trimmed;
}
}
if ("spawnedCwd" in patch) {
const raw = patch.spawnedCwd;
if (raw === null) {
if (existing?.spawnedCwd) return invalid("spawnedCwd cannot be cleared once set");
} else if (raw !== void 0) {
if (!supportsSpawnLineage(storeKey)) return invalid("spawnedCwd is only supported for subagent:* or acp:* sessions");
const trimmed = normalizeOptionalString(raw) ?? "";
if (!trimmed) return invalid("invalid spawnedCwd: empty");
if (existing?.spawnedCwd && existing.spawnedCwd !== trimmed) return invalid("spawnedCwd cannot be changed once set");
next.spawnedCwd = trimmed;
}
}
if ("spawnDepth" in patch) {
const raw = patch.spawnDepth;
if (raw === null) {
if (typeof existing?.spawnDepth === "number") return invalid("spawnDepth cannot be cleared once set");
} else if (raw !== void 0) {
if (!supportsSpawnLineage(storeKey)) return invalid("spawnDepth is only supported for subagent:* or acp:* sessions");
const numeric = raw;
if (!Number.isInteger(numeric) || numeric < 0) return invalid("invalid spawnDepth (use an integer >= 0)");
const normalized = numeric;
if (typeof existing?.spawnDepth === "number" && existing.spawnDepth !== normalized) return invalid("spawnDepth cannot be changed once set");
next.spawnDepth = normalized;
}
}
if ("subagentRole" in patch) {
const raw = patch.subagentRole;
if (raw === null) {
if (existing?.subagentRole) return invalid("subagentRole cannot be cleared once set");
} else if (raw !== void 0) {
if (!supportsSpawnLineage(storeKey)) return invalid("subagentRole is only supported for subagent:* or acp:* sessions");
const normalized = normalizeSubagentRole(raw);
if (!normalized) return invalid("invalid subagentRole (use \"orchestrator\" or \"leaf\")");
if (existing?.subagentRole && existing.subagentRole !== normalized) return invalid("subagentRole cannot be changed once set");
next.subagentRole = normalized;
}
}
if ("subagentControlScope" in patch) {
const raw = patch.subagentControlScope;
if (raw === null) {
if (existing?.subagentControlScope) return invalid("subagentControlScope cannot be cleared once set");
} else if (raw !== void 0) {
if (!supportsSpawnLineage(storeKey)) return invalid("subagentControlScope is only supported for subagent:* or acp:* sessions");
const normalized = normalizeSubagentControlScope(raw);
if (!normalized) return invalid("invalid subagentControlScope (use \"children\" or \"none\")");
if (existing?.subagentControlScope && existing.subagentControlScope !== normalized) return invalid("subagentControlScope cannot be changed once set");
next.subagentControlScope = normalized;
}
}
if ("inheritedToolDeny" in patch) {
const raw = patch.inheritedToolDeny;
if (raw === null) delete next.inheritedToolDeny;
else if (raw !== void 0) {
if (!Array.isArray(raw)) return invalid("invalid inheritedToolDeny (use an array of tool names)");
if (!supportsSpawnLineage(storeKey)) return invalid("inheritedToolDeny is only supported for subagent:* or acp:* sessions");
const inheritedToolDeny = normalizeInheritedToolDenylist(raw);
if (inheritedToolDeny.length > 0) next.inheritedToolDeny = inheritedToolDeny;
else delete next.inheritedToolDeny;
}
}
if ("inheritedToolAllow" in patch) {
const raw = patch.inheritedToolAllow;
if (raw === null) delete next.inheritedToolAllow;
else if (raw !== void 0) {
if (!Array.isArray(raw)) return invalid("invalid inheritedToolAllow (use an array of tool names)");
if (!supportsSpawnLineage(storeKey)) return invalid("inheritedToolAllow is only supported for subagent:* or acp:* sessions");
const inheritedToolAllow = normalizeInheritedToolAllowlist(raw);
if (inheritedToolAllow.length > 0) next.inheritedToolAllow = inheritedToolAllow;
else delete next.inheritedToolAllow;
}
}
if ("label" in patch) {
const raw = patch.label;
if (raw === null) delete next.label;
else if (raw !== void 0) {
const parsed = parseSessionLabel(raw);
if (!parsed.ok) return invalid(parsed.error);
for (const [key, entry] of Object.entries(store)) {
if (key === storeKey) continue;
if (entry?.label === parsed.label) return invalid(`label already in use: ${parsed.label}`);
}
next.label = parsed.label;
}
}
if ("thinkingLevel" in patch) {
const raw = patch.thinkingLevel;
if (raw === null) delete next.thinkingLevel;
else if (raw !== void 0) {
const normalized = normalizeThinkLevel(raw);
if (!normalized) return invalid(`invalid thinkingLevel (use ${formatThinkingLevels(normalizeOptionalString(existing?.providerOverride) || resolvedDefault.provider, normalizeOptionalString(existing?.modelOverride) || resolvedDefault.model, "|", await loadModelCatalogForPatch())})`);
next.thinkingLevel = normalized;
}
}
if ("fastMode" in patch) {
const raw = patch.fastMode;
if (raw === null) delete next.fastMode;
else if (raw !== void 0) {
const normalized = normalizeFastMode(raw);
if (normalized === void 0) return invalid("invalid fastMode (use true or false)");
next.fastMode = normalized;
}
}
if ("verboseLevel" in patch) {
const raw = patch.verboseLevel;
const parsed = parseVerboseOverride(raw);
if (!parsed.ok) return invalid(parsed.error);
applyVerboseOverride(next, parsed.value);
}
if ("traceLevel" in patch) {
const raw = patch.traceLevel;
const parsed = parseTraceOverride(raw);
if (!parsed.ok) return invalid(parsed.error);
applyTraceOverride(next, parsed.value);
}
if ("reasoningLevel" in patch) {
const raw = patch.reasoningLevel;
if (raw === null) delete next.reasoningLevel;
else if (raw !== void 0) {
const normalized = normalizeReasoningLevel(raw);
if (!normalized) return invalid("invalid reasoningLevel (use \"on\"|\"off\"|\"stream\")");
next.reasoningLevel = normalized;
}
}
if ("responseUsage" in patch) {
const raw = patch.responseUsage;
if (raw === null) delete next.responseUsage;
else if (raw !== void 0) {
const normalized = normalizeUsageDisplay(raw);
if (!normalized) return invalid("invalid responseUsage (use \"off\"|\"tokens\"|\"full\")");
if (normalized === "off") delete next.responseUsage;
else next.responseUsage = normalized;
}
}
if ("elevatedLevel" in patch) {
const raw = patch.elevatedLevel;
if (raw === null) delete next.elevatedLevel;
else if (raw !== void 0) {
const normalized = normalizeElevatedLevel(raw);
if (!normalized) return invalid("invalid elevatedLevel (use \"on\"|\"off\"|\"ask\"|\"full\")");
next.elevatedLevel = normalized;
}
}
if ("execHost" in patch) {
const raw = patch.execHost;
if (raw === null) delete next.execHost;
else if (raw !== void 0) {
const normalized = normalizeExecTarget(raw) ?? void 0;
if (!normalized) return invalid("invalid execHost (use \"auto\"|\"sandbox\"|\"gateway\"|\"node\")");
next.execHost = normalized;
}
}
if ("execSecurity" in patch) {
const raw = patch.execSecurity;
if (raw === null) delete next.execSecurity;
else if (raw !== void 0) {
const normalized = normalizeExecSecurity(raw);
if (!normalized) return invalid("invalid execSecurity (use \"deny\"|\"allowlist\"|\"full\")");
next.execSecurity = normalized;
}
}
if ("execAsk" in patch) {
const raw = patch.execAsk;
if (raw === null) delete next.execAsk;
else if (raw !== void 0) {
const normalized = normalizeExecAsk(raw);
if (!normalized) return invalid("invalid execAsk (use \"off\"|\"on-miss\"|\"always\")");
next.execAsk = normalized;
}
}
if ("execNode" in patch) {
const raw = patch.execNode;
if (raw === null) delete next.execNode;
else if (raw !== void 0) {
const trimmed = normalizeOptionalString(raw) ?? "";
if (!trimmed) return invalid("invalid execNode: empty");
next.execNode = trimmed;
}
}
if ("model" in patch) {
const raw = patch.model;
if (raw === null) {
applyModelOverrideToSessionEntry({
entry: next,
selection: {
provider: resolvedDefault.provider,
model: resolvedDefault.model,
isDefault: true
},
preserveAuthProfileOverride: shouldPreserveSessionAuthProfileOverride({
cfg,
currentProvider: next.providerOverride ?? next.modelProvider ?? resolvedDefault.provider,
entry: next,
provider: resolvedDefault.provider
})
});
delete next.liveModelSwitchPending;
} else if (raw !== void 0) {
const trimmed = normalizeOptionalString(raw) ?? "";
if (!trimmed) return invalid("invalid model: empty");
if (!params.loadGatewayModelCatalog) return {
ok: false,
error: errorShape(ErrorCodes.UNAVAILABLE, "model catalog unavailable")
};
const catalog = await loadModelCatalogForPatch();
if (!catalog) return {
ok: false,
error: errorShape(ErrorCodes.UNAVAILABLE, "model catalog unavailable")
};
const { model: modelWithoutProfile, profile: trailingProfile } = splitTrailingAuthProfile(trimmed);
const resolved = resolveAllowedModelRef({
cfg,
catalog,
raw: modelWithoutProfile,
defaultProvider: resolvedDefault.provider,
defaultModel: subagentModelHint ?? resolvedDefault.model
});
if ("error" in resolved) return invalid(resolved.error);
const isDefault = resolved.ref.provider === resolvedDefault.provider && resolved.ref.model === resolvedDefault.model;
applyModelOverrideToSessionEntry({
entry: next,
selection: {
provider: resolved.ref.provider,
model: resolved.ref.model,
isDefault
},
profileOverride: trailingProfile || void 0,
preserveAuthProfileOverride: shouldPreserveSessionAuthProfileOverride({
cfg,
currentProvider: next.providerOverride ?? next.modelProvider ?? resolvedDefault.provider,
entry: next,
provider: resolved.ref.provider
}),
markLiveSwitchPending: true
});
}
}
if (next.thinkingLevel) {
const effectiveProvider = next.providerOverride ?? resolvedDefault.provider;
const effectiveModel = next.modelOverride ?? resolvedDefault.model;
const thinkingLevel = normalizeThinkLevel(next.thinkingLevel);
const thinkingCatalog = await loadModelCatalogForPatch();
if (!thinkingLevel) delete next.thinkingLevel;
else if (!isThinkingLevelSupported({
provider: effectiveProvider,
model: effectiveModel,
level: thinkingLevel,
catalog: thinkingCatalog
})) {
if ("thinkingLevel" in patch) return invalid(`thinkingLevel "${thinkingLevel}" is not supported for ${effectiveProvider}/${effectiveModel} (use ${formatThinkingLevels(effectiveProvider, effectiveModel, "|", thinkingCatalog)})`);
next.thinkingLevel = resolveSupportedThinkingLevel({
provider: effectiveProvider,
model: effectiveModel,
level: thinkingLevel,
catalog: thinkingCatalog
});
}
}
if ("sendPolicy" in patch) {
const raw = patch.sendPolicy;
if (raw === null) delete next.sendPolicy;
else if (raw !== void 0) {
const normalized = normalizeSendPolicy(raw);
if (!normalized) return invalid("invalid sendPolicy (use \"allow\"|\"deny\")");
next.sendPolicy = normalized;
}
}
if ("groupActivation" in patch) {
const raw = patch.groupActivation;
if (raw === null) delete next.groupActivation;
else if (raw !== void 0) {
const normalized = normalizeGroupActivation(raw);
if (!normalized) return invalid("invalid groupActivation (use \"mention\"|\"always\")");
next.groupActivation = normalized;
}
}
store[storeKey] = next;
return {
ok: true,
entry: next
};
}
//#endregion
export { applySessionsPatchToStore as t };