blob: d23b379d47196b2bd9b322cef411f857ff4895de [file]
import type {
AgentRunEvent,
AgentRunEventType,
AgentRunHeader,
RuntimeEvent,
RuntimeContinuationAuthorityStore,
SessionHeader,
SessionListFilter,
SessionSummary,
StoredMessage,
ToolBoundaryProtocol,
TurnRecord,
} from '@maka/core';
import {
createSqliteAgentRunStore,
type AgentRunIdentitySearchResult,
type AdmitRootTurnInput,
type AdmitRootTurnResult,
type CommitRootTurnStartRejectionInput,
type BoundedEvidenceReadResult,
type DurableAgentRunStore,
type DurableRuntimeEventStore,
type EvidenceReadBudget,
type RootTurnAdmission,
type RootTurnSourceMessageReceipt,
} from './agent-run-store.js';
import {
createConversationOperationalStateStore,
type ConversationOperationalStateStore,
} from './conversation-operational-state.js';
import {
createSqliteMessageReceiptStore,
type MessageReceiptStore,
} from './message-receipt-store.js';
import { createSessionStore, type SessionAuthorityStore } from './session-store.js';
import {
assertStorageRootLease,
runWithStorageRootLease,
StorageRootAuthorityError,
type StorageRootKind,
type StorageRootLease,
} from './root-authority.js';
import {
closeSqliteInteractionStoreFacade,
openSqliteInteractiveInteractionStoreForRead,
openSqliteInteractiveInteractionStoreForWrite,
type InteractiveInteractionStoreReaderFacade,
type InteractiveInteractionStoreWriterFacade,
} from './interaction-store.js';
import {
openRuntimeEventPersistence,
openRuntimeEventReadPersistence,
} from './runtime-event-persistence.js';
import type {
CommitToolOutcomeInput,
CommitToolPreparedInput,
SessionRuntimeEventEntry,
ToolCommitResult,
ToolOperationRecord,
} from './sqlite-runtime-store.js';
const executionStoresWriterBrand: unique symbol = Symbol('ExecutionStoresWriter');
const executionStoresReaderBrand: unique symbol = Symbol('ExecutionStoresReader');
const executionStoresWriterKinds = new WeakMap<object, StorageRootKind>();
const executionStoresReaderKinds = new WeakMap<object, StorageRootKind>();
const executionStoresWritersByLease = new WeakMap<object, object>();
const executionStoresWritersOpeningByLease = new WeakMap<object, Promise<void>>();
export { normalizeRootTurnAdmissionPayload } from './agent-run-store.js';
export {
isSessionNotFoundError,
SessionReadMarkerMessageNotFoundError,
} from './session-store.js';
export {
SessionMetadataConflictError,
SessionMetadataVersionConflictError,
} from './sqlite-session-metadata-store.js';
export type {
AgentRunIdentitySearchResult,
AdmitRootTurnInput,
AdmitRootTurnResult,
CommitRootTurnStartRejectionInput,
CommitRootTurnStartRejectionResult,
BoundedEvidenceReadResult,
EvidenceReadBudget,
ImmutableSteeringMessageProof,
RootTurnAdmission,
RootTurnAdmissionStore,
RootTurnStartRejectionStore,
RootTurnSourceMessage,
RootTurnSourceMessageReceipt,
RootTurnStartRejection,
} from './agent-run-store.js';
export type {
MessageOperationReceipt,
MessageReceiptOperation,
MessageReceiptStore,
} from './message-receipt-store.js';
export type {
ProbeSessionRemovalResult,
SessionCatalogPageCursor,
SessionCatalogPageResult,
SessionCatalogRecord,
SessionHeaderSnapshot,
} from './session-store.js';
export type ExecutionSessionWriter = SessionAuthorityStore;
export type ExecutionAgentRunWriter = DurableAgentRunStore;
export type ExecutionRuntimeEventWriter = DurableRuntimeEventStore &
RuntimeContinuationAuthorityStore & {
readonly toolBoundaryProtocol: ToolBoundaryProtocol;
commitToolPrepared(input: CommitToolPreparedInput): Promise<ToolCommitResult>;
commitToolOutcome(input: CommitToolOutcomeInput): Promise<ToolCommitResult>;
listUnsettledToolOperations(sessionId: string): Promise<ToolOperationRecord[]>;
appendRuntimePartialBatch(
sessionId: string,
runId: string,
events: readonly RuntimeEvent[],
): Promise<void>;
readSessionRuntimeEventEntries(sessionId: string): Promise<SessionRuntimeEventEntry[]>;
};
export type ExecutionMessageReceiptWriter = MessageReceiptStore;
interface ExecutionStoresWriterBase<K extends StorageRootKind> {
readonly kind: K;
readonly [executionStoresWriterBrand]: K;
purgeConversationOperationalState(sessionId: string): Promise<void>;
readonly sessionStore: Readonly<ExecutionSessionWriter>;
readonly agentRunStore: Readonly<ExecutionAgentRunWriter>;
readonly runtimeEventStore: Readonly<ExecutionRuntimeEventWriter>;
readonly messageReceiptStore: Readonly<ExecutionMessageReceiptWriter>;
}
export interface InteractiveExecutionStoresWriter extends ExecutionStoresWriterBase<'interactive'> {
readonly interactionStore: InteractiveInteractionStoreWriterFacade;
}
export type HeadlessExecutionStoresWriter = ExecutionStoresWriterBase<'headless'>;
interface ExecutionStoresWriters {
readonly interactive: InteractiveExecutionStoresWriter;
readonly headless: HeadlessExecutionStoresWriter;
}
export type ExecutionStoresWriter<K extends StorageRootKind> = ExecutionStoresWriters[K];
export interface ExecutionSessionReader {
list(filter?: SessionListFilter): Promise<SessionSummary[]>;
readHeader(sessionId: string): Promise<SessionHeader>;
readMessages(sessionId: string): Promise<StoredMessage[]>;
listTurns(sessionId: string): Promise<TurnRecord[]>;
close?(): Promise<void>;
}
export interface ExecutionAgentRunReader {
readRun(sessionId: string, runId: string): Promise<AgentRunHeader>;
findRunsById(runId: string, limit: number): Promise<AgentRunIdentitySearchResult>;
listSessionRuns(sessionId: string): Promise<AgentRunHeader[]>;
listSessionRunsBounded(sessionId: string, limit: number): Promise<AgentRunIdentitySearchResult>;
readEvents(sessionId: string, runId: string): Promise<AgentRunEvent[]>;
readEventsBounded(
sessionId: string,
runId: string,
budget: EvidenceReadBudget,
): Promise<BoundedEvidenceReadResult<AgentRunEvent>>;
readEventsByTypeBounded(
sessionId: string,
runId: string,
type: AgentRunEventType,
budget: EvidenceReadBudget,
): Promise<BoundedEvidenceReadResult<AgentRunEvent>>;
readEventProjection(
sessionId: string,
type: AgentRunEventType,
): Promise<AgentRunEvent | null | undefined>;
readRootTurnAdmission(sessionId: string, turnId: string): Promise<RootTurnAdmission | undefined>;
readRootTurnSourceMessageReceipt(
sessionId: string,
sourceMessageId: string,
): Promise<RootTurnSourceMessageReceipt | undefined>;
}
export interface ExecutionRuntimeEventReader {
readRuntimeEvents(sessionId: string, runId: string): Promise<RuntimeEvent[]>;
readRuntimeEventsBounded(
sessionId: string,
runId: string,
budget: EvidenceReadBudget,
): Promise<BoundedEvidenceReadResult<RuntimeEvent>>;
readImmutableRuntimeEvents(sessionId: string, runId: string): Promise<RuntimeEvent[]>;
readSessionRuntimeEvents(sessionId: string): Promise<RuntimeEvent[]>;
}
interface ExecutionStoresReaderBase<K extends StorageRootKind> {
readonly kind: K;
readonly [executionStoresReaderBrand]: K;
readonly sessionStore: Readonly<ExecutionSessionReader>;
readonly agentRunStore: Readonly<ExecutionAgentRunReader>;
readonly runtimeEventStore: Readonly<ExecutionRuntimeEventReader>;
}
export interface InteractiveExecutionStoresReader extends ExecutionStoresReaderBase<'interactive'> {
readonly interactionStore: InteractiveInteractionStoreReaderFacade;
}
export type HeadlessExecutionStoresReader = ExecutionStoresReaderBase<'headless'>;
interface ExecutionStoresReaders {
readonly interactive: InteractiveExecutionStoresReader;
readonly headless: HeadlessExecutionStoresReader;
}
export type ExecutionStoresReader<K extends StorageRootKind> = ExecutionStoresReaders[K];
export function authenticateExecutionStoresWriter<K extends StorageRootKind>(
stores: ExecutionStoresWriter<K>,
expectedKind: K,
): ExecutionStoresWriter<K> {
if (executionStoresWriterKinds.get(stores) !== expectedKind) {
throw invalidExecutionStores(expectedKind, 'write');
}
return stores;
}
export function authenticateExecutionStoresReader<K extends StorageRootKind>(
stores: ExecutionStoresReader<K>,
expectedKind: K,
): ExecutionStoresReader<K> {
if (executionStoresReaderKinds.get(stores) !== expectedKind) {
throw invalidExecutionStores(expectedKind, 'read');
}
return stores;
}
export async function openInteractiveExecutionStoresForWrite(
lease: StorageRootLease<'interactive', 'write'>,
): Promise<ExecutionStoresWriter<'interactive'>> {
const interactionStore = await openSqliteInteractiveInteractionStoreForWrite(lease);
return openExecutionStoresForWrite(lease, 'interactive', { interactionStore });
}
export async function openHeadlessExecutionStoresForWrite(
lease: StorageRootLease<'headless', 'write'>,
): Promise<ExecutionStoresWriter<'headless'>> {
return openExecutionStoresForWrite(lease, 'headless', {});
}
async function openExecutionStoresForWrite<K extends StorageRootKind, E extends object>(
lease: StorageRootLease<K, 'write'>,
kind: K,
extension: E,
): Promise<ExecutionStoresWriterBase<K> & E> {
await assertStorageRootLease(lease, kind, 'write');
const existing = executionStoresWritersByLease.get(lease);
if (existing) return existing as ExecutionStoresWriterBase<K> & E;
const opening = executionStoresWritersOpeningByLease.get(lease);
if (opening) {
await opening;
return openExecutionStoresForWrite(lease, kind, extension);
}
let releaseOpening!: () => void;
const openingGate = new Promise<void>((resolve) => {
releaseOpening = resolve;
});
executionStoresWritersOpeningByLease.set(lease, openingGate);
try {
return await createExecutionStoresForWrite(lease, kind, extension);
} finally {
executionStoresWritersOpeningByLease.delete(lease);
releaseOpening();
}
}
async function createExecutionStoresForWrite<K extends StorageRootKind, E extends object>(
lease: StorageRootLease<K, 'write'>,
kind: K,
extension: E,
): Promise<ExecutionStoresWriterBase<K> & E> {
const sessionStore = createSessionStore(lease.canonicalPath);
const agentRunStore = createSqliteAgentRunStore(lease.canonicalPath);
const interactionStore =
'interactionStore' in extension
? (extension.interactionStore as InteractiveInteractionStoreWriterFacade)
: undefined;
const runtimePersistence = await openRuntimeEventPersistence({
workspaceRoot: lease.canonicalPath,
}).catch(async (error) => {
await sessionStore.close?.().catch(() => {});
agentRunStore.close?.();
if (interactionStore) closeSqliteInteractionStoreFacade(interactionStore);
throw error;
});
const runtimeEventStore = runtimePersistence.runtimeEventStore;
let conversationOperationalStateStore: ConversationOperationalStateStore;
try {
conversationOperationalStateStore = createConversationOperationalStateStore(
lease.canonicalPath,
);
} catch (error) {
await closeExecutionStorePersistence(sessionStore, runtimePersistence, {
agentRunStore,
interactionStore,
}).catch(() => {});
throw error;
}
const messageReceiptStore = createSqliteMessageReceiptStore(lease.canonicalPath);
await Promise.all([agentRunStore.ready?.(), messageReceiptStore.ready()]).catch(async (error) => {
await closeExecutionStorePersistence(sessionStore, runtimePersistence, {
agentRunStore,
conversationOperationalStateStore,
messageReceiptStore,
interactionStore,
}).catch(() => {});
throw error;
});
const run = <T>(operation: () => Promise<T>) =>
runWithStorageRootLease(lease, kind, 'write', operation);
const stores: ExecutionStoresWriterBase<K> & E = {
...extension,
kind,
[executionStoresWriterBrand]: kind,
purgeConversationOperationalState: (sessionId) =>
run(() => conversationOperationalStateStore.purge(sessionId)),
sessionStore: {
create: (input, initialBoundary) => run(() => sessionStore.create(input, initialBoundary)),
createImportedSession: (input, messages) =>
run(() => sessionStore.createImportedSession(input, messages)),
probeStableSessionCreate: (sessionId, requestFingerprint) =>
run(() => sessionStore.probeStableSessionCreate(sessionId, requestFingerprint)),
createStableSession: (request, initialBoundary) =>
run(() => sessionStore.createStableSession(request, initialBoundary)),
discardStableConversationCopy: (sessionId, requestFingerprint) =>
run(() => sessionStore.discardStableConversationCopy(sessionId, requestFingerprint)),
importSession: (header, messages) => run(() => sessionStore.importSession(header, messages)),
hasSession: (sessionId) => run(() => sessionStore.hasSession(sessionId)),
createSubagent: (input, initialBoundary) =>
run(() => sessionStore.createSubagent(input, initialBoundary)),
createAgentGraphOperator: (input, request, expectedRevision, initialBoundary) =>
run(() =>
sessionStore.createAgentGraphOperator(input, request, expectedRevision, initialBoundary),
),
readExecutionBoundary: (sessionId) =>
run(() => sessionStore.readExecutionBoundary(sessionId)),
createSandboxBoundaryRequest: (input) =>
run(() => sessionStore.createSandboxBoundaryRequest(input)),
readSandboxBoundaryRequest: (sessionId, requestId) =>
run(() => sessionStore.readSandboxBoundaryRequest(sessionId, requestId)),
listPendingSandboxBoundaryRequests: (sessionId) =>
run(() => sessionStore.listPendingSandboxBoundaryRequests(sessionId)),
listSandboxBoundaryRestartClosures: (sessionId) =>
run(() => sessionStore.listSandboxBoundaryRestartClosures(sessionId)),
settleSandboxBoundaryRequest: (input) =>
run(() => sessionStore.settleSandboxBoundaryRequest(input)),
setExecutionBoundaryKind: (sessionId, boundaryKind, projection) =>
run(() => sessionStore.setExecutionBoundaryKind(sessionId, boundaryKind, projection)),
list: (filter) => run(() => sessionStore.list(filter)),
listCatalogPage: (filter, cursor, limit, expectedRevision) =>
run(() => sessionStore.listCatalogPage(filter, cursor, limit, expectedRevision)),
listHeaders: () => run(() => sessionStore.listHeaders()),
listSessionsWithUnresolvedProject: () =>
run(() => sessionStore.listSessionsWithUnresolvedProject()),
listForRecovery: () => run(() => sessionStore.listForRecovery()),
readHeaderSnapshot: (sessionId) => run(() => sessionStore.readHeaderSnapshot(sessionId)),
readHeaderRecordSnapshot: (sessionId) =>
run(() => sessionStore.readHeaderRecordSnapshot(sessionId)),
readCatalogRecord: (sessionId) => run(() => sessionStore.readCatalogRecord(sessionId)),
probeSessionRemoval: (sessionId) => run(() => sessionStore.probeSessionRemoval(sessionId)),
readMessagesSnapshot: (sessionId) => run(() => sessionStore.readMessagesSnapshot(sessionId)),
readMessagesForRecovery: (sessionId) =>
run(() => sessionStore.readMessagesForRecovery(sessionId)),
listTurnsSnapshot: (sessionId) => run(() => sessionStore.listTurnsSnapshot(sessionId)),
readHeader: (sessionId) => run(() => sessionStore.readHeader(sessionId)),
readMessages: (sessionId) => run(() => sessionStore.readMessages(sessionId)),
listTurns: (sessionId) => run(() => sessionStore.listTurns(sessionId)),
appendMessage: (sessionId, message) =>
run(() => sessionStore.appendMessage(sessionId, message)),
appendMessages: (sessionId, messages) =>
run(() => sessionStore.appendMessages(sessionId, messages)),
updateHeader: (sessionId, patch) => run(() => sessionStore.updateHeader(sessionId, patch)),
updateHeaderVersioned: (sessionId, patch, expectedRevision) =>
run(() => sessionStore.updateHeaderVersioned(sessionId, patch, expectedRevision)),
updateSessionConfiguration: (sessionId, input) =>
run(() => sessionStore.updateSessionConfiguration(sessionId, input)),
markSessionReadThroughMessage: (sessionId, messageId) =>
run(() => sessionStore.markSessionReadThroughMessage(sessionId, messageId)),
markSessionReadThrough: (sessionId, readThroughTs) =>
run(() => sessionStore.markSessionReadThrough(sessionId, readThroughTs)),
archive: (sessionId) => run(() => sessionStore.archive(sessionId)),
unarchive: (sessionId) => run(() => sessionStore.unarchive(sessionId)),
setFlagged: (sessionId, isFlagged) =>
run(() => sessionStore.setFlagged(sessionId, isFlagged)),
rename: (sessionId, name) => run(() => sessionStore.rename(sessionId, name)),
setGeneratedTitleIfAbsent: (sessionId, title) =>
run(() => sessionStore.setGeneratedTitleIfAbsent(sessionId, title)),
remove: (sessionId) => run(() => sessionStore.remove(sessionId)),
setSessionsLifecycleVersioned: (sessions, state) =>
run(() => sessionStore.setSessionsLifecycleVersioned(sessions, state)),
removeSessionsVersioned: (sessions) =>
run(() => sessionStore.removeSessionsVersioned(sessions)),
reconcileOrphanedAgentGraphRetirements: () =>
run(() => sessionStore.reconcileOrphanedAgentGraphRetirements()),
listPendingSessionRetirementCleanupIds: (sessionId) =>
run(() => sessionStore.listPendingSessionRetirementCleanupIds(sessionId)),
completeSessionRetirementCleanup: (sessionId) =>
run(() => sessionStore.completeSessionRetirementCleanup(sessionId)),
close: () =>
closeExecutionStorePersistence(sessionStore, runtimePersistence, {
agentRunStore,
conversationOperationalStateStore,
messageReceiptStore,
interactionStore,
}),
},
agentRunStore: {
createRun: (header, options) => run(() => agentRunStore.createRun(header, options)),
updateRun: (sessionId, runId, patch, options) =>
run(() => agentRunStore.updateRun(sessionId, runId, patch, options)),
readRun: (sessionId, runId) => run(() => agentRunStore.readRun(sessionId, runId)),
findRunsById: (runId, limit) => run(() => agentRunStore.findRunsById(runId, limit)),
listSessionRuns: (sessionId) => run(() => agentRunStore.listSessionRuns(sessionId)),
listSessionRunsBounded: (sessionId, limit) =>
run(() => agentRunStore.listSessionRunsBounded(sessionId, limit)),
listSessionRunsForRecovery: (sessionId) =>
run(() => agentRunStore.listSessionRunsForRecovery(sessionId)),
appendEvent: (sessionId, runId, event, options) =>
run(() => agentRunStore.appendEvent(sessionId, runId, event, options)),
readEvents: (sessionId, runId) => run(() => agentRunStore.readEvents(sessionId, runId)),
readEventsBounded: (sessionId, runId, budget) =>
run(() => agentRunStore.readEventsBounded(sessionId, runId, budget)),
readEventsByTypeBounded: (sessionId, runId, type, budget) =>
run(() => agentRunStore.readEventsByTypeBounded(sessionId, runId, type, budget)),
readEventsForRecovery: (sessionId, runId) =>
run(() => agentRunStore.readEventsForRecovery(sessionId, runId)),
readEventsForEvidence: (sessionId, runId) =>
run(() => agentRunStore.readEventsForEvidence(sessionId, runId)),
readEventProjection: (sessionId, type) =>
run(() => agentRunStore.readEventProjection(sessionId, type)),
repairEventProjection: (sessionId, type, event, options) =>
run(() => agentRunStore.repairEventProjection(sessionId, type, event, options)),
admitRootTurn: (input: AdmitRootTurnInput): Promise<AdmitRootTurnResult> =>
run(() => agentRunStore.admitRootTurn(input)),
readRootTurnAdmission: (sessionId, turnId) =>
run(() => agentRunStore.readRootTurnAdmission(sessionId, turnId)),
readRootTurnStartRejection: (sessionId, turnId) =>
run(() => agentRunStore.readRootTurnStartRejection(sessionId, turnId)),
commitRootTurnStartRejection: (input: CommitRootTurnStartRejectionInput) =>
run(() => agentRunStore.commitRootTurnStartRejection(input)),
readRootTurnSourceMessageReceipt: (sessionId, sourceMessageId) =>
run(() => agentRunStore.readRootTurnSourceMessageReceipt(sessionId, sourceMessageId)),
listRootTurnAdmissionsForRecovery: (sessionId) =>
run(() => agentRunStore.listRootTurnAdmissionsForRecovery(sessionId)),
},
runtimeEventStore: {
durability: runtimeEventStore.durability,
continuationAuthorityCapability: runtimeEventStore.continuationAuthorityCapability,
toolBoundaryProtocol: runtimePersistence.runtimeCommitStore.toolBoundaryProtocol,
appendRuntimeEvent: (sessionId, runId, event, options) =>
run(() => runtimeEventStore.appendRuntimeEvent(sessionId, runId, event, options)),
appendRuntimePartialBatch: (sessionId, runId, events) =>
run(() => runtimeEventStore.appendRuntimePartialBatch(sessionId, runId, events)),
importConversationCopyRuntimeEvents: (sessionId, batches) =>
run(() => runtimeEventStore.importConversationCopyRuntimeEvents(sessionId, batches)),
ensureTerminalRuntimeEventDurable: (sessionId, runId, event) =>
run(() => runtimeEventStore.ensureTerminalRuntimeEventDurable(sessionId, runId, event)),
readRuntimeEvents: (sessionId, runId) =>
run(() => runtimeEventStore.readRuntimeEvents(sessionId, runId)),
readRuntimeEventsBounded: (sessionId, runId, budget) =>
run(() => runtimeEventStore.readRuntimeEventsBounded(sessionId, runId, budget)),
readImmutableRuntimeEvents: (sessionId, runId) =>
run(() => runtimeEventStore.readImmutableRuntimeEvents(sessionId, runId)),
readImmutableRuntimePrefix: (input) =>
run(() => runtimeEventStore.readImmutableRuntimePrefix(input)),
readSessionRuntimeEvents: (sessionId) =>
run(() => runtimeEventStore.readSessionRuntimeEvents(sessionId)),
readSessionRuntimeEventEntries: (sessionId) =>
run(() => runtimeEventStore.readSessionRuntimeEventEntries(sessionId)),
claimContinuation: (input) => run(() => runtimeEventStore.claimContinuation(input)),
readContinuationClaimByBoundary: (boundaryDigest) =>
run(() => runtimeEventStore.readContinuationClaimByBoundary(boundaryDigest)),
readContinuationClaimStateByBoundary: (boundaryDigest) =>
run(() => runtimeEventStore.readContinuationClaimStateByBoundary(boundaryDigest)),
listContinuationClaimsForRecovery: (sessionId) =>
run(() => runtimeEventStore.listContinuationClaimsForRecovery(sessionId)),
commitContinuationStart: (input) =>
run(() => runtimeEventStore.commitContinuationStart(input)),
commitContinuationRepairStart: (input) =>
run(() => runtimeEventStore.commitContinuationRepairStart(input)),
readImmutableSteeringMessageProof: (sessionId, messageId) =>
run(() => runtimeEventStore.readImmutableSteeringMessageProof(sessionId, messageId)),
repairImmutableSteeringMessageProofsForRecovery: (sessionId) =>
run(() => runtimeEventStore.repairImmutableSteeringMessageProofsForRecovery(sessionId)),
commitToolPrepared: (input) =>
run(() => runtimePersistence.runtimeCommitStore.commitToolPrepared(input)),
commitToolOutcome: (input) =>
run(() => runtimePersistence.runtimeCommitStore.commitToolOutcome(input)),
listUnsettledToolOperations: (sessionId) =>
run(() => runtimePersistence.runtimeCommitStore.listUnsettledToolOperations(sessionId)),
},
messageReceiptStore: {
beginHostEpoch: (hostEpoch) => run(() => messageReceiptStore.beginHostEpoch(hostEpoch)),
read: (hostEpoch, operation, sessionId, operationId) =>
run(() => messageReceiptStore.read(hostEpoch, operation, sessionId, operationId)),
commit: (hostEpoch, operation, sessionId, operationId, receipt) =>
run(() =>
messageReceiptStore.commit(hostEpoch, operation, sessionId, operationId, receipt),
),
},
};
freezeExecutionStoresFacade(stores);
executionStoresWriterKinds.set(stores, kind);
executionStoresWritersByLease.set(lease, stores);
return stores;
}
export async function openInteractiveExecutionStoresForRead(
lease: StorageRootLease<'interactive', 'read'>,
): Promise<ExecutionStoresReader<'interactive'>> {
const interactionStore = await openSqliteInteractiveInteractionStoreForRead(lease);
return openExecutionStoresForRead(lease, 'interactive', { interactionStore });
}
export async function openHeadlessExecutionStoresForRead(
lease: StorageRootLease<'headless', 'read'>,
): Promise<ExecutionStoresReader<'headless'>> {
return openExecutionStoresForRead(lease, 'headless', {});
}
async function openExecutionStoresForRead<K extends StorageRootKind, E extends object>(
lease: StorageRootLease<K, 'read'>,
kind: K,
extension: E,
): Promise<ExecutionStoresReaderBase<K> & E> {
await assertStorageRootLease(lease, kind, 'read');
const sessionStore = createSessionStore(lease.canonicalPath);
const agentRunStore = createSqliteAgentRunStore(lease.canonicalPath);
const interactionStore =
'interactionStore' in extension
? (extension.interactionStore as InteractiveInteractionStoreReaderFacade)
: undefined;
await agentRunStore.ready?.().catch(async (error) => {
await sessionStore.close?.().catch(() => {});
agentRunStore.close?.();
if (interactionStore) closeSqliteInteractionStoreFacade(interactionStore);
throw error;
});
const runtimePersistence = await openRuntimeEventReadPersistence({
workspaceRoot: lease.canonicalPath,
}).catch(async (error) => {
await sessionStore.close?.().catch(() => {});
agentRunStore.close?.();
if (interactionStore) closeSqliteInteractionStoreFacade(interactionStore);
throw error;
});
const runtimeEventStore = runtimePersistence.runtimeEventStore;
const run = <T>(operation: () => Promise<T>) =>
runWithStorageRootLease(lease, kind, 'read', operation);
const stores: ExecutionStoresReaderBase<K> & E = {
...extension,
kind,
[executionStoresReaderBrand]: kind,
sessionStore: {
list: (filter) => run(() => sessionStore.list(filter)),
readHeader: (sessionId) => run(() => sessionStore.readHeaderSnapshot(sessionId)),
readMessages: (sessionId) => run(() => sessionStore.readMessagesSnapshot(sessionId)),
listTurns: (sessionId) => run(() => sessionStore.listTurnsSnapshot(sessionId)),
close: () =>
closeExecutionStorePersistence(sessionStore, runtimePersistence, {
agentRunStore,
interactionStore,
}),
},
agentRunStore: {
readRun: (sessionId, runId) => run(() => agentRunStore.readRun(sessionId, runId)),
findRunsById: (runId, limit) => run(() => agentRunStore.findRunsById(runId, limit)),
listSessionRuns: (sessionId) => run(() => agentRunStore.listSessionRuns(sessionId)),
listSessionRunsBounded: (sessionId, limit) =>
run(() => agentRunStore.listSessionRunsBounded(sessionId, limit)),
readEvents: (sessionId, runId) => run(() => agentRunStore.readEvents(sessionId, runId)),
readEventsBounded: (sessionId, runId, budget) =>
run(() => agentRunStore.readEventsBounded(sessionId, runId, budget)),
readEventsByTypeBounded: (sessionId, runId, type, budget) =>
run(() => agentRunStore.readEventsByTypeBounded(sessionId, runId, type, budget)),
readEventProjection: (sessionId, type) =>
run(() => agentRunStore.readEventProjection(sessionId, type)),
readRootTurnAdmission: (sessionId, turnId) =>
run(() => agentRunStore.readRootTurnAdmission(sessionId, turnId)),
readRootTurnSourceMessageReceipt: (sessionId, sourceMessageId) =>
run(() => agentRunStore.readRootTurnSourceMessageReceipt(sessionId, sourceMessageId)),
},
runtimeEventStore: {
readRuntimeEvents: (sessionId, runId) =>
run(() => runtimeEventStore.readRuntimeEvents(sessionId, runId)),
readRuntimeEventsBounded: (sessionId, runId, budget) =>
run(() => runtimeEventStore.readRuntimeEventsBounded(sessionId, runId, budget)),
readImmutableRuntimeEvents: (sessionId, runId) =>
run(() => runtimeEventStore.readImmutableRuntimeEvents(sessionId, runId)),
readSessionRuntimeEvents: (sessionId) =>
run(() => runtimeEventStore.readSessionRuntimeEvents(sessionId)),
},
};
freezeExecutionStoresFacade(stores);
executionStoresReaderKinds.set(stores, kind);
return stores;
}
function freezeExecutionStoresFacade(stores: {
readonly sessionStore: object;
readonly agentRunStore: object;
readonly runtimeEventStore: object;
readonly messageReceiptStore?: object;
}): void {
Object.freeze(stores.sessionStore);
Object.freeze(stores.agentRunStore);
Object.freeze(stores.runtimeEventStore);
if (stores.messageReceiptStore) Object.freeze(stores.messageReceiptStore);
Object.freeze(stores);
}
async function closeExecutionStorePersistence(
sessionStore: { close?(): Promise<void> },
runtimePersistence: { close(): void },
extras: {
agentRunStore?: Pick<DurableAgentRunStore, 'close'>;
conversationOperationalStateStore?: Pick<ConversationOperationalStateStore, 'close'>;
messageReceiptStore?: { close(): void };
interactionStore?:
| InteractiveInteractionStoreReaderFacade
| InteractiveInteractionStoreWriterFacade;
} = {},
): Promise<void> {
const errors: unknown[] = [];
try {
runtimePersistence.close();
} catch (error) {
errors.push(error);
}
try {
await sessionStore.close?.();
} catch (error) {
errors.push(error);
}
try {
extras.agentRunStore?.close?.();
} catch (error) {
errors.push(error);
}
try {
extras.conversationOperationalStateStore?.close();
} catch (error) {
errors.push(error);
}
try {
extras.messageReceiptStore?.close();
} catch (error) {
errors.push(error);
}
try {
if (extras.interactionStore) {
closeSqliteInteractionStoreFacade(extras.interactionStore);
}
} catch (error) {
errors.push(error);
}
if (errors.length > 0) {
throw new AggregateError(errors, 'Unable to close execution store persistence');
}
}
function invalidExecutionStores(
kind: StorageRootKind,
access: 'read' | 'write',
): StorageRootAuthorityError {
return new StorageRootAuthorityError(
'invalid_lease',
`Expected authentic ${kind} ${access} execution stores`,
);
}