UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,120 lines 50.1 kB
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 };