UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

471 lines (470 loc) 25.6 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { O as parseAgentSessionKey } from "./session-key-BnWWjqNc.js"; import { a as getNodeSqliteKysely, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js"; import { E as tableExists } from "./openclaw-state-db-cache-C7ljO0xP.js"; import { i as openOpenClawStateDatabase, s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js"; import { o as deferOpenClawAgentPostCommitPublication, p as openOpenClawAgentDatabase } from "./openclaw-agent-db-CWtDoRbC.js"; import { i as ensureTranscriptHeader, n as appendTranscriptEventsInTransaction, p as resolveResetBoundaryHeaderCwd, x as loadTranscriptEventsFromDatabase } from "./session-accessor.sqlite-transcript-store-B0zw3fAU.js"; import { n as withOpenClawAgentDatabaseReadOnly } from "./openclaw-agent-db-readonly-CHjf8FxN.js"; import { B as withSqliteSessionDeletions, L as hasPreparedNativeSessionDeletion, Q as sqliteSessionEntriesEqual, Z as sqliteLifecycleTargetSnapshotsEqual, g as writeSessionEntry, t as assertLifecycleTargetUnchanged, u as readLifecycleTargetSnapshot, z as runSqliteSessionDeletionTransaction } from "./session-accessor.sqlite-entry-store-BxYl0nro.js"; import { l as resolveSqliteStoreScope, m as toDatabaseOptions, p as runExclusiveSqliteSessionWrite, s as resolveSqliteReadScope, t as cloneSessionEntry, u as resolveSqliteTranscriptArchiveDirectory } from "./session-accessor.sqlite-scope-2KfMzb44.js"; import { d as emitSessionIdentityMutation, i as kickSessionHistoryDiskBudgetMaintenance, l as emitCommittedSessionEntryRemovals, s as refreshSqliteSessionPlannerStatisticsBestEffort, t as collectAdmissionProtectedSessionIds } from "./session-history-eviction-C4srftLJ.js"; import { b as materializeSessionStateDeletePlans, c as planSessionStateDeleteIfUnreferenced, d as readReferencedSessionIdsAfterTargetMutation, f as readSessionGenerationIdsForKeys, g as emitArchivedTranscriptUpdates, h as publishSessionStateArchives, o as planSessionLifecycleArtifactCleanup, s as planSessionStateAfterEntryRemoval } from "./session-accessor.sqlite-lifecycle-state-BTh4yZ7R.js"; import { u as selectSessionTranscriptLeafControlledPath } from "./transcript-tree-BH69pMSi.js"; import { c as shouldDeleteSqliteSessionEntryLifecycle, i as createSessionEntryReclamationPlan, o as runExclusiveSqliteSessionReclamation, r as createLifecycleArtifactReclamationPlan, s as runSqliteSessionReclamation, t as createHistoricalGenerationReclamationPlan } from "./session-accessor.sqlite-reclamation-CoML3XOn.js"; import { a as isAgentHarnessSessionKey, c as isValidAgentHarnessSessionStoreEntry, d as resolveAgentHarnessSessionStoreEntryError, i as MODEL_SELECTION_LOCK_REMOVAL_MESSAGE } from "./agent-harness-session-key-BOz3yx0-.js"; import { randomUUID } from "node:crypto"; //#region src/state/github-personal-publication-lifecycle.ts /** Permanent session deletion owns all retained receipts, including pre-reset incarnations. */ function deletePersonalGitHubSessionReceipts(params) { const database = openOpenClawStateDatabase({ env: params.env }); if (!tableExists(database.db, "github_personal_publication_requests") || params.sessionKeys.length === 0) return; runOpenClawStateWriteTransaction(({ db }) => { executeSqliteQuerySync(db, getNodeSqliteKysely(db).deleteFrom("github_personal_publication_requests").where("agent_id", "=", params.agentId).where("session_key", "in", params.sessionKeys)); }, { database }, { operationLabel: "github-personal-publication.session-delete" }); } //#endregion //#region src/config/sessions/transcript-replay.ts /** Tail kept so DM continuity survives silent session rotations. */ const DEFAULT_REPLAY_MAX_MESSAGES = 6; function isValidReplayTimestamp(value) { if (typeof value === "number") return Number.isFinite(value); return typeof value === "string" && value.trim().length > 0; } function replayableTranscriptRole(record) { if (!record || record.type !== "message" || typeof record.id !== "string" || record.id.trim().length === 0 || !isValidReplayTimestamp(record.timestamp) || !(record.parentId === null || record.parentId === void 0 || typeof record.parentId === "string")) return; const role = record.message?.role; return role === "user" || role === "assistant" ? role : void 0; } function selectRecentUserAssistantReplayRecords(records, maxMessages = DEFAULT_REPLAY_MAX_MESSAGES) { const max = Math.max(0, maxMessages); if (max === 0) return []; const kept = []; for (const record of records) { const role = replayableTranscriptRole(record); if (role) kept.push({ role, record }); } return selectAlternatingReplayTail(kept, max).map((entry) => entry.record); } function selectAlternatingReplayTail(kept, max) { if (kept.length === 0) return []; let startIdx = Math.max(0, kept.length - max); while (startIdx < kept.length && kept[startIdx]?.role === "assistant") startIdx += 1; if (startIdx === kept.length) return []; return coalesceAlternatingReplayTail(kept.slice(startIdx)); } function coalesceAlternatingReplayTail(entries) { const tail = []; for (const entry of entries) { const lastIdx = tail.length - 1; if (lastIdx >= 0 && tail[lastIdx]?.role === entry.role) { tail[lastIdx] = entry; continue; } tail.push(entry); } return tail; } //#endregion //#region src/config/sessions/session-reset-boundary-event.ts function recordId(record) { if (!record || typeof record !== "object" || Array.isArray(record)) return; const id = record.id; return typeof id === "string" && id.trim() ? id : void 0; } function uniqueBoundaryId(records) { const ids = new Set(records.flatMap((record) => recordId(record) ? [recordId(record)] : [])); for (;;) { const id = randomUUID().slice(0, 8); if (!ids.has(id)) return id; } } function projectLatestBoundaryWindow(entries) { const boundaryIndex = entries.findLastIndex((entry) => { const type = entry && typeof entry === "object" && !Array.isArray(entry) ? entry.type : void 0; return type === "compaction" || type === "reset"; }); if (boundaryIndex < 0) return [...entries]; const boundary = entries[boundaryIndex]; const firstKeptIndex = typeof boundary.firstKeptEntryId === "string" ? entries.findIndex((entry, index) => index < boundaryIndex && recordId(entry) === boundary.firstKeptEntryId) : -1; return [...firstKeptIndex < 0 ? [] : entries.slice(firstKeptIndex, boundaryIndex).filter((entry) => { const role = entry?.message?.role; return role === "user" || role === "assistant"; }), ...entries.slice(boundaryIndex + 1)]; } function buildSessionResetBoundaryEvent(params) { const entries = params.events.filter((event) => event !== null && typeof event === "object" && !Array.isArray(event) && event.type !== "session"); const activeEntries = selectSessionTranscriptLeafControlledPath(entries) ?? entries; const firstKeptEntryId = recordId((params.context === "preserve-tail" ? selectRecentUserAssistantReplayRecords(projectLatestBoundaryWindow(activeEntries)) : [])[0]); return { type: "reset", id: uniqueBoundaryId(params.events), parentId: recordId(activeEntries.at(-1)) ?? null, timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason: params.reason, ...firstKeptEntryId ? { firstKeptEntryId } : {} }; } //#endregion //#region src/config/sessions/session-accessor.sqlite-lifecycle.ts async function withCommittedHistoryMaintenance({ agentId, storePath }, run, options = {}) { let committed = false; try { return await run((database) => { deferOpenClawAgentPostCommitPublication(database, () => { committed = true; }); }, () => { committed = true; }); } finally { if (committed && options.scheduleNext !== false) kickSessionHistoryDiskBudgetMaintenance({ agentId, storePath, force: true }); } } async function cleanupSessionLifecycleArtifactsCore(params) { const sessionKeySegmentPrefix = params.sessionKeySegmentPrefix.trim(); const transcriptContentMarker = params.transcriptContentMarker; const pluginOwnerId = params.pluginOwnerId?.trim(); if (!sessionKeySegmentPrefix || !transcriptContentMarker) return { removedEntries: 0, archivedTranscriptArtifacts: 0 }; const resolved = resolveSqliteReadScope({ ...params.agentId ? { agentId: params.agentId } : {}, storePath: params.storePath }); const databaseOptions = toDatabaseOptions(resolved); if (!withOpenClawAgentDatabaseReadOnly(() => true, databaseOptions).found) return { removedEntries: 0, archivedTranscriptArtifacts: 0 }; const cleanupPlan = await runExclusiveSqliteSessionWrite(resolved, async () => { const database = openOpenClawAgentDatabase(databaseOptions); return planSessionLifecycleArtifactCleanup(database, { ...params.agentId !== void 0 ? { agentId: resolved.agentId } : {}, archiveRemovedEntryTranscripts: params.archiveRemovedEntryTranscripts !== false, archiveDirectory: resolveSqliteTranscriptArchiveDirectory(resolved), ...pluginOwnerId ? { pluginOwnerId } : {}, sessionKeySegmentPrefix, transcriptContentMarker, orphanTranscriptMinAgeMs: params.orphanTranscriptMinAgeMs, nowMs: params.nowMs ?? Date.now() }); }); if (cleanupPlan.entries.length === 0 && cleanupPlan.deletePlans.length === 0) { await publishSessionStateArchives(resolved, []); return { removedEntries: 0, archivedTranscriptArtifacts: 0 }; } const committed = await withSqliteSessionDeletions(resolved, cleanupPlan.entries.flatMap(({ expectedEntry: entry, sessionKey }) => entry ? [{ entry, sessionKey }] : []), async (assertCurrent) => await runExclusiveSqliteSessionReclamation(async () => { const materializedPlans = await materializeSessionStateDeletePlans(cleanupPlan.deletePlans); return await runExclusiveSqliteSessionWrite(resolved, async () => { assertCurrent(); const plan = createLifecycleArtifactReclamationPlan({ databaseOptions, entries: cleanupPlan.entries, materializedPlans }); const reclaimed = await runSqliteSessionReclamation({ assertCommitAllowed: assertCurrent, forceInProcess: hasPreparedNativeSessionDeletion(), plan }); if (reclaimed.kind !== plan.kind) throw new Error(`SQLite session reclamation returned ${reclaimed.kind} for ${plan.kind}`); emitCommittedSessionEntryRemovals(cleanupPlan.entries); return reclaimed.value; }); }), { additionalIdentities: cleanupPlan.deletePlans.map((plan) => plan.sessionId) }); const deletedEntries = Math.max(committed.removedEntries, new Set(cleanupPlan.deletePlans.map((plan) => plan.sessionId)).size); await refreshSqliteSessionPlannerStatisticsBestEffort(resolved, deletedEntries); const archivedTranscripts = await publishSessionStateArchives(resolved, committed.archivedTranscripts); return { removedEntries: committed.removedEntries, archivedTranscriptArtifacts: archivedTranscripts.length }; } /** Resets one persisted session entry using SQLite session rows. */ async function resetSessionEntryLifecycle(params) { const agentId = params.agentId ?? parseAgentSessionKey(params.target.canonicalKey)?.agentId; const resolved = resolveSqliteStoreScope(params.storePath, { agentId }); return await withCommittedHistoryMaintenance({ agentId: resolved.agentId, storePath: params.storePath }, async (recordCommit) => runExclusiveSqliteSessionWrite(resolved, async () => { params.commitGuard?.(); const database = openOpenClawAgentDatabase(toDatabaseOptions(resolved)); const targetSnapshot = readLifecycleTargetSnapshot(database, params.target); const current = targetSnapshot[0]; const nextEntry = await params.buildNextEntry({ currentEntry: current ? cloneSessionEntry(current.entry) : void 0, primaryKey: params.target.canonicalKey }); const shouldAppendResetBoundary = params.resetBoundary && current?.entry.sessionId && !sqliteSessionEntriesEqual(current.entry, nextEntry); const mutation = { nextEntry: cloneSessionEntry(nextEntry), ...current ? { previousEntry: cloneSessionEntry(current.entry) } : {}, ...current?.entry.sessionId ? { previousSessionId: current.entry.sessionId } : {} }; runSqliteSessionDeletionTransaction((transactionDb) => { params.commitGuard?.(); assertLifecycleTargetUnchanged(transactionDb, params.target, current?.entry, "reset"); if (shouldAppendResetBoundary && current?.entry.sessionId && params.resetBoundary) { const boundaryScope = { ...resolved, sessionId: current.entry.sessionId, sessionKey: current.sessionKey }; ensureTranscriptHeader(transactionDb, boundaryScope, resolveResetBoundaryHeaderCwd(current.entry, params.resetBoundary.cwd)); const event = buildSessionResetBoundaryEvent({ events: loadTranscriptEventsFromDatabase(transactionDb, current.entry.sessionId, { projection: "reset-boundary" }), ...params.resetBoundary }); if (appendTranscriptEventsInTransaction(transactionDb, boundaryScope, [event]) !== 1) throw new Error(`Failed to append reset boundary for ${current.sessionKey}`); } writeSessionEntry(transactionDb, params.target.canonicalKey, nextEntry, { previousEntry: current?.entry ?? null }); recordCommit(transactionDb); }, toDatabaseOptions(resolved)); if (current) emitSessionIdentityMutation({ kind: "reset", previous: { ...current.entry.sessionId ? { sessionId: current.entry.sessionId } : {}, sessionKeys: targetSnapshot.map((row) => row.sessionKey) }, current: { ...nextEntry.sessionId ? { sessionId: nextEntry.sessionId } : {}, sessionKeys: [params.target.canonicalKey] } }); else emitSessionIdentityMutation({ kind: "create", previous: { sessionKeys: [] }, current: { ...nextEntry.sessionId ? { sessionId: nextEntry.sessionId } : {}, sessionKeys: [params.target.canonicalKey] } }); await params.afterEntryMutation?.(mutation); return { ...mutation, archivedTranscripts: [] }; })); } async function deleteSqliteSessionEntryLifecycleInternal(params, allowLockedEntryRemoval, expectedPluginOwnerId) { const agentId = params.agentId ?? parseAgentSessionKey(params.target.canonicalKey)?.agentId; const resolved = resolveSqliteStoreScope(params.storePath, { agentId }); return await withCommittedHistoryMaintenance(params, async (recordCommit, markCommitted) => deleteSqliteSessionEntryLifecycleLocked(resolved, params, allowLockedEntryRemoval, expectedPluginOwnerId, recordCommit, markCommitted)); } const DELETE_EXPECTED_ENTRY_MISMATCH = Symbol("delete-expected-entry-mismatch"); async function deleteSqliteSessionEntryLifecycleLocked(resolved, params, allowLockedEntryRemoval, expectedPluginOwnerId, recordCommit, markCommitted) { const prepared = await runExclusiveSqliteSessionWrite(resolved, async () => { params.commitGuard?.(); const database = openOpenClawAgentDatabase(toDatabaseOptions(resolved)); const targetSnapshot = readLifecycleTargetSnapshot(database, params.target); const current = targetSnapshot[0]; if (!current) return null; if (!shouldDeleteSqliteSessionEntryLifecycle(database, current.entry, params)) return DELETE_EXPECTED_ENTRY_MISMATCH; if (current.entry.modelSelectionLocked === true && !allowLockedEntryRemoval) throw new Error(MODEL_SELECTION_LOCK_REMOVAL_MESSAGE); if (expectedPluginOwnerId && targetSnapshot.some(({ entry, sessionKey }) => isAgentHarnessSessionKey(sessionKey) || entry.agentHarnessId !== void 0 || entry.modelSelectionLocked !== true || normalizeOptionalString(entry.pluginOwnerId) !== expectedPluginOwnerId)) throw new Error(MODEL_SELECTION_LOCK_REMOVAL_MESSAGE); const referencedAfterDelete = readReferencedSessionIdsAfterTargetMutation(database, params.target); const deleteTranscriptState = params.archiveTranscript || params.deleteTranscriptWithoutArchive === true; const archiveDirectory = resolveSqliteTranscriptArchiveDirectory(resolved); const entryPlans = deleteTranscriptState ? targetSnapshot.flatMap(({ entry }) => planSessionStateAfterEntryRemoval({ archiveDirectory, archiveTranscript: params.archiveTranscript, database, entry, reason: "deleted", referencedSessionIds: referencedAfterDelete })) : []; const entryPlanIds = new Set(entryPlans.map((plan) => plan.sessionId)); const historicalGenerationIds = deleteTranscriptState ? readSessionGenerationIdsForKeys(database, [ params.target.canonicalKey, ...params.target.storeKeys, ...targetSnapshot.map((row) => row.sessionKey) ]).filter((sessionId) => !entryPlanIds.has(sessionId)) : []; const preflightFence = collectAdmissionProtectedSessionIds({ database, storePath: params.storePath }); for (const sessionId of historicalGenerationIds) if (preflightFence.has(sessionId) && !referencedAfterDelete.has(sessionId)) throw new Error(`cannot delete session history while work is in flight for ${sessionId}; retry after the run completes`); return { archiveDirectory, current, entryPlans, historicalGenerationIds, targetSnapshot }; }); if (!prepared) { await publishSessionStateArchives(resolved, []); return { archivedTranscripts: [], deleted: false }; } if (prepared === DELETE_EXPECTED_ENTRY_MISMATCH) { await publishSessionStateArchives(resolved, []); return expectedEntryMismatchResult([]); } return await withSqliteSessionDeletions(resolved, prepared.targetSnapshot, async (assertCurrent) => { const historicalArchivedTranscripts = []; for (const sessionId of prepared.historicalGenerationIds) { const plan = await runExclusiveSqliteSessionWrite(resolved, async () => { params.commitGuard?.(); const database = openOpenClawAgentDatabase(toDatabaseOptions(resolved)); const targetSnapshot = readLifecycleTargetSnapshot(database, params.target); if (!sqliteLifecycleTargetSnapshotsEqual(prepared.targetSnapshot, targetSnapshot) || !shouldDeleteSqliteSessionEntryLifecycle(database, targetSnapshot[0]?.entry, params)) return DELETE_EXPECTED_ENTRY_MISMATCH; const referencedAfterDelete = readReferencedSessionIdsAfterTargetMutation(database, params.target); if (referencedAfterDelete.has(sessionId)) return null; if (collectAdmissionProtectedSessionIds({ database, storePath: params.storePath }).has(sessionId)) throw new Error(`cannot delete session history while work is in flight for ${sessionId}; retry after the run completes`); return planSessionStateDeleteIfUnreferenced({ archiveDirectory: prepared.archiveDirectory, archiveTranscript: params.archiveTranscript, database, reason: "deleted", referencedSessionIds: referencedAfterDelete, sessionId }); }); if (plan === DELETE_EXPECTED_ENTRY_MISMATCH) return expectedEntryMismatchResult(historicalArchivedTranscripts); if (!plan) continue; const archivedGeneration = await runExclusiveSqliteSessionReclamation(async () => { const materializedGeneration = await materializeSessionStateDeletePlans([plan]); return await runExclusiveSqliteSessionWrite(resolved, async () => { params.commitGuard?.(); assertCurrent(); const database = openOpenClawAgentDatabase(toDatabaseOptions(resolved)); const targetSnapshot = readLifecycleTargetSnapshot(database, params.target); if (!sqliteLifecycleTargetSnapshotsEqual(prepared.targetSnapshot, targetSnapshot) || !shouldDeleteSqliteSessionEntryLifecycle(database, targetSnapshot[0]?.entry, params)) return DELETE_EXPECTED_ENTRY_MISMATCH; const protectedSessionIds = collectAdmissionProtectedSessionIds({ database, storePath: params.storePath }); if (protectedSessionIds.has(sessionId)) throw new Error(`cannot delete session history while work is in flight for ${sessionId}; retry after the run completes`); const reclamationPlan = createHistoricalGenerationReclamationPlan({ databaseOptions: toDatabaseOptions(resolved), deleteParams: params, materializedPlans: materializedGeneration, preparedTargetSnapshot: prepared.targetSnapshot, protectedSessionIds, sessionId }); const reclaimed = await runSqliteSessionReclamation({ assertCommitAllowed: () => { params.commitGuard?.(); assertCurrent(); }, forceInProcess: hasPreparedNativeSessionDeletion(), onInProcessCommit: recordCommit, plan: reclamationPlan }); if (reclaimed.kind !== reclamationPlan.kind) throw new Error(`SQLite session reclamation returned ${reclaimed.kind} for ${reclamationPlan.kind}`); return reclaimed.value; }); }); if (archivedGeneration === DELETE_EXPECTED_ENTRY_MISMATCH) return expectedEntryMismatchResult(historicalArchivedTranscripts); if (archivedGeneration.expectedEntryMismatch) return expectedEntryMismatchResult(historicalArchivedTranscripts); if (archivedGeneration.deleted) markCommitted(); const publishedGeneration = await publishSessionStateArchives(resolved, archivedGeneration.archivedTranscripts); emitArchivedTranscriptUpdates(publishedGeneration); historicalArchivedTranscripts.push(...publishedGeneration); } const result = await runExclusiveSqliteSessionReclamation(async () => { const materializedPlans = await materializeSessionStateDeletePlans(prepared.entryPlans); return await runExclusiveSqliteSessionWrite(resolved, async () => { params.commitGuard?.(); assertCurrent(); const reclamationPlan = createSessionEntryReclamationPlan({ databaseOptions: toDatabaseOptions(resolved), deleteParams: params, materializedPlans, preparedTargetSnapshot: prepared.targetSnapshot }); const reclaimed = await runSqliteSessionReclamation({ assertCommitAllowed: () => { params.commitGuard?.(); assertCurrent(); }, forceInProcess: hasPreparedNativeSessionDeletion(), onInProcessCommit: recordCommit, plan: reclamationPlan }); if (reclaimed.kind !== reclamationPlan.kind) throw new Error(`SQLite session reclamation returned ${reclaimed.kind} for ${reclamationPlan.kind}`); return reclaimed.value; }); }); if (result.deleted) markCommitted(); if (result.deleted) { emitSessionIdentityMutation({ kind: "delete", previous: { ...prepared.current.entry.sessionId ? { sessionId: prepared.current.entry.sessionId } : {}, sessionKeys: prepared.targetSnapshot.map((row) => row.sessionKey) } }); deletePersonalGitHubSessionReceipts({ agentId: resolved.agentId, env: resolved.env, sessionKeys: [ params.target.canonicalKey, ...params.target.storeKeys, ...prepared.targetSnapshot.map((row) => row.sessionKey) ] }); } result.archivedTranscripts = await publishSessionStateArchives(resolved, result.archivedTranscripts); emitArchivedTranscriptUpdates(result.archivedTranscripts); result.archivedTranscripts.push(...historicalArchivedTranscripts); return result; }, { additionalIdentities: prepared.historicalGenerationIds }); } function expectedEntryMismatchResult(archivedTranscripts) { return { archivedTranscripts, deleted: false, expectedEntryMismatch: true }; } /** Deletes one persisted session entry using SQLite session rows. */ async function deleteSessionEntryLifecycle(params) { return await deleteSqliteSessionEntryLifecycleInternal(params, false); } /** Disk-budget owner: delete one exact archived row without recursively scheduling another pass. */ async function deleteDiskBudgetSessionEntryLifecycle(params) { const agentId = params.agentId ?? parseAgentSessionKey(params.target.canonicalKey)?.agentId; const resolved = resolveSqliteStoreScope(params.storePath, { agentId }); return await withCommittedHistoryMaintenance(params, async (recordCommit, markCommitted) => await deleteSqliteSessionEntryLifecycleLocked(resolved, params, false, void 0, recordCommit, markCommitted), { scheduleNext: false }); } /** Rolls back one exact locked row created by failed trusted harness initialization. */ async function rollbackAgentHarnessSessionEntryLifecycle(params) { const hasExactTarget = params.target.storeKeys.length === 1 && params.target.storeKeys[0] === params.target.canonicalKey; const expectedEntryError = resolveAgentHarnessSessionStoreEntryError(params.target.canonicalKey, params.expectedEntry); if (!hasExactTarget || expectedEntryError || !isValidAgentHarnessSessionStoreEntry(params.target.canonicalKey, params.expectedEntry)) throw new Error(expectedEntryError ?? "Model-selection-locked sessions cannot be removed, unlocked, or reassigned."); return await deleteSqliteSessionEntryLifecycleInternal(params, true); } /** Rolls back one exact locked CLI row created by a failed plugin initializer. */ async function rollbackPluginOwnedSessionEntryLifecycle(params) { const expectedEntry = params.expectedEntry; const validPluginOwner = normalizeOptionalString(expectedEntry.pluginOwnerId); const expectedPluginOwner = normalizeOptionalString(params.expectedPluginOwnerId); if (isAgentHarnessSessionKey(params.target.canonicalKey) || expectedEntry.agentHarnessId !== void 0 || expectedEntry.modelSelectionLocked !== true || !validPluginOwner || validPluginOwner !== expectedPluginOwner) throw new Error(MODEL_SELECTION_LOCK_REMOVAL_MESSAGE); return await deleteSqliteSessionEntryLifecycleInternal(params, true, expectedPluginOwner); } //#endregion export { rollbackAgentHarnessSessionEntryLifecycle as a, resetSessionEntryLifecycle as i, deleteDiskBudgetSessionEntryLifecycle as n, rollbackPluginOwnedSessionEntryLifecycle as o, deleteSessionEntryLifecycle as r, buildSessionResetBoundaryEvent as s, cleanupSessionLifecycleArtifactsCore as t };