openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,120 lines • 50.1 kB
JavaScript
import { a as asOptionalRecord, c as isRecord } from "./record-coerce-DItp3I4t.js";
import "./session-key-BnWWjqNc.js";
import { t as isIncognitoSessionKey } from "./incognito-session-key-BwpD1Lwd.js";
import { i as executeSqliteQueryTakeFirstSync, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { r as resolveRuntimeWorkerUrl } from "./runtime-worker-url-CpdriB1D.js";
import { a as getOwnedSessionTranscriptInitialWriter, t as SessionTranscriptWriterClaimReboundError } from "./transcript-write-context-mhuh2Sos.js";
import { t as buildSessionContext } from "./session-CcAchsL_.js";
import { l as createManagedSessionId, o as migrateToCurrentVersion, r as isSessionContextMetadataEntry, u as generateSessionEntryId } from "./session-manager-codec-DIXSw5vP.js";
import "./version-Bsehiavt.js";
import { A as withCurrentProjectionSnapshot, Ct as appendTranscriptEventSync, E as resolveTranscriptBoundaryWindow, Et as replaceSessionWithBranchedTranscript, O as getActiveTranscriptKysely, Ot as replaceTranscriptEventsSync, Pt as readActiveTranscriptEntryAnchor, Tt as appendTranscriptMessageSync, _ as DEFAULT_VISIBLE_MESSAGE_MAX_MESSAGES, b as normalizeVisibleMessageLimit, g as DEFAULT_VISIBLE_MESSAGE_MAX_BYTES, in as applyAssistantDeliveryDirectives, r as SessionEntryNavigation, v as MAX_VISIBLE_MESSAGE_MAX_BYTES, y as MAX_VISIBLE_MESSAGE_MAX_MESSAGES } from "./session-accessor-YsytfDtG.js";
import { B as copyCodeModeSourceAppendOptions, S as loadTranscriptEventsSync, V as getCodeModeSourceAppend, z as copyCodeModeSourceAppend } from "./session-accessor.sqlite-transcript-store-B0zw3fAU.js";
import { T as ensureSessionEntrySync } from "./session-accessor.sqlite-entry-CWk3jL7s.js";
import { r as isSessionTranscriptSideAppendEntry } from "./transcript-tree-BH69pMSi.js";
import { n as WorkerTaskPool } from "./worker-task-pool-BNbf5LmH.js";
import { a as withSessionContextAdmission, n as resolveSessionTranscriptReadFence, r as resolveSqliteSessionTranscriptReadFence } from "./session-transcript-read-fence-CeDmfWcC.js";
import { a as parseOpaqueLeafEntry, i as isIndexedSessionEntry, o as parseParentLinkedOpaqueEntry, s as partitionSessionFileEntries, t as assertCurrentSessionTranscriptHeader } from "./session-entry-codec-6GPwHDjQ.js";
import { i as validateSessionTranscriptContextVersion, n as readSessionTranscriptModelContext, r as validateSessionTranscriptContextAdmission, t as readSessionTranscriptContextMessages } from "./session-accessor.sqlite-model-context-BXprwG1K.js";
import { sql } from "kysely";
//#region src/config/sessions/session-accessor.sqlite-active-context.ts
function readBoundedRetentionRanges(projection, rows, headerOffset) {
const sequences = /* @__PURE__ */ new Map();
const cuts = rows.flatMap(({ event, seq }, endIndex) => {
const entry = asOptionalRecord(event);
if (typeof entry?.id !== "string") return [];
sequences.set(entry.id, seq);
return (entry.type === "compaction" || entry.type === "reset") && typeof entry.firstKeptEntryId === "string" ? [{
id: entry.id,
firstKeptEntryId: entry.firstKeptEntryId,
endIndex
}] : [];
});
const missing = [...new Set(cuts.map((cut) => cut.firstKeptEntryId))].filter((id) => !sequences.has(id));
if (missing.length > 0) {
const lastSelectedSeq = Math.max(...rows.map((row) => row.seq));
const db = getActiveTranscriptKysely(projection.database);
const anchors = executeSqliteQuerySync(projection.database.db, db.selectFrom("transcript_event_identities as identity").innerJoin("session_transcript_active_events as active", (join) => join.onRef("active.session_id", "=", "identity.session_id").onRef("active.event_seq", "=", "identity.seq")).select(["identity.event_id", "identity.seq"]).where("identity.session_id", "=", projection.resolved.sessionId).where("identity.event_id", "in", missing).where("identity.seq", "<=", lastSelectedSeq)).rows;
for (const anchor of anchors) sequences.set(anchor.event_id, anchor.seq);
}
const ranges = /* @__PURE__ */ new Map();
for (const cut of cuts) {
const firstSeq = sequences.get(cut.firstKeptEntryId);
if (firstSeq === void 0) continue;
const start = rows.findIndex(({ seq }, index) => index < cut.endIndex && seq >= firstSeq);
ranges.set(cut.id, {
startIndex: (start < 0 ? cut.endIndex : start) + headerOffset,
endIndex: cut.endIndex + headerOffset
});
}
return ranges;
}
/** Reads one byte-bounded active branch without materializing abandoned transcript history. */
function readSessionTranscriptBoundedActiveContextCore(scope, options) {
const maxBytes = normalizeVisibleMessageLimit(options.maxBytes, DEFAULT_VISIBLE_MESSAGE_MAX_BYTES, MAX_VISIBLE_MESSAGE_MAX_BYTES, "maxBytes");
const maxEvents = normalizeVisibleMessageLimit(options.maxEvents, DEFAULT_VISIBLE_MESSAGE_MAX_MESSAGES, MAX_VISIBLE_MESSAGE_MAX_MESSAGES, "maxEvents");
return withCurrentProjectionSnapshot(scope, (projection) => {
const db = getActiveTranscriptKysely(projection.database);
const fence = resolveSqliteSessionTranscriptReadFence({
database: projection.database,
...projection.resolved
});
const transcript = db.selectFrom("transcript_events").where("session_id", "=", projection.resolved.sessionId);
const header = executeSqliteQueryTakeFirstSync(projection.database.db, transcript.select("seq").where(sql`json_extract(event_json, '$.type')`, "=", "session").orderBy("seq", "asc").limit(1));
const headerBytes = header ? executeSqliteQueryTakeFirstSync(projection.database.db, transcript.select(sql`OCTET_LENGTH(event_json) + 1`.as("serialized_bytes")).where("seq", "=", header.seq)).serialized_bytes : 0;
if (headerBytes > maxBytes) throw new RangeError("Session transcript header exceeds the active-context byte limit");
const retained = resolveTranscriptBoundaryWindow(projection, "context", fence?.beforeRawSeq)?.keptMessagePositions.slice(-(maxEvents + 1)) ?? [];
const metadata = executeSqliteQuerySync(projection.database.db, db.selectFrom("session_transcript_active_events as active").innerJoin("transcript_events as event", (join) => join.onRef("event.session_id", "=", "active.session_id").onRef("event.seq", "=", "active.event_seq")).select(["active.event_seq", sql`OCTET_LENGTH(event.event_json) + 1`.as("serialized_bytes")]).where("active.session_id", "=", projection.resolved.sessionId).$if(fence !== void 0, (query) => query.where("active.event_seq", "<", fence.beforeRawSeq)).where((eb) => retained.length > 0 ? eb.or([eb("active.context_eligible", "=", 1), eb("active.message_position", "in", retained)]) : eb("active.context_eligible", "=", 1)).orderBy("active.active_position", "desc").limit(maxEvents + 1)).rows;
const selectedSequences = [];
let serializedBytes = headerBytes;
for (const row of metadata) {
if (selectedSequences.length >= maxEvents || serializedBytes + row.serialized_bytes > maxBytes) break;
selectedSequences.push(row.event_seq);
serializedBytes += row.serialized_bytes;
}
const boundary = executeSqliteQueryTakeFirstSync(projection.database.db, db.selectFrom(db.selectFrom("transcript_event_identities as identity").innerJoin("session_transcript_active_events as active", (join) => join.onRef("active.session_id", "=", "identity.session_id").onRef("active.event_seq", "=", "identity.seq")).select((eb) => ["identity.seq", eb.fn.count("identity.seq").over().as("boundary_count")]).where("identity.session_id", "=", projection.resolved.sessionId).where("identity.event_type", "in", ["compaction", "reset"]).$if(fence !== void 0, (query) => query.where("identity.seq", "<", fence.beforeRawSeq)).orderBy("active.active_position", "desc").limit(1).as("boundary")).innerJoin("transcript_events as event", (join) => join.on("event.session_id", "=", projection.resolved.sessionId).onRef("event.seq", "=", "boundary.seq")).select([
"boundary.seq",
"boundary.boundary_count",
sql`OCTET_LENGTH(event.event_json) + 1`.as("serialized_bytes")
]));
const contextSequences = selectedSequences.toSorted((left, right) => left - right);
let injectedBoundarySeq;
let boundaryOmitted = false;
if (boundary && !selectedSequences.includes(boundary.seq)) {
if (serializedBytes + boundary.serialized_bytes <= maxBytes) {
injectedBoundarySeq = boundary.seq;
contextSequences.unshift(boundary.seq);
serializedBytes += boundary.serialized_bytes;
} else boundaryOmitted = true;
}
const payloadSequences = header ? [header.seq, ...contextSequences] : contextSequences;
const payloads = new Map((payloadSequences.length === 0 ? [] : executeSqliteQuerySync(projection.database.db, transcript.select(["seq", "event_json"]).where("seq", "in", payloadSequences)).rows).map((row) => [row.seq, JSON.parse(row.event_json)]));
const events = header ? [payloads.get(header.seq)] : [];
const rows = contextSequences.map((seq) => ({
event: payloads.get(seq),
seq
}));
const opaqueParents = /* @__PURE__ */ new Map();
let previousId;
for (const { event, seq } of rows) {
const entry = asOptionalRecord(event);
if (seq === injectedBoundarySeq) previousId = entry?.id;
else if (entry && "id" in entry && "parentId" in entry) {
if (typeof previousId === "string" && typeof entry.parentId === "string" && entry.parentId !== previousId) opaqueParents.set(entry.parentId, previousId);
previousId = entry.id;
}
events.push(event);
}
const activeLeafEntryId = fence ? fence.admission.effectiveParentId : projection.state.leafEventId;
if (activeLeafEntryId && previousId !== activeLeafEntryId) opaqueParents.set(activeLeafEntryId, typeof previousId === "string" ? previousId : null);
return {
activeLeafEntryId,
opaqueParents,
firstKeptRanges: readBoundedRetentionRanges(projection, rows, header ? 1 : 0),
boundaryCount: boundary?.boundary_count ?? 0,
events,
serializedBytes,
totalEvents: projection.state.activeEventCount,
truncated: boundaryOmitted || metadata.length > selectedSequences.length
};
});
}
//#endregion
//#region src/config/sessions/session-model-context-worker-runtime.ts
const modelContextReads = new WorkerTaskPool({
workerUrl: resolveRuntimeWorkerUrl({
currentModuleUrl: import.meta.url,
sourceWorkerName: "session-model-context.worker",
distWorkerPath: "config/sessions/session-model-context.worker.js"
}),
maxWorkers: 1
});
async function readSessionTranscriptModelContextAsync(target, admission, signal) {
signal?.throwIfAborted();
if (isIncognitoSessionKey(target.sessionKey)) return readSessionTranscriptModelContext(target);
return modelContextReads.run({
target,
admission
}, {
timeoutMs: 6e4,
signal
});
}
//#endregion
//#region src/agents/sessions/session-manager-core.ts
var SessionManagerCore = class extends SessionEntryNavigation {
constructor(cwd, persistenceTarget, loadedEntries, boundedContext) {
super();
this.migrated = false;
this.sessionId = "";
this.fileEntries = [];
this.opaqueFileEntries = [];
this.boundedFirstKeptById = /* @__PURE__ */ new Map();
this.pendingDeliberateAppend = false;
this.persistenceHeaderPending = false;
this.boundedContextIncomplete = false;
this.cwd = cwd;
this.persistenceTarget = persistenceTarget;
this.boundedContextLimits = boundedContext?.limits;
this.boundedContextIncomplete = boundedContext !== void 0;
this.persistedBoundaryCount = boundedContext?.boundaryCount;
if (persistenceTarget || loadedEntries) this.setLoadedSessionTarget(persistenceTarget, loadedEntries ?? [], boundedContext);
else this.newSession();
}
setSessionTarget(target) {
const bounded = this.boundedContextLimits ? readSessionTranscriptBoundedActiveContextCore(target, this.boundedContextLimits) : void 0;
const entries = bounded?.events ?? loadTranscriptEventsSync(target);
this.boundedContextIncomplete = bounded !== void 0;
this.persistedBoundaryCount = bounded?.boundaryCount;
const header = entries.find((entry) => typeof entry === "object" && entry !== null && entry.type === "session");
this.setLoadedSessionTarget(target, entries, bounded);
if (header?.cwd) this.cwd = header.cwd;
}
/** Active-only loads can omit sibling rows even when they fit the context limits. */
ensureCompletePersistedHistory() {
if (!this.persistenceTarget || !this.boundedContextIncomplete) return;
const limits = this.boundedContextLimits;
this.boundedContextLimits = void 0;
this.setSessionTarget(this.persistenceTarget);
this.boundedContextLimits = limits;
}
setLoadedSessionTarget(target, entries, bounded) {
this.boundedFirstKeptById.clear();
const partitioned = partitionSessionFileEntries(entries);
if (partitioned.fileEntries.length === 0 && partitioned.opaqueEntries.length === 0) {
this.persistenceTarget = target ? { ...target } : void 0;
this.initializeSession({ id: target?.sessionId });
this.persistenceHeaderPending = target !== void 0;
return;
}
const header = partitioned.fileEntries.find((entry) => entry.type === "session");
if (target) assertCurrentSessionTranscriptHeader(header);
this.persistenceHeaderPending = false;
this.persistenceTarget = target ? { ...target } : void 0;
this.fileEntries = partitioned.fileEntries;
this.opaqueFileEntries = partitioned.opaqueEntries;
this.sessionId = header?.id ?? target?.sessionId ?? createManagedSessionId();
this.migrated = migrateToCurrentVersion(this.fileEntries, partitioned.fileEntriesByOriginalIndex);
this.buildIndex();
if (bounded) {
for (const [id, parentId] of bounded.opaqueParents) this.opaqueParentsById.set(id, parentId);
this.appendParentId = bounded.activeLeafEntryId;
for (const [boundaryId, range] of bounded.firstKeptRanges) {
let firstKeptEntryId = boundaryId;
for (let index = range.startIndex; index < range.endIndex; index++) {
const entry = partitioned.fileEntriesByOriginalIndex[index];
if (isIndexedSessionEntry(entry)) {
firstKeptEntryId = entry.id;
break;
}
}
this.boundedFirstKeptById.set(boundaryId, firstKeptEntryId);
}
}
}
reloadPersistedTranscript() {
if (this.persistenceTarget) {
const runtimeCwd = this.cwd;
this.setSessionTarget(this.persistenceTarget);
this.cwd = runtimeCwd;
}
}
newSession(options) {
if (this.persistenceTarget) throw new Error("Persisted session managers cannot change session identity in place");
return this.initializeSession(options);
}
initializeSession(options) {
this.sessionId = options?.id ?? this.persistenceTarget?.sessionId ?? createManagedSessionId();
this.migrated = false;
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const header = {
type: "session",
version: 3,
id: this.sessionId,
timestamp,
cwd: this.cwd,
parentSession: options?.parentSession
};
this.fileEntries = [header];
this.opaqueFileEntries = [];
this.byId.clear();
this.opaqueParentsById.clear();
this.boundedFirstKeptById.clear();
this.logicalParentsById.clear();
this.invalidLeafControlIds.clear();
this.labelsById.clear();
this.labelTimestampsById.clear();
this.leafId = null;
this.appendParentId = null;
this.appendMode = void 0;
this.pendingDeliberateAppend = false;
return this.persistenceTarget ? this.sessionId : void 0;
}
buildIndex() {
this.clearNavigation();
this.pendingDeliberateAppend = false;
let opaqueIndex = 0;
for (let index = 0; index <= this.fileEntries.length; index += 1) {
while (this.opaqueFileEntries[opaqueIndex]?.index === index) {
this.appendOpaqueNavigationRecord(this.opaqueFileEntries[opaqueIndex]?.record);
opaqueIndex += 1;
}
const entry = this.fileEntries[index];
if (!entry || entry.type === "session" || this.migrated && !isIndexedSessionEntry(entry)) continue;
this.appendCanonicalNavigationEntry(entry);
}
this.finishNavigation();
}
normalizeEntryParent(entry) {
const parentId = this.resolveEntryParentId(entry);
let normalized = super.normalizeEntryParent(entry);
const boundedFirstKept = this.boundedFirstKeptById.get(normalized.id);
if (boundedFirstKept !== void 0 && (normalized.type === "compaction" || normalized.type === "reset")) normalized = {
...normalized,
firstKeptEntryId: boundedFirstKept
};
if ((normalized.type === "compaction" || normalized.type === "reset") && normalized.firstKeptEntryId !== void 0 && !this.byId.has(normalized.firstKeptEntryId) && this.opaqueParentsById.has(normalized.firstKeptEntryId)) {
const firstKeptEntryId = this.resolveCanonicalParentId(normalized.firstKeptEntryId) ?? this.findFirstCanonicalDescendantOnBranch(normalized.firstKeptEntryId, normalized.parentId) ?? this.findFirstCanonicalDescendant(normalized.firstKeptEntryId) ?? parentId;
if (firstKeptEntryId && firstKeptEntryId !== normalized.firstKeptEntryId) normalized = {
...normalized,
firstKeptEntryId
};
}
return normalized;
}
findFirstCanonicalDescendantOnBranch(opaqueId, leafId) {
const seen = /* @__PURE__ */ new Set();
let currentId = leafId;
let firstCanonicalDescendant;
while (currentId && !seen.has(currentId)) {
if (currentId === opaqueId) return firstCanonicalDescendant;
seen.add(currentId);
const entry = this.byId.get(currentId);
if (entry) {
firstCanonicalDescendant = entry.id;
currentId = entry.parentId;
} else currentId = this.opaqueParentsById.get(currentId) ?? null;
}
}
findFirstCanonicalDescendant(opaqueId) {
for (const entry of this.fileEntries) {
if (!isIndexedSessionEntry(entry)) continue;
const seen = /* @__PURE__ */ new Set();
let parentId = entry.parentId;
while (parentId && this.opaqueParentsById.has(parentId) && !seen.has(parentId)) {
if (parentId === opaqueId) return entry.id;
seen.add(parentId);
parentId = this.opaqueParentsById.get(parentId) ?? null;
}
}
}
resolveBranchTargetId(branchFromId) {
if (this.byId.has(branchFromId)) return branchFromId;
if (!this.opaqueParentsById.has(branchFromId)) return;
return this.resolveCanonicalParentId(branchFromId);
}
clampOpaqueFileEntryIndexes() {
let previousOpaqueIndex = 0;
for (const opaqueEntry of this.opaqueFileEntries) {
opaqueEntry.index = Math.max(previousOpaqueIndex, Math.min(opaqueEntry.index, this.fileEntries.length));
previousOpaqueIndex = opaqueEntry.index;
}
}
createLeafControl(parentId, appendParentId = this.appendParentId, appendMode) {
return {
type: "leaf",
id: generateSessionEntryId(),
parentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
targetId: this.leafId,
...appendParentId !== this.leafId ? { appendParentId } : {},
...appendMode ? { appendMode } : {}
};
}
rememberLeafControl(leafEntry) {
this.opaqueFileEntries.push({
index: this.fileEntries.length,
record: leafEntry
});
this.opaqueParentsById.set(leafEntry.id, leafEntry.targetId);
}
getAppendParentId() {
return this.appendParentId;
}
getAppendMode() {
return this.appendMode;
}
getPersistedFileEntries(leafAppendParentId = this.appendParentId, leafAppendMode) {
this.clampOpaqueFileEntryIndexes();
const entries = [];
let opaqueIndex = 0;
for (let index = 0; index <= this.fileEntries.length; index += 1) {
while (this.opaqueFileEntries[opaqueIndex]?.index === index) {
entries.push(this.opaqueFileEntries[opaqueIndex]?.record);
opaqueIndex += 1;
}
const entry = this.fileEntries[index];
if (entry) entries.push(entry);
}
while (opaqueIndex < this.opaqueFileEntries.length) {
entries.push(this.opaqueFileEntries[opaqueIndex]?.record);
opaqueIndex += 1;
}
let persistedLeafId = null;
let persistedAppendParentId = null;
let rawTailId = null;
for (const entry of entries) {
const leafEntry = parseOpaqueLeafEntry(entry);
if (leafEntry) {
rawTailId = leafEntry.id;
if (this.invalidLeafControlIds.has(leafEntry.id)) continue;
const targetId = this.resolveOpaqueLeafTargetId(leafEntry.targetId);
persistedLeafId = targetId;
persistedAppendParentId = leafEntry.appendParentId === void 0 ? targetId : this.resolveOpaqueAppendParentId(leafEntry.appendParentId);
continue;
}
if (isIndexedSessionEntry(entry)) {
persistedLeafId = entry.id;
persistedAppendParentId = entry.id;
rawTailId = entry.id;
continue;
}
const opaqueLink = parseParentLinkedOpaqueEntry(entry);
if (opaqueLink) {
persistedAppendParentId = opaqueLink.id;
rawTailId = opaqueLink.id;
}
}
if (persistedLeafId !== this.leafId || persistedAppendParentId !== this.appendParentId) {
const leafEntry = this.createLeafControl(rawTailId, leafAppendParentId, leafAppendMode);
this.rememberLeafControl(leafEntry);
entries.push(leafEntry);
}
return entries;
}
getPersistedEntries() {
return this.getPersistedFileEntries();
}
clearPreservedOpaqueFileEntries() {
this.opaqueFileEntries = [];
this.opaqueParentsById.clear();
this.invalidLeafControlIds.clear();
this.appendParentId = null;
this.appendMode = void 0;
this.pendingDeliberateAppend = false;
}
/** SQLite appends are synchronous; retained for the AgentSession contract. */
flushPendingPersistence() {}
isPersisted() {
return this.persistenceTarget !== void 0;
}
getCwd() {
return this.cwd;
}
getSessionId() {
return this.sessionId;
}
getSessionTarget() {
return this.persistenceTarget ? { ...this.persistenceTarget } : void 0;
}
};
//#endregion
//#region src/agents/sessions/session-manager-persistence.ts
function requireTranscriptEventAppend(result, message) {
if (result.ok && result.value) return;
const cause = result.ok ? { code: "transcript-event-not-appended" } : result.error;
throw new Error(`${message}: ${cause.code}`, { cause });
}
var SessionManagerPersistence = class SessionManagerPersistence extends SessionManagerCore {
#initialWriter;
removeTrailingEntries(predicate, options) {
const prepared = new SessionManagerPersistence(this.cwd, this.persistenceTarget, this.fileEntries);
prepared.opaqueFileEntries = this.opaqueFileEntries.map((entry) => ({ ...entry }));
prepared.boundedContextIncomplete = this.boundedContextIncomplete;
prepared.ensureCompletePersistedHistory();
let preservedStart = prepared.fileEntries.length;
while (preservedStart > 1) {
const entry = prepared.fileEntries[preservedStart - 1];
if (!isIndexedSessionEntry(entry) || !options?.preserveTrailing?.(entry)) break;
preservedStart -= 1;
}
let removeStart = preservedStart;
while (removeStart > 1) {
const entry = prepared.fileEntries[removeStart - 1];
if (!isIndexedSessionEntry(entry) || !predicate(entry)) break;
removeStart -= 1;
}
if (removeStart === preservedStart) return 0;
const shiftOpaqueIndexesAfterRemoval = (start, count) => {
for (const opaqueEntry of prepared.opaqueFileEntries) {
const removedBeforeOpaque = Math.max(0, Math.min(count, opaqueEntry.index - start));
opaqueEntry.index -= removedBeforeOpaque;
}
};
const removedCount = preservedStart - removeStart;
shiftOpaqueIndexesAfterRemoval(removeStart, removedCount);
const removedEntries = prepared.fileEntries.splice(removeStart, removedCount);
const removedParentById = new Map(removedEntries.map((entry) => [entry.id, entry.parentId]));
for (let index = removeStart; index < prepared.fileEntries.length;) {
const entry = prepared.fileEntries[index];
if (isIndexedSessionEntry(entry) && entry.type === "label" && removedParentById.has(entry.targetId)) {
removedParentById.set(entry.id, entry.parentId);
shiftOpaqueIndexesAfterRemoval(index, 1);
prepared.fileEntries.splice(index, 1);
continue;
}
index += 1;
}
const resolveRetainedParentId = (parentId) => {
const seen = /* @__PURE__ */ new Set();
let currentId = parentId;
while (currentId && removedParentById.has(currentId) && !seen.has(currentId)) {
seen.add(currentId);
currentId = removedParentById.get(currentId) ?? null;
}
return currentId;
};
const replacementParentId = resolveRetainedParentId(removedEntries[0]?.parentId ?? null);
prepared.fileEntries = prepared.fileEntries.map((entry) => {
if (!isIndexedSessionEntry(entry)) return entry;
const parentId = resolveRetainedParentId(entry.parentId);
return parentId === entry.parentId ? entry : {
...entry,
parentId
};
});
prepared.opaqueFileEntries = prepared.opaqueFileEntries.map((opaqueEntry) => {
if (!isRecord(opaqueEntry.record)) return opaqueEntry;
const record = opaqueEntry.record;
const parentId = record.parentId === null || typeof record.parentId === "string" ? resolveRetainedParentId(record.parentId) : void 0;
const leafEntry = parseOpaqueLeafEntry(record);
const targetId = leafEntry ? resolveRetainedParentId(leafEntry.targetId) : void 0;
const appendParentId = leafEntry?.appendParentId !== void 0 ? resolveRetainedParentId(leafEntry.appendParentId) : void 0;
if ((parentId === void 0 || parentId === record.parentId) && (targetId === void 0 || targetId === leafEntry?.targetId) && (appendParentId === void 0 || appendParentId === leafEntry?.appendParentId)) return opaqueEntry;
return {
...opaqueEntry,
record: {
...record,
...parentId !== void 0 ? { parentId } : {},
...targetId !== void 0 ? { targetId } : {},
...appendParentId !== void 0 ? { appendParentId } : {}
}
};
});
prepared.clampOpaqueFileEntryIndexes();
prepared.buildIndex();
prepared.leafId = prepared.resolveCanonicalParentId(replacementParentId);
prepared.appendParentId = replacementParentId;
const events = prepared.getPersistedFileEntries(prepared.appendParentId, prepared.appendMode);
if (this.persistenceTarget && !replaceTranscriptEventsSync(this.persistenceTarget, events)) throw new Error("Session transcript replacement was not persisted");
this.setLoadedSessionTarget(this.persistenceTarget, events);
this.boundedContextIncomplete = false;
this.persistedBoundaryCount = void 0;
return removedEntries.length;
}
persistRecord(entry, options) {
if (this.persistenceTarget) return this.persistSqliteRecord(entry, options);
}
persist(entry, options) {
return this.persistRecord(entry, options);
}
persistSqliteRecord(entry, options) {
if (!this.persistenceTarget) return;
const scope = this.persistenceTarget;
const inheritedWriter = getOwnedSessionTranscriptInitialWriter({ sessionTarget: scope });
this.#initialWriter ??= inheritedWriter;
const initialWriter = this.#initialWriter;
if (initialWriter) {
initialWriter.assertActive();
if (!initialWriter.committedFence && inheritedWriter !== initialWriter) throw new SessionTranscriptWriterClaimReboundError();
Object.assign(scope, initialWriter.committedFence ?? {
expectedLifecycleRevision: void 0,
expectedWriterRunId: initialWriter.writerRunId
});
}
if (this.persistenceHeaderPending || initialWriter && !initialWriter.committedFence) {
if (!ensureSessionEntrySync(scope, {
sessionId: scope.sessionId,
updatedAt: Date.now()
})) throw new Error("Session transcript header was not persisted");
initialWriter?.assertActive();
if (initialWriter?.committedFence) Object.assign(scope, initialWriter.committedFence);
}
if (this.persistenceHeaderPending) {
const header = this.fileEntries[0];
if (!header || header.type !== "session") throw new Error("Session transcript header was not persisted");
requireTranscriptEventAppend(appendTranscriptEventSync(scope, header), "Session transcript header was not persisted");
this.persistenceHeaderPending = false;
}
const leafEntry = parseOpaqueLeafEntry(entry);
if (leafEntry) {
requireTranscriptEventAppend(appendTranscriptEventSync(scope, entry), `Session transcript leaf control was not persisted: ${leafEntry.id}`);
return;
}
if (!isIndexedSessionEntry(entry)) return;
if (entry.type !== "message") {
requireTranscriptEventAppend(appendTranscriptEventSync(scope, entry, options?.appendIntent === "active-branch" ? { appendIntent: options.appendIntent } : void 0), `Session transcript entry was not persisted: ${entry.id}`);
return;
}
const appendOptions = copyCodeModeSourceAppendOptions(options, {
cwd: this.cwd,
eventId: entry.id,
...options?.config ? { config: options.config } : {},
...options?.idempotencyLookup ? { idempotencyLookup: options.idempotencyLookup } : {},
message: entry.message,
now: Date.parse(entry.timestamp),
parentId: entry.parentId,
...options?.appendIntent === "active-branch" ? { appendIntent: options.appendIntent } : {}
});
const outcome = appendTranscriptMessageSync(scope, appendOptions);
if (!outcome.ok) throw new Error(`Session transcript message was not persisted: ${entry.id}`, { cause: outcome.error });
const result = outcome.value;
if (!result) throw new Error(`Session transcript message was not persisted: ${entry.id}`);
entry.message = result.message;
if (result.messageId !== entry.id) {
if ((entry.message.role === "user" && "idempotencyKey" in entry.message && typeof entry.message.idempotencyKey === "string" && entry.message.idempotencyKey.length > 0 ? entry.message.idempotencyKey : void 0) && options?.idempotencyLookup !== "caller-checked") {
if (!result.anchor) throw new Error(`Session transcript anchor was not returned: ${result.messageId}`);
return {
adoptedMessageId: result.messageId,
anchor: result.anchor,
appended: result.appended,
effectiveParentId: result.effectiveParentId ?? null
};
}
throw new Error(`Session transcript parent entry was not persisted: ${entry.id}`);
}
if (options?.idempotencyLookup === "caller-checked" && (!result?.appended || result.messageId !== entry.id)) throw new Error(`Session transcript append was not persisted: ${entry.id}`);
if (result.effectiveParentId === void 0) throw new Error(`Session transcript append parent was not returned: ${entry.id}`);
return {
...result.anchor ? { anchor: result.anchor } : {},
appended: result.appended,
effectiveParentId: result.effectiveParentId
};
}
};
//#endregion
//#region src/agents/sessions/session-manager-entries.ts
var SessionManagerEntries = class extends SessionManagerPersistence {
appendEntry(entry, options) {
const canonicalEntry = JSON.parse(JSON.stringify(entry));
if (!isIndexedSessionEntry(canonicalEntry)) throw new Error(`Invalid session transcript entry: ${entry.type}`);
if (entry.type === "message" && canonicalEntry.type === "message") copyCodeModeSourceAppend(entry.message, canonicalEntry.message, getCodeModeSourceAppend(options), (source) => source);
const activeBranchAppend = !this.pendingDeliberateAppend && this.appendMode !== "side" && !isSessionTranscriptSideAppendEntry(canonicalEntry);
const persistenceResult = this.persist(canonicalEntry, copyCodeModeSourceAppendOptions(options, {
...options,
...activeBranchAppend ? { appendIntent: "active-branch" } : {}
}));
if (persistenceResult?.adoptedMessageId) {
this.reloadPersistedTranscript();
if (this.resolveCurrentTurnEntryId() !== persistenceResult.adoptedMessageId) throw new Error(`Session transcript keyed user is outside the current turn: ${persistenceResult.adoptedMessageId}`);
canonicalEntry.id = persistenceResult.adoptedMessageId;
} else if (persistenceResult?.effectiveParentId !== void 0 && persistenceResult.effectiveParentId !== canonicalEntry.parentId) this.reloadPersistedTranscript();
else {
if (!isSessionTranscriptSideAppendEntry(canonicalEntry) && canonicalEntry.parentId === this.appendParentId && this.leafId !== this.appendParentId) this.logicalParentsById.set(canonicalEntry.id, this.leafId);
this.fileEntries.push(canonicalEntry);
this.byId.set(canonicalEntry.id, canonicalEntry);
this.appendParentId = canonicalEntry.id;
if (isSessionTranscriptSideAppendEntry(canonicalEntry)) this.appendMode = "side";
else {
this.leafId = canonicalEntry.id;
this.appendMode = void 0;
}
}
this.pendingDeliberateAppend = false;
return {
entry: canonicalEntry,
anchor: persistenceResult?.anchor,
appended: persistenceResult?.appended ?? true
};
}
resolveCurrentTurnEntryId(isInterruptedTail) {
let parentId = this.appendParentId;
let remainingAncestors = this.byId.size;
while (parentId && remainingAncestors-- > 0) {
const parent = this.byId.get(parentId);
if (!parent || !isSessionContextMetadataEntry(parent) && parent.type !== "compaction" && !isInterruptedTail?.(parent)) break;
parentId = parent.parentId;
}
return parentId;
}
appendMessage(message, options) {
return this.appendMessageWithTranscriptAnchor(message, options).entryId;
}
appendMessageWithTranscriptAnchor(message, options) {
if (message.role === "assistant") applyAssistantDeliveryDirectives(message);
if (options?.idempotencyLookup !== "caller-checked" && message.role === "user" && "idempotencyKey" in message && typeof message.idempotencyKey === "string" && message.idempotencyKey.length > 0) {
const currentTurnId = this.resolveCurrentTurnEntryId();
const current = currentTurnId ? this.byId.get(currentTurnId) : void 0;
if (current?.type === "message" && current.message.role === "user" && "idempotencyKey" in current.message && current.message.idempotencyKey === message.idempotencyKey) {
const anchor = this.persistenceTarget ? readActiveTranscriptEntryAnchor({
...this.persistenceTarget,
entryId: current.id
}) : void 0;
if (this.persistenceTarget && !anchor) throw new Error(`Session transcript anchor was not returned: ${current.id}`);
return {
entryId: current.id,
message: current.message,
...anchor ? { anchor } : {},
appended: false
};
}
}
const entry = {
type: "message",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
message
};
const { entry: persisted, anchor, appended } = this.appendEntry(entry, options);
return {
entryId: persisted.id,
message: persisted.message,
...anchor ? { anchor } : {},
appended
};
}
appendThinkingLevelChange(thinkingLevel) {
const entry = {
type: "thinking_level_change",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
thinkingLevel
};
this.appendEntry(entry);
return entry.id;
}
appendModelChange(provider, modelId) {
const entry = {
type: "model_change",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
provider,
modelId
};
this.appendEntry(entry);
return entry.id;
}
appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromHook, metadata) {
const entry = {
type: "compaction",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
summary,
firstKeptEntryId,
tokensBefore,
details,
fromHook,
...metadata?.runId || metadata?.itemId ? { __openclaw: metadata } : {}
};
this.appendEntry(entry, { invalidateSerializedPrefixCache: fromHook === true || details !== void 0 });
if (this.persistedBoundaryCount !== void 0) this.persistedBoundaryCount += 1;
return entry.id;
}
appendResetBoundary(reason, firstKeptEntryId) {
const entry = {
type: "reset",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
reason,
...firstKeptEntryId ? { firstKeptEntryId } : {}
};
this.appendEntry(entry);
if (this.persistedBoundaryCount !== void 0) this.persistedBoundaryCount += 1;
return entry.id;
}
appendCustomEntry(customType, data) {
const entry = {
type: "custom",
customType,
data,
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString()
};
this.appendEntry(entry, { invalidateSerializedPrefixCache: true });
return entry.id;
}
appendSessionInfo(name) {
const entry = {
type: "session_info",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
name: name.replace(/[\r\n]+/g, " ").trim()
};
this.appendEntry(entry);
return entry.id;
}
getSessionName() {
for (const entry of this.getEntries().toReversed()) if (entry.type === "session_info") return entry.name?.trim() || void 0;
}
appendCustomMessageEntry(customType, content, display, details) {
const entry = {
type: "custom_message",
customType,
content,
display,
details,
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString()
};
this.appendEntry(entry, { invalidateSerializedPrefixCache: true });
return entry.id;
}
getLeafId() {
return this.leafId;
}
appendLeafControl(params) {
if (params.targetId !== null && !this.byId.has(params.targetId)) throw new Error(`Entry ${params.targetId} not found`);
if (params.appendParentId !== null && !this.byId.has(params.appendParentId) && !this.opaqueParentsById.has(params.appendParentId)) throw new Error(`Append parent ${params.appendParentId} not found`);
const previousLeafId = this.leafId;
this.leafId = params.targetId;
const entry = this.createLeafControl(this.appendParentId, params.appendParentId, params.appendMode);
this.leafId = previousLeafId;
this.persistRecord(entry);
this.rememberLeafControl(entry);
this.leafId = params.targetId;
this.appendParentId = params.appendParentId;
this.appendMode = params.appendMode;
this.pendingDeliberateAppend = false;
return entry;
}
getLeafEntry() {
return this.leafId ? this.getEntry(this.leafId) : void 0;
}
getEntry(id) {
const entry = this.byId.get(id);
return entry ? this.normalizeEntryParent(entry) : void 0;
}
getChildren(parentId) {
const children = [];
for (const entry of this.byId.values()) {
const normalizedEntry = this.normalizeEntryParent(entry);
if (normalizedEntry.parentId === parentId) children.push(normalizedEntry);
}
return children;
}
getLabel(id) {
return this.labelsById.get(id);
}
appendLabelChange(targetId, label) {
if (!this.byId.has(targetId)) throw new Error(`Entry ${targetId} not found`);
const entry = {
type: "label",
id: generateSessionEntryId(),
parentId: this.appendParentId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
targetId,
label
};
this.appendEntry(entry);
if (label) {
this.labelsById.set(targetId, label);
this.labelTimestampsById.set(targetId, entry.timestamp);
} else {
this.labelsById.delete(targetId);
this.labelTimestampsById.delete(targetId);
}
return entry.id;
}
buildSessionContext() {
return buildSessionContext(this.getBranch());
}
getBoundaryCount() {
return this.persistedBoundaryCount ?? this.getBranch().filter((entry) => entry.type === "compaction" || entry.type === "reset").length;
}
getHeader() {
return this.fileEntries.find((entry) => entry.type === "session") ?? null;
}
getEntries() {
return this.fileEntries.filter((entry) => entry.type !== "session" && this.byId.has(entry.id)).map((entry) => this.normalizeEntryParent(entry));
}
getTree() {
const entries = this.getEntries();
const nodeMap = /* @__PURE__ */ new Map();
const roots = [];
for (const entry of entries) nodeMap.set(entry.id, {
entry,
children: [],
label: this.labelsById.get(entry.id),
labelTimestamp: this.labelTimestampsById.get(entry.id)
});
for (const entry of entries) {
const node = nodeMap.get(entry.id);
const parentId = this.resolveCanonicalParentId(entry.parentId);
if (parentId === null || parentId === entry.id) roots.push(node);
else {
const parent = nodeMap.get(parentId);
if (parent) parent.children.push(node);
else roots.push(node);
}
}
const stack = [...roots];
while (stack.length > 0) {
const node = stack.pop();
node.children.sort((left, right) => new Date(left.entry.timestamp).getTime() - new Date(right.entry.timestamp).getTime());
stack.push(...node.children);
}
return roots;
}
branch(branchFromId) {
if (!this.byId.has(branchFromId)) this.ensureCompletePersistedHistory();
const branchTargetId = this.resolveBranchTargetId(branchFromId);
if (branchTargetId === void 0) throw new Error(`Entry ${branchFromId} not found`);
this.leafId = branchTargetId;
this.appendParentId = branchTargetId;
this.appendMode = void 0;
this.pendingDeliberateAppend = true;
}
resetLeaf() {
this.leafId = null;
this.appendParentId = null;
this.appendMode = void 0;
this.pendingDeliberateAppend = true;
}
branchWithSummary(branchFromId, summary, details, fromHook) {
if (branchFromId !== null && !this.byId.has(branchFromId)) this.ensureCompletePersistedHistory();
const branchTargetId = branchFromId === null ? null : this.resolveBranchTargetId(branchFromId);
if (branchTargetId === void 0) throw new Error(`Entry ${branchFromId} not found`);
const entry = {
type: "branch_summary",
id: generateSessionEntryId(),
parentId: branchTargetId,
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
fromId: branchTargetId ?? "root",
summary,
details,
fromHook
};
this.appendEntry(entry, { invalidateSerializedPrefixCache: fromHook === true || details !== void 0 });
return entry.id;
}
};
//#endregion
//#region src/agents/sessions/session-manager-branching.ts
var SessionManagerBranching = class SessionManagerBranching extends SessionManagerEntries {
collectBranchedSessionPath(leafId) {
const opaqueById = /* @__PURE__ */ new Map();
for (const opaqueEntry of this.opaqueFileEntries) {
const link = parseOpaqueLeafEntry(opaqueEntry.record) ?? parseParentLinkedOpaqueEntry(opaqueEntry.record);
if (link && isRecord(opaqueEntry.record)) opaqueById.set(link.id, opaqueEntry.record);
}
const reversedNodes = [];
const seen = /* @__PURE__ */ new Set();
let currentId = leafId;
while (currentId && !seen.has(currentId)) {
seen.add(currentId);
const entry = this.byId.get(currentId);
if (entry) {
reversedNodes.push({
type: "entry",
entry
});
if (this.logicalParentsById.has(entry.id)) {
let physicalId = entry.parentId;
while (physicalId && !seen.has(physicalId)) {
const physicalRecord = opaqueById.get(physicalId);
if (!physicalRecord || !this.opaqueParentsById.has(physicalId)) break;
seen.add(physicalId);
reversedNodes.push({
type: "opaque",
id: physicalId,
record: physicalRecord
});
physicalId = this.opaqueParentsById.get(physicalId) ?? null;
}
currentId = this.logicalParentsById.get(entry.id) ?? null;
} else currentId = entry.parentId;
continue;
}
const record = opaqueById.get(currentId);
if (!record || !this.opaqueParentsById.has(currentId)) break;
reversedNodes.push({
type: "opaque",
id: currentId,
record
});
currentId = this.opaqueParentsById.get(currentId) ?? null;
}
const entries = [];
const opaqueEntries = [];
let tailId = null;
for (const node of reversedNodes.toReversed()) {
if (node.type === "entry") {
if (node.entry.type === "label") continue;
const branchEntry = node.entry.parentId === tailId ? node.entry : {
...node.entry,
parentId: tailId
};
entries.push(branchEntry);
tailId = branchEntry.id;
continue;
}
if (parseOpaqueLeafEntry(node.record)) continue;
opaqueEntries.push({
index: entries.length + 1,
record: {
...node.record,
parentId: tailId
}
});
tailId = node.id;
}
return {
entries,
opaqueEntries,
tailId
};
}
async createBranchedSession(leafId) {
this.ensureCompletePersistedHistory();
const previousSessionId = this.sessionId;
const branchPath = this.collectBranchedSessionPath(leafId);
if (branchPath.entries.length === 0) throw new Error(`Entry ${leafId} not found`);
const newSessionId = createManagedSessionId();
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
const persistenceTarget = this.persistenceTarget;
const header = {
type: "session",
version: 3,
id: newSessionId,
timestamp,
cwd: this.cwd,
parentSession: persistenceTarget ? previousSessionId : void 0
};
const pathEntryIds = new Set(branchPath.entries.map((entry) => entry.id));
const labelsToWrite = [];
for (const [targetId, label] of this.labelsById) if (pathEntryIds.has(targetId)) labelsToWrite.push({
targetId,
label,
timestamp: this.labelTimestampsById.get(targetId)
});
const labelEntries = [];
let parentId = branchPath.tailId;
for (const { targetId, label, timestamp: labelTimestamp } of labelsToWrite) {
const labelEntry = {
type: "label",
id: generateSessionEntryId(),
parentId,
timestamp: labelTimestamp,
targetId,
label
};
labelEntries.push(labelEntry);
parentId = labelEntry.id;
}
const branch = new SessionManagerBranching(this.cwd, void 0, [
header,
...branchPath.entries,
...labelEntries
]);
branch.opaqueFileEntries = branchPath.opaqueEntries;
branch.buildIndex();
const adoptBranch = (target) => {
this.fileEntries = branch.fileEntries;
this.opaqueFileEntries = branch.opaqueFileEntries;
this.sessionId = newSessionId;
this.buildIndex();
this.persistenceTarget = target;
this.persistenceHeaderPending = false;
};
if (persistenceTarget) await replaceSessionWithBranchedTranscript(persistenceTarget, {
sessionId: newSessionId,
events: branch.getPersistedFileEntries()
}, adoptBranch);
else adoptBranch();
return persistenceTarget ? newSessionId : void 0;
}
};
//#endregion
//#region src/agents/sessions/session-manager.ts
var SessionManager = class SessionManager extends SessionManagerBranching {
constructor(cwd, persistenceTarget, loadedEntries, boundedContext) {
super(cwd, persistenceTarget, loadedEntries, boundedContext);
}
/** Makes pending append-oriented persistence durable without rewriting committed entries. */
flushPendingPersistence() {
super.flushPendingPersistence();
}
appendMessage(message, options) {
return super.appendMessage(message, options);
}
appendMessageWithTranscriptAnchor(message, options) {
return super.appendMessageWithTranscriptAnchor(message, options);
}
static open(target, cwdOverride, contextLimits) {
if (contextLimits) return SessionManager.openBounded(target, {
...contextLimits,
...cwdOverride !== void 0 ? { cwd: cwdOverride } : {}
});
const entries = loadTranscriptEventsSync(target);
const header = entries.find((entry) => typeof entry === "object" && entry !== null && entry.type === "session");
return new SessionManager(cwdOverride ?? header?.cwd ?? process.cwd(), target, entries);
}
/** Opens only the selected model-context tail while preserving the complete durable transcript. */
static openBounded(target, options) {
const { cwd, onTruncated, ...limits } = options;
const context = readSessionTranscriptBoundedActiveContextCore(target, limits);
if (context.truncated) onTruncated?.();
const entries = context.events;
const header = entries.find((entry) => typeof entry === "object" && entry !== null && entry.type === "session");
return new SessionManager(cwd ?? header?.cwd ?? process.cwd(), target, entries, {
...context,
limits
});
}
/** Detached model view: selected payloads plus lightweight ancestry, never raw replay evidence. */
static openModelContext(target, options = {}) {
const context = withSessionContextAdmission(target, options.admission, () => readSessionTranscriptModelContext(target));
return SessionManager.fromModelContextEntries(context.events, options.cwd);
}
/** The same detached model view, with durable transcript scanning off the event loop. */
static async openModelContextAsync(target, options = {}) {
const readTarget = { ...target };
const receipt = options.admission ?? resolveSessionTranscriptReadFence(readTarget);
const admission = receipt ? { ...receipt } : void 0;
const context = await withSessionContextAdmission(readTarget, admission, () => readSessionTranscriptModelContextAsync(readTarget, admission, options.signal));
options.signal?.throwIfAborted();
if (admission) validateSessionTranscriptContextAdmission(readTarget, admission);
else validateSessionTranscriptContextVersion(readTarget, context.version);
return SessionManager.fromModelContextEntries(context.events, options.cwd);
}
static fromModelContextEntries(contextEntries, cwd) {
const entries = contextEntries;
const header = entries.find((entry) => entry.type === "session");
if (entries.length > 0 && (!header || (header.version ?? 1) < 3)) throw new Error("Persisted legacy session transcripts require doctor/import migration before runtime use");
return new SessionManager(cwd ?? header?.cwd ?? process.cwd(), void 0, entries);
}
/** Synchronously consumes full-fidelity context; its iterator closes with the read snapshot. */
static readSessionContext(target, read, options = {}) {
return withSessionContextAdmission(target, options.admission, () => readSessionTranscriptContextMessages(target, read));
}
/** Appends to the current transcript leaf without hydrating its history. */
static appendMessageToTranscript(target, message, options) {
const outcome = appendTranscriptMessageSync(target, {
cwd: process.cwd(),
message,
...options?.config ? { config: options.config } : {}
});
if (!outcome.ok) throw new Error("Session transcript message was not persisted", { cause: outcome.error });
const result = outcome.value;
if (!result) throw new Error("Session transcript message was not persisted");
return result.messageId;
}
static inMemory(cwd = process.cwd()) {
return new SessionManager(cwd);
}
static fromEntries(entries, cwdOverride) {
const fileEntries = structuredClone(entries);
const header = fileEntries.find((entry) => typeof entry === "object" && entry !== null && entry.type === "session");
return new SessionManager(cwdOverride ?? header?.cwd ?? process.cwd(), void 0, fileEntries);
}
};
//#endregion
export { SessionManager as t };