openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
154 lines (153 loc) • 7.7 kB
JavaScript
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 };