| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import type { AgentRunEvent, AgentRunEventType, AgentRunProjectionKey } from '@maka/core/agent-run'; |
| import type { RuntimeEvent, ToolBoundaryProtocol } from '@maka/core/runtime-event'; |
| import type { RuntimeContinuationAuthorityStore } from '@maka/core/runtime-event-store'; |
| import type { ImmutableRuntimePrefixProofV1 } from '@maka/core/runtime-boundary'; |
| import type { RuntimeTranscriptQueries } from './runtime-transcript-query.js'; |
| import type { |
| RuntimeInvocationPageInput, |
| RuntimeInvocationPageResult, |
| RuntimeInvocationRecord, |
| RuntimeInvocationSearchResult, |
| } from '@maka/core/runtime-invocation'; |
| import type { SessionHeader, SessionSummary, StoredMessage, TurnRecord } from '@maka/core/session'; |
| import type { SessionListFilter } from '@maka/core/runtime-inputs'; |
| import { |
| createSqliteAgentRunStore, |
| type AdmitRootTurnInput, |
| type AdmitRootTurnResult, |
| type CommitRootTurnStartRejectionInput, |
| type BoundedEvidenceReadResult, |
| type DurableAgentRunStore, |
| type DurableRuntimeEventStore, |
| type EvidenceReadBudget, |
| type RootTurnAdmission, |
| type RootTurnSourceMessageReceipt, |
| } from './agent-run-store.js'; |
| import type { ConversationOperationalStateStore } from './conversation-operational-state.js'; |
| import { createSessionStore } from './session-store.js'; |
| import type { SessionAuthorityStore } from './session-store-contract.js'; |
| import { |
| assertStorageRootLease, |
| assertStorageRootLeaseActive, |
| runWithStorageRootLease, |
| StorageRootAuthorityError, |
| type StorageRootKind, |
| type StorageRootLease, |
| } from './root-authority.js'; |
| import { |
| closeSqliteInteractionStoreFacade, |
| openSqliteInteractiveInteractionStoreForRead, |
| openSqliteInteractiveInteractionStoreForWrite, |
| createSqliteInteractionStore, |
| type InteractiveInteractionStoreReaderFacade, |
| type InteractiveInteractionStoreWriterFacade, |
| } from './interaction-store.js'; |
| import { openRuntimeEventReadPersistence } from './runtime-event-persistence.js'; |
| import type { |
| CommitToolOutcomeInput, |
| CommitToolPreparedInput, |
| SessionRuntimeEventEntry, |
| ToolCommitResult, |
| ToolOperationRecord, |
| ImmutableRuntimePrefixProofReadBudget, |
| } from './runtime-event-store-contract.js'; |
| |
| import { localExecutionPersistenceProvider } from './local-execution-persistence.js'; |
| import { |
| EXECUTION_GRAPH_METHODS, |
| type ExecutionGraphStore, |
| type ExecutionPersistenceProvider, |
| } from './execution-persistence-provider.js'; |
| import { |
| openInteractiveGoalAuthorityForWrite, |
| createSqliteGoalAuthority, |
| type InteractiveGoalAuthorityWriter, |
| } from './goal-authority.js'; |
| export type { |
| ExecutionPersistenceProvider, |
| ExecutionGraphStore, |
| } from './execution-persistence-provider.js'; |
| |
| const executionStoreProvidersByLease = new WeakMap<object, ExecutionPersistenceProvider>(); |
| const failedExecutionLeases = new WeakSet<object>(); |
| |
| 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, |
| rootTurnAdmissionRecordFits, |
| rootTurnSourceMessagePayloadsEqual, |
| } from './agent-run-store.js'; |
| export { isSessionNotFoundError } from './session-store-contract.js'; |
| export { |
| SessionMetadataConflictError, |
| SessionMetadataVersionConflictError, |
| } from './session-store-contract.js'; |
| |
| export type { |
| AdmitRootTurnInput, |
| AdmitRootTurnResult, |
| CommitRootTurnStartRejectionInput, |
| CommitRootTurnStartRejectionResult, |
| BoundedEvidenceReadResult, |
| EvidenceReadBudget, |
| ImmutableSteeringMessageProof, |
| RootTurnAdmission, |
| RootTurnAdmissionAuthorization, |
| RootTurnAdmissionStore, |
| RootTurnStartRejectionStore, |
| RootTurnSourceMessage, |
| RootTurnSourceMessageReceipt, |
| RootTurnStartRejection, |
| RuntimeEventScanBudget, |
| RuntimeEventScanResult, |
| } from './agent-run-store.js'; |
| export type { |
| MarkMessagesHandedOffInput, |
| MessageAdmissionStore, |
| PendingMessageAdmission, |
| ProvenSteeringMessageHandoff, |
| } from './message-admission-store.js'; |
| export { submittedTurnIntentsEqual } from './submitted-turn-intent.js'; |
| export type { SubmittedTurnIntent } from './submitted-turn-intent.js'; |
| export type { |
| CreateStableSessionRequest, |
| ProbeSessionRemovalResult, |
| ExternalSessionImportLookupResult, |
| SessionCatalogPageCursor, |
| SessionCatalogPageResult, |
| SessionCatalogRecord, |
| SessionHeaderSnapshot, |
| SessionTranscriptMessageLookupRequest, |
| SessionTranscriptPageRequest, |
| CoordinationTranscriptReference, |
| SessionTranscriptRecordScanPage, |
| SessionTranscriptRecordScanRequest, |
| SessionTranscriptStoragePage, |
| SessionTranscriptStorageFragment, |
| SessionTurnContribution, |
| SessionTurnContributionPage, |
| } from './session-store-contract.js'; |
| |
| export type ExecutionSessionWriter = SessionAuthorityStore; |
| export type { RuntimeTranscriptRun, RuntimeTranscriptTurn } from './runtime-transcript-query.js'; |
| export type ExecutionAgentRunWriter = DurableAgentRunStore; |
| export type ExecutionRuntimeEventWriter = DurableRuntimeEventStore & |
| RuntimeTranscriptQueries & |
| RuntimeContinuationAuthorityStore & { |
| readImmutableRuntimePrefixProof( |
| input: { sessionId: string; runId: string; upToEventSeq?: number }, |
| budget: ImmutableRuntimePrefixProofReadBudget, |
| ): Promise<ImmutableRuntimePrefixProofV1>; |
| 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[]>; |
| /** Called once per Session after each write that committed RuntimeEvents to it. */ |
| subscribeRuntimeEventCommits(listener: (sessionId: string) => void): () => void; |
| listSessionsWithRuntimeEventText( |
| sessionIds: readonly string[], |
| terms: readonly string[], |
| ): Promise<string[]>; |
| countRuntimeEventMessages(sessionIds: readonly string[]): Promise<number>; |
| }; |
| 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>; |
| } |
| |
| export interface InteractiveExecutionStoresWriter extends ExecutionStoresWriterBase<'interactive'> { |
| readonly graphControlStore: ExecutionGraphStore; |
| readonly goalStore: InteractiveGoalAuthorityWriter; |
| readonly interactionStore: InteractiveInteractionStoreWriterFacade; |
| } |
| |
| interface ExecutionStoresWriters { |
| readonly interactive: InteractiveExecutionStoresWriter; |
| } |
| |
| 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 { |
| 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: AgentRunProjectionKey, |
| ): Promise<AgentRunEvent | null | undefined>; |
| readRootTurnAdmission(sessionId: string, turnId: string): Promise<RootTurnAdmission | undefined>; |
| readRootTurnContinuationAdmission( |
| sessionId: string, |
| sourceTurnId: string, |
| sourceRunId: string, |
| ): Promise<RootTurnAdmission | undefined>; |
| readRootTurnSourceMessageReceipt( |
| sessionId: string, |
| sourceMessageId: string, |
| ): Promise<RootTurnSourceMessageReceipt | undefined>; |
| } |
| |
| export interface ExecutionRuntimeEventReader { |
| /** |
| * A Session's run inventory, read from its canonical events. This is the |
| * definition of the inventory, not a cache of it, so nothing writes or |
| * repairs it. |
| */ |
| listSessionInvocations(sessionId: string): Promise<RuntimeInvocationRecord[]>; |
| readRunInvocation(sessionId: string, runId: string): Promise<RuntimeInvocationRecord | undefined>; |
| listSessionInvocationsBounded( |
| sessionId: string, |
| limit: number, |
| ): Promise<RuntimeInvocationSearchResult>; |
| listSessionInvocationsPage( |
| sessionId: string, |
| input: RuntimeInvocationPageInput, |
| ): Promise<RuntimeInvocationPageResult>; |
| readInvocation(sessionId: string, invocationId: string): Promise<RuntimeInvocationRecord>; |
| 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[]>; |
| /** Session-wide events with the ordinal that fixes their transcript order. */ |
| readSessionRuntimeEventEntries( |
| sessionId: string, |
| ): Promise<ReadonlyArray<{ ordinal: number; event: RuntimeEvent }>>; |
| /** Recall's narrowing over the ledger; see `RuntimeEventStore`. */ |
| listSessionsWithRuntimeEventText( |
| sessionIds: readonly string[], |
| terms: readonly string[], |
| ): Promise<string[]>; |
| countRuntimeEventMessages(sessionIds: readonly string[]): Promise<number>; |
| } |
| |
| 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; |
| } |
| |
| interface ExecutionStoresReaders { |
| readonly interactive: InteractiveExecutionStoresReader; |
| } |
| |
| 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'>, |
| provider: ExecutionPersistenceProvider = localExecutionPersistenceProvider, |
| ): Promise<ExecutionStoresWriter<'interactive'>> { |
| await assertStorageRootLease(lease, 'interactive', 'write'); |
| if (failedExecutionLeases.has(lease)) { |
| throw new StorageRootAuthorityError( |
| 'invalid_lease', |
| 'Execution persistence requires a fresh owner after an uncertain open or close', |
| ); |
| } |
| const selected = executionStoreProvidersByLease.get(lease); |
| if (selected && selected !== provider) { |
| throw new StorageRootAuthorityError( |
| 'invalid_lease', |
| 'A different execution provider already owns this lease', |
| ); |
| } |
| const existing = executionStoresWritersByLease.get(lease); |
| if (existing) { |
| if (executionStoresWriterKinds.get(existing) !== 'interactive') |
| throw invalidExecutionStores('interactive', 'write'); |
| return existing as InteractiveExecutionStoresWriter; |
| } |
| const opening = executionStoresWritersOpeningByLease.get(lease); |
| if (opening) { |
| await opening; |
| return openInteractiveExecutionStoresForWrite(lease, provider); |
| } |
| executionStoreProvidersByLease.set(lease, provider); |
| let releaseOpening!: () => void; |
| const openingGate = new Promise<void>((resolve) => { |
| releaseOpening = resolve; |
| }); |
| executionStoresWritersOpeningByLease.set(lease, openingGate); |
| try { |
| return await createExecutionStoresForWrite(lease, provider); |
| } catch (error) { |
| if (!executionStoresWritersByLease.has(lease) && !failedExecutionLeases.has(lease)) |
| executionStoreProvidersByLease.delete(lease); |
| throw error; |
| } finally { |
| executionStoresWritersOpeningByLease.delete(lease); |
| releaseOpening(); |
| } |
| } |
| |
| async function createExecutionStoresForWrite( |
| lease: StorageRootLease<'interactive', 'write'>, |
| provider: ExecutionPersistenceProvider, |
| ): Promise<InteractiveExecutionStoresWriter> { |
| const kind = 'interactive' as const; |
| const persistence = await runWithStorageRootLease(lease, kind, 'write', () => |
| provider.open({ |
| rootId: lease.rootId, |
| canonicalPath: lease.canonicalPath, |
| }), |
| ).catch((error: unknown) => { |
| // An unsuccessful factory must clean up its own partial handles. Until a |
| // fresh owner is acquired, do not assume an unknown factory failure did so. |
| failedExecutionLeases.add(lease); |
| throw error; |
| }); |
| let closed = false; |
| let closeTask: Promise<void> | undefined; |
| const active = new Set<Promise<unknown>>(); |
| const subscriptions = new Set<() => void>(); |
| const run = <T>(operation: () => Promise<T>): Promise<T> => { |
| if (closed) return Promise.reject(invalidExecutionStores(kind, 'write')); |
| const pending = runWithStorageRootLease(lease, kind, 'write', () => { |
| if (closed) throw invalidExecutionStores(kind, 'write'); |
| return operation(); |
| }); |
| active.add(pending); |
| void pending.finally(() => active.delete(pending)).catch(() => undefined); |
| return pending; |
| }; |
| const sessionStore = persistence.sessionStore; |
| const agentRunStore = persistence.agentRunStore; |
| const runtimeEventStore = persistence.runtimeEventStore; |
| const runtimePersistence = { runtimeCommitStore: runtimeEventStore }; |
| const conversationOperationalStateStore = { |
| purge: (sessionId: string) => persistence.purgeConversationOperationalState(sessionId), |
| }; |
| let interactionStore: InteractiveInteractionStoreWriterFacade | undefined; |
| let goalStore: InteractiveGoalAuthorityWriter | undefined; |
| const releaseChildBindings: Array<() => void> = []; |
| const retainUntilGroupClose = (release: () => void) => releaseChildBindings.push(release); |
| try { |
| await assertStorageRootLease(lease, kind, 'write'); |
| await sessionStore.ready(); |
| await agentRunStore.ready?.(); |
| interactionStore = await openSqliteInteractiveInteractionStoreForWrite( |
| lease, |
| () => |
| new Proxy(persistence.interactionStore, { |
| get(target, property, receiver) { |
| if (property === 'close') return () => {}; |
| const value = Reflect.get(target, property, receiver); |
| return typeof value === 'function' |
| ? (...args: unknown[]) => run(async () => Reflect.apply(value, target, args)) |
| : value; |
| }, |
| }), |
| provider === localExecutionPersistenceProvider ? createSqliteInteractionStore : provider, |
| retainUntilGroupClose, |
| ); |
| goalStore = await openInteractiveGoalAuthorityForWrite( |
| lease, |
| () => ({ |
| list: () => run(async () => persistence.goalStore.list()), |
| read: (sessionId) => run(async () => persistence.goalStore.read(sessionId)), |
| commit: (input) => run(async () => persistence.goalStore.commit(input)), |
| // The group owns the backend handle; closing this facade only revokes it. |
| close: () => {}, |
| }), |
| provider === localExecutionPersistenceProvider ? createSqliteGoalAuthority : provider, |
| retainUntilGroupClose, |
| ); |
| } catch (error) { |
| closed = true; |
| const failures: unknown[] = [error]; |
| try { |
| if (interactionStore) closeSqliteInteractionStoreFacade(interactionStore); |
| } catch (closeError) { |
| failures.push(closeError); |
| } |
| try { |
| await goalStore?.close(); |
| } catch (closeError) { |
| failures.push(closeError); |
| } |
| try { |
| await persistence.close(); |
| } catch (closeError) { |
| failures.push(closeError); |
| } |
| if (failures.length > 1) { |
| failedExecutionLeases.add(lease); |
| throw new AggregateError(failures, 'Unable to compose execution persistence'); |
| } |
| for (const release of releaseChildBindings) release(); |
| throw error; |
| } |
| const close = () => |
| (closeTask ??= (async () => { |
| closed = true; |
| executionStoresWriterKinds.delete(stores); |
| const errors: unknown[] = []; |
| for (const unsubscribe of subscriptions) { |
| try { |
| unsubscribe(); |
| } catch (error) { |
| errors.push(error); |
| } |
| } |
| subscriptions.clear(); |
| await Promise.allSettled([...active]); |
| try { |
| await goalStore!.close(); |
| } catch (error) { |
| errors.push(error); |
| } |
| try { |
| closeSqliteInteractionStoreFacade(interactionStore!); |
| } catch (error) { |
| errors.push(error); |
| } |
| try { |
| await persistence.close(); |
| } catch (error) { |
| errors.push(error); |
| } |
| // Failed close retains the closed owner, so another backend cannot open over it. |
| if (errors.length) throw new AggregateError(errors, 'Unable to close execution persistence'); |
| for (const release of releaseChildBindings) release(); |
| if (executionStoresWritersByLease.get(lease) === stores) { |
| executionStoresWritersByLease.delete(lease); |
| executionStoreProvidersByLease.delete(lease); |
| } |
| })()); |
| const graphMethods = Object.fromEntries( |
| EXECUTION_GRAPH_METHODS.map((name) => [ |
| name, |
| (...args: unknown[]) => |
| run(() => |
| Reflect.apply(persistence.graphControlStore[name], persistence.graphControlStore, args), |
| ), |
| ]), |
| ) as Omit<ExecutionGraphStore, 'close'>; |
| const graphControlStore: ExecutionGraphStore = Object.freeze({ |
| ...graphMethods, |
| close: () => {}, |
| }); |
| |
| const stores: InteractiveExecutionStoresWriter = { |
| interactionStore, |
| graphControlStore, |
| goalStore, |
| kind, |
| [executionStoresWriterBrand]: kind, |
| purgeConversationOperationalState: (sessionId) => |
| run(() => conversationOperationalStateStore.purge(sessionId)), |
| sessionStore: { |
| ready: () => run(() => sessionStore.ready()), |
| create: (input, initialBoundary) => run(() => sessionStore.create(input, initialBoundary)), |
| createImportedSession: (input, messages, externalOrigin, options) => |
| run(() => sessionStore.createImportedSession(input, messages, externalOrigin, options)), |
| lookupExternalSessionImports: (adapterId, sourceSessionIds, recentSessionIdLimit) => |
| run(() => |
| sessionStore.lookupExternalSessionImports( |
| adapterId, |
| sourceSessionIds, |
| recentSessionIdLimit, |
| ), |
| ), |
| probeStableSessionCreate: (sessionId, requestFingerprint) => |
| run(() => sessionStore.probeStableSessionCreate(sessionId, requestFingerprint)), |
| createStableSession: (request, initialBoundary) => |
| run(() => sessionStore.createStableSession(request, initialBoundary)), |
| assignWorkHubMessage: (request) => run(() => sessionStore.assignWorkHubMessage(request)), |
| readWorkHubAssignment: (actionId) => run(() => sessionStore.readWorkHubAssignment(actionId)), |
| readActiveWorkHubAssignmentsByTarget: (targetSessionIds, maxAssignmentsPerTarget) => |
| run(() => |
| sessionStore.readActiveWorkHubAssignmentsByTarget( |
| targetSessionIds, |
| maxAssignmentsPerTarget, |
| ), |
| ), |
| readWorkHubReplacement: (delegationId) => |
| run(() => sessionStore.readWorkHubReplacement(delegationId)), |
| readWorkHubReplacementAbort: (delegationId) => |
| run(() => sessionStore.readWorkHubReplacementAbort(delegationId)), |
| readWorkHubSupersession: (delegationId) => |
| run(() => sessionStore.readWorkHubSupersession(delegationId)), |
| readWorkHubStopRequest: (delegationId) => |
| run(() => sessionStore.readWorkHubStopRequest(delegationId)), |
| readWorkHubStopResolution: (delegationId) => |
| run(() => sessionStore.readWorkHubStopResolution(delegationId)), |
| claimWorkHubAction: (claim) => run(() => sessionStore.claimWorkHubAction(claim)), |
| readWorkHubActionClaim: (actionId) => |
| run(() => sessionStore.readWorkHubActionClaim(actionId)), |
| discardStableConversationCopy: (sessionId, requestFingerprint) => |
| run(() => sessionStore.discardStableConversationCopy(sessionId, requestFingerprint)), |
| 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)), |
| hasExplicitSandboxBoundaryDenial: (identities) => |
| run(() => sessionStore.hasExplicitSandboxBoundaryDenial(identities)), |
| 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()), |
| listForRecovery: () => run(() => sessionStore.listForRecovery()), |
| readHeaderSnapshot: (sessionId) => run(() => sessionStore.readHeaderSnapshot(sessionId)), |
| readHeaderRecordSnapshot: (sessionId) => |
| run(() => sessionStore.readHeaderRecordSnapshot(sessionId)), |
| readCatalogRecord: (sessionId, roleScope) => |
| run(() => sessionStore.readCatalogRecord(sessionId, roleScope)), |
| probeSessionRemoval: (sessionId) => run(() => sessionStore.probeSessionRemoval(sessionId)), |
| readMessagesSnapshot: (sessionId) => run(() => sessionStore.readMessagesSnapshot(sessionId)), |
| readTranscriptMessagesSnapshot: (sessionId, request) => |
| run(() => sessionStore.readTranscriptMessagesSnapshot(sessionId, request)), |
| readCoordinationTranscriptIndexState: () => |
| run(() => sessionStore.readCoordinationTranscriptIndexState()), |
| appendCoordinationTranscriptIndex: (records) => |
| run(() => sessionStore.appendCoordinationTranscriptIndex(records)), |
| readCoordinationTranscriptIndex: (request) => |
| run(() => sessionStore.readCoordinationTranscriptIndex(request)), |
| readTranscriptHighWaterSnapshot: (sessionId) => |
| run(() => sessionStore.readTranscriptHighWaterSnapshot(sessionId)), |
| listTurnsSnapshot: (sessionId) => run(() => sessionStore.listTurnsSnapshot(sessionId)), |
| readHeader: (sessionId) => run(() => sessionStore.readHeader(sessionId)), |
| readMessages: (sessionId) => run(() => sessionStore.readMessages(sessionId)), |
| readMessagesAfter: (sessionId, request) => |
| run(() => sessionStore.readMessagesAfter(sessionId, request)), |
| listTurns: (sessionId) => run(() => sessionStore.listTurns(sessionId)), |
| appendMessage: (sessionId, message) => |
| run(() => sessionStore.appendMessage(sessionId, message)), |
| appendMessages: (sessionId, messages) => |
| run(() => sessionStore.appendMessages(sessionId, messages)), |
| commitMessageCatalogProjection: (sessionId, message) => |
| run(() => sessionStore.commitMessageCatalogProjection(sessionId, message)), |
| commitMessageAdmission: (admission) => |
| run(() => sessionStore.commitMessageAdmission(admission)), |
| readMessageAdmission: (sessionId, messageId) => |
| run(() => sessionStore.readMessageAdmission(sessionId, messageId)), |
| hasCancelledMessageAdmission: (sessionId, messageId) => |
| run(() => sessionStore.hasCancelledMessageAdmission(sessionId, messageId)), |
| claimMessageAdmissionCancellation: (sessionId, messageId, claimId) => |
| run(() => sessionStore.claimMessageAdmissionCancellation(sessionId, messageId, claimId)), |
| listMessageAdmissions: (sessionId) => |
| run(() => sessionStore.listMessageAdmissions(sessionId)), |
| markMessagesHandedOff: (input) => run(() => sessionStore.markMessagesHandedOff(input)), |
| updateMessageAdmission: (admission) => |
| run(() => sessionStore.updateMessageAdmission(admission)), |
| reorderMessageAdmissions: (sessionId, messageIds, disposition) => |
| run(() => sessionStore.reorderMessageAdmissions(sessionId, messageIds, disposition)), |
| cancelMessageAdmissions: (sessionId, messageIds) => |
| run(() => sessionStore.cancelMessageAdmissions(sessionId, messageIds)), |
| subscribeTranscriptChanges: (listener) => { |
| if (closed) throw invalidExecutionStores(kind, 'write'); |
| assertStorageRootLeaseActive(lease, kind, 'write'); |
| const unsubscribe = sessionStore.subscribeTranscriptChanges((sessionId) => { |
| if (!closed) listener(sessionId); |
| }); |
| subscriptions.add(unsubscribe); |
| return () => { |
| subscriptions.delete(unsubscribe); |
| unsubscribe(); |
| }; |
| }, |
| 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)), |
| 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)), |
| setSessionsArchivedVersioned: (sessions, isArchived) => |
| run(() => sessionStore.setSessionsArchivedVersioned(sessions, isArchived)), |
| removeSessionsVersioned: (sessions, archiveSessions) => |
| run(() => sessionStore.removeSessionsVersioned(sessions, archiveSessions)), |
| reconcileOrphanedAgentGraphRetirements: () => |
| run(() => sessionStore.reconcileOrphanedAgentGraphRetirements()), |
| listPendingSessionRetirementCleanupIds: (sessionId) => |
| run(() => sessionStore.listPendingSessionRetirementCleanupIds(sessionId)), |
| completeSessionRetirementCleanup: (sessionId) => |
| run(() => sessionStore.completeSessionRetirementCleanup(sessionId)), |
| close, |
| }, |
| agentRunStore: { |
| 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)), |
| readEventLedgerRevision: (sessionId) => |
| run(() => agentRunStore.readEventLedgerRevision(sessionId)), |
| 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)), |
| readRootTurnContinuationAdmission: (sessionId, sourceTurnId, sourceRunId) => |
| run(() => |
| agentRunStore.readRootTurnContinuationAdmission(sessionId, sourceTurnId, sourceRunId), |
| ), |
| 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)), |
| scanRuntimeEvents: (sessionId, runId, budget, visit) => |
| run(() => runtimeEventStore.scanRuntimeEvents(sessionId, runId, budget, visit)), |
| readRuntimeEventsBounded: (sessionId, runId, budget) => |
| run(() => runtimeEventStore.readRuntimeEventsBounded(sessionId, runId, budget)), |
| readImmutableRuntimeEvents: (sessionId, runId) => |
| run(() => runtimeEventStore.readImmutableRuntimeEvents(sessionId, runId)), |
| readImmutableRuntimePrefix: (input) => |
| run(() => runtimeEventStore.readImmutableRuntimePrefix(input)), |
| readImmutableRuntimePrefixProof: (input, budget) => |
| run(() => runtimeEventStore.readImmutableRuntimePrefixProof(input, budget)), |
| listSessionInvocations: (sessionId) => |
| run(() => runtimeEventStore.listSessionInvocations(sessionId)), |
| readRunInvocation: (sessionId, runId) => |
| run(() => runtimeEventStore.readRunInvocation(sessionId, runId)), |
| listSessionInvocationsBounded: (sessionId, limit) => |
| run(() => runtimeEventStore.listSessionInvocationsBounded(sessionId, limit)), |
| listSessionInvocationsPage: (sessionId, input) => |
| run(() => runtimeEventStore.listSessionInvocationsPage(sessionId, input)), |
| readInvocation: (sessionId, invocationId) => |
| run(() => runtimeEventStore.readInvocation(sessionId, invocationId)), |
| readSessionRuntimeEvents: (sessionId) => |
| run(() => runtimeEventStore.readSessionRuntimeEvents(sessionId)), |
| readSessionRuntimeEventEntries: (sessionId) => |
| run(() => runtimeEventStore.readSessionRuntimeEventEntries(sessionId)), |
| listSessionsWithRuntimeEventText: (sessionIds, terms) => |
| run(() => runtimeEventStore.listSessionsWithRuntimeEventText(sessionIds, terms)), |
| countRuntimeEventMessages: (sessionIds) => |
| run(() => runtimeEventStore.countRuntimeEventMessages(sessionIds)), |
| resequenceSessionEventOrdinals: (sessionId) => |
| run(() => runtimeEventStore.resequenceSessionEventOrdinals(sessionId)), |
| readTranscriptHighWater: (sessionId) => |
| run(() => runtimeEventStore.readTranscriptHighWater(sessionId)), |
| readTranscriptRun: (sessionId, request, project) => |
| run(() => runtimeEventStore.readTranscriptRun(sessionId, request, project)), |
| readTranscriptTurns: (sessionId, request) => |
| run(() => runtimeEventStore.readTranscriptTurns(sessionId, request)), |
| readTranscriptTurnCrossing: (sessionId, ordinal) => |
| run(() => runtimeEventStore.readTranscriptTurnCrossing(sessionId, ordinal)), |
| subscribeRuntimeEventCommits: (listener) => { |
| if (closed) throw invalidExecutionStores(kind, 'write'); |
| assertStorageRootLeaseActive(lease, kind, 'write'); |
| const unsubscribe = runtimeEventStore.subscribeRuntimeEventCommits((sessionId) => { |
| if (!closed) listener(sessionId); |
| }); |
| subscriptions.add(unsubscribe); |
| return () => { |
| subscriptions.delete(unsubscribe); |
| unsubscribe(); |
| }; |
| }, |
| 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)), |
| }, |
| }; |
| 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 }); |
| } |
| |
| 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: { |
| 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)), |
| readRootTurnContinuationAdmission: (sessionId, sourceTurnId, sourceRunId) => |
| run(() => |
| agentRunStore.readRootTurnContinuationAdmission(sessionId, sourceTurnId, sourceRunId), |
| ), |
| 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)), |
| listSessionInvocations: (sessionId) => |
| run(() => runtimeEventStore.listSessionInvocations(sessionId)), |
| readRunInvocation: (sessionId, runId) => |
| run(() => runtimeEventStore.readRunInvocation(sessionId, runId)), |
| listSessionInvocationsBounded: (sessionId, limit) => |
| run(() => runtimeEventStore.listSessionInvocationsBounded(sessionId, limit)), |
| listSessionInvocationsPage: (sessionId, input) => |
| run(() => runtimeEventStore.listSessionInvocationsPage(sessionId, input)), |
| readInvocation: (sessionId, invocationId) => |
| run(() => runtimeEventStore.readInvocation(sessionId, invocationId)), |
| readSessionRuntimeEvents: (sessionId) => |
| run(() => runtimeEventStore.readSessionRuntimeEvents(sessionId)), |
| readSessionRuntimeEventEntries: (sessionId) => |
| run(() => runtimeEventStore.readSessionRuntimeEventEntries(sessionId)), |
| listSessionsWithRuntimeEventText: (sessionIds, terms) => |
| run(() => runtimeEventStore.listSessionsWithRuntimeEventText(sessionIds, terms)), |
| countRuntimeEventMessages: (sessionIds) => |
| run(() => runtimeEventStore.countRuntimeEventMessages(sessionIds)), |
| }, |
| }; |
| freezeExecutionStoresFacade(stores); |
| executionStoresReaderKinds.set(stores, kind); |
| return stores; |
| } |
| |
| function freezeExecutionStoresFacade(stores: { |
| readonly sessionStore: object; |
| readonly agentRunStore: object; |
| readonly runtimeEventStore: object; |
| }): void { |
| Object.freeze(stores.sessionStore); |
| Object.freeze(stores.agentRunStore); |
| Object.freeze(stores.runtimeEventStore); |
| Object.freeze(stores); |
| } |
| |
| async function closeExecutionStorePersistence( |
| sessionStore: { close?(): Promise<void> }, |
| runtimePersistence: { close(): void }, |
| extras: { |
| agentRunStore?: Pick<DurableAgentRunStore, 'close'>; |
| conversationOperationalStateStore?: Pick<ConversationOperationalStateStore, 'close'>; |
| 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 { |
| 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`, |
| ); |
| } |