UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

570 lines (569 loc) 25.1 kB
import { a as normalizeFastMode, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js"; import { m as resolveAgentWorkspaceDir, u as resolveAgentDir, y as resolveDefaultAgentId } from "./agent-scope-config-DcbEhP0R.js"; import { O as parseAgentSessionKey, S as isAcpSessionKey, T as isSubagentSessionKey } from "./session-key-BnWWjqNc.js"; import { b as findModelCatalogEntry } from "./model-selection-shared-BlLyx1r2.js"; import { t as splitTrailingAuthProfile } from "./model-ref-profile-BIKs-96s.js"; import "./agent-scope-DbtJyKUL.js"; import { n as resolveSubagentConfiguredModelSelection, t as resolveDefaultModelForAgent } from "./model-selection-config-BrdmmqKD.js"; import { a as normalizeElevatedLevel, l as normalizeUsageDisplay, o as normalizeReasoningLevel, s as normalizeThinkLevel } from "./thinking.shared-bHYuuc1L.js"; import { t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js"; import { d as errorShape } from "./error-codes-Bo8q2D1o.js"; import { a as normalizeSessionIconValue, i as normalizeSessionColorValue, n as SESSION_COLOR_IDS, r as SESSION_ICON_GLYPH_IDS } from "./session-agent-status-Dd63sexE.js"; import { i as isUserModelAuthProfileId } from "./profile-usage-stats-dkZh2v8y.js"; import "./model-catalog-DOgUhUHe.js"; import { c as projectCanonicalSessionEntryShape } from "./store-writer-state-C4OG_EQ4.js"; import { n as buildSessionCreationStamp } from "./session-entry-provenance-jzrCUpdQ.js"; import { m as resolveMissingAgentHarnessSessionError, o as isAgentHarnessSessionKeyOwnedBy } from "./agent-harness-session-key-BOz3yx0-.js"; import { s as isModelSelectionLocked, t as MODEL_SELECTION_LOCKED_MESSAGE } from "./model-overrides-Bm7jo7Di.js"; import { n as isThinkingLevelSupported, o as resolveSupportedThinkingLevel, t as formatThinkingLevels } from "./thinking-DterdXhr.js"; import { o as resolveEffectiveAgentRuntime } from "./thinking-runtime-BiFbTVdI.js"; import { n as requiresAgentHarnessPluginSelection, r as resolveAgentHarnessOwnerPluginIds } from "./runtime-plugin-load-plan-bSDzapSx.js"; import { t as resolveAllowedModelRefCore } from "./model-selection-resolve-DJs652CE.js"; import "./model-selection-di2kjKCB.js"; import { a as readAcpSessionMetaForEntry } from "./session-meta-dzG4VkYo.js"; import { g as normalizeInheritedToolDenylist, h as normalizeInheritedToolAllowlist } from "./subagent-capabilities-Cd0-4jrx.js"; import { v as normalizeExecTarget } from "./exec-approvals-policy-CCFUzTzd.js"; import "./exec-approvals-BSZ-fPIY.js"; import { D as sessionAgentStatusExpiresAt, E as sanitizeSessionAgentStatusNote, T as resolveActiveSessionAgentStatus, w as isSessionAgentAttentionIconId } from "./session-utils-list-B0k8KJn5.js"; import { t as applyModelOverrideWithAuthProfileCompatibility } from "./auth-profile-preservation-DCNwoc5M.js"; import { t as parseSessionLabel } from "./session-label-DSD-L6TD.js"; import { n as snapshotAgentModelFallback, t as isAgentSessionModelPatchOrigin } from "./session-model-patch-origin-OUh-HtF1.js"; import { t as normalizeSendPolicy } from "./send-policy-CO-YZiTT.js"; import { t as normalizeGroupActivation } from "./group-activation-B6ER3hWD.js"; import { i as parseVerboseOverride, n as applyVerboseOverride, r as parseTraceOverride, t as applyTraceOverride } from "./level-overrides-DeO3Zvqm.js"; import { t as normalizeSessionToolOverrides } from "./session-tool-overrides-BnsUT7mq.js"; import { randomUUID } from "node:crypto"; //#region src/gateway/sessions-patch-context-window.ts function* applySessionContextWindowPatch(params) { if ("contextWindow" in params.patch) { const previous = params.next.contextWindow; const raw = params.patch.contextWindow; if (raw === null) delete params.next.contextWindow; else if (raw !== void 0) params.next.contextWindow = raw.trim(); if (previous !== params.next.contextWindow) params.next.liveModelSwitchPending = true; } if (!("contextWindow" in params.patch) && !("model" in params.patch)) return { ok: true }; const selected = normalizeOptionalString(params.next.contextWindow); if (!selected) { delete params.next.contextWindow; return { ok: true }; } const provider = params.next.providerOverride ?? params.defaultProvider; const model = params.next.modelOverride ?? params.defaultModel; const catalog = yield* params.loadModelCatalog(); const catalogEntry = catalog ? findModelCatalogEntry(catalog, { provider, modelId: model }) : void 0; if (catalogEntry?.contextWindows?.some((option) => option.id === selected)) return { ok: true }; if (!("contextWindow" in params.patch)) { delete params.next.contextWindow; return { ok: true }; } const allowed = catalogEntry?.contextWindows?.map((option) => option.id).join("|"); return { ok: false, error: `contextWindow "${selected}" is not supported for ${provider}/${model}${allowed ? ` (use ${allowed})` : ""}` }; } //#endregion //#region src/gateway/sessions-patch-display-metadata.ts /** Applies display-metadata patch fields onto the next entry; returns an error message on invalid input. */ function applySessionsPatchDisplayMetadata(params) { const { patch, next } = params; 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 parsed.error; if (params.isLabelInUse(parsed.label)) return `label already in use: ${parsed.label}`; next.label = parsed.label; } } if ("icon" in patch) { const raw = patch.icon; if (raw === null || raw === "") delete next.icon; else if (raw !== void 0) { const icon = normalizeSessionIconValue(raw); if (!icon) return `icon must be a single emoji or one of: ${SESSION_ICON_GLYPH_IDS.join(", ")}`; next.icon = icon; } } if ("color" in patch) { const raw = patch.color; if (raw === null || raw === "") delete next.color; else if (raw !== void 0) { const color = normalizeSessionColorValue(raw); if (!color) return `color must be one of: ${SESSION_COLOR_IDS.join(", ")}`; next.color = color; } } if ("category" in patch) { const raw = patch.category; if (raw === null) delete next.category; else if (raw !== void 0) { const trimmed = normalizeOptionalString(raw) ?? ""; if (!trimmed) return "invalid category: empty"; if (trimmed.length > 512) return `invalid category: too long (max 512)`; next.category = trimmed; } } if ("boardFace" in patch && patch.boardFace !== void 0) next.boardFace = patch.boardFace; } //#endregion //#region src/gateway/sessions-patch-subagent-policy.ts function supportsSpawnPolicy(storeKey) { return isSubagentSessionKey(storeKey) || isAcpSessionKey(storeKey); } function unsupportedField(field, storeKey) { return supportsSpawnPolicy(storeKey) ? void 0 : `${field} is only supported for subagent:* or acp:* sessions`; } /** Applies the remaining public child-policy fields after lineage became creation-only. */ function applySessionsPatchSubagentPolicy(params) { const { existing, next, patch, storeKey } = params; if ("completionOwnerSessionKey" in patch) { const raw = patch.completionOwnerSessionKey; if (raw === null && existing?.completionOwnerSessionKey) return "completionOwnerSessionKey cannot be cleared once set"; if (raw !== null && raw !== void 0) { const unsupported = unsupportedField("completionOwnerSessionKey", storeKey); if (unsupported) return unsupported; const normalized = normalizeOptionalString(raw); if (!normalized) return "invalid completionOwnerSessionKey: empty"; if (existing?.completionOwnerSessionKey && existing.completionOwnerSessionKey !== normalized) return "completionOwnerSessionKey cannot be changed once set"; next.completionOwnerSessionKey = normalized; } } if ("inheritedToolPolicyVersion" in patch) { const raw = patch.inheritedToolPolicyVersion; if (raw === null && existing?.inheritedToolPolicyVersion !== void 0) return "inheritedToolPolicyVersion cannot be cleared once set"; if (raw !== null && raw !== void 0) { const unsupported = unsupportedField("inheritedToolPolicyVersion", storeKey); if (unsupported) return unsupported; if (raw !== 1) return "invalid inheritedToolPolicyVersion (expected 1)"; next.inheritedToolPolicyVersion = 1; } } for (const field of ["inheritedToolDeny", "inheritedToolAllow"]) { if (!(field in patch)) continue; const raw = patch[field]; if (raw === null) { delete next[field]; continue; } if (raw === void 0) continue; if (!Array.isArray(raw)) return `invalid ${field} (use an array of tool names)`; const unsupported = unsupportedField(field, storeKey); if (unsupported) return unsupported; const normalized = field === "inheritedToolDeny" ? normalizeInheritedToolDenylist(raw) : normalizeInheritedToolAllowlist(raw); if (normalized.length > 0) next[field] = normalized; else delete next[field]; } } //#endregion //#region src/gateway/sessions-patch.ts function invalid(message) { return { ok: false, error: errorShape(ErrorCodes.INVALID_REQUEST, message) }; } function resolveSessionPatchModelSelection(params) { const { model: modelWithoutProfile, profile } = splitTrailingAuthProfile(params.raw); const resolved = resolveAllowedModelRefCore({ cfg: params.cfg, agentId: params.agentId, catalog: params.catalog, raw: modelWithoutProfile, defaultProvider: params.defaultProvider, defaultModel: params.subagentModelHint ?? params.defaultModel }); if ("error" in resolved) return { ok: false, error: resolved.error }; return { ok: true, provider: resolved.ref.provider, model: resolved.ref.model, ...profile ? { profile } : {}, isDefault: resolved.ref.provider === params.defaultProvider && resolved.ref.model === params.defaultModel }; } /** Stop at the first actual catalog use without committing or acquiring runtime effects. */ function prepareSessionsPatchEntry(params) { const projection = projectSessionPatchSteps(params); const first = projection.next(); if (first.done) return { kind: "complete", result: first.value }; return { kind: "model-catalog", finish: (catalog) => { const completed = projection.next(catalog); if (!completed.done) throw new Error("Session patch preparation requested the catalog more than once"); return completed.value; } }; } /** Project a validated gateway session patch for one session entry. */ async function projectSessionsPatchEntry(params) { const preparation = prepareSessionsPatchEntry(params); if (preparation.kind === "complete") return preparation.result; if (!params.loadGatewayModelCatalog) return preparation.finish(void 0); const catalog = await params.loadGatewayModelCatalog(); return preparation.finish(Array.isArray(catalog) ? catalog : []); } function* projectSessionPatchSteps(params) { const { cfg, storeKey, patch, creation } = params; if ("execSecurity" in patch || "execAsk" in patch) return invalid("execSecurity/execAsk are retired; set permissionMode (read-only|guarded|workspace|full) instead, or use /exec for this run only."); const harnessSessionError = params.existingEntry === void 0 && isAgentHarnessSessionKeyOwnedBy(storeKey, params.authorizedAgentHarnessId) ? void 0 : resolveMissingAgentHarnessSessionError(storeKey, params.existingEntry); if (harnessSessionError) return invalid(harnessSessionError); if (typeof patch.archived === "boolean") { if (!params.existingEntry?.sessionId) return invalid(`session not found: ${storeKey}`); if (patch.expectedSessionId === void 0) return invalid(`expectedSessionId required for session lifecycle patch: ${storeKey}`); } if ("model" in patch && isModelSelectionLocked(params.existingEntry)) return invalid(MODEL_SELECTION_LOCKED_MESSAGE); 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; const resolveThinkingRuntime = (provider, model, entry) => { const acpMeta = readAcpSessionMetaForEntry({ sessionKey: storeKey, agentId: sessionAgentId, entry }); return params.preparedAgentRuntime ?? acpMeta?.backend ?? resolveEffectiveAgentRuntime({ cfg, provider, modelId: model, agentId: sessionAgentId, sessionKey: storeKey, sessionEntry: entry }); }; let loadedModelCatalog; let catalogPrepared = false; function* loadPreparedModelCatalogForPatch() { if (!catalogPrepared) { loadedModelCatalog = yield; catalogPrepared = true; } return loadedModelCatalog; } const existing = params.existingEntry && projectCanonicalSessionEntryShape({ ...params.existingEntry }); const next = { ...existing, sessionId: existing?.sessionId || randomUUID(), ...existing?.sessionId ? {} : { lifecycleRevision: randomUUID() }, updatedAt: Math.max(existing?.updatedAt ?? 0, now), ...params.preparedSessionRoot ? { sessionRoot: params.preparedSessionRoot } : {}, ...creation && params.existingEntry === void 0 ? buildSessionCreationStamp(creation) : {} }; if (existing && !existing.sessionId) { delete next.label; delete next.category; delete next.displayName; } const subagentPolicyError = applySessionsPatchSubagentPolicy({ existing, next, patch, storeKey }); if (subagentPolicyError) return invalid(subagentPolicyError); const displayMetadataError = applySessionsPatchDisplayMetadata({ patch, next, isLabelInUse: params.isLabelInUse }); if (displayMetadataError) return invalid(displayMetadataError); if ("statusNote" in patch || "attention" in patch || "ttlMinutes" in patch) { const rawNote = patch.statusNote; const rawAttention = patch.attention; const ttlMinutes = patch.ttlMinutes; if (ttlMinutes !== void 0 && (!Number.isInteger(ttlMinutes) || ttlMinutes < 1 || ttlMinutes > 120)) return invalid(`invalid ttlMinutes (use 1-120)`); if (rawNote === null || rawAttention === null) { if (rawNote !== void 0 && rawNote !== null || rawAttention !== void 0 && rawAttention !== null) return invalid("cannot clear and set agent status in the same patch"); delete next.agentStatus; } else { const current = resolveActiveSessionAgentStatus(next.agentStatus, now); const note = rawNote === void 0 ? current?.note : sanitizeSessionAgentStatusNote(rawNote); if (!note) return invalid("statusNote required before setting attention or ttlMinutes"); if (rawAttention !== void 0 && !isSessionAgentAttentionIconId(rawAttention)) return invalid("invalid attention icon"); const attention = rawAttention ?? current?.attention; next.agentStatus = { note, expiresAt: sessionAgentStatusExpiresAt(now, ttlMinutes), ...attention ? { attention } : {} }; } } if ("archived" in patch) { if (patch.archived === true) { if (next.archivedAt === void 0) { next.archivedAt = now; next.archiveReason = "manual"; if (params.archivedBy) next.archivedBy = params.archivedBy; else delete next.archivedBy; } delete next.pinnedAt; } else { delete next.archivedAt; delete next.archivedBy; delete next.archiveReason; } } if ("pinned" in patch) { if (patch.pinned === true) { if (next.archivedAt !== void 0) return invalid("cannot pin an archived session; restore it first"); next.pinnedAt ??= now; } else delete next.pinnedAt; } if ("unread" in patch) { if (patch.unread === true) next.markedUnreadAt = Math.max(now, (params.existingEntry?.markedUnreadAt ?? 0) + 1); else { next.lastReadAt = now; delete next.markedUnreadAt; delete next.agentStatus; } } 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) { const hintProvider = normalizeOptionalString(existing?.providerOverride) || resolvedDefault.provider; const hintModel = normalizeOptionalString(existing?.modelOverride) || resolvedDefault.model; const thinkingCatalog = yield* loadPreparedModelCatalogForPatch(); const thinkingRuntime = resolveThinkingRuntime(hintProvider, hintModel, existing); return invalid(`invalid thinkingLevel (use ${formatThinkingLevels(hintProvider, hintModel, "|", thinkingCatalog, thinkingRuntime)})`); } 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, false, or \"auto\")"); next.fastMode = normalized; } } if ("toolOverrides" in patch) { const raw = patch.toolOverrides; if (raw === null) delete next.toolOverrides; else if (raw !== void 0) { const normalized = normalizeSessionToolOverrides(raw); if (normalized) next.toolOverrides = normalized; else delete next.toolOverrides; } } 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\")"); 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 ("execNode" in patch) { if (patch.execNode === null) { delete next.execNode; delete next.execCwd; if (next.execHost === "node") delete next.execHost; } else if (patch.execNode !== void 0) { const trimmed = normalizeOptionalString(patch.execNode) ?? ""; if (!trimmed) return invalid("invalid execNode: empty"); if (trimmed !== next.execNode) delete next.execCwd; next.execNode = trimmed; } } if ("permissionMode" in patch) { if (patch.permissionMode === null) delete next.permissionMode; else if (patch.permissionMode !== void 0) next.permissionMode = patch.permissionMode; } if ("model" in patch) { const agentModelFallback = isAgentSessionModelPatchOrigin() ? next.modelFallback?.source === "agent-patch" ? { ...next.modelFallback, ts: Math.max(now, next.modelFallback.ts + 1) } : snapshotAgentModelFallback(cfg, next, sessionAgentId, now) : void 0; delete next.modelFallback; const raw = patch.model; let selection; if (raw === null) selection = { ...resolvedDefault, isDefault: true }; else if (raw !== void 0) { const trimmed = normalizeOptionalString(raw) ?? ""; if (!trimmed) return invalid("invalid model: empty"); const catalog = yield* loadPreparedModelCatalogForPatch(); if (!catalog) return { ok: false, error: errorShape(ErrorCodes.UNAVAILABLE, "model catalog is still loading; retry in a few seconds") }; const resolved = resolveSessionPatchModelSelection({ cfg, agentId: sessionAgentId, catalog, raw: trimmed, defaultProvider: resolvedDefault.provider, defaultModel: resolvedDefault.model, subagentModelHint }); if (!resolved.ok) return invalid(resolved.error); selection = resolved; } if (selection) { if (selection.profile && isUserModelAuthProfileId(selection.profile)) { if (params.personalModelSelection?.authProfileId !== selection.profile) return { ok: false, error: errorShape(ErrorCodes.FORBIDDEN, "Choose your personal account from an identified Gateway connection.") }; params.personalModelSelection.assertCurrent(); } const harnessSelection = { provider: selection.provider, modelId: selection.model, runtime: resolveThinkingRuntime(selection.provider, selection.model, next), agentId: sessionAgentId }; if (!readAcpSessionMetaForEntry({ sessionKey: storeKey, agentId: sessionAgentId, entry: next }) && requiresAgentHarnessPluginSelection(harnessSelection, cfg) && resolveAgentHarnessOwnerPluginIds({ ...harnessSelection, config: cfg, workspaceDir: resolveAgentWorkspaceDir(cfg, sessionAgentId) }).length === 0) return invalid(`Model ${selection.provider}/${selection.model} requires agent harness "${harnessSelection.runtime}", but no enabled plugin provides it. Install and enable its plugin, restart the Gateway, then select the model again.`); applyModelOverrideWithAuthProfileCompatibility({ cfg, agentDir: resolveAgentDir(cfg, sessionAgentId), entry: next, currentProvider: next.providerOverride ?? next.modelProvider ?? resolvedDefault.provider, selection, profileOverride: selection.profile, ...params.providerAuthMetadataSnapshot ? { metadataSnapshot: params.providerAuthMetadataSnapshot } : {}, markLiveSwitchPending: raw !== null }); if (raw === null) delete next.liveModelSwitchPending; } if (agentModelFallback) next.modelFallback = agentModelFallback; } if ("thinkingLevel" in patch || "model" in patch) { const effectiveProvider = next.providerOverride ?? resolvedDefault.provider; const effectiveModel = next.modelOverride ?? resolvedDefault.model; const thinkingLevel = normalizeThinkLevel(next.thinkingLevel); let thinkingRuntime; if (!thinkingLevel) delete next.thinkingLevel; else { const thinkingCatalog = yield* loadPreparedModelCatalogForPatch(); thinkingRuntime = resolveThinkingRuntime(effectiveProvider, effectiveModel, next); if (!isThinkingLevelSupported({ provider: effectiveProvider, model: effectiveModel, level: thinkingLevel, catalog: thinkingCatalog, agentRuntime: thinkingRuntime })) { if ("thinkingLevel" in patch) return invalid(`thinkingLevel "${thinkingLevel}" is not supported for ${effectiveProvider}/${effectiveModel} (use ${formatThinkingLevels(effectiveProvider, effectiveModel, "|", thinkingCatalog, thinkingRuntime)})`); next.thinkingLevel = resolveSupportedThinkingLevel({ provider: effectiveProvider, model: effectiveModel, level: thinkingLevel, catalog: thinkingCatalog, agentRuntime: thinkingRuntime }); } } } const contextWindowPatch = yield* applySessionContextWindowPatch({ defaultModel: resolvedDefault.model, defaultProvider: resolvedDefault.provider, loadModelCatalog: loadPreparedModelCatalogForPatch, next, patch }); if (!contextWindowPatch.ok) return invalid(contextWindowPatch.error); if ("thinkingLevel" in patch && !("model" in patch) && next.modelFallback?.source === "agent-patch") next.modelFallback.prevThinkingLevel = next.thinkingLevel; if ("contextWindow" in patch && !("model" in patch) && next.modelFallback?.source === "agent-patch") next.modelFallback.prevContextWindow = next.contextWindow; 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; } } if (!existing?.sessionId) delete next.liveModelSwitchPending; return { ok: true, entry: next }; } //#endregion export { projectSessionsPatchEntry as n, resolveSessionPatchModelSelection as r, prepareSessionsPatchEntry as t };