UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

154 lines (153 loc) 7.7 kB
import { o as asDateTimestampMs } from "./number-coercion-CLj0HTDM.js"; import { f as resolveAgentIdFromSessionKey } from "./session-key-BnWWjqNc.js"; import { n as SESSION_WORK_START_INVALIDATED_ERROR_CODE, t as SESSION_WORK_START_CHANGED_ERROR_CODE } from "./work-start-error-Dt2kfIXb.js"; import "./session-accessor-YsytfDtG.js"; import { C as loadTranscriptHeaderSync, M as readTranscriptStatsSync } from "./session-accessor.sqlite-transcript-store-B0zw3fAU.js"; import { n as isTerminalSessionStatus } from "./types-BHV0IaPK.js"; import { n as canonicalizeMainSessionAlias } from "./main-session-Br0F9dzh.js"; //#region src/config/sessions/lifecycle.ts function isRestartRecoveryTombstone(entry) { return entry?.mainRestartRecovery?.tombstone !== void 0; } /** Stable Gateway error detail for stale session lifecycle requests. */ const SESSION_LIFECYCLE_CHANGED_ERROR_REASON = "session-changed"; const SESSION_RESTART_RECOVERY_TOMBSTONE_ERROR_CODE = "SESSION_RESTART_RECOVERY_TOMBSTONE"; var SessionWorkStartInvalidatedError = class extends Error { constructor(message) { super(message); this.code = SESSION_WORK_START_INVALIDATED_ERROR_CODE; this.name = "SessionWorkStartInvalidatedError"; } }; var SessionWorkStartChangedError = class extends Error { constructor(message) { super(message); this.code = SESSION_WORK_START_CHANGED_ERROR_CODE; this.name = "SessionWorkStartChangedError"; } }; function createSessionWorkStartChangedError(sessionKey) { return new SessionWorkStartChangedError(`Session "${sessionKey}" changed while starting work. Retry.`); } function isSessionWorkStartInvalidatedError(error) { return error instanceof SessionWorkStartInvalidatedError || error instanceof SessionWorkStartChangedError || typeof error === "object" && error !== null && "code" in error && (error.code === "SESSION_WORK_START_INVALIDATED" || error.code === "SESSION_WORK_START_CHANGED"); } var SessionRestartRecoveryTombstoneError = class extends Error { constructor(message) { super(message); this.code = SESSION_RESTART_RECOVERY_TOMBSTONE_ERROR_CODE; this.name = "SessionRestartRecoveryTombstoneError"; } }; /** Lifecycle-owned initializing, restart-tombstoned, and archived sessions reject new work. */ function resolveSessionWorkStartError(sessionKey, entry, options) { if (options?.expectedSessionId && !entry) return `Session "${sessionKey}" was deleted while starting work. Retry.`; if (options?.expectedSessionId && entry?.sessionId !== options.expectedSessionId) return `Session "${sessionKey}" changed while starting work. Retry.`; if (entry?.initializationPending === true) return `Session "${sessionKey}" is still initializing. Retry after initialization completes.`; if (isRestartRecoveryTombstone(entry)) { if (options?.allowRestartTombstoneReplacement === true) return; return entry?.modelSelectionLocked === true ? `Session "${sessionKey}" ended during restart recovery and cannot be replaced while model selection is locked. Open it in WebChat and use Resume in new session.` : `Session "${sessionKey}" ended during restart recovery. Use /new or /reset to start a replacement session.`; } if (entry?.archivedAt !== void 0) return `Session "${sessionKey}" is archived. Restore it before starting new work.`; if (!options?.allowPendingWorkspace && (entry?.pendingProjectGitUrl !== void 0 || entry?.pendingWorktree !== void 0)) return `Session "${sessionKey}" workspace is not ready. Wait for setup to finish or retry in chat.`; } function resolveTimestamp(value) { const timestampMs = asDateTimestampMs(value); return timestampMs !== void 0 && timestampMs >= 0 ? timestampMs : void 0; } function resolvePositiveTimestamp(value) { const timestampMs = resolveTimestamp(value); return timestampMs !== void 0 && timestampMs > 0 ? timestampMs : void 0; } function parseTimestampMs(value) { if (typeof value === "number") return resolveTimestamp(value); if (typeof value !== "string" || !value.trim()) return; return resolveTimestamp(Date.parse(value)); } function readSessionHeaderStartedAtMs(params) { const sessionId = params.entry.sessionId?.trim(); const sessionKey = params.sessionKey?.trim(); const agentId = params.agentId ?? (sessionKey ? resolveAgentIdFromSessionKey(sessionKey) : void 0); if (!sessionId || !agentId) return; try { const header = loadTranscriptHeaderSync({ agentId, sessionId, ...params.storePath ? { storePath: params.storePath } : {}, ...sessionKey ? { sessionKey } : {} }); if (header?.type !== "session" || typeof header.id === "string" && header.id.trim() && header.id !== sessionId) return; return parseTimestampMs(header.timestamp); } catch { return; } } function resolveSessionLifecycleTimestamps(params) { const entry = params.entry; if (!entry) return {}; return { sessionStartedAt: resolveTimestamp(entry.sessionStartedAt) ?? readSessionHeaderStartedAtMs({ ...params, entry }), lastInteractionAt: resolveTimestamp(entry.lastInteractionAt) }; } function resolveTerminalMainSessionTranscriptRegistryCheck(params) { if (!params.entry || !params.sessionKey) return; const configuredMainSessionKey = canonicalizeMainSessionAlias({ cfg: { session: { scope: params.sessionScope, mainKey: params.mainKey } }, agentId: params.agentId, sessionKey: params.mainKey ?? "main" }); if (canonicalizeMainSessionAlias({ cfg: { session: { scope: params.sessionScope, mainKey: params.mainKey } }, agentId: params.agentId, sessionKey: params.sessionKey }) !== configuredMainSessionKey) return; if (!(isTerminalSessionStatus(params.entry.status) || resolvePositiveTimestamp(params.entry.endedAt) !== void 0)) return; if (params.entry.status === "done") return; if (params.entry.status === "failed") return; const registryTimestampMs = resolvePositiveTimestamp(params.entry.updatedAt); if (registryTimestampMs === void 0) return; const sessionId = typeof params.entry.sessionId === "string" ? params.entry.sessionId.trim() : ""; if (!sessionId) return; return { sessionId, registryTimestampMs }; } function isTranscriptMutationNewerThanRegistry(params) { const transcriptMutationAtMs = Math.floor(params.transcriptMutationAtMs); const registryTimestampMs = Math.floor(params.registryTimestampMs); return Number.isFinite(transcriptMutationAtMs) && transcriptMutationAtMs > registryTimestampMs; } function hasTerminalMainSessionTranscriptNewerThanRegistrySync(params) { const check = resolveTerminalMainSessionTranscriptRegistryCheck(params); if (!check) return false; try { const stats = readTranscriptStatsSync({ agentId: params.agentId, sessionId: check.sessionId, storePath: params.storePath }); if (stats.lastMutationAtMs === void 0) return false; return isTranscriptMutationNewerThanRegistry({ transcriptMutationAtMs: stats.lastMutationAtMs, registryTimestampMs: stats.lastObservedMutationAtMs ?? check.registryTimestampMs }); } catch { return false; } } async function hasTerminalMainSessionTranscriptNewerThanRegistry(params) { return hasTerminalMainSessionTranscriptNewerThanRegistrySync(params); } //#endregion export { SessionWorkStartInvalidatedError as a, hasTerminalMainSessionTranscriptNewerThanRegistrySync as c, resolveSessionLifecycleTimestamps as d, resolveSessionWorkStartError as f, SessionWorkStartChangedError as i, isRestartRecoveryTombstone as l, SESSION_RESTART_RECOVERY_TOMBSTONE_ERROR_CODE as n, createSessionWorkStartChangedError as o, resolveTerminalMainSessionTranscriptRegistryCheck as p, SessionRestartRecoveryTombstoneError as r, hasTerminalMainSessionTranscriptNewerThanRegistry as s, SESSION_LIFECYCLE_CHANGED_ERROR_REASON as t, isSessionWorkStartInvalidatedError as u };