UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

226 lines (225 loc) 10.5 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js"; import { k as parseCronRunScopeSuffix } from "./session-key-BnWWjqNc.js"; import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js"; import { c as getAgentEventLifecycleGeneration } from "./agent-events-CoxiItUi.js"; import "./session-accessor-YsytfDtG.js"; import { d as patchSessionEntryCore } from "./session-accessor.sqlite-entry-CWk3jL7s.js"; import { i as buildAgentRunTerminalOutcomeFromLifecycleEvent, o as classifyAgentRunTerminalOutcome } from "./agent-run-terminal-outcome-CigeY75d.js"; import { t as renderUserFacingText } from "./user-facing-text-0KenW-TQ.js"; import { r as loadGatewaySessionEntry } from "./session-utils-store-CInT2loy.js"; import "./session-utils-Cai0_C6U.js"; import { n as projectMainSessionRecoveryLifecycle, t as isMainSessionRecoveryLifecycleEvent } from "./main-session-recovery-lifecycle-1GvLnUmT.js"; //#region src/agents/agent-lifecycle-parent-state.ts function isAgentLifecycleYieldedWaiting(event) { return event.phase === "end" && event.yielded === true && event.livenessState === "paused" && event.stopReason === "end_turn" && event.aborted !== true && event.status !== "cancelled" && event.status !== "timed_out" && event.timeoutPhase == null && event.error == null; } //#endregion //#region src/gateway/session-lifecycle-state.ts const restartRecoveryLog = createSubsystemLogger("main-session-restart-recovery"); const SESSION_RUN_ERROR_MAX_CHARS = 160; function isFiniteTimestamp(value) { return typeof value === "number" && Number.isFinite(value) && value > 0; } function resolveLifecyclePhase(event) { const phase = typeof event.data?.phase === "string" ? event.data.phase : ""; return phase === "start" || phase === "end" || phase === "error" ? phase : null; } const SESSION_STATUS_BY_TERMINAL_CLASSIFICATION = { success: "done", timeout: "timeout", cancellation: "killed", failure: "failed" }; function resolveTerminalOutcome(event) { const phase = resolveLifecyclePhase(event); return buildAgentRunTerminalOutcomeFromLifecycleEvent({ phase: phase === "error" ? "error" : "end", data: event.data, endedAt: event.data?.endedAt ?? event.ts }); } function resolveSettledLifecycleTerminalOutcome(event) { const phase = resolveLifecyclePhase(event); if (phase !== "end" && phase !== "error") return; const outcome = resolveTerminalOutcome(event); return isAgentLifecycleYieldedWaiting({ phase, yielded: event.data?.yielded, livenessState: event.data?.livenessState, stopReason: outcome.stopReason, aborted: event.data?.aborted, status: event.data?.status, timeoutPhase: event.data?.timeoutPhase, error: event.data?.error }) ? void 0 : outcome; } function resolveSessionRunError(outcome, status) { if (status !== "failed" && status !== "timeout" || !outcome.error) return; const sanitized = renderUserFacingText(outcome.error, { errorContext: true }).replace(/\s+/g, " ").trim(); return sanitized ? truncateUtf16Safe(sanitized, SESSION_RUN_ERROR_MAX_CHARS) : void 0; } function resolveLifecycleStartedAt(existingStartedAt, event) { if (isFiniteTimestamp(event.data?.startedAt)) return event.data.startedAt; if (isFiniteTimestamp(existingStartedAt)) return existingStartedAt; return isFiniteTimestamp(event.ts) ? event.ts : void 0; } function resolveLifecycleEndedAt(event) { if (isFiniteTimestamp(event.data?.endedAt)) return event.data.endedAt; return isFiniteTimestamp(event.ts) ? event.ts : void 0; } function resolveRuntimeMs(params) { const { startedAt, endedAt, existingRuntimeMs } = params; if (isFiniteTimestamp(startedAt) && isFiniteTimestamp(endedAt)) return Math.max(0, endedAt - startedAt); if (typeof existingRuntimeMs === "number" && Number.isFinite(existingRuntimeMs) && existingRuntimeMs >= 0) return existingRuntimeMs; } function deriveGatewaySessionLifecycleSnapshot(params) { const phase = resolveLifecyclePhase(params.event); if (!phase) return {}; const existing = params.session ?? void 0; if (phase === "start") { const startedAt = resolveLifecycleStartedAt(existing?.startedAt, params.event); return { updatedAt: startedAt ?? existing?.updatedAt, status: "running", lastRunError: void 0, startedAt, endedAt: void 0, runtimeMs: void 0, abortedLastRun: false }; } const startedAt = resolveLifecycleStartedAt(existing?.startedAt, params.event); const endedAt = resolveLifecycleEndedAt(params.event); const updatedAt = endedAt ?? existing?.updatedAt; const terminal = resolveSettledLifecycleTerminalOutcome(params.event); const status = terminal ? SESSION_STATUS_BY_TERMINAL_CLASSIFICATION[classifyAgentRunTerminalOutcome(terminal)] : "running"; return { updatedAt, status, lastRunError: terminal ? resolveSessionRunError(terminal, status) : void 0, startedAt, endedAt, runtimeMs: resolveRuntimeMs({ startedAt, endedAt, existingRuntimeMs: existing?.runtimeMs }), abortedLastRun: status === "killed" }; } function derivePersistedSessionLifecyclePatch(params) { const snapshot = deriveGatewaySessionLifecycleSnapshot({ session: params.entry ?? void 0, event: params.event }); const snapshotPatch = { ...snapshot, updatedAt: typeof snapshot.updatedAt === "number" ? snapshot.updatedAt : void 0 }; const projection = projectMainSessionRecoveryLifecycle({ currentLifecycleGeneration: getAgentEventLifecycleGeneration(), entry: params.entry, event: params.event, snapshotPatch }); if (projection.action === "suppress") return {}; const phase = resolveLifecyclePhase(params.event); const runId = normalizeOptionalString(params.event.runId); const clientRunId = normalizeOptionalString(params.event.clientRunId) ?? runId; return { ...projection.patch, ...phase === "start" ? { lifecycleRunId: runId, lastRunId: void 0 } : projection.patch.status && projection.patch.status !== "running" ? { lifecycleRunId: void 0, lastRunId: clientRunId } : {} }; } function deriveGatewaySessionLifecycleProjectionPatch(params) { const { restartRecoveryRuns: _restartRecoveryRuns, lifecycleRunId: _lifecycleRunId, ...patch } = derivePersistedSessionLifecyclePatch(params); return patch; } function isRestartRecoveryLifecycleEvent(params) { return isMainSessionRecoveryLifecycleEvent(params); } /** * Reject pre-reset runs and explicitly older runs sharing one session so late * lifecycle events cannot overwrite a newer run's authoritative state. */ function isStaleLifecycleEventForSession(params) { if (params.owningSessionId && params.currentSessionId && params.owningSessionId !== params.currentSessionId) return true; const eventRunId = normalizeOptionalString(params.eventRunId); const currentRunId = normalizeOptionalString(params.currentRunId); if (eventRunId && currentRunId && eventRunId === currentRunId) return false; return isFiniteTimestamp(params.eventStartedAt) && isFiniteTimestamp(params.currentStartedAt) && params.eventStartedAt < params.currentStartedAt; } function acceptsCronRunContinuationLifecycleEvent(params) { const marker = params.entry.cronRunContinuation; if (marker?.phase === "running") return true; const runId = params.event.runId?.trim(); return Boolean(marker?.phase === "continuing" && runId && marker.ownerRunId === runId); } let lifecyclePersistenceVersion = 0; function readSessionLifecyclePersistenceVersion() { return lifecyclePersistenceVersion; } async function persistGatewaySessionLifecycleEvent(params) { const phase = resolveLifecyclePhase(params.event); if (!phase) return; const sessionEntry = loadGatewaySessionEntry(params.sessionKey, { ...params.agentId ? { agentId: params.agentId } : {}, clone: false }); if (!sessionEntry.entry) return; const owningSessionId = typeof params.event.sessionId === "string" && params.event.sessionId ? params.event.sessionId : void 0; const exactCronRun = parseCronRunScopeSuffix(sessionEntry.canonicalKey).runId !== void 0; let terminalRecovery; if (await patchSessionEntryCore({ storePath: sessionEntry.storePath, sessionKey: sessionEntry.canonicalKey }, async (storedEntry) => { terminalRecovery = void 0; const entry = storedEntry; if (exactCronRun && !acceptsCronRunContinuationLifecycleEvent({ entry, event: params.event })) return null; if (isStaleLifecycleEventForSession({ owningSessionId, currentSessionId: entry.sessionId, eventRunId: params.event.runId, currentRunId: entry.lifecycleRunId, eventStartedAt: params.event.data?.startedAt, currentStartedAt: entry.startedAt })) return null; const eventRunId = normalizeOptionalString(params.event.runId); const eventClientRunId = normalizeOptionalString(params.event.clientRunId); const terminalRunId = normalizeOptionalString(entry.lastRunId); if (phase === "start" && entry.status !== "running" && terminalRunId !== void 0 && (eventRunId === terminalRunId || eventClientRunId === terminalRunId)) return null; const patch = derivePersistedSessionLifecyclePatch({ entry, event: params.event }); const recoveryTerminalRunId = normalizeOptionalString(params.event.runId); const terminalOutcome = params.event.mainSessionRestartRecovery === true && params.event.lifecycleGeneration === getAgentEventLifecycleGeneration() && recoveryTerminalRunId !== void 0 && (phase === "end" || phase === "error") ? resolveSettledLifecycleTerminalOutcome(params.event) : void 0; if (terminalOutcome && recoveryTerminalRunId && Object.keys(patch).length > 0) terminalRecovery = { runId: recoveryTerminalRunId, outcome: terminalOutcome }; return Object.keys(patch).length > 0 ? patch : null; }, { skipMaintenance: true, takeCacheOwnership: true, requireWriteSuccess: true, ...params.assertCommitAllowed ? { assertCommitAllowed: params.assertCommitAllowed } : {} }) && terminalRecovery) { const message = `main-session restart recovery terminal: session=${sessionEntry.canonicalKey} run=${terminalRecovery.runId} status=${terminalRecovery.outcome.status} reason=${terminalRecovery.outcome.reason}`; restartRecoveryLog[terminalRecovery.outcome.status === "ok" ? "info" : "warn"](message); } lifecyclePersistenceVersion += 1; } //#endregion export { persistGatewaySessionLifecycleEvent as a, isStaleLifecycleEventForSession as i, deriveGatewaySessionLifecycleSnapshot as n, readSessionLifecyclePersistenceVersion as o, isRestartRecoveryLifecycleEvent as r, isAgentLifecycleYieldedWaiting as s, deriveGatewaySessionLifecycleProjectionPatch as t };