UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,548 lines 65 kB
import { s as asFiniteNumber } from "./number-coercion-CLj0HTDM.js";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { n as createLazyPromise, r as createLazyPromiseLoader } from "./lazy-promise-DGqyc4Y4.js";
import { n as getRuntimeConfig } from "./io.runtime-B9iJRs3w.js";
import { h as redactToolPayloadText } from "./redact-BtvPPfTi.js";
import { D as tryResolveLegacyCompatibilityAgentId } from "./agent-scope-config-DcbEhP0R.js";
import "./legacy.default-agent-owner-BGwEdQRe.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { _ as onTrustedToolExecutionEvent } from "./diagnostic-events-Cwe92uV3.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import { _ as resolveSessionAgentId } from "./agent-scope-DbtJyKUL.js";
import { n as resolvePersistedSessionStoreOwnerForKey } from "./session-store-owner-CR2Ag3xK.js";
import "./io-bdCzpGWJ.js";
import { p as onAgentRuntimeEvent, u as onAgentAuditEvent, y as createAgentRunStaleLifecycleError } from "./agent-events-CoxiItUi.js";
import { a as clearAgentRunContext, c as getAgentRunContext, l as getAgentRunContextOwnerStatus } from "./agent-run-registry-CKYKdfNd.js";
import { m as onGatewaySuspendAdmissionChange } from "./gateway-work-admission-R1IpuDim.js";
import { t as configureRuntimeActionDecisionSink } from "./runtime-action-decision-C4JNkXkP.js";
import { a as configureChannelAdmissionEvidenceCollection, i as configureChannelAdmissionDecisionSink } from "./admission-evidence-BW_2Woj4.js";
import { d as readSessionTranscriptBoundedMessageTailPage } from "./session-accessor-YsytfDtG.js";
import { m as onSessionLifecycleEvent } from "./session-history-eviction-C4srftLJ.js";
import { r as onInternalSessionTranscriptUpdate } from "./transcript-events-wgPr4ILk.js";
import { a as resolveStoredSessionKeyForAgentStore } from "./session-store-key-8xEjWSNi.js";
import { i as onUserProfilesChanged } from "./user-profiles-owner-DdMEsuGt.js";
import { t as configureExecutionIdentityAdmissionSink } from "./execution-identity-admission-Ch95Lxwv.js";
import { T as isTerminalTaskStatus } from "./task-registry.store.sqlite-BK18rNh0.js";
import { c as isDefinitiveRunLifecycle, t as AGENT_RUN_TERMINAL_RETRY_GRACE_MS } from "./agent-run-terminal-outcome-CigeY75d.js";
import { n as isExecutionIdentityCollectionEnabled, r as resolveAuditMessageMode, t as isAuditLedgerEnabled } from "./audit-config-BKFiXlHH.js";
import { r as onTrustedMessageAuditEvent } from "./message-audit-events-DGtoPYvb.js";
import { t as configureExecutionDecisionWorkSink } from "./execution-decision-work-BR8wVcpq.js";
import { n as resolveSessionSubscriptionKeys, t as resolveSessionSubscriptionKey } from "./session-subscription-keys-BwUDpvzj.js";
import { i as tryResolveSessionCompatibilityOwnerAgentId } from "./session-request-agent-CCRSEGCB.js";
import { i as loadGatewaySessionEntryReadOnly } from "./session-utils-store-CInT2loy.js";
import "./session-utils-Cai0_C6U.js";
import { t as extractStoredAssistantText } from "./chat-history-text-C2UOMgRe.js";
import { n as resolveUtilityModelRefForAgent } from "./utility-model-C0ozcQ73.js";
import { t as configureMessageActionDecisionSink } from "./message-action-decision-o6mn6Seg.js";
import { t as stripMarkdown } from "./strip-markdown-B6s98ZUi.js";
import { c as removeChatAbortControllerEntry, p as markChatAbortTerminalPersistenceError } from "./chat-abort-qLn3eFOg.js";
import { i as terminalHealthFor, n as flushSessionActivityAssistantNote, r as noteSessionActivityEvent, t as createSessionActivityNoteState } from "./session-activity-notes-CEJKpygi.js";
import { a as sanitizeProgressStatusText } from "./progress-draft-status-text-DAFR4vkg.js";
import { r as onHeartbeatEvent } from "./heartbeat-events-bg9alNGv.js";
import { a as persistGatewaySessionLifecycleEvent } from "./session-lifecycle-state-DCd4j8Bd.js";
import { i as resolveVisibleActiveSessionRunState } from "./session-active-runs-CCNuJW7B.js";
import { n as onGatewaySessionReset } from "./session-reset-notifications-DgKdsPPS.js";
import { t as createAuditEventRecorder } from "./audit-recorder-BCaTSxYZ.js";
import { a as defaultPersistDigest, c as markSessionObserverRunSuperseded, d as rememberSessionObserverDisabledRun, f as rememberSessionObserverDormantRun, h as synthesizeSessionObserverTerminalDigest, i as defaultCompleteModel, l as normalizeSessionObserverModelOutput, m as sessionObserverScopeKey, n as buildSessionObserverPrompt, o as defaultPrepareModel, p as rememberSessionObserverRevisionFloor, r as createDormantSessionObserverRun, s as defaultReadSession, t as SESSION_OBSERVER_SYSTEM_PROMPT } from "./session-observer-model-D5MYTAfa.js";
import { i as resolveTaskRequesterSessionTarget, t as mapTaskSummary } from "./task-summary-CgTKm-QC.js";
import { n as createSessionCompanionAskRuntime } from "./session-companion-ask-NbxUgDmR.js";
//#region src/gateway/session-companion-context.ts
const CONTEXT_MAX_MESSAGES = 40;
const CONTEXT_MAX_BYTES = 24576;
const CONTEXT_MESSAGE_MAX_CHARS = 4e3;
const CONTEXT_READ_MAX_SCANNED_MESSAGES = 4096;
const CONTEXT_READ_MAX_BYTES = 1048576;
const CONTEXT_READ_PAGE_MESSAGES = 128;
function normalizeContextText(value) {
	return truncateUtf16Safe(redactToolPayloadText(value).replace(/\s+/gu, " ").trim(), CONTEXT_MESSAGE_MAX_CHARS);
}
function extractUserText(message) {
	if (!message || typeof message !== "object") return;
	const content = message.content;
	if (typeof content === "string") return normalizeContextText(content) || void 0;
	if (!Array.isArray(content)) return;
	return normalizeContextText(content.flatMap((block) => {
		if (!block || typeof block !== "object" || block.type !== "text") return [];
		const blockText = block.text;
		return typeof blockText === "string" ? [blockText] : [];
	}).join("\n")) || void 0;
}
function readMessageTimestamp(message) {
	if (!message || typeof message !== "object") return 0;
	const value = message.timestamp;
	return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
}
function appendContextMessages(events, messages) {
	for (let index = events.length - 1; index >= 0 && messages.length < CONTEXT_MAX_MESSAGES; index--) {
		const event = events[index]?.event;
		if (!event || typeof event !== "object") continue;
		const message = event.message;
		if (!message || typeof message !== "object") continue;
		const role = message.role;
		const text = role === "assistant" ? normalizeContextText(extractStoredAssistantText(message) ?? "") : role === "user" ? extractUserText(message) : void 0;
		if (text && (role === "assistant" || role === "user")) messages.push({
			role,
			text,
			ts: readMessageTimestamp(message)
		});
	}
}
function selectContextMessages(messages) {
	const selected = [];
	let bytes = 2;
	for (const message of messages) {
		const messageBytes = Buffer.byteLength(JSON.stringify(message), "utf8") + 1;
		if (bytes + messageBytes > CONTEXT_MAX_BYTES) break;
		selected.push(message);
		bytes += messageBytes;
	}
	return selected.toReversed();
}
async function readSessionCompanionContext(params) {
	const loaded = loadGatewaySessionEntryReadOnly(params.sessionKey, { agentId: params.agentId });
	const sessionId = loaded.entry?.sessionId?.trim();
	if (!sessionId) return { kind: "missing" };
	try {
		const scope = {
			agentId: params.agentId,
			sessionId,
			sessionKey: params.sessionKey,
			storePath: loaded.storePath
		};
		if (params.signal?.aborted) return { kind: "unavailable" };
		let offset = 0;
		let rawBytes = 0;
		let scannedMessages = 0;
		let totalMessages = 0;
		let stoppedAtOlderByteBoundary = false;
		let snapshot;
		const contextMessages = [];
		while (contextMessages.length < CONTEXT_MAX_MESSAGES && scannedMessages < CONTEXT_READ_MAX_SCANNED_MESSAGES) {
			const page = readSessionTranscriptBoundedMessageTailPage(scope, {
				maxBytes: CONTEXT_READ_MAX_BYTES - rawBytes,
				maxMessages: Math.min(CONTEXT_READ_PAGE_MESSAGES, CONTEXT_READ_MAX_SCANNED_MESSAGES - scannedMessages),
				offset
			});
			if (params.signal?.aborted) return { kind: "unavailable" };
			const pageSnapshot = {
				activeLeafEntryId: page.activeLeafEntryId,
				generation: page.snapshot.generation,
				indexedSeq: page.snapshot.indexedSeq,
				totalMessages: page.totalMessages
			};
			snapshot ??= pageSnapshot;
			if (pageSnapshot.activeLeafEntryId !== snapshot.activeLeafEntryId || pageSnapshot.generation !== snapshot.generation || pageSnapshot.indexedSeq !== snapshot.indexedSeq || pageSnapshot.totalMessages !== snapshot.totalMessages) return { kind: "unavailable" };
			totalMessages = page.totalMessages;
			const pageIsPartial = page.newestContiguousEventCount !== page.scannedMessages;
			const pageEvents = page.newestContiguousEventCount === page.events.length ? page.events : page.events.slice(page.events.length - page.newestContiguousEventCount);
			rawBytes += page.serializedBytes;
			scannedMessages += page.scannedMessages;
			offset += page.scannedMessages;
			appendContextMessages(pageEvents, contextMessages);
			if (pageIsPartial) {
				if (contextMessages.length === 0) return { kind: "unavailable" };
				stoppedAtOlderByteBoundary = true;
				break;
			}
			if (page.scannedMessages === 0 || offset >= totalMessages) break;
		}
		if (contextMessages.length < CONTEXT_MAX_MESSAGES && offset < totalMessages && !stoppedAtOlderByteBoundary) return { kind: "unavailable" };
		const fence = readSessionTranscriptBoundedMessageTailPage(scope, {
			maxBytes: 0,
			maxMessages: 0,
			offset: 0
		});
		if (params.signal?.aborted || !snapshot || fence.activeLeafEntryId !== snapshot.activeLeafEntryId || fence.snapshot.generation !== snapshot.generation || fence.snapshot.indexedSeq !== snapshot.indexedSeq || fence.totalMessages !== snapshot.totalMessages) return { kind: "unavailable" };
		return {
			kind: "ready",
			context: {
				empty: totalMessages === 0,
				messages: selectContextMessages(contextMessages),
				sessionId
			}
		};
	} catch {
		return { kind: "unavailable" };
	}
}
const defaultSessionCompanionContextReader = {
	currentSessionId: ({ agentId, sessionKey }) => loadGatewaySessionEntryReadOnly(sessionKey, { agentId }).entry?.sessionId?.trim() || void 0,
	read: readSessionCompanionContext
};
//#endregion
//#region src/gateway/session-companion.ts
const SESSION_COMPANION_IDLE_TTL_MS = 72e5;
const SESSION_COMPANION_SWEEP_INTERVAL_MS = 6e5;
function createSessionCompanion(deps) {
	const now = deps.now ?? Date.now;
	const setIntervalFn = deps.setIntervalFn ?? setInterval;
	const clearIntervalFn = deps.clearIntervalFn ?? clearInterval;
	const threads = /* @__PURE__ */ new Map();
	let disposed = false;
	const askRuntime = createSessionCompanionAskRuntime({
		...deps,
		now,
		threads,
		isDisposed: () => disposed
	});
	const reset = (target, cancellation) => {
		const sessionKey = target.sessionKey.trim();
		const agentId = target.agentId.trim();
		if (!sessionKey || !agentId) return;
		const key = sessionObserverScopeKey(sessionKey, agentId);
		askRuntime.cancel(sessionKey, agentId, cancellation);
		threads.delete(key);
	};
	const sweep = () => {
		const cutoff = now() - SESSION_COMPANION_IDLE_TTL_MS;
		for (const [key, thread] of threads) if (!thread.busy && thread.lastUsedAt <= cutoff) threads.delete(key);
	};
	const sweepTimer = setIntervalFn(sweep, SESSION_COMPANION_SWEEP_INTERVAL_MS);
	sweepTimer.unref?.();
	const unsubscribeReset = onGatewaySessionReset((sessionKey, suppliedAgentId) => {
		let agentId = suppliedAgentId;
		try {
			agentId ??= resolveSessionAgentId({
				sessionKey,
				config: deps.getConfig()
			});
		} catch {
			return;
		}
		reset({
			sessionKey,
			agentId
		}, "backing-session-revoked");
	});
	return {
		ask: askRuntime.ask,
		state(target) {
			const key = sessionObserverScopeKey(target.sessionKey.trim(), target.agentId.trim());
			const thread = threads.get(key);
			if (!thread) return { exchanges: [] };
			thread.lastUsedAt = now();
			return { exchanges: thread.exchanges.map(({ question, answer, ts }) => ({
				question,
				answer,
				ts
			})) };
		},
		reset(target) {
			reset(target, "explicit-reset");
		},
		dispose() {
			if (disposed) return;
			disposed = true;
			clearIntervalFn(sweepTimer);
			unsubscribeReset();
			askRuntime.dispose();
			threads.clear();
		}
	};
}
//#endregion
//#region src/gateway/session-lifecycle-persistence-owner.ts
function assertTerminalAuthority(authority) {
	if (getAgentRunContextOwnerStatus(authority.runId, authority.claimId, authority.lifecycleGeneration) !== "active") throw createAgentRunStaleLifecycleError();
}
function terminalEventAuthority(event) {
	return event.contextClaimId && event.lifecycleGeneration && event.runId ? {
		claimId: event.contextClaimId,
		lifecycleGeneration: event.lifecycleGeneration,
		runId: event.runId
	} : void 0;
}
function terminalEventKey(event) {
	if (!event.runId || event.seq === void 0) return;
	return `${event.contextClaimId ?? ""}\0${event.lifecycleGeneration ?? ""}\0${event.runId}\0${event.seq}`;
}
/** Owns each definitive lifecycle write before optional chat presentation code runs. */
function createSessionLifecyclePersistenceOwner() {
	const prepared = /* @__PURE__ */ new Map();
	const inFlight = /* @__PURE__ */ new Set();
	const observe = (params) => {
		const key = terminalEventKey(params.event);
		const existing = key ? prepared.get(key)?.promise : void 0;
		if (existing) return existing;
		const authority = params.authority;
		const promise = persistGatewaySessionLifecycleEvent({
			sessionKey: params.sessionKey,
			...params.agentId ? { agentId: params.agentId } : {},
			event: {
				...params.event,
				...params.event.lifecycleGeneration ? { lifecycleGeneration: params.event.lifecycleGeneration } : {},
				...params.event.mainSessionRestartRecovery === true ? { mainSessionRestartRecovery: true } : {},
				...params.clientRunId ? { clientRunId: params.clientRunId } : {}
			},
			...authority ? { assertCommitAllowed: () => assertTerminalAuthority(authority) } : {}
		});
		inFlight.add(promise);
		let entry;
		const settle = () => {
			inFlight.delete(promise);
			if (!entry) return;
			entry.settled = true;
			if (entry.expired && key && prepared.get(key) === entry) prepared.delete(key);
		};
		promise.then(settle, settle);
		if (key) {
			const preparedEntry = {
				expired: false,
				promise,
				settled: false,
				timer: setTimeout(() => {
					if (prepared.get(key) !== preparedEntry) return;
					preparedEntry.expired = true;
					if (preparedEntry.settled) prepared.delete(key);
				}, AGENT_RUN_TERMINAL_RETRY_GRACE_MS)
			};
			entry = preparedEntry;
			preparedEntry.timer.unref?.();
			prepared.set(key, preparedEntry);
		}
		return promise;
	};
	const take = (event) => {
		const key = terminalEventKey(event);
		if (!key) return;
		const entry = prepared.get(key);
		if (!entry) return;
		clearTimeout(entry.timer);
		prepared.delete(key);
		return entry.promise;
	};
	return {
		observe,
		persist: (params) => {
			const preparedPersistence = take(params.event);
			if (preparedPersistence) return preparedPersistence;
			if (isDefinitiveRunLifecycle({
				phase: params.event.data?.phase,
				data: params.event.data
			}) && terminalEventKey(params.event)) return Promise.reject(createAgentRunStaleLifecycleError());
			const authority = terminalEventAuthority(params.event);
			return persistGatewaySessionLifecycleEvent({
				...params,
				...authority ? { assertCommitAllowed: () => assertTerminalAuthority(authority) } : {}
			});
		},
		async drain() {
			await Promise.allSettled(inFlight);
			for (const entry of prepared.values()) clearTimeout(entry.timer);
			prepared.clear();
		}
	};
}
//#endregion
//#region src/gateway/session-observer-audience.ts
function createSessionObserverAudience(params) {
	const messageSubscriberKeys = (sessionKey, agentId) => {
		const canonicalKeys = resolveSessionSubscriptionKeys(sessionKey, agentId);
		if (canonicalKeys[0] === sessionKey) return canonicalKeys;
		const config = params.getConfig();
		const persistedOwner = resolvePersistedSessionStoreOwnerForKey(config, sessionKey);
		const compatibilityAgentId = persistedOwner.kind === "configured" ? persistedOwner.agentId : tryResolveLegacyCompatibilityAgentId(config);
		return resolveSessionSubscriptionKeys(sessionKey, agentId, compatibilityAgentId);
	};
	const messageRecipients = (sessionKey, agentId) => {
		const recipients = /* @__PURE__ */ new Set();
		for (const key of messageSubscriberKeys(sessionKey, agentId)) for (const connId of params.subscribers.get(key)) recipients.add(connId);
		return recipients;
	};
	const classify = (sessionKey, agentId) => {
		for (const key of messageSubscriberKeys(sessionKey, agentId)) for (const connId of params.subscribers.get(key)) if (params.isVisible(connId)) return "direct";
		for (const connId of params.sessionEventSubscribers?.getAll() ?? []) if (params.isVisible(connId)) return "broad";
		return "none";
	};
	return {
		classify,
		deliveryOptions(sessionKey, agentId) {
			return {
				agentId,
				dropIfSlow: true,
				sessionKeys: messageSubscriberKeys(sessionKey, agentId),
				sessionSubscriptionVerified: true
			};
		},
		recipients(sessionKey, agentId) {
			const recipients = messageRecipients(sessionKey, agentId);
			for (const connId of params.sessionEventSubscribers?.getAll() ?? []) if (params.isVisible(connId)) recipients.add(connId);
			return recipients;
		},
		criticalRecipients(sessionKey, agentId) {
			const recipients = messageRecipients(sessionKey, agentId);
			for (const connId of params.sessionEventSubscribers?.getAll() ?? []) recipients.add(connId);
			return recipients;
		}
	};
}
function createSessionObserverAudienceLifecycle(params) {
	const reconcileState = (state) => {
		const audience = params.audience.classify(state.sessionKey, state.agentId);
		if (audience === "none") params.suspend(state);
		else if (state.utilityModelRef && audience !== "direct") params.demote(state);
	};
	const reconcileAll = () => {
		for (const state of params.states.values()) reconcileState(state);
	};
	const unsubscribe = params.subscribers.onChange((sessionKey) => {
		const state = params.states.get(sessionKey);
		if (state) reconcileState(state);
		else if (sessionKey.toLowerCase() === "global") reconcileAll();
	});
	const stateIsCurrent = (state, observedAudience) => params.isCurrent(state) && (observedAudience ?? params.audience.classify(state.sessionKey, state.agentId)) !== "none";
	const modelStateIsCurrent = (state, observedAudience) => Boolean(state.utilityModelRef) && (observedAudience ?? params.audience.classify(state.sessionKey, state.agentId)) === "direct" && params.isCurrent(state) && params.resolveUtilityModelRef(state.agentId) === state.utilityModelRef;
	return {
		stateIsCurrent,
		modelStateIsCurrent,
		reconcileAll,
		unsubscribe
	};
}
//#endregion
//#region src/gateway/session-observer-companion.ts
function createSessionObserverCompanionSnapshotReader(params) {
	return (sessionKey, selectedAgentId) => {
		const cfg = params.getConfig();
		const agentId = resolveSessionAgentId({
			sessionKey,
			config: cfg,
			...selectedAgentId ? { agentId: selectedAgentId } : {}
		});
		const canonicalSessionKey = resolveStoredSessionKeyForAgentStore({
			cfg,
			agentId,
			sessionKey
		});
		const state = params.states.get(resolveSessionSubscriptionKey(canonicalSessionKey, agentId));
		if (state) {
			flushSessionActivityAssistantNote(state);
			return {
				agentId: state.agentId,
				runId: state.runId,
				...state.previousDigest ? { digest: state.previousDigest } : {},
				notes: state.notes.map((note) => ({
					sequence: note.sequence,
					text: note.text
				}))
			};
		}
		const digest = params.readSession(canonicalSessionKey, agentId)?.observerDigest;
		return {
			agentId,
			...digest?.runId ? { runId: digest.runId } : {},
			...digest ? { digest } : {},
			notes: []
		};
	};
}
//#endregion
//#region src/gateway/session-observer-completion.ts
const MODEL_TIMEOUT_MS = 1e4;
function createSessionObserverCompletion(params) {
	const ensurePrepared = async (state) => {
		const modelRef = state.utilityModelRef;
		if (!modelRef) throw new Error("session observer utility model is unavailable");
		const preparedPromise = state.preparedPromise ??= params.prepareModel({
			cfg: params.getConfig(),
			agentId: state.agentId,
			modelRef,
			useUtilityModel: true
		});
		let failed = true;
		try {
			const prepared = await preparedPromise;
			failed = false;
			return prepared;
		} finally {
			if (failed && state.preparedPromise === preparedPromise) state.preparedPromise = void 0;
		}
	};
	return async (state, notes) => {
		const controller = new AbortController();
		state.activeController = controller;
		const timeout = params.setTimeoutFn(() => controller.abort(), MODEL_TIMEOUT_MS);
		const aborted = new Promise((_resolve, reject) => {
			controller.signal.addEventListener("abort", () => reject(/* @__PURE__ */ new Error("session observer model call timed out or was cancelled")), { once: true });
		});
		try {
			const execute = async () => {
				const prepared = await ensurePrepared(state);
				if (!params.isCurrent(state) || controller.signal.aborted) throw new Error("session observer state is no longer active");
				for (let attempt = 0; attempt < 2; attempt += 1) {
					if (!params.isCurrent(state) || controller.signal.aborted) throw new Error("session observer state is no longer active");
					const result = await params.completeModel({
						...prepared,
						config: params.getConfig(),
						systemPrompt: SESSION_OBSERVER_SYSTEM_PROMPT,
						prompt: buildSessionObserverPrompt(state, notes),
						timeoutMs: MODEL_TIMEOUT_MS,
						abortSignal: controller.signal,
						streamParams: {
							maxTokens: 300,
							temperature: .2
						}
					});
					const parsed = normalizeSessionObserverModelOutput(result.text);
					if (parsed) return parsed;
				}
				throw new Error("session observer returned invalid JSON twice");
			};
			return await Promise.race([execute(), aborted]);
		} finally {
			params.clearTimeoutFn(timeout);
			if (state.activeController === controller) state.activeController = void 0;
		}
	};
}
//#endregion
//#region src/gateway/session-observer-model-slots.ts
function createSessionObserverModelSlots(params) {
	const demoted = /* @__PURE__ */ new WeakSet();
	const requestGenerations = /* @__PURE__ */ new WeakMap();
	return {
		beginRequest(state) {
			const generation = (requestGenerations.get(state) ?? 0) + 1;
			requestGenerations.set(state, generation);
			return generation;
		},
		invalidateRequest(state) {
			requestGenerations.set(state, (requestGenerations.get(state) ?? 0) + 1);
			state.activeController?.abort();
		},
		requestIsCurrent(state, generation) {
			return requestGenerations.get(state) === generation;
		},
		claim(agentId, current) {
			const resolved = params.resolve(agentId);
			if (!resolved || current?.utilityModelRef === resolved) return resolved;
			let occupied = 0;
			let evicted;
			for (const state of params.states.values()) {
				if (state === current || !state.utilityModelRef) continue;
				occupied += 1;
				if (!state.terminalHealth && !state.finalPending && (!evicted || (state.lastActivityAt - evicted.lastActivityAt || state.sessionKey.localeCompare(evicted.sessionKey)) < 0)) evicted = state;
			}
			if (occupied >= params.maxSessions) {
				if (current && demoted.has(current) || !evicted) return;
				demoted.add(evicted);
				params.demote(evicted);
			} else if (current) demoted.delete(current);
			return resolved;
		}
	};
}
//#endregion
//#region src/gateway/session-observer-persistence.ts
const PERSIST_INTERVAL_MS = 6e4;
function createSessionObserverDigestPersister(params) {
	const preamblePersistedAt = /* @__PURE__ */ new WeakMap();
	return async (state, digest, final, kind = "model") => {
		const lastPersistedAt = kind === "preamble" ? preamblePersistedAt.get(state) : state.lastPersistedAt;
		const due = lastPersistedAt === void 0 || params.now() - lastPersistedAt >= PERSIST_INTERVAL_MS;
		if (!final && !due) return;
		const attempts = final ? 2 : 1;
		for (let attempt = 0; attempt < attempts; attempt += 1) try {
			const accepted = await params.persistDigest({
				sessionKey: state.sessionKey,
				sessionId: state.sessionId,
				agentId: state.agentId,
				digest,
				stillCurrent: params.stillCurrent(state.runId, state.sessionKey, state.agentId)
			});
			if (accepted === null) {
				params.onMissingEntry(state);
				return;
			}
			if (accepted) {
				if (kind === "preamble") preamblePersistedAt.set(state, params.now());
				else state.lastPersistedAt = params.now();
			}
			return;
		} catch (error) {
			if (attempt + 1 === attempts) params.onError(state, error);
		}
	};
}
//#endregion
//#region src/agents/session-preamble.ts
function normalizeSessionPreambleText(value, maxChars) {
	if (typeof value !== "string") return "";
	const sanitized = sanitizeProgressStatusText(value);
	if (!sanitized) return "";
	const normalized = stripMarkdown(sanitized, { linkStyle: "label" }).replace(/\s+/gu, " ").trim();
	return truncateUtf16Safe(normalized, maxChars);
}
//#endregion
//#region src/gateway/session-observer-preamble.ts
const PREAMBLE_HEADLINE_MAX_CHARS = 120;
const PREAMBLE_PUBLISH_INTERVAL_MS = 2e3;
function createSessionObserverPreamblePublisher(params) {
	const entries = /* @__PURE__ */ new Map();
	const generations = /* @__PURE__ */ new WeakMap();
	const clear = (state) => {
		const entry = entries.get(state);
		if (entry?.timer) params.clearTimeoutFn(entry.timer);
		entries.delete(state);
	};
	const publish = (state, entry) => {
		entry.timer = void 0;
		if (!params.isCurrent(state)) {
			clear(state);
			return;
		}
		const previous = state.previousDigest;
		if (previous?.runId === state.runId && previous.headline === entry.headline) {
			clear(state);
			return;
		}
		state.revision += 1;
		const digest = {
			sessionKey: state.sessionKey,
			agentId: state.agentId,
			runId: state.runId,
			revision: state.revision,
			updatedAt: Math.max(entry.updatedAt, (previous?.updatedAt ?? -1) + 1),
			headline: entry.headline,
			health: previous?.runId === state.runId && previous.health !== "done" && previous.health !== "failed" ? previous.health : "on-track",
			...state.planProgress ? { planProgress: state.planProgress } : {}
		};
		state.previousDigest = digest;
		state.lastPublishedPreambleHeadline = entry.headline;
		entry.lastPublishedAt = params.now();
		entry.published = true;
		params.publish(state, digest);
	};
	return {
		handle(state, event) {
			if (event.stream !== "item" || event.data.kind !== "preamble") return false;
			const headline = normalizeSessionPreambleText(event.data.progressText, PREAMBLE_HEADLINE_MAX_CHARS);
			if (!headline) return true;
			const existing = entries.get(state);
			const previousHeadline = state.lastPreambleHeadline ?? (state.previousDigest?.runId === state.runId ? state.previousDigest.headline : "");
			if (!existing && previousHeadline === headline) {
				state.lastPreambleHeadline = headline;
				state.lastPublishedPreambleHeadline = headline;
				return true;
			}
			const entry = existing ?? {
				headline: "",
				lastPublishedAt: 0,
				published: false,
				updatedAt: event.ts
			};
			if (previousHeadline !== headline) generations.set(state, (generations.get(state) ?? 0) + 1);
			state.lastPreambleHeadline = headline;
			entry.headline = headline;
			entry.updatedAt = event.ts;
			entries.set(state, entry);
			const elapsed = params.now() - entry.lastPublishedAt;
			if (!entry.published || elapsed >= PREAMBLE_PUBLISH_INTERVAL_MS) {
				if (entry.timer) params.clearTimeoutFn(entry.timer);
				publish(state, entry);
			} else if (!entry.timer) {
				entry.timer = params.setTimeoutFn(() => publish(state, entry), PREAMBLE_PUBLISH_INTERVAL_MS - elapsed);
				entry.timer.unref?.();
			}
			return true;
		},
		generation(state) {
			return generations.get(state) ?? 0;
		},
		flush(state) {
			const entry = entries.get(state);
			if (entry) {
				if (entry.timer) params.clearTimeoutFn(entry.timer);
				publish(state, entry);
			}
		},
		clear,
		dispose() {
			for (const state of entries.keys()) clear(state);
		}
	};
}
//#endregion
//#region src/gateway/session-observer.ts
const observerLog = createSubsystemLogger("gateway/session-observer");
const MIN_NOTES_PER_DIGEST = 4;
const MIN_DIGEST_INTERVAL_MS = 12e3;
const MAX_DIGESTS_PER_RUN = 40;
const MAX_LIVE_DIGESTS_PER_RUN = 39;
const MAX_CONSECUTIVE_FAILURES = 2;
const FINAL_DIGEST_MIN_RUN_MS = 3e4;
const MAX_CONCURRENT_MODEL_SESSIONS = 6;
function createSessionObserver(deps) {
	const now = deps.now ?? Date.now;
	const setTimeoutFn = deps.setTimeoutFn ?? setTimeout;
	const clearTimeoutFn = deps.clearTimeoutFn ?? clearTimeout;
	const resolveUtilityModelRef = deps.resolveUtilityModelRef ?? resolveUtilityModelRefForAgent;
	const prepareModel = deps.prepareModel ?? defaultPrepareModel;
	const completeModel = deps.completeModel ?? defaultCompleteModel;
	const readSession = deps.readSession ?? defaultReadSession;
	const persistDigest = deps.persistDigest ?? defaultPersistDigest;
	const states = /* @__PURE__ */ new Map();
	const dormantRuns = /* @__PURE__ */ new Map();
	const revisionFloors = /* @__PURE__ */ new Map();
	const supersededRuns = /* @__PURE__ */ new Map();
	const contextlessTerminalRuns = /* @__PURE__ */ new Map();
	const terminalRuns = /* @__PURE__ */ new Map();
	const pendingTerminalErrors = /* @__PURE__ */ new Map();
	const disabledRuns = /* @__PURE__ */ new Set();
	const visibleConnections = /* @__PURE__ */ new Set();
	let disposed = false;
	const clearPendingTerminalError = (runId) => {
		clearTimeoutFn(pendingTerminalErrors.get(runId));
		pendingTerminalErrors.delete(runId);
	};
	const getCompanionSnapshot = createSessionObserverCompanionSnapshotReader({
		getConfig: deps.getConfig,
		readSession,
		states
	});
	const audience = createSessionObserverAudience({
		subscribers: deps.subscribers,
		sessionEventSubscribers: deps.sessionEventSubscribers,
		isVisible: (connId) => visibleConnections.has(connId),
		getConfig: deps.getConfig
	});
	const broadcastDigest = (digest, connIds, agentId) => deps.broadcastToConnIds("session.observer", digest, connIds, audience.deliveryOptions(digest.sessionKey, agentId));
	const runStillCurrent = (runId, sessionKey, agentId) => () => !disposed && !supersededRuns.has(runId) && (states.get(resolveSessionSubscriptionKey(sessionKey, agentId))?.runId ?? runId) === runId;
	const persistAcceptedDigest = createSessionObserverDigestPersister({
		now,
		persistDigest,
		stillCurrent: runStillCurrent,
		onMissingEntry: (state) => {
			disableModelForRun(state);
		},
		onError: (state, error) => observerLog.warn("session observer digest persistence failed", {
			sessionKey: state.sessionKey,
			runId: state.runId,
			error: formatErrorMessage(error)
		})
	});
	const preamblePublisher = createSessionObserverPreamblePublisher({
		now,
		setTimeoutFn,
		clearTimeoutFn,
		isCurrent: (state) => audienceLifecycle.stateIsCurrent(state),
		publish: (state, digest) => {
			broadcastDigest(digest, audience.recipients(state.sessionKey, state.agentId), state.agentId);
			persistAcceptedDigest(state, digest, false, "preamble");
		}
	});
	async function synthesizeTerminalDigest(source) {
		const runId = source.event?.runId ?? source.state?.runId;
		if (!runId) return;
		const dormant = dormantRuns.get(runId);
		const sessionKey = source.event?.sessionKey ?? source.state?.sessionKey ?? dormant?.sessionKey;
		const agentId = source.event?.agentId ?? source.state?.agentId ?? dormant?.agentId;
		if (!sessionKey || !agentId) return;
		const stillCurrent = runStillCurrent(runId, sessionKey, agentId);
		if (!stillCurrent()) return;
		try {
			const digest = await synthesizeSessionObserverTerminalDigest({
				source,
				dormant,
				readSession,
				persistDigest,
				now,
				stillCurrent
			});
			if (digest && stillCurrent()) broadcastDigest(digest, audience.recipients(digest.sessionKey, agentId), agentId);
		} catch (error) {
			observerLog.warn("session observer terminal digest synthesis failed", {
				runId,
				error: formatErrorMessage(error)
			});
		}
	}
	const stateIsTracked = (state) => states.get(resolveSessionSubscriptionKey(state.sessionKey, state.agentId)) === state;
	const dropState = (state) => {
		preamblePublisher.clear(state);
		if (state.timer) clearTimeoutFn(state.timer);
		modelSlots.invalidateRequest(state);
		if (stateIsTracked(state)) {
			const scopeKey = resolveSessionSubscriptionKey(state.sessionKey, state.agentId);
			if (state.terminalHealth === "failed" && !terminalRuns.has(state.runId) && state.previousDigest) rememberSessionObserverRevisionFloor(revisionFloors, scopeKey, {
				revision: state.revision,
				previousDigest: state.previousDigest
			});
			states.delete(scopeKey);
		}
	};
	const retireTerminalState = (state) => {
		synthesizeTerminalDigest({ state });
		dormantRuns.delete(state.runId);
		dropState(state);
	};
	const suspendState = (state) => {
		if (state.terminalHealth) {
			retireTerminalState(state);
			return;
		}
		rememberSessionObserverDormantRun(dormantRuns, revisionFloors, createDormantSessionObserverRun(state));
		dropState(state);
	};
	const retireInactiveState = (state) => (disposed ? dropState : suspendState)(state);
	const demoteUtilityModel = (state) => {
		if (state.timer) {
			clearTimeoutFn(state.timer);
			state.timer = void 0;
		}
		modelSlots.invalidateRequest(state);
		state.preparedPromise = void 0;
		state.utilityModelRef = void 0;
		state.consecutiveFailures = 0;
	};
	const modelSlots = createSessionObserverModelSlots({
		states,
		maxSessions: MAX_CONCURRENT_MODEL_SESSIONS,
		resolve: (agentId) => resolveUtilityModelRef({
			cfg: deps.getConfig(),
			agentId
		}),
		demote: demoteUtilityModel
	});
	const disableModelForRun = (state) => {
		rememberSessionObserverDisabledRun(disabledRuns, state.runId);
		demoteUtilityModel(state);
	};
	const audienceLifecycle = createSessionObserverAudienceLifecycle({
		audience,
		states,
		subscribers: deps.subscribers,
		isCurrent: (state) => !disposed && stateIsTracked(state) && deps.getConfig().gateway?.controlUi?.sessionObserver !== false,
		resolveUtilityModelRef: (agentId) => resolveUtilityModelRef({
			cfg: deps.getConfig(),
			agentId
		}),
		suspend: suspendState,
		demote: demoteUtilityModel
	});
	const { modelStateIsCurrent } = audienceLifecycle;
	const requestModelDigest = createSessionObserverCompletion({
		getConfig: deps.getConfig,
		prepareModel,
		completeModel,
		setTimeoutFn,
		clearTimeoutFn,
		isCurrent: modelStateIsCurrent
	});
	const schedule = (state, run, observedAudience) => {
		const currentAudience = observedAudience ?? audience.classify(state.sessionKey, state.agentId);
		if (!audienceLifecycle.stateIsCurrent(state, currentAudience)) {
			retireInactiveState(state);
			return;
		}
		if (!modelStateIsCurrent(state, currentAudience) || state.inFlight || state.timer || state.terminalHealth || state.digestCount >= MAX_LIVE_DIGESTS_PER_RUN || (state.notes.at(-4)?.sequence ?? 0) <= state.lastDigestNoteSequence) return;
		const delay = Math.max(0, MIN_DIGEST_INTERVAL_MS - (now() - state.lastRunAt));
		if (delay === 0) {
			run(state, false);
			return;
		}
		state.timer = setTimeoutFn(() => {
			state.timer = void 0;
			run(state, false);
		}, delay);
	};
	const runDigest = (state, final) => {
		const currentAudience = audience.classify(state.sessionKey, state.agentId);
		if (!audienceLifecycle.stateIsCurrent(state, currentAudience)) {
			retireInactiveState(state);
			return;
		}
		if (!modelStateIsCurrent(state, currentAudience)) {
			if (final) retireTerminalState(state);
			return;
		}
		if (state.inFlight) {
			state.finalPending ||= final;
			return;
		}
		const digestLimit = final ? MAX_DIGESTS_PER_RUN : MAX_LIVE_DIGESTS_PER_RUN;
		if (state.digestCount >= digestLimit) return;
		flushSessionActivityAssistantNote(state);
		const selectedNotes = state.notes.filter((note) => note.sequence > state.lastDigestNoteSequence);
		if (!final && selectedNotes.length < MIN_NOTES_PER_DIGEST) return;
		if (!final && now() - state.lastRunAt < MIN_DIGEST_INTERVAL_MS) {
			schedule(state, runDigest);
			return;
		}
		if (state.timer) {
			clearTimeoutFn(state.timer);
			state.timer = void 0;
		}
		state.inFlight = true;
		state.lastRunAt = now();
		const lastSelectedSequence = selectedNotes.at(-1)?.sequence ?? state.lastDigestNoteSequence;
		const retireSelectedNotes = () => {
			state.lastDigestNoteSequence = Math.max(state.lastDigestNoteSequence, lastSelectedSequence);
		};
		const requestGeneration = modelSlots.beginRequest(state);
		const digestIsStale = () => !modelStateIsCurrent(state) || !modelSlots.requestIsCurrent(state, requestGeneration) || !final && state.terminalHealth !== void 0;
		state.digestCount += 1;
		(async () => {
			try {
				const modelDigest = await requestModelDigest(state, selectedNotes.map((note) => note.text));
				if (digestIsStale()) {
					retireSelectedNotes();
					if (final && stateIsTracked(state)) retireTerminalState(state);
					return;
				}
				if (state.sessionId && readSession(state.sessionKey, state.agentId)?.sessionId !== state.sessionId) return disableModelForRun(state);
				preamblePublisher.clear(state);
				state.consecutiveFailures = 0;
				state.revision += 1;
				retireSelectedNotes();
				const digest = {
					sessionKey: state.sessionKey,
					agentId: state.agentId,
					runId: state.runId,
					revision: state.revision,
					updatedAt: now(),
					headline: modelDigest.headline,
					...modelDigest.assessment ? { assessment: modelDigest.assessment } : {},
					health: final ? state.terminalHealth ?? modelDigest.health : modelDigest.health,
					...state.planProgress ?? modelDigest.planProgress ? { planProgress: state.planProgress ?? modelDigest.planProgress } : {}
				};
				const previous = state.previousDigest?.health;
				const next = digest.health;
				const criticalTransition = (next === "stuck" || next === "waiting-on-user") && previous !== next;
				state.previousDigest = digest;
				const recipients = criticalTransition ? audience.criticalRecipients(state.sessionKey, state.agentId) : audience.recipients(state.sessionKey, state.agentId);
				broadcastDigest(digest, recipients, state.agentId);
				await persistAcceptedDigest(state, digest, final);
				if (final) dormantRuns.delete(state.runId);
			} catch (error) {
				if (digestIsStale()) {
					retireSelectedNotes();
					if (final && stateIsTracked(state)) retireTerminalState(state);
					return;
				}
				state.consecutiveFailures += 1;
				if (state.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
					observerLog.warn("session observer disabled after consecutive failures", {
						sessionKey: state.sessionKey,
						runId: state.runId,
						error: formatErrorMessage(error)
					});
					if (final || state.finalPending || state.terminalHealth) retireTerminalState(state);
					else disableModelForRun(state);
				} else if (final) state.finalPending = true;
			} finally {
				if (stateIsTracked(state)) {
					state.inFlight = false;
					const runFinal = state.finalPending;
					state.finalPending = false;
					if (runFinal) runDigest(state, true);
					else if (final) dropState(state);
					else schedule(state, runDigest);
				}
			}
		})();
	};
	const admitState = (event, allowPreambleOnly, sessionKey, agentId, observedAudience) => {
		if (observedAudience === "none") return;
		const scopeKey = resolveSessionSubscriptionKey(sessionKey, agentId);
		if (deps.getConfig().gateway?.controlUi?.sessionObserver === false) return;
		const utilityModelRef = disabledRuns.has(event.runId) || observedAudience !== "direct" ? void 0 : modelSlots.claim(agentId);
		if (!utilityModelRef && !allowPreambleOnly) return;
		const dormant = dormantRuns.get(event.runId);
		if (dormant) {
			dormantRuns.delete(event.runId);
			const { utilityModelRef: _dormantModelRef, ...dormantState } = dormant;
			const state = {
				...createSessionActivityNoteState(),
				...dormantState,
				...dormantState.lastPreambleHeadline ? { lastPublishedPreambleHeadline: dormantState.lastPreambleHeadline } : {},
				...utilityModelRef ? { utilityModelRef } : {},
				lastActivityAt: event.ts,
				lastRunAt: now(),
				lastDigestNoteSequence: 0,
				inFlight: false,
				finalPending: false
			};
			states.set(scopeKey, state);
			return state;
		}
		const session = readSession(sessionKey, agentId);
		const startedAt = asFiniteNumber(event.data.startedAt) ?? session?.startedAt ?? event.ts ?? now();
		const state = {
			...createSessionActivityNoteState(),
			sessionKey,
			sessionId: event.sessionId ?? session?.sessionId,
			runId: event.runId,
			agentId,
			...utilityModelRef ? { utilityModelRef } : {},
			startedAt,
			lastActivityAt: event.ts,
			lastRunAt: startedAt,
			lastPersistedAt: session?.observerDigest?.updatedAt,
			revision: session?.observerDigest?.revision ?? 0,
			digestCount: 0,
			consecutiveFailures: 0,
			lastDigestNoteSequence: 0,
			previousDigest: session?.observerDigest,
			inFlight: false,
			finalPending: false
		};
		states.set(scopeKey, state);
		return state;
	};
	const handleEvent = (event, settledError = false) => {
		if (disposed || getAgentRunContext(event.runId)?.isHeartbeat) return;
		const lifecyclePhase = event.stream === "lifecycle" ? event.data.phase : void 0;
		const terminal = settledError || isDefinitiveRunLifecycle({
			phase: lifecyclePhase,
			data: event.data
		});
		if (lifecyclePhase === "error" && !terminal) {
			clearPendingTerminalError(event.runId);
			const timer = setTimeoutFn(() => handleEvent(event, true), AGENT_RUN_TERMINAL_RETRY_GRACE_MS);
			pendingTerminalErrors.set(event.runId, timer);
			return;
		}
		if (terminal || lifecyclePhase === "start") clearPendingTerminalError(event.runId);
		if (terminalRuns.has(event.runId)) return;
		if (supersededRuns.has(event.runId)) {
			if (terminal) {
				markSessionObserverRunSuperseded(terminalRuns, event.runId, event.ts);
				contextlessTerminalRuns.delete(event.runId);
				supersededRuns.delete(event.runId);
				dormantRuns.delete(event.runId);
				disabledRuns.delete(event.runId);
			}
			return;
		}
		if (contextlessTerminalRuns.has(event.runId) && !terminal) return;
		const eventSessionKey = event.sessionKey?.trim();
		const eventAgentId = event.agentId?.trim();
		let knownRun;
		if (terminal && (!eventSessionKey || !eventAgentId)) {
			for (const candidate of states.values()) if (candidate.runId === event.runId) {
				knownRun = candidate;
				break;
			}
			knownRun ??= dormantRuns.get(event.runId);
		}
		const sessionKey = eventSessionKey || knownRun?.sessionKey;
		if (!sessionKey) {
			if (terminal) markSessionObserverRunSuperseded(contextlessTerminalRuns, event.runId, event.ts);
			return;
		}
		const agentId = eventAgentId || knownRun?.agentId;
		if (terminal) {
			contextlessTerminalRuns.delete(event.runId);
			if (!settledError) markSessionObserverRunSuperseded(terminalRuns, event.runId, event.ts);
		}
		const isPreamble = event.stream === "item" && event.data.kind === "preamble";
		if (!agentId) {
			if (terminal) {
				synthesizeTerminalDigest({ event });
				dormantRuns.delete(event.runId);
				disabledRuns.delete(event.runId);
			}
			return;
		}
		const currentAudience = audience.classify(sessionKey, agentId);
		const scopeKey = resolveSessionSubscriptionKey(sessionKey, agentId);
		if (terminal && audience.recipients(sessionKey, agentId).size === 0) {
			synthesizeTerminalDigest({
				event,
				state: states.get(scopeKey)
			});
			dormantRuns.delete(event.runId);
			disabledRuns.delete(event.runId);
			return;
		}
		const isRunStart = event.stream === "lifecycle" && event.data.phase === "start";
		let revisionFloor = revisionFloors.get(scopeKey);
		let state = states.get(scopeKey);
		if (state && state.runId !== event.runId) {
			const candidate = {
				revision: state.revision,
				previousDigest: state.previousDigest
			};
			if (!revisionFloor || candidate.revision > revisionFloor.revision) revisionFloor = candidate;
			const supersededRunId = state.runId;
			clearPendingTerminalError(supersededRunId);
			if (isRunStart) markSessionObserverRunSuperseded(supersededRuns, supersededRunId, event.ts);
			suspendState(state);
			if (isRunStart) dormantRuns.delete(supersededRunId);
			state = void 0;
		}
		if (!state) {
			const superseded = [...dormantRuns.values()].filter((run) => resolveSessionSubscriptionKey(run.sessionKey, run.agentId) === scopeKey && run.runId !== event.runId).toSorted((left, right) => right.revision - left.revision || left.runId.localeCompare(right.runId));
			const latest = superseded[0];
			if (latest && (!revisionFloor || latest.revision > revisionFloor.revision)) revisionFloor = {
				revision: latest.revision,
				previousDigest: latest.previousDigest
			};
			if (isRunStart) {
				if (revisionFloor) {
					rememberSessionObserverRevisionFloor(revisionFloors, scopeKey, revisionFloor);
					const previousRunId = revisionFloor.previousDigest?.runId;
					if (previousRunId && previousRunId !== event.runId) markSessionObserverRunSuperseded(supersededRuns, previousRunId, event.ts);
				}
				for (const run of superseded) {
					markSessionObserverRunSuperseded(supersededRuns, run.runId, event.ts);
					clearPendingTerminalError(run.runId);
					dormantRuns.delete(run.runId);
				}
			}
		}
		if (state && (currentAudience === "none" || deps.getConfig().gateway?.controlUi?.sessionObserver === false)) {
			suspendState(state);
			state = void 0;
		}
		if (!state) state = admitState(event, isPreamble, sessionKey, agentId, currentAudience);
		if (!state) {
			if (terminal) {
				synthesizeTerminalDigest({ event });
				dormantRuns.delete(event.runId);
				disabledRuns.delete(event.runId);
			}
			return;
		}
		if (state.terminalHealth) return;
		if (revisionFloor && revisionFloor.revision > state.revision) {
			state.revision = revisionFloor.revision;
			state.previousDigest = revisionFloor.previousDigest;
		}
		revisionFloors.delete(scopeKey);
		const utilityModelRef = disabledRuns.has(state.runId) || currentAudience !== "direct" ? void 0 : modelSlots.claim(state.agentId, state);
		if (state.utilityModelRef !== utilityModelRef) {
			modelSlots.invalidateRequest(state);
			state.preparedPromise = void 0;
			state.utilityModelRef = utilityModelRef;
			state.consecutiveFailures = 0;
		}
		state.lastActivityAt = event.ts;
		const eventStartedAt = asFiniteNumber(event.data.startedAt);
		if (eventStartedAt !== void 0) state.startedAt = Math.min(state.startedAt, eventStartedAt);
		noteSessionActivityEvent(state, event);
		preamblePublisher.handle(state, event);
		if (terminal) {
			if (!state.terminalHealth) modelSlots.invalidateRequest(state);
			preamblePublisher.flush(state);
			preamblePublisher.clear(state);
			state.terminalHealth = terminalHealthFor(event);
			disabledRuns.delete(event.runId);
			const endedAt = asFiniteNumber(event.data.endedAt) ?? now();
			if (!(state.previousDigest?.runId === state.runId) && endedAt - state.startedAt < FINAL_DIGEST_MIN_RUN_MS) {
				dormantRuns.delete(state.runId);
				dropState(state);
				return;
			}
			runDigest(state, true);
			return;
		}
		schedule(state, runDigest, currentAudience);
	};
	return {
		handleEvent,
		setConnectionVisibility(connId, visible) {
			if (visible) {
				visibleConnections.add(connId);
				return;
			}
			visibleConnections.delete(connId);
			audienceLifecycle.reconcileAll();
		},
		removeConnection(connId) {
			if (visibleConnections.delete(connId)) audienceLifecycle.reconcileAll();
		},
		getCompanionSnapshot,
		dispose() {
			disposed = true;
			pendingTerminalErrors.forEach((_timer, runId) => clearPendingTerminalError(runId));
			preamblePublisher.dispose();
			audienceLifecycle.unsubscribe();
			for (const state of states.values()) dropState(state);
			dormantRuns.clear();
			revisionFloors.clear();
			supersededRuns.clear();
			terminalRuns.clear();
			contextlessTerminalRuns.clear();
			disabledRuns.clear();
			visibleConnections.clear();
		}
	};
}
//#endregion
//#region src/gateway/server-runtime-subscriptions.ts
function dispatchEventHandler(params) {
	return params.loadHandler().then((handler) => handler(params.event)).then(() => void 0).catch((error) => {
		params.log.warn(params.failureMessage, {
			...params.context,
			error
		});
		params.onFailure?.();
	});
}
function terminalTaskId(event) {
	if (event.kind !== "upserted" || !isTerminalTaskStatus(event.task.status)) return;
	if (event.previous && isTerminalTaskStatus(event.previous.status)) return;
	return event.task.taskId;
}
/** Register gateway runtime event subscriptions and return unsubscribe handles. */
function startGatewayEventSubscriptions(params) {
	const runtimeConfig = getRuntimeConfig();
	const auditEnabled = isAuditLedgerEnabled(runtimeConfig);
	const auditMessageMode = resolveAuditMessageMode(runtimeConfig);
	const auditRecorder = createAuditEventRecorder({ messageMode: auditEnabled ? auditMessageMode : "off" });
	const clearExecutionIdentityAdmissionSink = configureExecutionIdentityAdmissionSink(auditRecorder.recordExecutionIdentity);
	const clearExecutionDecisionWorkSink = configureExecutionDecisionWorkSink(auditRecorder.recordExecutionDecisionWork);
	const clearChannelAdmissionEvidenceCollection = configureChannelAdmissionEvidenceCollection(isExecutionIdentityCollectionEnabled(runtimeConfig));
	const clearChannelAdmissionDecisionSink = configureChannelAdmissionDecisionSink(auditRecorder.recordExecutionDecision);
	const clearMessageActionDecisionSink = configureMessageActionDecisionSink(auditRecorder.recordExecutionDecision);
	const clearRuntimeActionDecisionSink = configureRuntimeActionDecisionSink(auditRecorder.recordExecutionDecision);
	const sessionObserver = createSessionObserver({
		getConfig: getRuntimeConfig,
		subscribers: params.sessionMessageSubscribers,
		sessionEventSubscribers: params.sessionEventSubscribers,
		broadcastToConnIds: params.broadcastToConnIds
	});
	const sessionCompanion = createSessionCompanion({
		contextReader: defaultSessionCompanionContextReader,
		getConfig: getRuntimeConfig,
		sessionObserver
	});
	const unsubscribePrivateAuditEvents = auditEnabled ? onAgentAuditEvent(auditRecorder.record) : void 0;
	const unsubscribeToolAuditEvents = auditEnabled ? onTrustedToolExecutionEvent(auditRecorder.recordTool) : void 0;
	const unsubscribeMessageAuditEvents = auditEnabled && auditMessageMode !== "off" ? onTrustedMessageAuditEvent(auditRecorder.recordMessage) : void 0;
	const sessionLifecyclePersistence = createSessionLifecyclePersistenceOwner();
	const agentEventDispatches = /* @__PURE__ */ new Set();
	const trackedRunIds = (runId, clientRunId) => runId === clientRunId ? [runId] : [runId, clientRunId];
	const clearTrackedActiveRun = (run) => {
		for (const candidateRunId of trackedRunIds(run.runId, run.clientRunId)) {
			const entry = params.chatAbortControllers.get(candidateRunId);
			if (!entry) continue;
			entry.projectSessionActive = false;
			entry.projectSessionTerminalPersisted = false;
			markChatAbortTerminalPersistenceError(entry, void 0);
			queueMicrotask(() => {
				if (params.chatAbortControllers.get(candidateRunId) === entry && entry.registrationCleanupRequested === true && !entry.projectSessionTerminalPersistence) removeChatAbortControllerEntry(params.chatAbortControllers, candidateRunId, entry);
			});
		}
	};
	const settleTrackedTerminal = (run) => {
		const persisted = run.persisted ?? true;
		for (const candidateRunId of trackedRunIds(run.runId, run.clientRunId)) {
			const entry = params.chatAbortControllers.get(candidateRunId);
			if (!entry) continue;
			if (persisted) {
				params.restartRecoveryCandidates.delete(candidateRunId);
				markChatAbortTerminalPersistenceError(entry, void 0);
			}
			entry.projectSessionTerminalPending = false;
			entry.projectSessionTerminalPersistence = void 0;
			entry.projectSessionTerminalPersisted = persisted;
			if (entry.registrationCleanupRequested === true) removeChatAbortControllerEntry(params.chatAbortControllers, candidateRunId, entry);
		}
	};
	const trackTrackedRunTerminalPersistence = (run) => {
		let tracked = false;
		for (const candidateRunId of trackedRunIds(run.runId, run.clientRunId)) {
			const entry = params.chatAbortControllers.get(candidateRunId);
			if (!entry) continue;
			tracked = true;
			entry.projectSessionTerminalPersistence = run.persistence;
			run.persistence.catch((error) => {
				markChatAbortTerminalPersistenceError(entry, error);
			});
			const lifecycleGeneration = entry.lifecycleGeneration?.trim();
			const sessionKey = entry.sessionKey.trim();
			const sessionId = run.sessionId?.trim() || entry.sessionId.trim();
			const observedAt = entry.projectSessionTerminalObservedAt;
			if (entry.controlUiVisible !== false && lifecycleGeneration && sessionKey && sessionId) run.persistence.catch(() => {
				params.restartRecoveryCandidates.set(candidateRunId, {
					runId: candidateRunId,
					lifecycleGeneration,
					sessionKey,
					sessionId,
					observedAt
				});
			});
		}
		return tracked;
	};
	const getSessionKeyModule = createLazyPromise(() => import("./server-session-key-5JJwsoGu.js"), { cacheRejections: true });
	const agentEventHandlerLoader = createLazyPromiseLoader(() => {
		return Promise.all([import("./server-chat-DhlqkrkS.js"), getSessionKeyModule()]).then(([{ createAgentEventHandler }, { resolveSessionKeyForRun }]) => createAgentEventHandler({
			broadcast: params.broadcast,
			broadcastToConnIds: params.broadcastToConnIds,
			nodeSendToSession: params.nodeSendToSession,
			agentRunSeq: params.agentRunSeq,
			chatRunState: params.chatRunState,
			resolveSessionKeyForRun,
			clearAgentRunContext,
			toolEventRecipients: params.toolEventRecipients,
			sessionEventSubscribers: params.sessionEventSubscribers,
			sessionMessageSubscribers: params.sessionMessageSubscribers,
			persistGatewaySessionLifecycleEventForEvent: sessionLifecyclePersistence.persist,
			updateRunToolErrorSummary: ({ runId, clientRunId, summary }) => {
				for (const candidateRunId of /* @__PURE__ */ new Set([runId, clientRunId])) {
					const entry = params.chatAbortControllers.get(candidateRunId);
					if (entry) entry.toolErrorSummary = summary;
				}
			},
			clearTrackedActiveRun,
			settleTrackedTerminal,
			trackTrackedRunTerminalPersistence,
			isChatSendRunActive: (runId) => {
				const entry = params.chatAbortControllers.get(runId);
				return entry !== void 0 && entry.kind !== "agent";
			},
			resolveActiveLifecycleGenerationForRun: (runId) => params.chatAbortControllers.get(runId)?.lifecycleGeneration,
			resolveSessionActiveRunState: (session) => resolveVisibleActiveSessionRunState({
				context: params,
				...session,
				defaultAgentId: tryResolveSessionCompatibilityOwnerAgentId(getRuntimeConfig(), session.requestedKey)
			})
		}));
	}, { cacheRejections: true });
	const getAgentEventHandler = agentEventHandlerLoader.load;
	const getSessionEventsModule = createLazyPromise(() => import("./server-session-events-nntFoRej.js"), { cacheRejections: true });
	let transcriptUpdateHandlerPromise = null;
	const getTranscriptUpdateHandler = () => {
		transcriptUpdateHandlerPromise ??= getSessionEventsModule().then(({ createTranscriptUpdateBroadcastHandler }) => createTranscriptUpdateBroadcastHandler({
			broadcastToConnIds: params.broadcastToConnIds,
			sessionEventSubscribers: params.sessionEventSubscribers,
			sessionMessageSubscribers: params.sessionMessageSubscribers,
			chatAbortControllers: params.chatAbortControllers
		}));
		return transcriptUpdateHandlerPromise;
	};
	let lifecycleEventHandlerPromise = null;
	const getLifecycleEventHandler = () => {
		lifecycleEventHandlerPromise ??= getSessionEventsModule().then(({ createLifecycleEventBroadcastHandler }) => createLifecycleEventBroadcastHandler({
			broadcastToConnIds: params.broadcastToConnIds,
			sessionEventSubscribers: params.sessionEventSubscribers,
			chatAbortControllers: params.chatAbortControllers
		}));
		return lifecycleEventHandlerPromise;
	};
	const unsubscribeAgentEvents = onAgentRuntimeEvent((evt) => {
		let failedDispatchCleanup;
		let terminalPreparation;
		sessionObserver.handleEvent(evt);
		if (auditEnabled) auditRecorder.record(evt);
		const lifecyclePhase = evt.stream === "lifecycle" && typeof evt.data?.phase === "string" ? evt.data.phase : void 0;
		if (lifecyclePhase === "end" || lifecyclePhase === "error") {
			const clientRunId = (evt.contextClaimId ? void 0 : params.chatRunState.registry.peek(evt.runId))?.clientRunId ?? evt.runId;
			const candidateRunIds = evt.runId === clientRunId ? [evt.runId] : [evt.runId, clientRunId];
			const observedAt = typeof evt.data.endedAt === "number" && Number.isFinite(evt.data.endedAt) ? evt.data.endedAt : evt.ts;
			for (const candidateRunId of candidateRunIds) {
				const entry = params.chatAbortControllers.get(candidateRunId);
				const eventLifecycleGeneration = evt.lifecycleGeneration?.trim();
				if (entry && (!eventLifecycleGeneration || !entry.lifecycleGeneration || entry.lifecycleGeneration === eventLifecycleGeneration)) {
					entry.projectSessionTerminalPending = true;
					entry.projectSessionTerminalObservedAt = observedAt;
				}
			}
			const trackedEntry = candidateRunIds.map((candidateRunId) => params.chatAbortControllers.get(candidateRunId)).find((entry) => entry !== void 0);
			const runContext = getAgentRunContext(evt.runId);
			const sessionAgentId = trackedEntry?.agentId ?? evt.agentId ?? runContext?.agentId;
			const knownSessionKey = evt.deliverySessionKey ?? evt.sessionKey ?? trackedEntry?.sessionKey ?? runContext?.sessionKey;
			const eventLifecycleGeneration = evt.lifecycleGeneration?.trim();
			const terminalAuthority = evt.contextClaimId && eventLifecycleGeneration ? {
				claimId: evt.contextClaimId,
				lifecycleGeneration: eventLifecycleGeneration,
				runId: evt.runId
			} : void 0;
			const trackedOwnerIsCurrent = !trackedEntry || !eventLifecycleGeneration || !trackedEntry.lifecycleGeneration || trackedEntry.lifecycleGeneration === eventLifecycleGeneration;
			const claimIsComplete = !evt.contextClaimId || terminalAuthority !== void 0;
			const canPersistTerminal = isDefinitiveRunLifecycle({
				phase: lifecyclePhase,
				data: evt.data
			}) && evt.projectSessionLifecycle !== false && trackedOwnerIsCurrent && claimIsComplete;
			const prepareTerminalPersistence = (sessionKey) => {
				const persistence = sessionLifecyclePersistence.observe({
					sessionKey,
					...sessionAgentId ? { agentId: sessionAgentId } : {},
					event: evt,
					...terminalAuthority ? { authority: terminalAuthority } : {},
					...clientRunId !== evt.runId ? { clientRunId } : {}
				});
				if (terminalAuthority) {
					const clearTerminalAuthority = () => clearAgentRunContext(terminalAuthority.runId, terminalAuthority.lifecycleGeneration, terminalAuthority.claimId);
					failedDispatchCleanup = () => {
						persistence.then(clearTerminalAuthority, clearTerminalAuthority);
					};
				}
				clearTrackedActiveRun({
					runId: evt.runId,
					clientRunId
				});
				if (!trackTrackedRunTerminalPersistence({
					runId: evt.runId,
					clientRunId,
					sessionId: evt.sessionId,
					persistence
				})) persistence.catch((error) => {
					params.log.warn("Terminal session persistence failed", {
						runId: evt.runId,
						error
					});
				});
				persistence.then(() => settleTrackedTerminal({
					runId: evt.runId,
					clientRunId
				}), () => settleTrackedTerminal({
					runId: evt.runId,
					clientRunId,
					persisted: false
				}));
			};
			if (canPersistTerminal) {
				if (knownSessionKey) prepareTerminalPersistence(knownSessionKey);
				else terminalPreparation = getSessionKeyModule().then(({ resolveSessionKeyForRun }) => {
					const sessionKey = resolveSessionKeyForRun(evt.runId, sessionAgentId ? { agentId: sessionAgentId } : void 0);
					if (sessionKey) prepareTerminalPersistence(sessionKey);
				});
			}
		} else if (lifecyclePhase === "start") {
			const clientRunId = (evt.contextClaimId ? void 0 : params.chatRunState.registry.peek(evt.runId))?.clientRunId ?? evt.runId;
			const candidateRunIds = evt.runId === clientRunId ? [evt.runId] : [evt.runId, clientRunId];
			const eventLifecycleGeneration = evt.lifecycleGeneration?.trim();
			for (const candidateRunId of candidateRunIds) {
				const entry = params.chatAbortControllers.get(candidateRunId);
				if (entry && (!eventLifecycleGeneration || !entry.lifecycleGeneration || entry.lifecycleGeneration === eventLifecycleGeneration)) {
					entry.projectSessionTerminalPending = false;
					entry.projectSessionTerminalObservedAt = void 0;
				}
			}
		}
		const dispatchPreparation = terminalPreparation;
		const dispatch = dispatchEventHandler({
			loadHandler: dispatchPreparation ? () => dispatchPreparation.then(() => getAgentEventHandler()) : getAgentEventHandler,
			event: evt,
			log: params.log,
			failureMessage: "Agent event dispatch failed",
			context: {
				runId: evt.runId,
				stream: evt.stream
			},
			onFailure: () => failedDispatchCleanup?.()
		});
		agentEventDispatches.add(dispatch);
		dispatch.then(() => agentEventDispatches.delete(dispatch));
	});
	const agentUnsub = async () => {
		unsubscribeAgentEvents();
		sessionCompanion.dispose();
		sessionObserver.dispose();
		unsubscribePrivateAuditEvents?.();
		unsubscribeToolAuditEvents?.();
		unsubscribeMessageAuditEvents?.();
		clearExecutionDecisionWorkSink();
		clearExecutionIdentityAdmissionSink();
		clearChannelAdmissionEvidenceCollection();
		clearChannelAdmissionDecisionSink();
		clearMessageActionDecisionSink();
		clearRuntimeActionDecisionSink();
		await Promise.allSettled(agentEventDispatches);
		await agentEventHandlerLoader.peek()?.then((handler) => handler.dispose()).catch(() => void 0);
		await sessionLifecyclePersistence.drain();
		await auditRecorder.stop();
	};
	const heartbeatUnsub = onHeartbeatEvent((evt) => {
		params.broadcast("heartbeat", evt, { dropIfSlow: true });
	});
	const transcriptUnsub = onInternalSessionTranscriptUpdate((evt) => {
		dispatchEventHandler({
			loadHandler: getTranscriptUpdateHandler,
			event: evt,
			log: params.log,
			failureMessage: "Transcript update dispatch failed",
			context: { sessionKey: evt.sessionKey }
		});
	});
	const unsubscribeProfileChanges = onUserProfilesChanged(() => {
		params.broadcastToConnIds("sessions.changed", { reason: "profile-identity" }, params.sessionEventSubscribers.getAll());
	});
	const unsubscribeLifecycle = onSessionLifecycleEvent((evt) => {
		dispatchEventHandler({
			loadHandler: getLifecycleEventHandler,
			event: evt,
			log: params.log,
			failureMessage: "Lifecycle event dispatch failed",
			context: { sessionKey: evt.sessionKey }
		});
	});
	const unsubscribeSuspension = onGatewaySuspendAdmissionChange((phase) => {
		params.broadcast("gateway.suspension", { phase });
	});
	const lifecycleUnsub = () => {
		unsubscribeSuspension();
		unsubscribeProfileChanges();
		unsubscribeLifecycle();
	};
	let taskObserverDisposed = false;
	const lastTaskSummaryById = /* @__PURE__ */ new Map();
	const taskObservers = { onEvent: (event) => {
		let payload;
		let sessionTarget;
		switch (event.kind) {
			case "upserted": {
				const task = mapTaskSummary(event.task);
				const summary = JSON.stringify(task);
				if (lastTaskSummaryById.get(task.id) === summary) return;
				lastTaskSummaryById.set(task.id, summary);
				payload = {
					action: "upserted",
					task
				};
				sessionTarget = resolveTaskRequesterSessionTarget(event.task);
				break;
			}
			case "deleted":
				lastTaskSummaryById.delete(event.taskId);
				payload = {
					action: "deleted",
					taskId: event.taskId
				};
				sessionTarget = resolveTaskRequesterSessionTarget(event.previous);
				break;
			case "restored":
				lastTaskSummaryById.clear();
				payload = { action: "restored" };
		}
		params.broadcast("task", payload, {
			dropIfSlow: true,
			...sessionTarget ? {
				sessionKeys: [sessionTarget.sessionKey],
				agentId: sessionTarget.agentId
			} : {}
		});
		const taskId = terminalTaskId(event);
		if (taskId) params.terminalSessions.closeTaskSessions(taskId);
	} };
	const taskObserverRuntimePromise = import("./task-registry.store-C7GsX6ua.js").then((module) => {
		if (!taskObserverDisposed) module.configureTaskRegistryRuntime({ observers: taskObservers });
		return module;
	});
	taskObserverRuntimePromise.catch((error) => {
		params.log.warn("Task registry observer registration failed", { error });
	});
	const taskUnsub = () => {
		taskObserverDisposed = true;
		return taskObserverRuntimePromise.then((module) => {
			if (module.getTaskRegistryObservers() === taskObservers) module.configureTaskRegistryRuntime({ observers: null });
		}).catch(() => void 0);
	};
	return {
		sessionCompanion,
		sessionObserver,
		agentUnsub,
		heartbeatUnsub,
		transcriptUnsub,
		lifecycleUnsub,
		taskUnsub
	};
}
//#endregion
export { startGatewayEventSubscriptions };