UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

610 lines (609 loc) • 33.3 kB
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { p as shortenHomePath } from "./utils-CitNzLyF.mjs"; import { o as MODEL_SELECTION_LOCKED_MESSAGE } from "./openclaw-state-worker-error-Bi-BnNrV.mjs"; import { r as normalizeProviderId } from "./provider-id-D1VImdKk.mjs"; import { a as resolveAgentDir } from "./agent-scope-config-BEk6-ThS.mjs"; import { r as resolveCollapsedSessionAuthPinSource } from "./auth-profile-override-provenance-Jztnei-C.mjs"; import "./agent-scope-bJOokm5r.mjs"; import { t as modelKey } from "./model-key-5zORvMDY.mjs"; import { o as dedupeModelCatalogEntries, r as buildConfiguredModelCatalog } from "./model-selection-shared-CHVNQnp1.mjs"; import { x as resolveAuthStorePathForDisplay } from "./profiles-6K3k_v-S.mjs"; import { c as resolveSystemEventQueueKey } from "./system-event-ownership-BHF-l6Ie.mjs"; import { s as enqueueSystemEvent } from "./system-events-GQMoAcQt.mjs"; import { c as readSessionInputProfileId } from "./host-context-builder-DqHC41dw.mjs"; import { c as resolveSupportedThinkingLevel, n as formatThinkingLevels, r as isThinkingLevelSupported } from "./thinking-7OesKvXK.mjs"; import { t as resolveConfiguredModelEntries } from "./configured-model-entries-DkAcaE3S.mjs"; import { o as resolveEffectiveAgentRuntime } from "./thinking-runtime-CveqpPq0.mjs"; import "./model-selection-Cpoq0B2R.mjs"; import "./auth-profiles-DpzfW6rH.mjs"; import { n as emitSessionLifecycleEvent } from "./session-lifecycle-events-CfTrpgyR.mjs"; import { t as getChannelPlugin } from "./registry-Bcrk1FVy.mjs"; import "./plugins-BXkKReMp.mjs"; import { a as isModelSelectionLocked } from "./model-overrides-DqarTyJt.mjs"; import { a as formatFastModeCurrentStatus, c as formatFastModeValue, i as formatFastModeConfirmation, r as formatFastModeCommandOptions } from "./fast-mode-smSVCJXN.mjs"; import { n as resolveSandboxRuntimeStatus } from "./runtime-status-BBzhhE6k.mjs"; import { n as createModelVisibilityPolicy, t as RUNTIME_MODEL_VISIBILITY_NORMALIZATION } from "./model-visibility-policy-JItm7p_F.mjs"; import { u as renderExecTargetLabel } from "./bash-tools.exec-runtime-BBz5Ib5z.mjs"; import { r as resolveExecDefaults } from "./exec-defaults-aistZeP6.mjs"; import { t as resolveFastModeState } from "./fast-mode-C1fXnIGu.mjs"; import { r as resolveSelectedAndActiveModel, t as readSessionFallbackModel } from "./session-fallback-model-Byd7A3XM.mjs"; import { n as resolveQueueSettingsCore } from "./settings-Dr_sydzk.mjs"; import { u as refreshQueuedFollowupSession } from "./state-Bzhk4z8e.mjs"; import { t as persistStickyModelSelectionBestEffort } from "./sticky-model-selection-Bkhqr74O.mjs"; import "./queue-DoAEwH02.mjs"; import { t as triggerSessionPatchHook } from "./session-patch-hooks-BM2NA0oq.mjs"; import { t as applyModelOverrideWithAuthProfileCompatibility } from "./auth-profile-preservation-cMoEt-ZZ.mjs"; import { r as prefixSystemMessage } from "./system-message-C34aMvlW.mjs"; import "./sandbox-GCAKkjF5.mjs"; import { a as formatElevatedEvent, c as formatReasoningEvent, d as withOptions, f as resolveModelSelectionFromDirective, i as canPersistSessionDirectiveDefaults, l as persistSessionDirectiveSnapshot, m as resolveInvalidExecDirectiveMessage, n as acknowledgeIgnoredSessionDirective, o as formatElevatedUnavailableText, p as maybeHandleUnexpectedDirectiveArguments, r as applySessionDirectiveFields, s as formatModelSelectionScopeAck, t as DIRECTIVE_ACK_MESSAGES, u as resolveDirectiveTouchedSessionFields } from "./directive-handling.shared-DyDy2vXI.mjs"; import { t as applyModelRuntimeDirective } from "./directive-handling.model-runtime-BwL-VoQs.mjs"; import { a as prepareModelSelectionRuntime, t as findSelectedCatalogEntry } from "./model-runtime-normalization-CiI73t9q.mjs"; import { t as resolveRuntimePolicySessionKey } from "./runtime-policy-session-key-BfYuB8uy.mjs"; import { i as resolveModelsCommandReply } from "./commands-models-BYrP0p9u.mjs"; //#region src/auto-reply/reply/directive-handling.model.ts async function maybeHandleModelDirectiveInfo(params) { if (!params.directives.hasModelDirective) return; const directive = normalizeOptionalLowercaseString(params.directives.rawModelDirective); const isLiteralModelDirective = params.directives.modelDirectiveSource !== "alias"; const wantsStatus = isLiteralModelDirective && directive === "status"; const wantsSummary = isLiteralModelDirective && !directive; const wantsLegacyList = isLiteralModelDirective && directive === "list"; if (!wantsSummary && !wantsStatus && !wantsLegacyList) return; if (params.directives.rawModelProfile) return { text: "Auth profile override requires a model selection.", isError: true }; if (params.directives.rawModelRuntime) return { text: "Runtime override requires a model selection.", isError: true }; if (params.directives.modelScope) return { text: `${params.directives.modelScope === "session" ? "Session-only" : params.directives.modelScope === "agent" ? "Agent" : "Global"} scope requires a model selection.`, isError: true }; if (wantsLegacyList) return await resolveModelsCommandReply({ cfg: params.cfg, commandBodyNormalized: "/models", surface: params.surface, currentModel: `${params.provider}/${params.model}`, agentId: params.activeAgentId, agentDir: params.agentDir, workspaceDir: params.workspaceDir, sessionEntry: params.sessionEntry }) ?? { text: "No models available." }; const modelParams = { selectedProvider: params.provider, selectedModel: params.model, sessionEntry: params.sessionEntry }; const completedModel = readSessionFallbackModel({ ...modelParams, config: params.cfg, sessionScope: { agentId: params.activeAgentId, sessionKey: params.sessionKey, storePath: params.storePath } }); const modelRefs = resolveSelectedAndActiveModel({ ...modelParams, sessionEntry: completedModel ?? params.sessionEntry }); const lines = [`Current: ${modelRefs.selected.label}${modelRefs.activeDiffers ? " (selected)" : ""}`, modelRefs.activeDiffers ? `Active: ${modelRefs.active.label} (runtime)` : null].filter((line) => Boolean(line)); if (wantsSummary) { const thinkingRuntime = resolveEffectiveAgentRuntime({ cfg: params.cfg, provider: params.provider, modelId: params.model, agentId: params.activeAgentId, sessionKey: params.runtimePolicySessionKey, sessionEntry: params.sessionEntry }); const effectiveThinkLevel = resolveSupportedThinkingLevel({ provider: params.provider, model: params.model, level: params.currentThinkLevel, catalog: params.thinkingCatalog, agentRuntime: thinkingRuntime }); const channelData = (params.surface ? getChannelPlugin(params.surface) : null)?.commands?.buildModelBrowseChannelData?.(); const instructions = channelData ? [ "Tap below to select a model, or use:", "/model <provider/model> -s for this session only", "/model <provider/model> -a to update this agent's default", "/model <provider/model> -g to update the global default", "/model <provider/model> --runtime <runtime> -s to switch harnesses", "/model status for details" ] : [ "Session: /model <provider/model> -s", "Agent default: /model <provider/model> -a", "Global default: /model <provider/model> -g", "Runtime: /model <provider/model> --runtime <runtime> -s", "Browse: /models (providers) or /models <provider> (models)", "More: /model status" ]; return { text: [ ...lines, `Think: ${effectiveThinkLevel} (change with /think <level>)`, ...instructions ].join("\n"), ...channelData ? { channelData } : {} }; } const configured = resolveConfiguredModelEntries({ cfg: params.cfg, defaultProvider: params.defaultProvider, defaultModel: params.defaultModel, agentId: params.activeAgentId, aliasIndex: params.aliasIndex, ...RUNTIME_MODEL_VISIBILITY_NORMALIZATION }); const catalog = dedupeModelCatalogEntries([...params.allowedModelCatalog.flatMap((entry) => { const id = normalizeOptionalString(entry.id); const provider = normalizeProviderId(entry.provider); return id && provider ? [{ provider, id, name: entry.name ?? id }] : []; }), ...configured.entries.map(({ ref }) => ({ provider: ref.provider, id: ref.model, name: ref.model }))]); const pickerCatalog = createModelVisibilityPolicy({ cfg: params.cfg, agentId: params.activeAgentId, defaultProvider: params.defaultProvider, defaultModel: configured.defaultRef, catalog, ...RUNTIME_MODEL_VISIBILITY_NORMALIZATION }).allowedCatalog; if (pickerCatalog.length === 0) return { text: "No models available." }; const { loadPreparedModelCatalogView } = await import("./model-catalog-view-12cvCZIF.mjs"); const prepared = await loadPreparedModelCatalogView({ kind: "status", config: params.cfg, agentId: params.activeAgentId, agentDir: params.agentDir, workspaceDir: params.workspaceDir, entries: pickerCatalog, sessionEntry: params.sessionEntry }); const authByProvider = prepared.providerAuthLabels; lines.push(`Default: ${params.defaultProvider}/${params.defaultModel}`, `Agent: ${params.activeAgentId}`, `Auth store: ${shortenHomePath(resolveAuthStorePathForDisplay(params.agentDir))}`); if (params.resetModelOverride) lines.push(`(previous selection reset to default)`); const configuredKeys = new Set(buildConfiguredModelCatalog({ cfg: params.cfg }).map((entry) => modelKey(entry.provider, entry.id))); const wrapperKeys = new Set(pickerCatalog.flatMap((entry) => { const id = normalizeOptionalString(entry.id) ?? ""; const slash = id.indexOf("/"); if (slash <= 0) return []; const nestedProvider = normalizeProviderId(id.slice(0, slash)); const nestedModel = normalizeOptionalString(id.slice(slash + 1)); return nestedProvider && nestedModel && nestedProvider !== normalizeProviderId(entry.provider) ? [modelKey(nestedProvider, nestedModel)] : []; })); const byProvider = /* @__PURE__ */ new Map(); for (const entry of pickerCatalog) { const provider = normalizeProviderId(entry.provider); if (wrapperKeys.size > 0) { const key = modelKey(provider, normalizeOptionalString(entry.id) ?? ""); if (!configuredKeys.has(key) && authByProvider.get(provider) === "missing" && wrapperKeys.has(key)) continue; } const models = byProvider.get(provider) ?? []; models.push(entry); byProvider.set(provider, models); } for (const [provider, models] of byProvider) { const authLabel = authByProvider.get(provider) ?? "missing"; const endpoint = prepared.providerEndpoints.get(provider); const endpointSuffix = endpoint?.endpoint ? ` endpoint: ${endpoint?.endpoint}` : " endpoint: default"; const apiSuffix = endpoint?.api ? ` api: ${endpoint?.api}` : ""; lines.push(""); lines.push(`[${provider}]${endpointSuffix}${apiSuffix} auth: ${authLabel}`); for (const entry of models) { const label = `${provider}/${entry.id}`; const aliases = params.aliasIndex.byKey.get(label); const aliasSuffix = aliases && aliases.length > 0 ? ` (${aliases.join(", ")})` : ""; lines.push(` • ${label}${aliasSuffix}`); } } return { text: lines.join("\n") }; } //#endregion //#region src/auto-reply/reply/directive-handling.queue-validation.ts function maybeHandleQueueDirective(params) { const { directives } = params; if (!directives.hasQueueDirective) return; if (!directives.queueMode && !directives.queueReset && !directives.hasQueueOptions && directives.rawQueueMode === void 0 && directives.rawDebounce === void 0 && directives.rawCap === void 0 && directives.rawDrop === void 0) { const settings = resolveQueueSettingsCore({ cfg: params.cfg, channel: params.channel, sessionEntry: params.sessionEntry }); const debounceLabel = typeof settings.debounceMs === "number" ? `${settings.debounceMs}ms` : "default"; const capLabel = typeof settings.cap === "number" ? String(settings.cap) : "default"; return { text: withOptions(`Current queue settings: mode=${settings.mode}, debounce=${debounceLabel}, cap=${capLabel}, drop=${settings.dropPolicy ?? "default"}.`, "modes steer, followup, collect, interrupt; debounce:<ms|s|m>, cap:<n>, drop:old|new|summarize") }; } const errors = [ !directives.queueMode && !directives.queueReset && directives.rawQueueMode ? `Unrecognized queue mode "${directives.rawQueueMode}". Valid modes: steer, followup, collect, interrupt.` : void 0, directives.rawDebounce !== void 0 && typeof directives.debounceMs !== "number" ? `Invalid debounce "${directives.rawDebounce ?? ""}". Use ms/s/m (e.g. debounce:1500ms, debounce:2s).` : void 0, directives.rawCap !== void 0 && typeof directives.cap !== "number" ? `Invalid cap "${directives.rawCap ?? ""}". Use a positive integer (e.g. cap:10).` : void 0, directives.rawDrop !== void 0 && !directives.dropPolicy ? `Invalid drop policy "${directives.rawDrop ?? ""}". Use drop:old, drop:new, or drop:summarize.` : void 0 ].filter(Boolean); return errors.length ? { text: errors.join(" ") } : void 0; } //#endregion //#region src/auto-reply/reply/directive-handling.impl.ts const LEVEL_QUERY_OPTIONS = { Verbose: ["off, on, full", "on, full, off"], Trace: ["off, on, raw", "on, off, raw"], Reasoning: ["on, off, stream", "on, off, stream"] }; const ELEVATED_RUNTIME_HINT = prefixSystemMessage("Runtime is direct; sandboxing does not apply."); async function handleDirectiveOnly(params) { const { directives, sessionEntry, sessionStore, sessionKey, storePath, elevatedEnabled, elevatedAllowed, defaultProvider, defaultModel, aliasIndex, allowedModelKeys, allowedModelCatalog, provider, model, formatModelSwitchEvent, currentThinkLevel, currentFastMode, currentVerboseLevel, currentReasoningLevel, currentElevatedLevel } = params; const allowPrivilegedPersistence = canPersistSessionDirectiveDefaults(params); const rejectModelTransaction = (errorText) => { params.onRejection?.(); if (params.persistenceState) params.persistenceState.outcome = { kind: "rejected", errorText }; return { text: errorText, isError: true }; }; const acknowledgeIgnoredDirective = (reply, ignoredDirective) => acknowledgeIgnoredSessionDirective({ reply: typeof reply === "string" ? { text: reply } : reply, directives, ignoredDirective, persistenceState: params.persistenceState, applyRemainingDirectives: (remainingDirectives) => handleDirectiveOnly({ ...params, directives: remainingDirectives }) }); const acknowledgeLevel = (name, currentLevel) => { const [validLevels, options] = LEVEL_QUERY_OPTIONS[name]; const rawLevel = directives[`raw${name}Level`]; return acknowledgeIgnoredDirective(rawLevel ? `Unrecognized ${name.toLowerCase()} level "${rawLevel}". Valid levels: ${validLevels}.` : withOptions(`Current ${name.toLowerCase()} level: ${currentLevel ?? "off"}.`, options), `has${name}Directive`); }; const delegatedTraceAllowed = (params.gatewayClientScopes ?? []).includes("operator.admin"); if (directives.hasTraceDirective && !params.senderIsOwner && !delegatedTraceAllowed) return acknowledgeIgnoredDirective("❌ /trace is restricted to owners and gateway clients with operator.admin scope.", "hasTraceDirective"); const activeAgentId = params.agentId; const agentDir = resolveAgentDir(params.cfg, activeAgentId); const runtimePolicySessionKey = resolveRuntimePolicySessionKey(params); const runtimeIsSandboxed = resolveSandboxRuntimeStatus({ cfg: params.cfg, agentId: activeAgentId, sessionKey, classificationSessionKey: runtimePolicySessionKey }).sandboxed; const shouldHintDirectRuntime = directives.hasElevatedDirective && !runtimeIsSandboxed; let thinkingCatalog = params.thinkingCatalog?.length ? params.thinkingCatalog : allowedModelCatalog.length > 0 ? allowedModelCatalog : void 0; const modelInfo = await maybeHandleModelDirectiveInfo({ ...params, agentDir, activeAgentId, currentThinkLevel: currentThinkLevel ?? "off", thinkingCatalog, runtimePolicySessionKey }); if (modelInfo) return acknowledgeIgnoredDirective(modelInfo, "hasModelDirective"); const modelResolution = await resolveModelSelectionFromDirective({ directives, cfg: params.cfg, agentDir, defaultProvider, defaultModel, aliasIndex, allowedModelKeys, agentId: activeAgentId, modelPolicy: params.modelPolicy, operatorAuthority: params.operatorAuthority, requesterProfileId: params.ctx ? readSessionInputProfileId(params.ctx) : void 0 }); if (modelResolution.errorText) return rejectModelTransaction(modelResolution.errorText); const { modelSelection, profileOverride } = modelResolution; if (modelSelection && isModelSelectionLocked(sessionEntry)) return rejectModelTransaction(MODEL_SELECTION_LOCKED_MESSAGE); const resolvedProvider = modelSelection?.provider ?? provider; const resolvedModel = modelSelection?.model ?? model; const preparedModel = modelSelection ? await prepareModelSelectionRuntime({ cfg: params.cfg, agentId: activeAgentId, workspaceDir: params.workspaceDir, provider: resolvedProvider, model: resolvedModel, catalog: thinkingCatalog ?? [], rawRuntime: directives.rawModelRuntime, sessionEntry, profileOverride }) : void 0; if (preparedModel?.status === "rejected") return rejectModelTransaction(preparedModel.message); thinkingCatalog = preparedModel?.catalog ?? thinkingCatalog; const modelRuntimeResolution = preparedModel?.runtime ?? { kind: "unchanged" }; const validateSelection = () => modelResolution.validateModelSelection?.() ?? preparedModel?.validateRuntimeSelection?.(); const prospectiveSessionEntry = { ...sessionEntry }; applyModelRuntimeDirective(prospectiveSessionEntry, modelRuntimeResolution); const selectedCatalogEntry = findSelectedCatalogEntry({ catalog: thinkingCatalog, provider: resolvedProvider, model: resolvedModel }); const resolveThinkingRuntime = (entry) => resolveEffectiveAgentRuntime({ cfg: params.cfg, provider: resolvedProvider, modelId: resolvedModel, modelApi: selectedCatalogEntry?.api, modelBaseUrl: selectedCatalogEntry?.baseUrl, agentId: activeAgentId, sessionKey: runtimePolicySessionKey, sessionEntry: entry }); const thinkingRuntime = resolveThinkingRuntime(prospectiveSessionEntry); const thinkingPolicy = { provider: resolvedProvider, model: resolvedModel, catalog: thinkingCatalog, agentRuntime: thinkingRuntime }; const fastModeState = resolveFastModeState({ cfg: params.cfg, provider: resolvedProvider, model: resolvedModel, agentId: activeAgentId, sessionEntry: directives.clearFastMode ? void 0 : sessionEntry }); const effectiveFastMode = directives.fastMode ?? (directives.clearFastMode ? fastModeState.mode : currentFastMode) ?? fastModeState.mode; if (directives.hasThinkDirective && !directives.thinkLevel && !directives.clearThinkLevel) { if (!directives.rawThinkLevel) { const level = resolveSupportedThinkingLevel({ ...thinkingPolicy, level: currentThinkLevel ?? "off" }); return acknowledgeIgnoredDirective(withOptions(`Current thinking level: ${level}.`, `default, ${formatThinkingLevels(resolvedProvider, resolvedModel, ", ", thinkingCatalog, thinkingRuntime)}`), "hasThinkDirective"); } return acknowledgeIgnoredDirective(`Unrecognized thinking level "${directives.rawThinkLevel}". Valid levels: default, ${formatThinkingLevels(resolvedProvider, resolvedModel, ", ", thinkingCatalog, thinkingRuntime)}.`, "hasThinkDirective"); } if (directives.hasVerboseDirective && !directives.verboseLevel) return acknowledgeLevel("Verbose", currentVerboseLevel); if (directives.hasTraceDirective && !directives.traceLevel) return acknowledgeLevel("Trace", sessionEntry.traceLevel); if (directives.hasFastDirective && directives.fastMode === void 0 && !directives.clearFastMode) { const isFastStatus = normalizeLowercaseStringOrEmpty(directives.rawFastMode) === "status"; if (!directives.rawFastMode || isFastStatus) { const statusText = formatFastModeCurrentStatus({ mode: effectiveFastMode, source: fastModeState.source, fastAutoOnSeconds: fastModeState.fastAutoOnSeconds }); return acknowledgeIgnoredDirective(isFastStatus ? statusText : withOptions(statusText, formatFastModeCommandOptions({ fastAutoOnSeconds: fastModeState.fastAutoOnSeconds })), "hasFastDirective"); } return acknowledgeIgnoredDirective(`Unrecognized fast mode "${directives.rawFastMode}". Valid levels: on, off, ultrafast, auto, default, status.`, "hasFastDirective"); } if (directives.hasReasoningDirective && !directives.reasoningLevel) return acknowledgeLevel("Reasoning", currentReasoningLevel); if (directives.hasElevatedDirective) { if (!directives.elevatedLevel && directives.rawElevatedLevel) return acknowledgeIgnoredDirective(`Unrecognized elevated level "${directives.rawElevatedLevel}". Valid levels: off, on, ask, full.`, "hasElevatedDirective"); if (!elevatedEnabled || !elevatedAllowed) return acknowledgeIgnoredDirective(formatElevatedUnavailableText({ runtimeSandboxed: runtimeIsSandboxed, failures: params.elevatedFailures, sessionKey: params.sessionKey }), "hasElevatedDirective"); if (!directives.elevatedLevel) return acknowledgeIgnoredDirective([withOptions(`Current elevated level: ${currentElevatedLevel ?? "off"}.`, "on, off, ask, full"), shouldHintDirectRuntime ? ELEVATED_RUNTIME_HINT : null].filter(Boolean).join("\n"), "hasElevatedDirective"); } if (directives.hasExecDirective) { const invalidExecMessage = resolveInvalidExecDirectiveMessage(directives); if (invalidExecMessage) return acknowledgeIgnoredDirective(invalidExecMessage, "hasExecDirective"); const unexpectedExecArguments = maybeHandleUnexpectedDirectiveArguments(directives); if (unexpectedExecArguments) { params.onRejection?.(); return unexpectedExecArguments; } if (!directives.hasExecOptions) { const execDefaults = resolveExecDefaults({ cfg: params.cfg, sessionEntry, agentId: activeAgentId, sandboxAvailable: runtimeIsSandboxed }); const nodeLabel = execDefaults.node ? `node=${execDefaults.node}` : "node=(unset)"; return acknowledgeIgnoredDirective(withOptions(`Current exec defaults: host=${renderExecTargetLabel(execDefaults.host)}, effective=${execDefaults.effectiveHost}, security=${execDefaults.security}, ask=${execDefaults.ask}, ${nodeLabel}.`, "host=auto|sandbox|gateway|node, security=deny|allowlist|full, ask=off|on-miss|always, node=<id>"), "hasExecDirective"); } } const queueAck = maybeHandleQueueDirective({ directives, cfg: params.cfg, channel: provider, sessionEntry }); if (queueAck) return acknowledgeIgnoredDirective(queueAck, "hasQueueDirective"); const unexpectedArguments = maybeHandleUnexpectedDirectiveArguments(directives); if (unexpectedArguments) { params.onRejection?.(); return unexpectedArguments; } if (directives.hasThinkDirective && directives.thinkLevel && !isThinkingLevelSupported({ ...thinkingPolicy, level: directives.thinkLevel })) return rejectModelTransaction(`Thinking level "${directives.thinkLevel}" is not supported for ${resolvedProvider}/${resolvedModel}. Use one of: ${formatThinkingLevels(resolvedProvider, resolvedModel, ", ", thinkingCatalog, thinkingRuntime)}.`); const nextThinkLevel = sessionEntry.thinkingLevel; const remappedUnsupportedThinkLevel = nextThinkLevel && (params.persistenceState ? modelSelection : !directives.hasThinkDirective) ? resolveSupportedThinkingLevel({ ...thinkingPolicy, level: nextThinkLevel }) : void 0; const shouldRemapUnsupportedThinkLevel = Boolean(remappedUnsupportedThinkLevel) && remappedUnsupportedThinkLevel !== nextThinkLevel; const prevReasoningLevel = currentReasoningLevel ?? sessionEntry.reasoningLevel ?? "off"; const elevatedChanged = directives.hasElevatedDirective && directives.elevatedLevel !== void 0 && directives.elevatedLevel !== (currentElevatedLevel ?? sessionEntry.elevatedLevel ?? "off") && elevatedEnabled && elevatedAllowed; let modelSelectionUpdated = false; let configuredDefaultUpdate; const touchedSessionFields = resolveDirectiveTouchedSessionFields({ directives, allowPrivilegedPersistence, directiveOnly: !params.persistenceState }); if (shouldRemapUnsupportedThinkLevel && !touchedSessionFields.includes("thinkingLevel")) touchedSessionFields.push("thinkingLevel"); const fastModeChanged = directives.hasFastDirective && directives.fastMode !== void 0 && directives.fastMode !== currentFastMode || directives.clearFastMode && currentFastMode !== fastModeState.mode; const reasoningChanged = directives.hasReasoningDirective && directives.reasoningLevel !== void 0 && directives.reasoningLevel !== prevReasoningLevel; if (touchedSessionFields.length > 0) { const authProfileError = validateSelection(); if (authProfileError) return rejectModelTransaction(authProfileError); const initialSessionEntry = { ...sessionEntry }; const directiveFieldsUpdated = !params.persistenceState && applySessionDirectiveFields({ directives, sessionEntry, allowPrivilegedPersistence, allowElevatedPersistence: elevatedEnabled && elevatedAllowed }); if (shouldRemapUnsupportedThinkLevel && remappedUnsupportedThinkLevel) sessionEntry.thinkingLevel = remappedUnsupportedThinkLevel; if (modelSelection) { const applied = applyModelOverrideWithAuthProfileCompatibility({ cfg: params.cfg, agentDir, entry: sessionEntry, currentProvider: provider, selection: modelSelection, explicitDefaultSelection: modelSelection.isDefault, profileOverride, markLiveSwitchPending: true }); const appliedRuntime = applyModelRuntimeDirective(sessionEntry, modelRuntimeResolution); modelSelectionUpdated = applied.updated || appliedRuntime.updated; } sessionEntry.updatedAt = Date.now(); sessionStore[sessionKey] = sessionEntry; if (storePath) { const persistence = await persistSessionDirectiveSnapshot({ storePath, sessionKey, initialEntry: initialSessionEntry, sessionEntry, sessionStore, hasModelSelection: Boolean(modelSelection), reassertLiveModelSwitchPending: modelSelectionUpdated && sessionEntry.liveModelSwitchPending === true, touchedFields: touchedSessionFields, commitGuard: modelResolution.modelSelectionSource, validateCommit: preparedModel?.validateRuntimeSelection }); if (persistence.status !== "applied") return rejectModelTransaction(persistence.status === "commit-rejected" ? persistence.error : persistence.status === "model-selection-locked" ? MODEL_SELECTION_LOCKED_MESSAGE : modelSelection ? "Model change was not applied because the session changed. Retry." : "Session settings were not applied because the session changed. Retry."); } if (modelSelection && params.canPersistStickyModelSelection === true && params.stickyModelSelectionTarget) { const modelError = modelResolution.validateModelSelection?.(); if (modelError) return rejectModelTransaction(modelError); configuredDefaultUpdate = persistStickyModelSelectionBestEffort({ agentId: activeAgentId, model: `${modelSelection.provider}/${modelSelection.model}`, target: params.stickyModelSelectionTarget }); } if (sessionKey && (directiveFieldsUpdated || shouldRemapUnsupportedThinkLevel || modelSelectionUpdated)) emitSessionLifecycleEvent({ sessionKey, agentId: activeAgentId, reason: "patch", ...modelSelectionUpdated ? { catalogChanged: true } : {} }); if (modelSelection && modelSelectionUpdated && sessionKey) { triggerSessionPatchHook({ cfg: params.cfg, sessionEntry, sessionKey, patch: { key: sessionKey, model: directives.rawModelDirective ?? `${modelSelection.provider}/${modelSelection.model}` } }); refreshQueuedFollowupSession({ key: sessionKey, nextProvider: modelSelection.provider, nextModel: modelSelection.model, nextRouteResolution: "resolved", nextModelOverrideSource: modelSelection.isDefault ? void 0 : "user", nextAuthProfileId: sessionEntry.authProfileOverride, nextAuthProfileIdSource: resolveCollapsedSessionAuthPinSource(sessionEntry), nextThinking: { level: sessionEntry.thinkingLevel, catalog: thinkingCatalog, agentRuntime: resolveThinkingRuntime(sessionEntry) } }); } } if (modelSelection) { const nextLabel = `${modelSelection.provider}/${modelSelection.model}`; if (nextLabel !== params.initialModelLabel) enqueueSystemEvent(formatModelSwitchEvent(nextLabel, modelSelection.alias), { sessionKey: resolveSystemEventQueueKey(sessionKey, activeAgentId), contextKey: `model:${nextLabel}` }); } if (!params.persistenceState) { const eventSessionKey = resolveSystemEventQueueKey(sessionKey, activeAgentId); for (const [changed, mode, format] of [[ elevatedChanged, "elevated", () => formatElevatedEvent(sessionEntry.elevatedLevel) ], [ reasoningChanged, "reasoning", () => formatReasoningEvent(sessionEntry.reasoningLevel) ]]) if (changed) enqueueSystemEvent(format(), { sessionKey: eventSessionKey, contextKey: `mode:${mode}` }); } if (params.persistenceState) params.persistenceState.outcome = { kind: "applied", provider: resolvedProvider, model: resolvedModel, modelCatalog: thinkingCatalog }; const parts = []; const addSystemAck = (message) => parts.push(prefixSystemMessage(message)); if (directives.clearThinkLevel) parts.push("Thinking level reset to default."); else if (directives.hasThinkDirective && directives.thinkLevel) parts.push(directives.thinkLevel === "off" ? "Thinking disabled." : `Thinking level set to ${directives.thinkLevel}.`); if (directives.clearFastMode) addSystemAck("Fast mode reset to default."); else if (directives.hasFastDirective && directives.fastMode !== void 0) addSystemAck(formatFastModeConfirmation(directives.fastMode)); if (directives.hasVerboseDirective && directives.verboseLevel) addSystemAck(allowPrivilegedPersistence ? DIRECTIVE_ACK_MESSAGES.verbose[directives.verboseLevel] : "Verbose logging set for the current reply only."); if (directives.hasTraceDirective && directives.traceLevel) addSystemAck(DIRECTIVE_ACK_MESSAGES.trace[directives.traceLevel]); if (directives.hasVerboseDirective && directives.verboseLevel && !allowPrivilegedPersistence) addSystemAck("Verbose defaults require operator.admin for gateway callers; skipped persistence."); if (directives.hasReasoningDirective && directives.reasoningLevel) addSystemAck(DIRECTIVE_ACK_MESSAGES.reasoning[directives.reasoningLevel]); if (directives.hasElevatedDirective && directives.elevatedLevel) { addSystemAck(DIRECTIVE_ACK_MESSAGES.elevated[directives.elevatedLevel]); if (shouldHintDirectRuntime) parts.push(ELEVATED_RUNTIME_HINT); } if (directives.hasExecDirective && directives.hasExecOptions) for (const [label, options] of [[allowPrivilegedPersistence && "Exec defaults set", { host: directives.execHost, node: directives.execNode }], ["Exec policy for this run only", { security: directives.execSecurity, ask: directives.execAsk }]]) { const execParts = Object.entries(options).filter(([, value]) => Boolean(value)).map(([key, value]) => `${key}=${value}`); if (execParts.length > 0) addSystemAck(label ? `${label} (${execParts.join(", ")}).` : "Exec defaults require operator.admin for gateway callers; skipped persistence."); } if (modelSelection) { parts.push(formatModelSelectionScopeAck({ selection: modelSelection, configuredDefaultUpdate, ...params.stickyModelSelectionTarget ? { stickyModelSelectionTarget: params.stickyModelSelectionTarget } : {} })); if (profileOverride) parts.push(`Auth profile set to ${profileOverride}.`); if (modelRuntimeResolution.kind === "clear") parts.push("Runtime reset to configured policy."); else if (modelRuntimeResolution.kind === "set") parts.push(`Runtime set to ${modelRuntimeResolution.runtime} for this session.`); } if (shouldRemapUnsupportedThinkLevel && remappedUnsupportedThinkLevel) parts.push(`Thinking level set to ${remappedUnsupportedThinkLevel} (${nextThinkLevel} not supported for ${resolvedProvider}/${resolvedModel}).`); if (directives.hasQueueDirective && directives.queueMode) addSystemAck(`Queue mode set to ${directives.queueMode}.`); else if (directives.hasQueueDirective && directives.queueReset) addSystemAck("Queue mode reset to default."); if (directives.hasQueueDirective && typeof directives.debounceMs === "number") addSystemAck(`Queue debounce set to ${directives.debounceMs}ms.`); if (directives.hasQueueDirective && typeof directives.cap === "number") addSystemAck(`Queue cap set to ${directives.cap}.`); if (directives.hasQueueDirective && directives.dropPolicy) addSystemAck(`Queue drop set to ${directives.dropPolicy}.`); if (fastModeChanged && !params.persistenceState) { const nextFastMode = directives.clearFastMode ? fastModeState.mode : sessionEntry.fastMode; enqueueSystemEvent(formatFastModeConfirmation(nextFastMode), { sessionKey: resolveSystemEventQueueKey(sessionKey, activeAgentId), contextKey: `fast:${formatFastModeValue(nextFastMode)}` }); } const ack = parts.join(" ").trim(); return !ack && directives.hasStatusDirective ? void 0 : { text: ack || "OK." }; } //#endregion export { handleDirectiveOnly };