import { ControllerCodexOriginMigrationModel } from '../ts_migration/v29_codexorigins.js'; import { ControllerCodexConnectionStore } from './classes.codexconnectionstore.js'; import { ControllerCodexProfileModel, ControllerCodexMappingModel } from './classes.codexconnectionmodels.js'; import * as plugins from './plugins.js'; import { ControllerCodexCreationModel, type ICodexCreationRuntime, type IControllerCodexCreationDocument } from './classes.codexcreationmodels.js'; import type { ICodexCreationScope, IBeginCodexCreationInput } from './classes.codexcreationstore.js'; import { EmbeddedControllerDatabase } from './classes.embeddeddb.js'; import { AuthError, type IAuthStore, type IConsumeCeremonyOptions, type IControllerAuditEvent, type IControllerAuthDocument, type IRecordAuditEventInput, type IMintedTempPassword, type ISetupAuthority, type IStoredPasskeyCredential, type TWebAuthnCeremonyKind, type IWebAuthnCeremonyDocument, type IWebAuthnSetupCeremonyDocument, } from './interfaces.auth.js'; import type { IControllerRuntimeConfig } from './interfaces.config.js'; import type { IControllerIssuerIdentity } from './interfaces.identity.js'; import { applyResourceAttachmentIntent, cloneResourceAttachmentEntry, } from './functions.resourceattachments.js'; import { ControllerAuditEventModel, ControllerAuthModel, ControllerProjectModel, ControllerResourceModel, ControllerSessionGroupsModel, ControllerTrackedConversationModel, controllerTrackedConversationId, isStandardProjectDirectoryList, ControllerSessionStateModel, ControllerSettingsModel, ControllerTempPasswordModel, isProjectRelativePathName, WebAuthnCeremonyModel, controllerSessionStateId, } from './classes.authmodels.js'; import { ControllerIssuerIdentityModel } from './classes.issueridentitymodels.js'; import { ControllerSystemMetricsHistoryModel, listSystemMetricsHistoryPoints, pruneSystemMetricsHistory, recordSystemMetricsHistoryPoint, } from './classes.systemmetricshistory.js'; import { ControllerSessionIdentityModel, ControllerSessionLocatorModel, ControllerSessionRuntimeBindingModel, } from './classes.sessionidentitymodels.js'; import { ControllerSessionParentRelationshipModel } from './classes.sessionrelationshipmodels.js'; import { cloneControllerFlexCleanupCohort, controllerFlexCleanupCohortsEqual, ControllerManagedSessionModel, ControllerSessionCreationObligationModel, type IControllerFlexCleanupEntry, } from './classes.managedsessionmodels.js'; import { ControllerSessionIdentityService, type IAdmitControllerManagedSessionInput, type IBeginControllerManagedSessionCreationInput, type IBeginControllerManagedSessionDeletionInput, type ICancelControllerManagedSessionDeletionInput, type ICompleteControllerManagedSessionCreationAdmissionInput, type ICompleteControllerManagedSessionDeletionInput, type IControllerManagedSessionDeletionObligation, type IControllerSessionManagedObservation, type IControllerSessionCreationObligation, type IGetControllerSessionCreationObligationInput, type IListControllerManagedSessionDeletionObligationsInput, type IListControllerFlexManagedCleanupRootsInput, type IListControllerSessionCreationObligationsInput, type IMarkControllerManagedSessionCreationDispatchedInput, type IMarkControllerManagedSessionDeletionDispatchedInput, type IRequireControllerManagedSessionInput, type IRetireControllerManagedSessionCreationInput, type IRetireControllerProjectManagedSessionsInput, type IResolveControllerFlexHostSessionAuthorityInput, type IResolveControllerFlexProjectManagementRegistrationLoadAuthorityInput, type IResolveControllerPendingFlexProjectManagementLoadAuthorityInput, type IResolveControllerSessionLayoutAuthorityInput, type IResolveRetiredManagedSessionIdentityInput, type TControllerFlexHostSessionAuthority, type TControllerFlexProjectManagementRegistrationLoadAuthority, type TControllerPendingFlexProjectManagementLoadAuthority, type TControllerSessionLayoutAuthority, } from './classes.sessionidentityservice.js'; import { authStoreSessionIdentityCapability, type IAuthStoreSessionIdentityCapability, } from './classes.sessionidentityintegration.js'; import { controllerProjectLimit, controllerResourceLimit, controllerResourcesPerProjectLimit, controllerTrackedConversationLimit, controllerTrackedConversationsPerProjectLimit, type IControllerTrackedConversationDocument, type IControllerProjectFilesystemIdentity, type IControllerProjectDocument, type IControllerResourceDocument, type IControllerResourceAttachmentEntryDocument, type IControllerTerminalAgentMetadata, type IControllerSessionGroupsDocument, type IControllerSessionStateDocument, type IControllerSessionState, type IControllerIntelligenceAdmission, type IControllerInterruptedIntelligenceRecovery, type IControllerSettingsDocument, } from './interfaces.projects.js'; import type { TControllerModelChoice, IControllerSystemMetrics, IControllerSystemMetricsHistoryPoint, IControllerRuntimeId, IControllerSessionGroup, IControllerSessionLayout, TControllerResourceKind, TControllerTerminalAgentDesiredState, TControllerTerminalAgentLaunchMode, TControllerTerminalAgentFailure, TControllerSessionId, TControllerSessionHarnessId, TControllerLayoutItemRef, TControllerConversationOrigin, IControllerIntelligenceExchange, TControllerScratchpadUpdater, TControllerProjectRemovalBlockedCode, } from '../ts_interfaces/index.js'; import { controllerLayoutItemRefKey, controllerProjectRemovalBlockedCodes, controllerProjectRemovalBlockedReasonMaxLength, controllerRuntimeIdKey, } from '../ts_interfaces/index.js'; import { PostDatabaseDocumentMigrationRunner, } from '../ts_migration/classes.documentmigrationrunner.js'; import { ControllerLegacyManagedSessionMigrationModel, type IControllerLegacyManagedSessionMigrationResult, } from '../ts_migration/v26_legacymanagedsessionmigration.js'; import { ControllerLegacyManagedSessionMigrationRunner, type TControllerLegacyManagedSessionMigrationRunnerInput, } from '../ts_migration/classes.managedsessionmigrationrunner.js'; import type { IFlexDatabaseDescriptor } from './interfaces.flexipc.js'; import { filesystemIdentitiesEqual } from './functions.canonicaldirectory.js'; export interface ISmartDataAuthStoreOptions { /** external server URL; absent = embedded smartdb engine */ mongoDbUrl?: string; mongoDbName: string; /** storage directory for the embedded engine; required without mongoDbUrl */ embeddedDataDirectory?: string; /** previous installed default that may be logically migrated */ legacyEmbeddedDataDirectory?: string; } // The audit log is write-only telemetry with no reader; retention keeps the // embedded engine's working set bounded. This is load-bearing on smartdb: // the engine resolves indexed lookups and unique checks by scanning, so // per-operation cost grows linearly with collection size — a hard count cap // keeps every scan trivially small. Deletes run in limit-bounded id batches // because broad deleteMany filters can exceed smartdb's per-operation // materialization capacity on large collections. const auditRetentionMs = 7 * 24 * 60 * 60 * 1000; const auditRetentionMaxEvents = 2_000; const auditRetentionCountSlack = 500; const auditPruneBatchSize = 1000; const auditPruneMaxBatchesPerSweep = 50; // The count cap must be able to clear a large pre-existing backlog in one // sweep; each batch is a bounded id page, so this stays cheap per round trip. const auditCountPruneMaxBatchesPerSweep = 250; const auditPruneIntervalMs = 30 * 60 * 1000; const setupLifetimeMs = 30 * 60 * 1000; // CLI-minted temporary passwords: a debugging credential, deliberately capped. const tempPasswordTtlMinMs = 60 * 1000; const tempPasswordTtlMaxMs = 24 * 60 * 60 * 1000; const maxActiveTempPasswords = 8; const tempPasswordPruneBatchSize = 128; const tempPasswordPattern = /^[A-Za-z0-9_-]{32}$/; const sessionProviderConnectionClearTransitionMaxAttempts = 16; // Operator-provided codes are allowed, so the accepted shape is any printable // non-whitespace ASCII within DoS-safe length bounds; only the hash is stored. const setupCodePattern = /^[\x21-\x7e]{4,256}$/; const ceremonyIdPattern = /^[A-Za-z0-9_-]{43}$/; const randomAuthorityValue = (): string => plugins.crypto.randomBytes(32).toString('base64url'); const randomResourceId = (): string => plugins.crypto.randomBytes(16).toString('base64url'); const randomResourceOperationId = (): string => plugins.crypto.randomBytes(16).toString('base64url'); const projectRemovalOperationPattern = /^[A-Za-z0-9_-]{32}$/; const isResourceSessionIdentityId = (valueArg: unknown): valueArg is string => ( typeof valueArg === 'string' && /^[A-Za-z0-9_-]+$/.test(valueArg) && Buffer.from(valueArg, 'base64url').byteLength === 32 && Buffer.from(valueArg, 'base64url').toString('base64url') === valueArg ); const cloneSessionId = (sessionIdArg: TControllerSessionId | null): TControllerSessionId | null => ( sessionIdArg === null ? null : { ...sessionIdArg } ); const cloneLayoutItemRef = (itemRefArg: TControllerLayoutItemRef): TControllerLayoutItemRef => ( itemRefArg.kind === 'session' ? { kind: 'session', id: { ...itemRefArg.id }, projectId: itemRefArg.projectId } : { kind: 'resource', id: itemRefArg.id, projectId: itemRefArg.projectId } ); const cloneProjectDocument = ( projectArg: IControllerProjectDocument, ): IControllerProjectDocument => ({ ...projectArg, createdAt: new Date(projectArg.createdAt), ...(projectArg.removalStartedAt === undefined ? {} : { removalStartedAt: new Date(projectArg.removalStartedAt) }), ...(projectArg.removalBlockedAt === undefined ? {} : { removalBlockedAt: new Date(projectArg.removalBlockedAt) }), ...(projectArg.removedAt === undefined ? {} : { removedAt: new Date(projectArg.removedAt) }), ...(projectArg.directoryIdentity === undefined ? {} : { directoryIdentity: structuredClone(projectArg.directoryIdentity) }), ...(projectArg.flexCleanupCohort === undefined ? {} : { flexCleanupCohort: cloneControllerFlexCleanupCohort(projectArg.flexCleanupCohort) }), }); const isSessionId = (valueArg: IControllerRuntimeId): valueArg is TControllerSessionId => valueArg.harnessId === 'opencode' || valueArg.harnessId === 'flex' || valueArg.harnessId === 'codex'; /** * A chat that cannot start — missing binary, rebound project directory, a conversation held by * another process — must degrade once instead of being retried on every boot forever. */ const maxConsecutiveTerminalAgentFailures = 3; const cloneTerminalAgentMetadata = ( agentArg: IControllerTerminalAgentMetadata, ): IControllerTerminalAgentMetadata => ({ ...agentArg }); const applyTerminalAgentOutcome = ( agentArg: IControllerTerminalAgentMetadata, outcomeArg: { desiredState?: TControllerTerminalAgentDesiredState; failure?: { code: TControllerTerminalAgentFailure; message: string }; }, ): IControllerTerminalAgentMetadata => { const consecutiveFailures = outcomeArg.failure ? Math.min(agentArg.consecutiveFailures + 1, 1024) : 0; const exhausted = consecutiveFailures >= maxConsecutiveTerminalAgentFailures; const desiredState = exhausted ? 'stopped' : outcomeArg.desiredState ?? agentArg.desiredState; return { kind: agentArg.kind, sessionId: agentArg.sessionId, desiredState, consecutiveFailures, ...(agentArg.launchMode === undefined ? {} : { launchMode: agentArg.launchMode }), ...(outcomeArg.failure === undefined ? {} : { lastFailure: outcomeArg.failure.code, lastFailureMessage: outcomeArg.failure.message.slice(0, 1024), }), }; }; const cloneResourceDocument = ( resourceArg: IControllerResourceDocument, ): IControllerResourceDocument => ({ ...resourceArg, attachments: resourceArg.attachments.map(cloneResourceAttachmentEntry), ...(resourceArg.pendingAttachment === undefined ? {} : { pendingAttachment: { ...resourceArg.pendingAttachment, ...(resourceArg.pendingAttachment.entry === undefined ? {} : { entry: cloneResourceAttachmentEntry(resourceArg.pendingAttachment.entry) }), ...(resourceArg.pendingAttachment.replaces === undefined ? {} : { replaces: cloneResourceAttachmentEntry(resourceArg.pendingAttachment.replaces) }), requestedAt: new Date(resourceArg.pendingAttachment.requestedAt), }, }), ...(resourceArg.terminal === undefined ? {} : { terminal: { ...resourceArg.terminal, args: [...resourceArg.terminal.args], ...(resourceArg.terminal.stoppedAt === undefined ? {} : { stoppedAt: new Date(resourceArg.terminal.stoppedAt) }), ...(resourceArg.terminal.agent === undefined ? {} : { agent: cloneTerminalAgentMetadata(resourceArg.terminal.agent) }), }, }), createdAt: new Date(resourceArg.createdAt), updatedAt: new Date(resourceArg.updatedAt), ...(resourceArg.retiringAt === undefined ? {} : { retiringAt: new Date(resourceArg.retiringAt) }), ...(resourceArg.retiredAt === undefined ? {} : { retiredAt: new Date(resourceArg.retiredAt) }), }); const trackedConversationPageLimit = 256; const cloneTrackedConversationDocument = ( documentArg: IControllerTrackedConversationDocument, ): IControllerTrackedConversationDocument => ({ ...documentArg, runtimeId: { ...documentArg.runtimeId }, trackedAt: new Date(documentArg.trackedAt.getTime()), ...(documentArg.archivedAt === undefined ? {} : { archivedAt: new Date(documentArg.archivedAt.getTime()) }), ...(documentArg.titleCacheAt === undefined ? {} : { titleCacheAt: new Date(documentArg.titleCacheAt.getTime()) }), }); /** One line of printable text, bounded; anything else leaves the cache untouched. */ const normalizeTrackedConversationTitle = (titleArg: string | undefined): string | undefined => { if (typeof titleArg !== 'string') return undefined; const title = titleArg.trim(); // eslint-disable-next-line no-control-regex if (title.length === 0 || /[\x00-\x1f\x7f]/.test(title)) return undefined; return Buffer.byteLength(title, 'utf8') > 1024 ? Buffer.from(title, 'utf8').subarray(0, 1024).toString('utf8').replace(/\uFFFD+$/u, '') : title; }; const cloneModelChoice = (modelArg: TControllerModelChoice): TControllerModelChoice => ( modelArg.harnessId === 'codex' ? { harnessId: 'codex', providerID: 'codex', modelID: modelArg.modelID, ...(modelArg.variant === undefined ? {} : { variant: modelArg.variant }) } : modelArg.harnessId === 'opencode' ? { harnessId: 'opencode', providerID: modelArg.providerID, modelID: modelArg.modelID, ...(modelArg.variant === undefined ? {} : { variant: modelArg.variant }), } : { harnessId: 'flex', providerID: modelArg.providerID, modelID: modelArg.modelID, ...(modelArg.variant === undefined ? {} : { variant: modelArg.variant }), } ); export const assertProvidedSetupCode = (setupCodeArg: string): string => { if (typeof setupCodeArg !== 'string' || !setupCodePattern.test(setupCodeArg)) { throw new Error('A setup code must be 4-256 printable non-whitespace ASCII characters.'); } return setupCodeArg; }; const cloneRuntimeConfig = ( runtimeConfigArg: IControllerRuntimeConfig, ): IControllerRuntimeConfig => ({ controllerPort: runtimeConfigArg.controllerPort, publicOrigin: runtimeConfigArg.publicOrigin, rpId: runtimeConfigArg.rpId, projectsRoot: runtimeConfigArg.projectsRoot, opencodePort: runtimeConfigArg.opencodePort, tlsMode: runtimeConfigArg.tlsMode, }); const runtimeConfigsEqual = ( leftArg: IControllerRuntimeConfig, rightArg: IControllerRuntimeConfig, ): boolean => leftArg.controllerPort === rightArg.controllerPort && leftArg.publicOrigin === rightArg.publicOrigin && leftArg.rpId === rightArg.rpId && leftArg.projectsRoot === rightArg.projectsRoot && leftArg.opencodePort === rightArg.opencodePort && leftArg.tlsMode === rightArg.tlsMode; const hashSetupCode = (setupCodeArg: string): string => plugins.crypto.createHash('sha256').update(setupCodeArg, 'utf8').digest('base64url'); const hashesEqual = (leftArg: string, rightArg: string): boolean => { const left = Buffer.from(leftArg, 'base64url'); const right = Buffer.from(rightArg, 'base64url'); return left.byteLength === right.byteLength && plugins.crypto.timingSafeEqual(left, right); }; const requireSafeCounter = (counterArg: number, labelArg: string): void => { if (!Number.isSafeInteger(counterArg) || counterArg < 0) { throw new AuthError('counter_conflict', `${labelArg} must be a non-negative safe integer.`); } }; const requireCeremonyKind = (kindArg: unknown): TWebAuthnCeremonyKind => { if (kindArg !== 'setup' && kindArg !== 'authentication') { throw new AuthError('ceremony_invalid', 'The WebAuthn ceremony kind is invalid.'); } return kindArg; }; const requireCeremonyId = (ceremonyIdArg: unknown): string => { if (typeof ceremonyIdArg !== 'string' || !ceremonyIdPattern.test(ceremonyIdArg)) { throw new AuthError('ceremony_invalid', 'The WebAuthn ceremony identifier is invalid.'); } return ceremonyIdArg; }; const requirePeerId = (peerIdArg: unknown): string => { if ( typeof peerIdArg !== 'string' || peerIdArg.length === 0 || peerIdArg.length > 512 || peerIdArg.trim() !== peerIdArg ) { throw new AuthError('peer_mismatch', 'The connection identity is malformed.'); } return peerIdArg; }; const requireOrigin = (originArg: unknown): string => { if (typeof originArg !== 'string' || originArg.length === 0 || originArg.length > 2048) { throw new AuthError('origin_mismatch', 'The ceremony origin is malformed.'); } try { const origin = new URL(originArg); if ( origin.origin !== originArg || (origin.protocol !== 'http:' && origin.protocol !== 'https:') || origin.username.length > 0 || origin.password.length > 0 || origin.pathname !== '/' || origin.search.length > 0 || origin.hash.length > 0 ) { throw new Error('not an origin'); } } catch { throw new AuthError('origin_mismatch', 'The ceremony origin is malformed.'); } return originArg; }; const rpIdPattern = /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/; const requireRpId = (rpIdArg: unknown): string => { if (typeof rpIdArg !== 'string' || !rpIdPattern.test(rpIdArg)) { throw new AuthError('ceremony_invalid', 'The ceremony RP ID is malformed.'); } return rpIdArg; }; const requireUserId = (userIdArg: unknown): string => { if (typeof userIdArg !== 'string' || !ceremonyIdPattern.test(userIdArg)) { throw new AuthError('ceremony_invalid', 'The ceremony user identifier is malformed.'); } return userIdArg; }; const requireValidDate = (dateArg: unknown, labelArg: string): Date => { if (!(dateArg instanceof Date) || !Number.isFinite(dateArg.getTime())) { throw new AuthError('ceremony_invalid', `${labelArg} must be a valid date.`); } return new Date(dateArg); }; const isAmbiguousWrite = (errorArg: unknown): boolean => errorArg instanceof plugins.smartdata.SmartdataExactPersistenceError && errorArg.code === 'ambiguous_write'; type TStoredCeremonyModel = NonNullable>>; type TStoredSessionStateModel = NonNullable>>; type TStoredResourceModel = NonNullable>>; type TFlexProjectManagementRecord = plugins.flexharness.TFlexProjectManagementRecord; type TFlexProjectManagementSnapshot = plugins.flexharness.IFlexProjectManagementSnapshot; type TFlexProjectManagementTombstone = plugins.flexharness.IFlexProjectManagementTombstone; type TFlexProjectManagementWriteContext = plugins.flexharness.IFlexProjectManagementWriteContext; type TFlexSessionGeneration = plugins.flexharness.IFlexSessionGeneration; type TFlexProjectManagementSessionContext = plugins.flexharness.IFlexProjectManagementSessionContext; const sessionStateRecoveryLimit = 500; const sessionStateRetirementPageLimit = 100; const maxSessionStateRetirementPages = 100; const maxSessionStateExchanges = 8; const maxSessionStateExchangeBytes = 256 * 1024; const toPublicIntelligenceExchange = ( exchangeArg: IControllerSessionStateDocument['intelligenceExchanges'][number], ): IControllerIntelligenceExchange => ({ id: exchangeArg.id, question: exchangeArg.question, status: exchangeArg.status, ...(exchangeArg.answer === undefined ? {} : { answer: exchangeArg.answer }), ...(exchangeArg.error === undefined ? {} : { error: exchangeArg.error }), ...(exchangeArg.model === undefined ? {} : { model: exchangeArg.model }), ...(exchangeArg.scratchpadConflict === undefined ? {} : { scratchpadConflict: exchangeArg.scratchpadConflict }), createdAt: exchangeArg.createdAt.getTime(), ...(exchangeArg.completedAt === undefined ? {} : { completedAt: exchangeArg.completedAt.getTime() }), }); const toPublicSessionState = ( documentArg: IControllerSessionStateDocument, ): IControllerSessionState => ({ scratchpad: { id: documentArg.id, text: documentArg.scratchpad.text, revision: documentArg.scratchpad.revision, ...(documentArg.scratchpad.updatedAt === undefined ? {} : { updatedAt: documentArg.scratchpad.updatedAt.getTime() }), ...(documentArg.scratchpad.updatedBy === undefined ? {} : { updatedBy: documentArg.scratchpad.updatedBy }), }, intelligenceExchanges: documentArg.intelligenceExchanges.map(toPublicIntelligenceExchange), ...(documentArg.modelChoice === undefined ? {} : { modelChoice: cloneModelChoice(documentArg.modelChoice) }), ...(documentArg.providerConnectionId === undefined ? {} : { providerConnectionId: documentArg.providerConnectionId }), }); const trimSessionStateExchanges = ( exchangesArg: IControllerSessionStateDocument['intelligenceExchanges'], protectedExchangeIdArg?: string, ): IControllerSessionStateDocument['intelligenceExchanges'] => { const exchanges = exchangesArg.slice(-maxSessionStateExchanges); while ( exchanges.length > 1 && Buffer.byteLength(JSON.stringify(exchanges), 'utf8') > maxSessionStateExchangeBytes ) { const removableIndex = exchanges.findIndex( (exchange) => exchange.id !== protectedExchangeIdArg && exchange.status !== 'running', ); if (removableIndex < 0) break; exchanges.splice(removableIndex, 1); } return exchanges; }; const projectManagementStoreKey = (projectIdArg: string, sessionIdArg: string): string => JSON.stringify([projectIdArg, sessionIdArg]); const cloneProjectManagementRecord = ( recordArg: TRecord, ): TRecord => structuredClone(recordArg); const sameProjectManagementGeneration = ( leftArg: TFlexProjectManagementRecord, rightArg: TFlexProjectManagementRecord, ): boolean => leftArg.sessionGenerationId === rightArg.sessionGenerationId && leftArg.sessionGenerationSequence === rightArg.sessionGenerationSequence; const canInitializeProjectManagementGeneration = ( currentArg: TFlexProjectManagementRecord, nextArg: TFlexProjectManagementRecord, expectedRevisionArg: number, ): boolean => Object.hasOwn(currentArg, 'deletedAt') && expectedRevisionArg === 0 && nextArg.sessionGenerationId !== currentArg.sessionGenerationId && nextArg.sessionGenerationSequence > currentArg.sessionGenerationSequence; const assertFlexSessionGeneration = ( generationArg: Readonly, ): void => { if ( !generationArg || typeof generationArg.sessionGenerationId !== 'string' || generationArg.sessionGenerationId.trim().length === 0 || Buffer.byteLength(generationArg.sessionGenerationId, 'utf8') > plugins.flexharness.FLEX_SESSION_GENERATION_ID_MAX_BYTES || !Number.isSafeInteger(generationArg.sessionGenerationSequence) || generationArg.sessionGenerationSequence < 1 ) { throw new plugins.flexharness.FlexHarnessStoreFormatError( 'The Flex project-management session generation is invalid.', ); } }; const assertFlexProjectManagementSessionContext = ( contextArg: Readonly, sessionIdArg: string, ): void => { assertFlexSessionGeneration(contextArg); const subagent = contextArg.subagent; if (subagent === undefined) return; if ( !subagent || typeof subagent !== 'object' || subagent.parentSessionId === sessionIdArg || typeof subagent.parentSessionId !== 'string' || subagent.parentSessionId.length === 0 || typeof subagent.parentSessionGenerationId !== 'string' || subagent.parentSessionGenerationId.length === 0 || !Number.isSafeInteger(subagent.parentSessionGenerationSequence) || subagent.parentSessionGenerationSequence < 1 || typeof subagent.originParentRunId !== 'string' || subagent.originParentRunId.length === 0 || typeof subagent.originParentToolCallId !== 'string' || subagent.originParentToolCallId.length === 0 || typeof subagent.agent !== 'string' || subagent.agent.length === 0 || !Number.isSafeInteger(subagent.depth) || subagent.depth < 1 ) throw new plugins.flexharness.FlexHarnessStoreFormatError( 'The Flex project-management subagent context is invalid.', ); }; const assertProjectManagementWriteContext = ( writeContextArg: TFlexProjectManagementWriteContext, ): void => { if ( !writeContextArg || typeof writeContextArg !== 'object' || Array.isArray(writeContextArg) || ![Object.prototype, null].includes(Object.getPrototypeOf(writeContextArg)) || Object.keys(writeContextArg).some( (key) => !['actor', 'runId', 'agent', 'toolCallId'].includes(key), ) || (writeContextArg.actor !== 'agent' && writeContextArg.actor !== 'application') || ['runId', 'agent', 'toolCallId'].some((key) => { const value = writeContextArg[key as 'runId' | 'agent' | 'toolCallId']; return value !== undefined && (typeof value !== 'string' || value.trim().length === 0); }) ) { throw new plugins.flexharness.FlexHarnessStoreFormatError( 'The Flex project-management write context is invalid.', ); } }; const assertProjectManagementNextRevision = ( recordArg: TFlexProjectManagementRecord, expectedRevisionArg: number, ): void => { if (!Number.isSafeInteger(expectedRevisionArg) || expectedRevisionArg < 0) { throw new plugins.flexharness.FlexHarnessStoreFormatError( 'The expected Flex project-management revision is invalid.', ); } if (recordArg.revision !== expectedRevisionArg + 1) { throw new plugins.flexharness.FlexHarnessStoreFormatError( 'The Flex project-management revision must advance by exactly one.', ); } }; const projectManagementUpdater = ( writeContextArg: TFlexProjectManagementWriteContext, ): TControllerScratchpadUpdater => writeContextArg.actor; let activeModelOwner: object | undefined; const inactiveAuthStoreModelManager: { db: plugins.smartdata.SmartdataDb } = { get db(): plugins.smartdata.SmartdataDb { throw new Error('SmartDataAuthStore models do not have an active store owner.'); }, }; const setAuthStoreModelManager = ( managerArg: { db: plugins.smartdata.SmartdataDb }, ): void => { plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerAuthModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerIssuerIdentityModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, WebAuthnCeremonyModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerAuditEventModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSystemMetricsHistoryModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerProjectModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionIdentityModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionRuntimeBindingModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionLocatorModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionParentRelationshipModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerManagedSessionModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionCreationObligationModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerCodexCreationModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerCodexProfileModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerCodexMappingModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerCodexOriginMigrationModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerResourceModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSettingsModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerTempPasswordModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionGroupsModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerSessionStateModel); plugins.smartdata.setDefaultManagerForDoc(managerArg, ControllerTrackedConversationModel); plugins.smartdata.setDefaultManagerForDoc( managerArg, ControllerLegacyManagedSessionMigrationModel, ); }; const claimAuthStoreModelOwnership = ( ownerArg: object, managerArg: { db: plugins.smartdata.SmartdataDb }, ): (() => void) => { if (activeModelOwner !== undefined) { throw new Error('SmartDataAuthStore models already have an active store owner.'); } setAuthStoreModelManager(managerArg); activeModelOwner = ownerArg; let released = false; return () => { if (released) return; if (activeModelOwner !== ownerArg) { throw new Error('SmartDataAuthStore model ownership changed before release.'); } setAuthStoreModelManager(inactiveAuthStoreModelManager); released = true; activeModelOwner = undefined; }; }; export const assertControllerResourceCreationCapacity = ( projectResourceCountArg: number, controllerResourceCountArg: number, ): void => { if (controllerResourceCountArg >= controllerResourceLimit) { throw new AuthError( 'invalid_resource', `The controller supports at most ${controllerResourceLimit} resources.`, ); } if (projectResourceCountArg >= controllerResourcesPerProjectLimit) { throw new AuthError( 'invalid_resource', `A project supports at most ${controllerResourcesPerProjectLimit} resources.`, ); } }; export class SmartDataAuthStore implements IAuthStore, IAuthStoreSessionIdentityCapability { private database?: plugins.smartdata.SmartdataDb; private embeddedDatabase?: EmbeddedControllerDatabase; // smartdata resolves the manager's db lazily per operation, so the // manager can be registered before init() constructs the database. private readonly smartDataManager: { db: plugins.smartdata.SmartdataDb }; private initialized = false; private initPromise?: Promise; private initializationDataRoot?: string | null; private initializedDataRoot?: string; private issuerIdentity?: IControllerIssuerIdentity; private sessionIdentityService?: ControllerSessionIdentityService; private codexConnectionStore?: ControllerCodexConnectionStore; private readonly modelOwner = {}; private releaseModelOwnership?: () => void; private controllerId?: string; private resolvedMongoDbUrl?: string; private closePromise?: Promise; private closeStarted = false; private projectMutationAdmissionOpen = false; private projectMutationTail = Promise.resolve(); private sessionStateMutationTail = Promise.resolve(); private auditPruneTimer?: ReturnType; private auditPrunePromise?: Promise; private runSessionStateMutation(operationArg: () => Promise): Promise { if (this.closeStarted) { return Promise.reject(new AuthError( 'not_initialized', 'The authentication store is shutting down.', )); } const result = this.sessionStateMutationTail.then(operationArg); this.sessionStateMutationTail = result.then(() => undefined, () => undefined); return result; } constructor(private readonly options: ISmartDataAuthStoreOptions) { const hasExternalUrl = typeof options.mongoDbUrl === 'string' && options.mongoDbUrl.trim().length > 0; const hasEmbeddedDirectory = typeof options.embeddedDataDirectory === 'string' && plugins.path.isAbsolute(options.embeddedDataDirectory); if (!hasExternalUrl && !hasEmbeddedDirectory) { throw new Error('Either mongoDbUrl or an absolute embeddedDataDirectory must be provided.'); } if (!/^[A-Za-z0-9_-]{1,63}$/.test(options.mongoDbName)) { throw new Error('mongoDbName must use 1-63 letters, digits, underscores, or dashes.'); } const storeSelf = this; this.smartDataManager = { get db(): plugins.smartdata.SmartdataDb { return storeSelf.db; }, }; } private get db(): plugins.smartdata.SmartdataDb { if (!this.database) { throw new Error('The auth store database has not been initialized.'); } return this.database; } public async init(): Promise { await this.initialize(); } public async initForController( dataRootDirectoryArg: string, ): Promise { await this.initialize(dataRootDirectoryArg); if (!this.issuerIdentity) { throw new AuthError( 'not_initialized', 'The controller issuer identity was not initialized.', ); } return { ...this.issuerIdentity }; } public recordSystemMetricsHistory(metricsArg: IControllerSystemMetrics): Promise { if (!this.initialized || this.closeStarted) { return Promise.reject(new Error('Controller metrics history is unavailable.')); } return recordSystemMetricsHistoryPoint(metricsArg); } public listSystemMetricsHistory(): Promise { if (!this.initialized || this.closeStarted) { return Promise.reject(new Error('Controller metrics history is unavailable.')); } return listSystemMetricsHistoryPoints(); } public pruneSystemMetricsHistory(): Promise { if (!this.initialized || this.closeStarted) return Promise.resolve(); return pruneSystemMetricsHistory(); } private async initialize(dataRootDirectoryArg?: string): Promise { if (this.closeStarted) { throw new Error('SmartDataAuthStore cannot initialize after shutdown has begun.'); } if (this.initialized) { if (dataRootDirectoryArg === undefined) return; if ( this.initializedDataRoot === dataRootDirectoryArg && this.issuerIdentity !== undefined ) return; throw new Error( 'SmartDataAuthStore cannot enable controller issuer validation after legacy initialization.', ); } if (this.initPromise) { const requestedDataRoot = dataRootDirectoryArg ?? null; if (this.initializationDataRoot !== requestedDataRoot) { throw new Error('SmartDataAuthStore initialization mode changed concurrently.'); } return this.initPromise; } if (this.database || this.embeddedDatabase) { throw new Error('SmartData cleanup must complete before initialization can be retried.'); } const initPromise = this.initOnce(dataRootDirectoryArg); this.initPromise = initPromise; this.initializationDataRoot = dataRootDirectoryArg ?? null; try { await initPromise; } finally { if (this.initPromise === initPromise) this.initPromise = undefined; if (this.initializationDataRoot === (dataRootDirectoryArg ?? null)) { this.initializationDataRoot = undefined; } } } private async initOnce(dataRootDirectoryArg?: string): Promise { let databaseInitialized = false; try { this.releaseModelOwnership = claimAuthStoreModelOwnership( this.modelOwner, this.smartDataManager, ); let mongoDbUrl = this.options.mongoDbUrl?.trim(); if (!mongoDbUrl) { const embedded = new EmbeddedControllerDatabase({ dataDirectory: this.options.embeddedDataDirectory!, legacyDataDirectory: this.options.legacyEmbeddedDataDirectory, }); this.embeddedDatabase = embedded; mongoDbUrl = await embedded.start(); } this.database = new plugins.smartdata.SmartdataDb({ mongoDbUrl, mongoDbName: this.options.mongoDbName, }); this.resolvedMongoDbUrl = mongoDbUrl; await this.db.init(); databaseInitialized = true; if (dataRootDirectoryArg !== undefined) { await ControllerIssuerIdentityModel.init(); this.issuerIdentity = await new PostDatabaseDocumentMigrationRunner(this.database) .runControllerIssuerIdentity(dataRootDirectoryArg); this.initializedDataRoot = dataRootDirectoryArg; } await ControllerAuthModel.init(); await WebAuthnCeremonyModel.init(); await ControllerAuditEventModel.init(); await ControllerSystemMetricsHistoryModel.init(); await ControllerProjectModel.init(); if (this.issuerIdentity) { await ControllerSessionIdentityModel.init(); await ControllerSessionRuntimeBindingModel.init(); await ControllerSessionLocatorModel.init(); await ControllerSessionParentRelationshipModel.init(); await ControllerManagedSessionModel.init(); await ControllerSessionCreationObligationModel.init(); await ControllerCodexCreationModel.init(); await ControllerCodexProfileModel.init(); await ControllerCodexMappingModel.init(); await ControllerCodexOriginMigrationModel.init(); this.codexConnectionStore = new ControllerCodexConnectionStore(this.issuerIdentity.issuerId); await ControllerLegacyManagedSessionMigrationModel.init(); this.sessionIdentityService = new ControllerSessionIdentityService( this.issuerIdentity.issuerId, ); } await ControllerResourceModel.init(); await ControllerSettingsModel.init(); await ControllerTempPasswordModel.init(); await ControllerSessionGroupsModel.init(); await ControllerSessionStateModel.init(); await ControllerTrackedConversationModel.init(); await new PostDatabaseDocumentMigrationRunner(this.database).run(); this.initialized = true; if (!this.closeStarted) this.projectMutationAdmissionOpen = true; } catch (errorArg) { const cleanupErrors: unknown[] = []; if (this.codexConnectionStore) { await this.codexConnectionStore.close(); this.codexConnectionStore = undefined; } if (this.sessionIdentityService) { try { await this.closeSessionIdentityService(); } catch (closeErrorArg) { cleanupErrors.push(closeErrorArg); } } if (this.database) { if (!databaseInitialized) { this.database = undefined; this.resolvedMongoDbUrl = undefined; this.issuerIdentity = undefined; this.initializedDataRoot = undefined; } else { try { await this.database.close(); this.database = undefined; this.resolvedMongoDbUrl = undefined; this.issuerIdentity = undefined; this.initializedDataRoot = undefined; } catch (closeErrorArg) { cleanupErrors.push(closeErrorArg); } } } if (!this.database) { try { await this.stopEmbeddedDatabase(); } catch (stopErrorArg) { cleanupErrors.push(stopErrorArg); } } try { this.releaseModelOwnershipIfResourcesClosed(); } catch (releaseErrorArg) { cleanupErrors.push(releaseErrorArg); } if (cleanupErrors.length > 0) { throw new AggregateError( [errorArg, ...cleanupErrors], 'SmartData initialization failed and cleanup was incomplete.', { cause: errorArg }, ); } throw errorArg; } // Retention is maintenance and must never gate readiness: clearing a // large pre-existing backlog takes far longer than the controller's // startup budget, so the first sweep runs detached like every later one. this.startAuditPrune(); this.auditPruneTimer = setInterval(() => { this.startAuditPrune(); }, auditPruneIntervalMs); this.auditPruneTimer.unref?.(); } private startAuditPrune(): void { if (this.closeStarted || this.auditPrunePromise) return; const prunePromise = this.pruneAuditHistory(); this.auditPrunePromise = prunePromise; void prunePromise.then(() => { if (this.auditPrunePromise === prunePromise) this.auditPrunePromise = undefined; }); } private async pruneAuditHistory(): Promise { try { const collection = ControllerAuditEventModel.collection.mongoDbCollection; // Count cap first: it bounds the collection regardless of event rate, // which the age rule alone cannot (heavy days produce tens of // thousands of events). const count = await collection.estimatedDocumentCount(); if (count > auditRetentionMaxEvents + auditRetentionCountSlack) { // One bounded batch per round trip: a sorted find whose limit is the // full excess exceeds the engine's per-operation materialization // ceiling once the backlog is large (which is exactly when this // runs). Re-reading the oldest page after each delete converges. let remaining = count - auditRetentionMaxEvents; for (let batch = 0; batch < auditCountPruneMaxBatchesPerSweep && remaining > 0; batch++) { const pageSize = Math.min(auditPruneBatchSize, remaining); const excess = await collection .find({}, { projection: { _id: 1 } }) .sort({ timestamp: 1 }) .limit(pageSize) .toArray(); if (excess.length === 0) break; await collection.deleteMany({ _id: { $in: excess.map((doc) => doc._id) } }); remaining -= excess.length; } } const cutoffIso = new Date(Date.now() - auditRetentionMs).toISOString(); for (let batch = 0; batch < auditPruneMaxBatchesPerSweep; batch++) { const expired = await collection .find({ timestamp: { $lt: cutoffIso } }, { projection: { _id: 1 } }) .limit(auditPruneBatchSize) .toArray(); if (expired.length === 0) return; await collection.deleteMany({ _id: { $in: expired.map((doc) => doc._id) } }); if (expired.length < auditPruneBatchSize) return; } } catch (errorArg) { // Retention is maintenance; it must never take the controller down. console.error('Audit history pruning failed.', errorArg); } } private async stopEmbeddedDatabase(): Promise { const embedded = this.embeddedDatabase; if (embedded) { await embedded.stop(); if (this.embeddedDatabase === embedded) this.embeddedDatabase = undefined; } } private async closeSessionIdentityService(): Promise { const service = this.sessionIdentityService; if (!service) return; await service.close(); if (this.sessionIdentityService === service) this.sessionIdentityService = undefined; } private releaseModelOwnershipIfResourcesClosed(): void { if ( !this.releaseModelOwnership || this.sessionIdentityService || this.database || this.embeddedDatabase ) return; this.releaseModelOwnership(); this.releaseModelOwnership = undefined; } public async close(): Promise { this.closeStarted = true; this.projectMutationAdmissionOpen = false; if (this.closePromise) return this.closePromise; const closePromise = (async () => { if (this.initPromise) await this.initPromise.catch(() => undefined); await this.projectMutationTail; await this.sessionStateMutationTail; if ( !this.database && !this.embeddedDatabase && !this.sessionIdentityService && !this.releaseModelOwnership ) return; if (this.auditPruneTimer) { clearInterval(this.auditPruneTimer); this.auditPruneTimer = undefined; } if (this.auditPrunePromise) await this.auditPrunePromise; const cleanupErrors: unknown[] = []; if (this.codexConnectionStore) { await this.codexConnectionStore.close(); this.codexConnectionStore = undefined; } if (this.sessionIdentityService) { try { await this.closeSessionIdentityService(); } catch (errorArg) { cleanupErrors.push(errorArg); } } if (this.database) { try { await this.database.close(); this.database = undefined; this.resolvedMongoDbUrl = undefined; } catch (errorArg) { cleanupErrors.push(errorArg); } } if (!this.database) { try { await this.stopEmbeddedDatabase(); } catch (errorArg) { cleanupErrors.push(errorArg); } } try { this.releaseModelOwnershipIfResourcesClosed(); } catch (errorArg) { cleanupErrors.push(errorArg); } if (cleanupErrors.length === 1) throw cleanupErrors[0]; if (cleanupErrors.length > 1) { throw new AggregateError(cleanupErrors, 'SmartData shutdown was incomplete.'); } })(); this.closePromise = closePromise; try { await closePromise; this.initialized = false; this.issuerIdentity = undefined; this.initializedDataRoot = undefined; this.sessionIdentityService = undefined; this.releaseModelOwnership = undefined; } finally { if (this.closePromise === closePromise) this.closePromise = undefined; } } public async getRuntimeConfig( controllerPortArg: number, ): Promise { this.assertInitialized(); if ( !Number.isSafeInteger(controllerPortArg) || controllerPortArg < 1 || controllerPortArg > 65535 ) { throw new Error('controllerPort must be an integer between 1 and 65535.'); } this.controllerId = `controller:${controllerPortArg}`; const stored = await ControllerAuthModel.exact.findStoredOne({ id: this.controllerId }); if (!stored) return undefined; return cloneRuntimeConfig(ControllerAuthModel.exact.toPersisted(stored).config); } /** Process-private connection data for the isolated Flex child and projection reader. */ public getDatabaseDescriptor(): IFlexDatabaseDescriptor { this.assertInitialized(); if (!this.resolvedMongoDbUrl) { throw new AuthError('not_initialized', 'The controller database connection is unavailable.'); } return { mongoDbUrl: this.resolvedMongoDbUrl, mongoDbName: this.options.mongoDbName, }; } public async resolveOrCreateRuntimeConfig( runtimeConfigArg: IControllerRuntimeConfig, ): Promise { this.assertInitialized(); if ( !Number.isSafeInteger(runtimeConfigArg.controllerPort) || runtimeConfigArg.controllerPort < 1 || runtimeConfigArg.controllerPort > 65535 ) { throw new Error('controllerPort must be an integer between 1 and 65535.'); } const controllerId = `controller:${runtimeConfigArg.controllerPort}`; this.controllerId = controllerId; const existing = await ControllerAuthModel.exact.findStoredOne({ id: controllerId }); if (existing) { const document = ControllerAuthModel.exact.toPersisted(existing); return this.finalizeRuntimeConfigDocument(document, runtimeConfigArg); } const candidate: IControllerAuthDocument = { id: controllerId, config: cloneRuntimeConfig(runtimeConfigArg), authState: 'setupRequired', setup: null, webauthnUserId: randomAuthorityValue(), credentials: [], }; try { const insertResult = await ControllerAuthModel.exact.insert(candidate); const document = ControllerAuthModel.exact.toPersisted(insertResult.document); return this.finalizeRuntimeConfigDocument(document, runtimeConfigArg); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await ControllerAuthModel.exact.findStoredOne({ id: controllerId }); if (!reconciled) { throw new AuthError( 'ambiguous_write', 'Controller authentication initialization has an ambiguous outcome.', { cause: errorArg }, ); } const document = ControllerAuthModel.exact.toPersisted(reconciled); return this.finalizeRuntimeConfigDocument(document, runtimeConfigArg); } } private async finalizeRuntimeConfigDocument( documentArg: IControllerAuthDocument, runtimeConfigArg: IControllerRuntimeConfig, ): Promise { this.assertRuntimeConfigMatches(documentArg.config, runtimeConfigArg); await new PostDatabaseDocumentMigrationRunner(this.database) .runProjectFilesystemIdentity(documentArg.id); return documentArg; } public async rotateSetupAuthority(providedCodeArg?: string): Promise { const current = await this.findStoredState(); const currentBody = ControllerAuthModel.exact.toPersisted(current); if (currentBody.credentials.length > 0) return null; const setupCode = providedCodeArg === undefined ? randomAuthorityValue() : assertProvidedSetupCode(providedCodeArg); const setupHash = hashSetupCode(setupCode); const generation = randomAuthorityValue(); const expiresAt = new Date(Date.now() + setupLifetimeMs); try { const result = await ControllerAuthModel.exact.transition({ current, change: (model) => { model.authState = 'setupRequired'; model.setup = { generation, hash: setupHash, expiresAt, }; }, }); if (result.status !== 'transitioned') { throw new AuthError( 'concurrent_change', 'Setup authority changed concurrently and was not rotated.', ); } return { setupCode, generation, expiresAt: new Date(expiresAt) }; } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await this.findStoredState(); const document = ControllerAuthModel.exact.toPersisted(reconciled); if ( document.setup?.generation === generation && document.setup.hash === setupHash && document.setup.expiresAt.getTime() === expiresAt.getTime() ) { return { setupCode, generation, expiresAt: new Date(expiresAt) }; } throw new AuthError( 'ambiguous_write', 'Setup authority rotation has an ambiguous outcome.', { cause: errorArg }, ); } } public async getState(): Promise { const stored = await this.findStoredState(); return ControllerAuthModel.exact.toPersisted(stored); } public async assertSetupAuthority( setupCodeArg: string, ceremonyArg?: IWebAuthnSetupCeremonyDocument, nowArg: Date = new Date(), ): Promise { if ( typeof setupCodeArg !== 'string' || setupCodeArg.length > 256 || !setupCodePattern.test(setupCodeArg) ) { throw new AuthError('setup_invalid', 'The setup code is invalid or expired.'); } if (!(nowArg instanceof Date) || !Number.isFinite(nowArg.getTime())) { throw new AuthError('setup_invalid', 'The setup validation time is malformed.'); } const state = await this.getState(); if (state.credentials.length > 0 || state.authState !== 'setupRequired' || !state.setup) { throw new AuthError('setup_unavailable', 'Passkey setup is no longer available.'); } if (state.setup.expiresAt.getTime() <= nowArg.getTime()) { throw new AuthError('setup_expired', 'The setup code is invalid or expired.'); } if ( ceremonyArg && ( ceremonyArg.setupGeneration !== state.setup.generation || ceremonyArg.setupHash !== state.setup.hash ) ) { throw new AuthError('setup_invalid', 'The setup code is invalid or expired.'); } const candidateHash = hashSetupCode(setupCodeArg); if (!hashesEqual(candidateHash, state.setup.hash)) { throw new AuthError('setup_invalid', 'The setup code is invalid or expired.'); } return state; } public async countActiveCeremonies( kindArg: TWebAuthnCeremonyKind, nowArg: Date, ): Promise { this.assertInitialized(); const kind = requireCeremonyKind(kindArg); const now = requireValidDate(nowArg, 'Ceremony count time'); return WebAuthnCeremonyModel.exact.count({ kind, state: 'pending', expiresAt: { $gt: now }, }); } public async createCeremony( ceremonyArg: IWebAuthnCeremonyDocument, ): Promise { this.assertInitialized(); if (ceremonyArg.state !== 'pending') { throw new AuthError('ceremony_invalid', 'A new WebAuthn ceremony must be pending.'); } const result = await WebAuthnCeremonyModel.exact.insert(ceremonyArg); if (result.status === 'conflict') { throw new AuthError('ceremony_invalid', 'The WebAuthn ceremony identifier already exists.'); } return WebAuthnCeremonyModel.exact.toPersisted(result.document); } public async consumeCeremony( optionsArg: IConsumeCeremonyOptions, ): Promise { const { current, body, now } = await this.findPendingCeremony(optionsArg); const consumedAt = now; const consumedNonce = randomAuthorityValue(); try { const result = await WebAuthnCeremonyModel.exact.transition({ current, change: (model) => { model.state = 'consumed'; model.consumedAt = consumedAt; model.consumedNonce = consumedNonce; }, }); if (result.status !== 'transitioned') { throw new AuthError('ceremony_replayed', 'The WebAuthn ceremony was already consumed.'); } return WebAuthnCeremonyModel.exact.toPersisted(result.document); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await WebAuthnCeremonyModel.exact.findStoredOne({ id: body.id, }); if (reconciled) { const document = WebAuthnCeremonyModel.exact.toPersisted(reconciled); if (document.state === 'consumed' && document.consumedNonce === consumedNonce) { return document; } } throw new AuthError( 'ambiguous_write', 'WebAuthn ceremony consumption has an ambiguous outcome.', { cause: errorArg }, ); } } public async assertPendingCeremony( optionsArg: IConsumeCeremonyOptions, ): Promise { const { body } = await this.findPendingCeremony(optionsArg); return body; } private async findPendingCeremony(optionsArg: IConsumeCeremonyOptions): Promise<{ current: TStoredCeremonyModel; body: IWebAuthnCeremonyDocument; now: Date; }> { this.assertInitialized(); const ceremonyId = requireCeremonyId(optionsArg.ceremonyId); const kind = requireCeremonyKind(optionsArg.kind); const peerId = requirePeerId(optionsArg.peerId); const origin = requireOrigin(optionsArg.origin); const rpId = requireRpId(optionsArg.rpId); const userId = requireUserId(optionsArg.userId); const now = requireValidDate(optionsArg.now, 'Ceremony validation time'); const current = await WebAuthnCeremonyModel.exact.findStoredOne({ id: ceremonyId }); if ( optionsArg.ceremonyId !== ceremonyId || optionsArg.kind !== kind || optionsArg.peerId !== peerId || optionsArg.origin !== origin || optionsArg.rpId !== rpId || optionsArg.userId !== userId || !(optionsArg.now instanceof Date) || optionsArg.now.getTime() !== now.getTime() ) { throw new AuthError( 'ceremony_invalid', 'The WebAuthn ceremony validation authority changed during lookup.', ); } if (!current) throw new AuthError('ceremony_invalid', 'The WebAuthn ceremony is invalid.'); const body = WebAuthnCeremonyModel.exact.toPersisted(current); if (body.kind !== kind) { throw new AuthError('ceremony_invalid', 'The WebAuthn ceremony has the wrong kind.'); } if (body.peerId !== peerId) { throw new AuthError('peer_mismatch', 'The WebAuthn ceremony belongs to another connection.'); } if (body.origin !== origin) { throw new AuthError('origin_mismatch', 'The WebAuthn ceremony origin changed.'); } if (body.rpId !== rpId || body.userId !== userId) { throw new AuthError('ceremony_invalid', 'The WebAuthn ceremony authority changed.'); } if (body.expiresAt.getTime() <= now.getTime()) { throw new AuthError('ceremony_expired', 'The WebAuthn ceremony expired.'); } if (body.state !== 'pending') { throw new AuthError('ceremony_replayed', 'The WebAuthn ceremony was already consumed.'); } return { current, body, now }; } public async commitFirstCredential( ceremonyArg: IWebAuthnSetupCeremonyDocument, credentialArg: IStoredPasskeyCredential, ): Promise { if (ceremonyArg.state !== 'consumed') { throw new AuthError('ceremony_invalid', 'The setup ceremony has not been consumed.'); } const current = await this.findStoredState(); const body = ControllerAuthModel.exact.toPersisted(current); if (body.credentials.length > 0 || body.authState !== 'setupRequired' || !body.setup) { throw new AuthError('concurrent_change', 'Another passkey won first enrollment.'); } if ( body.webauthnUserId !== ceremonyArg.userId || body.config.publicOrigin !== ceremonyArg.origin || body.config.rpId !== ceremonyArg.rpId || body.setup.generation !== ceremonyArg.setupGeneration || body.setup.hash !== ceremonyArg.setupHash ) { throw new AuthError('concurrent_change', 'Setup authority changed before enrollment.'); } try { const result = await ControllerAuthModel.exact.transition({ current, change: (model) => { model.authState = 'ready'; model.setup = null; model.credentials = [structuredClone(credentialArg)]; }, }); if (result.status !== 'transitioned') { throw new AuthError('concurrent_change', 'Another passkey won first enrollment.'); } return ControllerAuthModel.exact.toPersisted(result.document); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await this.findStoredState(); const document = ControllerAuthModel.exact.toPersisted(reconciled); if ( document.credentials.length === 1 && document.credentials[0].id === credentialArg.id && document.credentials[0].counterUpdateId === credentialArg.counterUpdateId && document.authState === 'ready' && document.setup === null ) { return document; } throw new AuthError( 'ambiguous_write', 'First passkey enrollment has an ambiguous outcome.', { cause: errorArg }, ); } } public async updateCounter( credentialIdArg: string, expectedCounterArg: number, newCounterArg: number, ): Promise { if (typeof credentialIdArg !== 'string' || credentialIdArg.length === 0) { throw new AuthError('credential_not_found', 'The passkey credential is malformed.'); } requireSafeCounter(expectedCounterArg, 'expectedCounter'); requireSafeCounter(newCounterArg, 'newCounter'); const current = await this.findStoredState(); const body = ControllerAuthModel.exact.toPersisted(current); const credentialIndex = body.credentials.findIndex( (credential) => credential.id === credentialIdArg, ); if (credentialIndex < 0) { throw new AuthError('credential_not_found', 'The passkey credential is unknown.'); } const credential = body.credentials[credentialIndex]; if (credential.counter !== expectedCounterArg) { throw new AuthError('counter_conflict', 'The passkey counter changed concurrently.'); } if ( (expectedCounterArg === 0 && newCounterArg !== 0 && newCounterArg <= expectedCounterArg) || (expectedCounterArg > 0 && newCounterArg <= expectedCounterArg) ) { throw new AuthError('counter_conflict', 'The passkey counter did not advance.'); } const counterUpdateId = randomAuthorityValue(); try { const result = await ControllerAuthModel.exact.transition({ current, change: (model) => { model.credentials = model.credentials.map((entry) => entry.id === credentialIdArg ? { ...entry, counter: newCounterArg, counterUpdateId } : entry); }, }); if (result.status !== 'transitioned') { throw new AuthError('counter_conflict', 'The passkey counter changed concurrently.'); } const document = ControllerAuthModel.exact.toPersisted(result.document); return document.credentials.find((entry) => entry.id === credentialIdArg)!; } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await this.findStoredState(); const document = ControllerAuthModel.exact.toPersisted(reconciled); const credentialAfter = document.credentials.find( (entry) => entry.id === credentialIdArg, ); if ( credentialAfter?.counter === newCounterArg && credentialAfter.counterUpdateId === counterUpdateId ) { return credentialAfter; } throw new AuthError( 'ambiguous_write', 'Passkey counter update has an ambiguous outcome.', { cause: errorArg }, ); } } public async recordAuditEvent( eventArg: IRecordAuditEventInput, ): Promise { this.assertInitialized(); if (!this.controllerId) { throw new AuthError('not_initialized', 'The controller runtime configuration is unresolved.'); } const event: IControllerAuditEvent = { id: randomAuthorityValue(), controllerId: this.controllerId, timestamp: eventArg.timestamp === undefined ? new Date() : new Date(eventArg.timestamp), type: eventArg.type, outcome: eventArg.outcome, ...(eventArg.operationId === undefined ? {} : { operationId: eventArg.operationId }), ...(eventArg.peerId === undefined ? {} : { peerId: eventArg.peerId }), ...(eventArg.credentialId === undefined ? {} : { credentialId: eventArg.credentialId }), ...(eventArg.sessionId === undefined ? {} : { sessionId: { ...eventArg.sessionId } }), ...(eventArg.requestId === undefined ? {} : { requestId: { ...eventArg.requestId } }), }; try { const result = await ControllerAuditEventModel.exact.insert(event); if (result.status === 'conflict') { throw new AuthError('concurrent_change', 'The audit event identifier conflicts.'); } return ControllerAuditEventModel.exact.toPersisted(result.document); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await ControllerAuditEventModel.exact.findStoredOne({ id: event.id }); if (reconciled) { const document = ControllerAuditEventModel.exact.toPersisted(reconciled); if (this.auditEventsEqual(document, event)) return document; } throw new AuthError( 'ambiguous_write', 'Audit event persistence has an ambiguous outcome.', { cause: errorArg }, ); } } private assertInitialized(): void { if (!this.initialized) { throw new AuthError('not_initialized', 'The authentication store is not initialized.'); } } private async requireSessionIdentityServiceForActiveProject( projectIdentityIdArg: string, inactiveMessageArg: string, ): Promise { const project = await this.getProject(projectIdentityIdArg); if (!project) { throw new AuthError('invalid_project', inactiveMessageArg); } const service = this.sessionIdentityService; if (!service) { throw new AuthError( 'not_initialized', 'The controlled session identity service is unavailable.', ); } return service; } private async requireSessionIdentityServiceForPendingProjectRemoval( projectIdentityIdArg: string, ): Promise { const project = await this.getPendingProjectRemoval(projectIdentityIdArg); if (!project) { throw new AuthError( 'invalid_project', 'The managed session recovery project is not pending removal.', ); } const service = this.sessionIdentityService; if (!service) { throw new AuthError( 'not_initialized', 'The controlled session identity service is unavailable.', ); } return service; } private withActiveProjectSessionIdentityMutation( projectIdentityIdArg: string, operationArg: (serviceArg: ControllerSessionIdentityService) => Promise, ): Promise { this.assertInitialized(); return this.withProjectMutation(async () => { const service = await this.requireSessionIdentityServiceForActiveProject( projectIdentityIdArg, 'The managed session project is not active.', ); return operationArg(service); }); } private withPendingProjectRemovalSessionIdentityMutation( projectIdentityIdArg: string, operationArg: (serviceArg: ControllerSessionIdentityService) => Promise, ): Promise { this.assertInitialized(); return this.withProjectMutation(async () => { const service = await this.requireSessionIdentityServiceForPendingProjectRemoval( projectIdentityIdArg, ); return operationArg(service); }); } public admitManagedSession( inputArg: IAdmitControllerManagedSessionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.admitManagedSession(inputArg), ); } public requireCodexConnections(): ControllerCodexConnectionStore { if (!this.initialized || this.closeStarted || !this.codexConnectionStore) throw new Error('Codex profile persistence is unavailable.'); return this.codexConnectionStore; } public listCodexMemberships(projectIdArg: string, signalArg?: AbortSignal): ReturnType { return this.withActiveProjectSessionIdentityMutation(projectIdArg, service => service.listCodexMemberships(projectIdArg, signalArg)); } public listCodexMembershipsForProjectRemoval(projectIdArg: string, signalArg?: AbortSignal): ReturnType { return this.withPendingProjectRemovalSessionIdentityMutation(projectIdArg, service => service.listCodexMemberships(projectIdArg, signalArg)); } public getCodexOrigin(projectIdArg: string, nativeIdArg: string, signalArg?: AbortSignal): ReturnType { return this.withActiveProjectSessionIdentityMutation(projectIdArg, service => service.codexOrigin(projectIdArg, nativeIdArg, signalArg)); } public beginCodexCreation(inputArg: IBeginCodexCreationInput): Promise { return this.withActiveProjectSessionIdentityMutation(inputArg.projectIdentityId, (service) => service.beginCodexCreation(inputArg)); } public observeManagedCodexSession(inputArg: Parameters[0]): ReturnType { return this.withActiveProjectSessionIdentityMutation(inputArg.projectIdentityId, (service) => service.observeManagedCodexSession(inputArg)); } public getCodexCreation(scopeArg: ICodexCreationScope): Promise { return this.withActiveProjectSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.getCodexCreation(scopeArg)); } public listCodexCreations(projectIdArg: string, signalArg?: AbortSignal): Promise { return this.withActiveProjectSessionIdentityMutation(projectIdArg, (service) => service.listCodexCreations(projectIdArg, signalArg)); } public listCodexCreationsForProjectRemoval(projectIdArg: string, signalArg?: AbortSignal): Promise { return this.withPendingProjectRemovalSessionIdentityMutation(projectIdArg, (service) => service.listCodexCreations(projectIdArg, signalArg)); } public admitCodexCreationForProjectRemoval(scopeArg: ICodexCreationScope, generationArg: string): Promise { return this.withPendingProjectRemovalSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.admitCodexCreation(scopeArg, generationArg)); } public retireCodexCreationForProjectRemoval(scopeArg: ICodexCreationScope, assertRuntimeTerminatedArg: (runtimeArg: ICodexCreationRuntime) => Promise): Promise { return this.withPendingProjectRemovalSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.retireCodexCreation(scopeArg, assertRuntimeTerminatedArg)); } public dispatchCodexCreation(scopeArg: ICodexCreationScope, runtimeArg: ICodexCreationRuntime): Promise { return this.withActiveProjectSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.dispatchCodexCreation(scopeArg, runtimeArg)); } public bindCodexCreation(scopeArg: ICodexCreationScope, generationArg: string, nativeIdArg: string, createdAtArg: number): Promise { return this.withActiveProjectSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.bindCodexCreation(scopeArg, generationArg, nativeIdArg, createdAtArg)); } public admitCodexCreation(scopeArg: ICodexCreationScope, generationArg: string): Promise { return this.withActiveProjectSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.admitCodexCreation(scopeArg, generationArg)); } public findUnmaterializedCodexCreation(projectIdArg: string, nativeIdArg: string): Promise { return this.withActiveProjectSessionIdentityMutation(projectIdArg, (service) => service.findUnmaterializedCodexCreation(projectIdArg, nativeIdArg)); } public finalizeCodexCreationMetadata(scopeArg: ICodexCreationScope): Promise { return this.withActiveProjectSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.finalizeCodexCreationMetadata(scopeArg)); } public prepareCodexTurn(projectIdArg: string, nativeIdArg: string): Promise { return this.withActiveProjectSessionIdentityMutation(projectIdArg, (service) => service.prepareCodexTurn(projectIdArg, nativeIdArg)); } public cancelUndispatchedCodexTurn(projectIdArg: string, nativeIdArg: string): Promise { return this.withActiveProjectSessionIdentityMutation(projectIdArg, (service) => service.cancelUndispatchedCodexTurn(projectIdArg, nativeIdArg)); } public markCodexMaterialized(projectIdArg: string, nativeIdArg: string): Promise { return this.withActiveProjectSessionIdentityMutation(projectIdArg, (service) => service.markCodexMaterialized(projectIdArg, nativeIdArg)); } public completeCodexCreationDeletion(projectIdArg: string, nativeIdArg: string, projectRemovalArg = false): Promise { return projectRemovalArg ? this.withPendingProjectRemovalSessionIdentityMutation(projectIdArg, (service) => service.completeCodexCreationDeletion(projectIdArg, nativeIdArg)) : this.withActiveProjectSessionIdentityMutation(projectIdArg, (service) => service.completeCodexCreationDeletion(projectIdArg, nativeIdArg)); } public retireCodexCreation(scopeArg: ICodexCreationScope, assertRuntimeTerminatedArg: (runtimeArg: ICodexCreationRuntime) => Promise): Promise { return this.withActiveProjectSessionIdentityMutation(scopeArg.projectIdentityId, (service) => service.retireCodexCreation(scopeArg, assertRuntimeTerminatedArg)); } public migrateLegacyManagedSessions( inputArg: TControllerLegacyManagedSessionMigrationRunnerInput, ): Promise { return this.withActiveProjectSessionIdentityMutation(inputArg.projectId, async (service) => { const issuerId = this.issuerIdentity?.issuerId; if (!issuerId) { throw new AuthError( 'not_initialized', 'The controller issuer identity is unavailable for legacy session migration.', ); } return new ControllerLegacyManagedSessionMigrationRunner({ controllerId: this.requireControllerId(), issuerId, sessionIdentityService: service, }).run(inputArg); }); } public requireManagedSession( inputArg: IRequireControllerManagedSessionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.requireManagedSession(inputArg), ); } public resolveManagedSession( inputArg: IRequireControllerManagedSessionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.resolveManagedSession(inputArg), ); } public resolveSessionLayoutAuthority( inputArg: IResolveControllerSessionLayoutAuthorityInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.resolveSessionLayoutAuthority(inputArg), ); } public pruneDefinitivelyUnmanagedSessionLayoutEntries( projectIdentityIdArg: string, sessionIdsArg: readonly IControllerRuntimeId[], signalArg?: AbortSignal, ): Promise<{ changed: boolean; sessionIds: IControllerRuntimeId[]; }> { this.assertInitialized(); return this.withProjectMutation(async () => { signalArg?.throwIfAborted(); const service = await this.requireSessionIdentityServiceForActiveProject( projectIdentityIdArg, 'The session-layout project is not active.', ); signalArg?.throwIfAborted(); const unique = new Map(sessionIdsArg.filter(isSessionId).map((sessionId) => [ controllerRuntimeIdKey(sessionId), sessionId, ])); const unmanaged: IControllerRuntimeId[] = []; for (const sessionId of unique.values()) { const authority = await service.resolveSessionLayoutAuthority({ projectIdentityId: projectIdentityIdArg, runtimeId: sessionId, signal: signalArg, }); if (authority === 'definitively_unmanaged') unmanaged.push({ ...sessionId }); } signalArg?.throwIfAborted(); if (unmanaged.length === 0) return { changed: false, sessionIds: [] }; const unmanagedKeys = new Set(unmanaged.map(controllerRuntimeIdKey)); const changed = await this.filterSessionLayout( (itemRef) => itemRef.kind !== 'session' || itemRef.projectId !== projectIdentityIdArg || !unmanagedKeys.has(controllerRuntimeIdKey(itemRef.id)), signalArg, ); return { changed, sessionIds: unmanaged }; }); } public resolveRetiredManagedSessionIdentity( inputArg: IResolveRetiredManagedSessionIdentityInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.resolveRetiredManagedSessionIdentity(inputArg), ); } public resolveFlexHostSessionAuthority( inputArg: IResolveControllerFlexHostSessionAuthorityInput, ): Promise { this.assertInitialized(); return this.withProjectMutation(async () => { const controllerId = this.requireControllerId(); const stored = await ControllerProjectModel.exact.findStoredOne({ id: inputArg.projectIdentityId, }); if (!stored) return 'definitively_unmanaged'; const project = ControllerProjectModel.exact.toPersisted(stored); if (project.controllerId !== controllerId || project.removedAt !== undefined) { return 'definitively_unmanaged'; } if (project.removalStartedAt !== undefined) { if ( project.removalOperationId === undefined || project.flexCleanupCohort === undefined ) return 'uncertain'; if (project.flexCleanupCohort.some((entry) => ( entry.sessionId === inputArg.runtimeId.nativeId && entry.providerSessionGeneration === inputArg.providerSessionGeneration ))) return 'project_removal_cleanup'; const service = this.sessionIdentityService; if (!service) { throw new AuthError( 'not_initialized', 'The controlled session identity service is unavailable.', ); } const managedAuthority = await service.resolveFlexHostSessionAuthority(inputArg); return managedAuthority === 'deleting_cleanup' || managedAuthority === 'uncertain' ? managedAuthority : 'definitively_unmanaged'; } const service = this.sessionIdentityService; if (!service) { throw new AuthError( 'not_initialized', 'The controlled session identity service is unavailable.', ); } return service.resolveFlexHostSessionAuthority(inputArg); }); } public resolvePendingFlexProjectManagementLoadAuthority( inputArg: IResolveControllerPendingFlexProjectManagementLoadAuthorityInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.resolvePendingFlexProjectManagementLoadAuthority(inputArg), ); } public resolveFlexProjectManagementRegistrationLoadAuthority( inputArg: IResolveControllerFlexProjectManagementRegistrationLoadAuthorityInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.resolveFlexProjectManagementRegistrationLoadAuthority(inputArg), ); } public beginManagedSessionDeletion( inputArg: IBeginControllerManagedSessionDeletionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.beginManagedSessionDeletion(inputArg), ); } public markManagedSessionDeletionDispatched( inputArg: IMarkControllerManagedSessionDeletionDispatchedInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.markManagedSessionDeletionDispatched(inputArg), ); } public cancelManagedSessionDeletion( inputArg: ICancelControllerManagedSessionDeletionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.cancelManagedSessionDeletion(inputArg), ); } public completeManagedSessionDeletion( inputArg: ICompleteControllerManagedSessionDeletionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.completeManagedSessionDeletion(inputArg), ); } public listManagedSessionDeletionObligations( inputArg: IListControllerManagedSessionDeletionObligationsInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.listManagedSessionDeletionObligations(inputArg), ); } public listFlexManagedCleanupRoots( inputArg: IListControllerFlexManagedCleanupRootsInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.listFlexManagedCleanupRoots(inputArg), ); } public retireProjectManagedSessions( inputArg: IRetireControllerProjectManagedSessionsInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.retireProjectManagedSessions(inputArg), ); } public beginManagedSessionCreation( inputArg: IBeginControllerManagedSessionCreationInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.beginManagedSessionCreation(inputArg), ); } public markManagedSessionCreationDispatched( inputArg: IMarkControllerManagedSessionCreationDispatchedInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.markManagedSessionCreationDispatched(inputArg), ); } public completeManagedSessionCreationAdmission( inputArg: ICompleteControllerManagedSessionCreationAdmissionInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.completeManagedSessionCreationAdmission(inputArg), ); } public retireManagedSessionCreation( inputArg: IRetireControllerManagedSessionCreationInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.retireManagedSessionCreation(inputArg), ); } public listPendingSessionCreationObligations( inputArg: IListControllerSessionCreationObligationsInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.listPendingSessionCreationObligations(inputArg), ); } public getSessionCreationObligation( inputArg: IGetControllerSessionCreationObligationInput, ): Promise { return this.withActiveProjectSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.getSessionCreationObligation(inputArg), ); } /** Internal recovery path: normal callers must use the active-project wrapper. */ public listManagedSessionDeletionObligationsForProjectRemoval( inputArg: IListControllerManagedSessionDeletionObligationsInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.listManagedSessionDeletionObligations(inputArg), ); } /** Internal recovery path: normal callers must use the active-project wrapper. */ public markManagedSessionDeletionDispatchedForProjectRemoval( inputArg: IMarkControllerManagedSessionDeletionDispatchedInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.markManagedSessionDeletionDispatched(inputArg), ); } /** Internal recovery path: normal callers must use the active-project wrapper. */ public completeManagedSessionDeletionForProjectRemoval( inputArg: ICompleteControllerManagedSessionDeletionInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.completeManagedSessionDeletion(inputArg), ); } /** Internal recovery path: normal callers must use the active-project wrapper. */ public retireProjectManagedSessionsForProjectRemoval( inputArg: IRetireControllerProjectManagedSessionsInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.retireProjectManagedSessions(inputArg), ); } /** Internal recovery path: normal callers must use the active-project wrapper. */ public listPendingSessionCreationObligationsForProjectRemoval( inputArg: IListControllerSessionCreationObligationsInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.listPendingSessionCreationObligations(inputArg), ); } /** Internal recovery path: caller must prove the provider session exists. */ public completeManagedSessionCreationAdmissionForProjectRemoval( inputArg: ICompleteControllerManagedSessionCreationAdmissionInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.completeManagedSessionCreationAdmission(inputArg), ); } /** Internal recovery path: caller must first prove the provider session absent. */ public retireManagedSessionCreationForProjectRemoval( inputArg: IRetireControllerManagedSessionCreationInput, ): Promise { return this.withPendingProjectRemovalSessionIdentityMutation( inputArg.projectIdentityId, (service) => service.retireManagedSessionCreation(inputArg), ); } public [authStoreSessionIdentityCapability]( inputArg: Parameters< IAuthStoreSessionIdentityCapability[typeof authStoreSessionIdentityCapability] >[0], ): ReturnType { this.assertInitialized(); return this.withProjectMutation(async () => { const service = await this.requireSessionIdentityServiceForActiveProject( inputArg.projectIdentityId, 'The session identity snapshot project is not active.', ); return service.reconcileFencedProviderSnapshot(inputArg); }); } private async findStoredState(): Promise> & object> { this.assertInitialized(); if (!this.controllerId) { throw new AuthError('not_initialized', 'The controller runtime configuration is unresolved.'); } const stored = await ControllerAuthModel.exact.findStoredOne({ id: this.controllerId }); if (!stored) { throw new AuthError('not_initialized', 'The controller authentication document is missing.'); } return stored; } private requireControllerId(): string { if (!this.controllerId) { throw new AuthError('not_initialized', 'The controller identity has not been resolved yet.'); } return this.controllerId; } public async listProjects(): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const stored = await ControllerProjectModel.exact.findStored({ filter: { controllerId, removalStartedAt: { $exists: false }, removedAt: { $exists: false }, }, sort: { createdAt: 1 }, limit: controllerProjectLimit + 1, }); if (stored.length > controllerProjectLimit) { throw new AuthError( 'invalid_project', `The controller exceeds the ${controllerProjectLimit}-project limit.`, ); } return stored.map((entry) => cloneProjectDocument( ControllerProjectModel.exact.toPersisted(entry), )); } public async createProject( nameArg: string, directoryArg: string, identityArg: IControllerProjectFilesystemIdentity, ): Promise<{ project: IControllerProjectDocument; created: boolean }> { this.assertInitialized(); const controllerId = this.requireControllerId(); if (!isProjectRelativePathName(nameArg)) { throw new AuthError( 'invalid_project', 'Project names must be directory paths without dot navigation.', ); } if (typeof directoryArg !== 'string' || !plugins.path.isAbsolute(directoryArg)) { throw new AuthError('invalid_project', 'The project directory must be an absolute path.'); } return this.withProjectMutation(async () => { const activeProjects = await this.listProjects(); const existing = activeProjects.find((entry) => entry.directory === directoryArg); if (existing) { if ( existing.directoryIdentityState !== 'bound' || existing.directoryIdentity === undefined || !filesystemIdentitiesEqual(existing.directoryIdentity, identityArg) ) { throw new AuthError( 'concurrent_change', 'The registered project path has a different filesystem identity.', ); } return { project: cloneProjectDocument(existing), created: false }; } const pendingRemovals = await this.listPendingProjectRemovals(); if (activeProjects.length + pendingRemovals.length >= controllerProjectLimit) { throw new AuthError( 'invalid_project', `The controller supports at most ${controllerProjectLimit} active or pending-removal projects.`, ); } if (pendingRemovals.some((entry) => ( entry.directory === directoryArg || entry.name === nameArg ))) { throw new AuthError( 'concurrent_change', 'The project path is still being removed and cannot be registered again.', ); } if (activeProjects.some((entry) => entry.name === nameArg)) { throw new AuthError('invalid_project', 'A project with this name already exists.'); } const candidate: IControllerProjectDocument = { id: plugins.crypto.randomBytes(16).toString('base64url'), controllerId, name: nameArg, directory: directoryArg, directoryIdentityState: 'bound', directoryIdentity: { ancestry: structuredClone(identityArg.ancestry) }, createdAt: new Date(), }; const result = await ControllerProjectModel.exact.insert(candidate); if (result.status === 'conflict') { throw new AuthError('concurrent_change', 'The project was created concurrently.'); } return { project: cloneProjectDocument(ControllerProjectModel.exact.toPersisted(result.document)), created: true, }; }); } public async removeProject(projectIdArg: string, operationIdArg: string): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64) { throw new AuthError('invalid_project', 'The project identifier is malformed.'); } if (!projectRemovalOperationPattern.test(operationIdArg)) { throw new AuthError('invalid_project', 'The project removal operation identifier is malformed.'); } return this.withProjectMutation(async () => { const stored = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (!stored) return false; const document = ControllerProjectModel.exact.toPersisted(stored); if (document.controllerId !== controllerId) return false; if (document.removedAt !== undefined) { return document.removalStartedAt === undefined && document.removalOperationId === operationIdArg && document.flexCleanupCohort === undefined; } if ( document.removalStartedAt === undefined || document.removalOperationId !== operationIdArg || document.flexCleanupCohort === undefined ) { return false; } try { const result = await ControllerProjectModel.exact.transition({ current: stored, change: (model) => { delete model.removalStartedAt; delete model.flexCleanupCohort; model.removedAt = new Date(); }, }); if (result.status === 'transitioned') return true; } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; } const reconciled = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (reconciled) { const after = ControllerProjectModel.exact.toPersisted(reconciled); if ( after.controllerId === controllerId && after.removedAt !== undefined && after.removalStartedAt === undefined && after.removalOperationId === operationIdArg && after.flexCleanupCohort === undefined ) return true; } throw new AuthError( 'concurrent_change', 'The project changed concurrently and was not removed.', ); }); } public async beginProjectRemoval( projectIdArg: string, operationIdArg: string, flexCleanupCohortArg: readonly IControllerFlexCleanupEntry[], ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64) { throw new AuthError('invalid_project', 'The project identifier is malformed.'); } if (!projectRemovalOperationPattern.test(operationIdArg)) { throw new AuthError('invalid_project', 'The project removal operation identifier is malformed.'); } let flexCleanupCohort: IControllerFlexCleanupEntry[]; try { flexCleanupCohort = cloneControllerFlexCleanupCohort(flexCleanupCohortArg); } catch (errorArg) { throw new AuthError( 'invalid_project', 'The project removal Flex cleanup cohort is malformed.', { cause: errorArg }, ); } return this.withProjectMutation(async () => { const stored = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (!stored) return undefined; const document = ControllerProjectModel.exact.toPersisted(stored); if (document.controllerId !== controllerId || document.removedAt !== undefined) return undefined; if (document.removalStartedAt !== undefined) { if ( document.removalOperationId === undefined || document.flexCleanupCohort === undefined ) return cloneProjectDocument(document); if ( document.removalOperationId !== operationIdArg || !controllerFlexCleanupCohortsEqual( document.flexCleanupCohort, flexCleanupCohort, ) ) { throw new AuthError( 'concurrent_change', 'The project is already owned by a different removal operation.', ); } return cloneProjectDocument(document); } try { const result = await ControllerProjectModel.exact.transition({ current: stored, change: (model) => { model.removalStartedAt = new Date(); model.removalOperationId = operationIdArg; model.flexCleanupCohort = cloneControllerFlexCleanupCohort(flexCleanupCohort); }, }); if (result.status === 'transitioned') { return cloneProjectDocument(ControllerProjectModel.exact.toPersisted(result.document)); } } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; } const reconciled = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (reconciled) { const after = ControllerProjectModel.exact.toPersisted(reconciled); if ( after.controllerId === controllerId && after.removedAt === undefined && after.removalStartedAt !== undefined && after.removalOperationId === operationIdArg && after.flexCleanupCohort !== undefined && controllerFlexCleanupCohortsEqual(after.flexCleanupCohort, flexCleanupCohort) ) return cloneProjectDocument(after); } throw new AuthError( 'concurrent_change', 'The project changed concurrently and was not retired.', ); }); } /** * Records why a pending removal stopped being retried. Only the removal operation that owns the * project may block it, so a stale attempt can never label a removal it no longer drives. */ public async blockProjectRemoval( projectIdArg: string, operationIdArg: string, codeArg: TControllerProjectRemovalBlockedCode, reasonArg: string, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64) { throw new AuthError('invalid_project', 'The project identifier is malformed.'); } if (!projectRemovalOperationPattern.test(operationIdArg)) { throw new AuthError('invalid_project', 'The project removal operation identifier is malformed.'); } if (!controllerProjectRemovalBlockedCodes.includes(codeArg)) { throw new AuthError('invalid_project', 'The project removal block code is unknown.'); } if ( typeof reasonArg !== 'string' || reasonArg.length === 0 || reasonArg.length > controllerProjectRemovalBlockedReasonMaxLength ) { throw new AuthError('invalid_project', 'The project removal block reason is malformed.'); } return this.withProjectMutation(async () => { const stored = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (!stored) return false; const document = ControllerProjectModel.exact.toPersisted(stored); if ( document.controllerId !== controllerId || document.removedAt !== undefined || document.removalStartedAt === undefined || document.removalOperationId !== operationIdArg ) return false; try { const result = await ControllerProjectModel.exact.transition({ current: stored, change: (model) => { model.removalBlockedAt = new Date(); model.removalBlockedCode = codeArg; model.removalBlockedReason = reasonArg; }, }); if (result.status === 'transitioned') return true; } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; } const reconciled = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (reconciled) { const after = ControllerProjectModel.exact.toPersisted(reconciled); if ( after.controllerId === controllerId && after.removalOperationId === operationIdArg && after.removalBlockedCode === codeArg && after.removalBlockedReason === reasonArg ) return true; } throw new AuthError( 'concurrent_change', 'The project changed concurrently and its removal block was not recorded.', ); }); } /** * Clears a recorded removal block so the pending removal can be attempted again. The removal * itself is never discarded; the returned document is the still-pending project. */ public async clearProjectRemovalBlock( projectIdArg: string, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64) { throw new AuthError('invalid_project', 'The project identifier is malformed.'); } return this.withProjectMutation(async () => { const stored = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (!stored) return undefined; const document = ControllerProjectModel.exact.toPersisted(stored); if ( document.controllerId !== controllerId || document.removedAt !== undefined || document.removalStartedAt === undefined ) return undefined; if (document.removalBlockedAt === undefined) return cloneProjectDocument(document); try { const result = await ControllerProjectModel.exact.transition({ current: stored, change: (model) => { delete model.removalBlockedAt; delete model.removalBlockedCode; delete model.removalBlockedReason; }, }); if (result.status === 'transitioned') { return cloneProjectDocument(ControllerProjectModel.exact.toPersisted(result.document)); } } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; } const reconciled = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (reconciled) { const after = ControllerProjectModel.exact.toPersisted(reconciled); if ( after.controllerId === controllerId && after.removedAt === undefined && after.removalStartedAt !== undefined && after.removalBlockedAt === undefined ) return cloneProjectDocument(after); } throw new AuthError( 'concurrent_change', 'The project changed concurrently and its removal block was not cleared.', ); }); } public async listPendingProjectRemovals(): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const stored = await ControllerProjectModel.exact.findStored({ filter: { controllerId, removalStartedAt: { $exists: true }, removedAt: { $exists: false }, }, sort: { removalStartedAt: 1 }, limit: controllerProjectLimit + 1, }); if (stored.length > controllerProjectLimit) { throw new AuthError('invalid_project', 'The pending project-removal limit was exceeded.'); } return stored.map((entry) => cloneProjectDocument( ControllerProjectModel.exact.toPersisted(entry), )); } private withProjectMutation(operationArg: () => Promise): Promise { if (!this.projectMutationAdmissionOpen) { return Promise.reject(new AuthError( 'not_initialized', 'Project mutations are unavailable while the authentication store is closing.', )); } const operation = this.projectMutationTail.then(async () => { if (!this.projectMutationAdmissionOpen) { throw new AuthError( 'not_initialized', 'Project mutations are unavailable while the authentication store is closing.', ); } return operationArg(); }); this.projectMutationTail = operation.then(() => undefined, () => undefined); return operation; } private async insertResourceDocument( candidateArg: IControllerResourceDocument, ): Promise { try { const result = await ControllerResourceModel.exact.insert(candidateArg); if (result.status === 'conflict') { throw new AuthError('concurrent_change', 'The resource identifier conflicted.'); } return cloneResourceDocument(ControllerResourceModel.exact.toPersisted(result.document)); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await ControllerResourceModel.exact.findStoredOne({ id: candidateArg.id }); if (reconciled) { const document = ControllerResourceModel.exact.toPersisted(reconciled); if (document.updateId === candidateArg.updateId) return cloneResourceDocument(document); } throw new AuthError( 'ambiguous_write', 'The resource insertion has an ambiguous outcome.', { cause: errorArg }, ); } } private async transitionResourceDocument( currentArg: TStoredResourceModel, changeArg: (modelArg: InstanceType) => void, concurrentMessageArg: string, ambiguousMessageArg: string, ): Promise { const current = ControllerResourceModel.exact.toPersisted(currentArg); const updateId = randomAuthorityValue(); try { const result = await ControllerResourceModel.exact.transition({ current: currentArg, change: (model) => { changeArg(model); model.updateId = updateId; }, }); if (result.status !== 'transitioned') { throw new AuthError('concurrent_change', concurrentMessageArg); } return cloneResourceDocument(ControllerResourceModel.exact.toPersisted(result.document)); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await ControllerResourceModel.exact.findStoredOne({ id: current.id }); if (reconciled) { const document = ControllerResourceModel.exact.toPersisted(reconciled); if (document.updateId === updateId) return cloneResourceDocument(document); } throw new AuthError('ambiguous_write', ambiguousMessageArg, { cause: errorArg }); } } public async getProject(projectIdArg: string): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64) { return undefined; } const stored = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (!stored) return undefined; const document = ControllerProjectModel.exact.toPersisted(stored); if ( document.controllerId !== controllerId || document.removalStartedAt !== undefined || document.removedAt !== undefined ) { return undefined; } return cloneProjectDocument(document); } public async getPendingProjectRemoval( projectIdArg: string, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64) { return undefined; } const stored = await ControllerProjectModel.exact.findStoredOne({ id: projectIdArg }); if (!stored) return undefined; const document = ControllerProjectModel.exact.toPersisted(stored); if ( document.controllerId !== controllerId || document.removalStartedAt === undefined || document.removedAt !== undefined ) return undefined; return cloneProjectDocument(document); } public async listResources( projectIdArg: string, optionsArg: { includeRetired?: boolean; signal?: AbortSignal } = {}, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const stored = await ControllerResourceModel.exact.findStored({ filter: { controllerId, projectId: projectIdArg, ...(optionsArg.includeRetired ? {} : { retiredAt: { $exists: false } }), }, sort: { createdAt: 1 }, limit: controllerResourcesPerProjectLimit + 1, signal: optionsArg.signal, }); if (stored.length > controllerResourcesPerProjectLimit) { throw new AuthError( 'invalid_resource', `The project exceeds the ${controllerResourcesPerProjectLimit}-resource limit.`, ); } return stored.map((entry) => cloneResourceDocument( ControllerResourceModel.exact.toPersisted(entry), )); } public async listRecoverableResources(): Promise { this.assertInitialized(); const stored = await ControllerResourceModel.exact.findStored({ filter: { controllerId: this.requireControllerId(), retiredAt: { $exists: false }, }, sort: { createdAt: 1 }, limit: controllerResourceLimit + 1, }); if (stored.length > controllerResourceLimit) { throw new AuthError('invalid_resource', 'The controller resource limit was exceeded.'); } return stored.map((entry) => cloneResourceDocument( ControllerResourceModel.exact.toPersisted(entry), )); } public async getResource( projectIdArg: string, resourceIdArg: string, optionsArg: { includeRetired?: boolean } = {}, ): Promise { this.assertInitialized(); const stored = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!stored) return undefined; const resource = ControllerResourceModel.exact.toPersisted(stored); if ( resource.controllerId !== this.requireControllerId() || resource.projectId !== projectIdArg || (!optionsArg.includeRetired && resource.lifecycle === 'retired') ) return undefined; return cloneResourceDocument(resource); } public async createResource(inputArg: { projectId: string; kind: TControllerResourceKind; title: string; terminal?: IControllerResourceDocument['terminal']; }): Promise { this.assertInitialized(); return this.withProjectMutation(async () => { const project = await this.getProject(inputArg.projectId); if (!project) throw new AuthError('invalid_project', 'The resource project is unavailable.'); const [existing, recoverable] = await Promise.all([ this.listResources(inputArg.projectId), this.listRecoverableResources(), ]); assertControllerResourceCreationCapacity(existing.length, recoverable.length); const now = new Date(); const candidate: IControllerResourceDocument = { id: randomResourceId(), controllerId: this.requireControllerId(), projectId: inputArg.projectId, kind: inputArg.kind, title: inputArg.title, attachmentAuthorityId: randomAuthorityValue(), attachmentRevision: 0, attachments: [], lifecycle: 'active', ...(inputArg.terminal === undefined ? {} : { terminal: { ...inputArg.terminal } }), createdAt: now, updatedAt: now, updateId: randomAuthorityValue(), }; return this.insertResourceDocument(candidate); }); } public async renameResource( projectIdArg: string, resourceIdArg: string, titleArg: string, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if ( before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg || before.lifecycle !== 'active' ) throw new AuthError('invalid_resource', 'The resource is unavailable.'); return this.transitionResourceDocument( current, (model) => { model.title = titleArg; model.updatedAt = new Date(); }, 'The resource changed concurrently.', 'The resource rename has an ambiguous outcome.', ); } /** * Opens the single in-flight attachment obligation. `add` installs an entry (idempotent when * the same entry is already present), `remove` takes one out or clears the set when no entry is * named, and `replace` does both atomically for a Move. */ public async beginResourceAttachmentTransition( projectIdArg: string, resourceIdArg: string, intentArg: { op: 'add' | 'remove' | 'replace'; entry?: IControllerResourceAttachmentEntryDocument; replaces?: IControllerResourceAttachmentEntryDocument; }, expectedAttachmentRevisionArg: number, ): Promise { if (!Number.isSafeInteger(expectedAttachmentRevisionArg) || expectedAttachmentRevisionArg < 0) { throw new AuthError('invalid_resource', 'The expected attachment revision is malformed.'); } if (intentArg.op !== 'remove' && intentArg.entry === undefined) { throw new AuthError('invalid_resource', 'The attachment intent names no entry.'); } if ((intentArg.op === 'replace') !== (intentArg.replaces !== undefined)) { throw new AuthError('invalid_resource', 'Only a replace names the entry it supersedes.'); } const entry = intentArg.entry; if (entry?.kind === 'session' && !isResourceSessionIdentityId(entry.sessionIdentityId)) { throw new AuthError('invalid_resource', 'The target session identity is malformed.'); } if (entry?.kind === 'terminal' && entry.id === resourceIdArg) { throw new AuthError('invalid_resource', 'A terminal cannot be attached to itself.'); } const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if ( before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg || before.lifecycle !== 'active' ) throw new AuthError('invalid_resource', 'The resource is unavailable.'); if (entry !== undefined && entry.projectId !== before.projectId) { throw new AuthError('invalid_resource', 'The attachment subject belongs to another project.'); } if (entry?.kind === 'terminal' && before.kind !== 'browser') { throw new AuthError('invalid_resource', 'Only a browser may be attached to a terminal.'); } if (before.pendingAttachment) { throw new AuthError('concurrent_change', 'The resource attachment is already changing.'); } if (before.attachmentRevision !== expectedAttachmentRevisionArg) { throw new AuthError('concurrent_change', 'The resource attachment changed concurrently.'); } const operationId = randomResourceOperationId(); return this.transitionResourceDocument( current, (model) => { model.pendingAttachment = { operationId, attachmentRevision: before.attachmentRevision + 1, op: intentArg.op, ...(entry === undefined ? {} : { entry: cloneResourceAttachmentEntry(entry) }), ...(intentArg.replaces === undefined ? {} : { replaces: cloneResourceAttachmentEntry(intentArg.replaces) }), requestedAt: new Date(), }; model.updatedAt = new Date(); }, 'The resource attachment changed concurrently.', 'Preparing the resource attachment has an ambiguous outcome.', ); } public async commitResourceAttachmentTransition( projectIdArg: string, resourceIdArg: string, operationIdArg: string, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if ( before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg || before.lifecycle !== 'active' || before.pendingAttachment?.operationId !== operationIdArg ) throw new AuthError('concurrent_change', 'The pending resource attachment no longer matches.'); const pending = before.pendingAttachment; if ( pending.entry?.kind === 'session' && !isResourceSessionIdentityId(pending.entry.sessionIdentityId) ) { throw new AuthError('invalid_resource', 'The pending resource attachment has no exact identity.'); } return this.transitionResourceDocument( current, (model) => { model.attachmentRevision = pending.attachmentRevision; model.attachments = applyResourceAttachmentIntent(before.attachments, pending); delete model.pendingAttachment; model.updatedAt = new Date(); }, 'The resource attachment changed concurrently.', 'Committing the resource attachment has an ambiguous outcome.', ); } public async cancelResourceAttachmentTransition( projectIdArg: string, resourceIdArg: string, operationIdArg: string, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if ( before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg || before.lifecycle !== 'active' || before.pendingAttachment?.operationId !== operationIdArg ) throw new AuthError('concurrent_change', 'The pending resource attachment no longer matches.'); return this.transitionResourceDocument( current, (model) => { delete model.pendingAttachment; model.updatedAt = new Date(); }, 'The resource attachment changed concurrently.', 'Cancelling the resource attachment has an ambiguous outcome.', ); } public async beginResourceRetirement( projectIdArg: string, resourceIdArg: string, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if (before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg) { throw new AuthError('invalid_resource', 'The resource is unavailable.'); } if (before.lifecycle !== 'active') return cloneResourceDocument(before); if (before.pendingAttachment) { throw new AuthError('concurrent_change', 'The resource attachment must settle before retirement.'); } const now = new Date(); return this.transitionResourceDocument( current, (model) => { model.lifecycle = 'retiring'; model.retiringAt = now; model.updatedAt = now; }, 'The resource changed concurrently.', 'Preparing resource retirement has an ambiguous outcome.', ); } public async completeResourceRetirement( projectIdArg: string, resourceIdArg: string, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if (before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg) { throw new AuthError('invalid_resource', 'The resource is unavailable.'); } if (before.lifecycle === 'retired') return cloneResourceDocument(before); if (before.lifecycle !== 'retiring') { throw new AuthError('concurrent_change', 'The resource is not retiring.'); } const now = new Date(); return this.transitionResourceDocument( current, (model) => { model.lifecycle = 'retired'; model.retiredAt = now; model.updatedAt = now; }, 'The resource changed concurrently.', 'Completing resource retirement has an ambiguous outcome.', ); } public async markTerminalResourceStopped( projectIdArg: string, resourceIdArg: string, outcomeArg: { stoppedAt?: Date; lastExitCode?: number; /** * Absent preserves the recorded intent, which is the controller-shutdown case: the chat was * closed underneath the user and must come back on the next start. */ desiredState?: TControllerTerminalAgentDesiredState; failure?: { code: TControllerTerminalAgentFailure; message: string }; } = {}, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The terminal resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if ( before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg || before.kind !== 'terminal' || !before.terminal || before.lifecycle === 'retired' ) throw new AuthError('invalid_resource', 'The terminal resource is unavailable.'); const stoppedAt = outcomeArg.stoppedAt ?? new Date(); return this.transitionResourceDocument( current, (model) => { model.terminal = { command: before.terminal!.command, args: [...before.terminal!.args], cwd: before.terminal!.cwd, stoppedAt, ...(outcomeArg.lastExitCode === undefined ? {} : { lastExitCode: outcomeArg.lastExitCode }), ...(before.terminal!.agent === undefined ? {} : { agent: applyTerminalAgentOutcome(before.terminal!.agent, outcomeArg), }), }; model.updatedAt = stoppedAt; }, 'The terminal resource changed concurrently.', 'Persisting the stopped terminal resource has an ambiguous outcome.', ); } public async markTerminalResourceRunning( projectIdArg: string, resourceIdArg: string, outcomeArg: { launchMode?: TControllerTerminalAgentLaunchMode } = {}, ): Promise { const current = await ControllerResourceModel.exact.findStoredOne({ id: resourceIdArg }); if (!current) throw new AuthError('invalid_resource', 'The terminal resource does not exist.'); const before = ControllerResourceModel.exact.toPersisted(current); if ( before.controllerId !== this.requireControllerId() || before.projectId !== projectIdArg || before.kind !== 'terminal' || !before.terminal || before.lifecycle !== 'active' ) throw new AuthError('invalid_resource', 'The terminal resource is unavailable.'); const now = new Date(); return this.transitionResourceDocument( current, (model) => { model.terminal = { command: before.terminal!.command, args: [...before.terminal!.args], cwd: before.terminal!.cwd, ...(before.terminal!.agent === undefined ? {} : { agent: { kind: before.terminal!.agent.kind, sessionId: before.terminal!.agent.sessionId, desiredState: 'running', consecutiveFailures: 0, ...(outcomeArg.launchMode === undefined ? (before.terminal!.agent.launchMode === undefined ? {} : { launchMode: before.terminal!.agent.launchMode }) : { launchMode: outcomeArg.launchMode }), }, }), }; model.updatedAt = now; }, 'The terminal resource changed concurrently.', 'Persisting the running terminal resource has an ambiguous outcome.', ); } public async getSettings(): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const stored = await ControllerSettingsModel.exact.findStoredOne({ id: `${controllerId}:settings`, }); if (stored) { const persisted = ControllerSettingsModel.exact.toPersisted(stored); return { ...persisted, defaultModels: persisted.defaultModels.map(cloneModelChoice), ...(persisted.standardProjectDirectories === undefined ? {} : { standardProjectDirectories: [...persisted.standardProjectDirectories] }), }; } return { id: `${controllerId}:settings`, controllerId, defaultModels: [] }; } public async updateSettings( patchArg: { defaultModels?: TControllerModelChoice[]; autoAcceptPermissions?: boolean; browserVideoBackend?: 'chromium' | 'native'; selectedOpenCodeProviderConnectionId?: string | null; /** Full replacement; an empty list clears the key. */ standardProjectDirectories?: string[]; lastSessionHarnessId?: TControllerSessionHarnessId; }, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const settingsId = `${controllerId}:settings`; const defaultModelsUpdate = patchArg.defaultModels === undefined ? undefined : patchArg.defaultModels.map(cloneModelChoice); if ( patchArg.standardProjectDirectories !== undefined && !isStandardProjectDirectoryList(patchArg.standardProjectDirectories) ) { throw new AuthError( 'invalid_project', 'Standard project directories must be unique absolute normalized paths.', ); } const standardProjectDirectoriesUpdate = patchArg.standardProjectDirectories === undefined ? undefined : [...patchArg.standardProjectDirectories]; const applyPatch = (targetArg: IControllerSettingsDocument): void => { if (patchArg.browserVideoBackend !== undefined) { if (patchArg.browserVideoBackend === 'native') targetArg.browserVideoBackend = 'native'; else if (patchArg.browserVideoBackend === 'chromium') delete targetArg.browserVideoBackend; else throw new Error('Invalid browser video backend.'); } if (defaultModelsUpdate !== undefined) { targetArg.defaultModels = defaultModelsUpdate; } if (patchArg.autoAcceptPermissions !== undefined) { if (patchArg.autoAcceptPermissions) { targetArg.autoAcceptPermissions = true; } else { // Disabled is an absent key; undefined values are dropped on write. delete targetArg.autoAcceptPermissions; } } if (patchArg.selectedOpenCodeProviderConnectionId !== undefined) { if (patchArg.selectedOpenCodeProviderConnectionId === null) { delete targetArg.selectedOpenCodeProviderConnectionId; } else { targetArg.selectedOpenCodeProviderConnectionId = patchArg.selectedOpenCodeProviderConnectionId; } } if (standardProjectDirectoriesUpdate !== undefined) { if (standardProjectDirectoriesUpdate.length === 0) { // No configured directory is an absent key, never a stored empty array. delete targetArg.standardProjectDirectories; } else { targetArg.standardProjectDirectories = standardProjectDirectoriesUpdate; } } if (patchArg.lastSessionHarnessId !== undefined) { targetArg.lastSessionHarnessId = patchArg.lastSessionHarnessId; } }; const stored = await ControllerSettingsModel.exact.findStoredOne({ id: settingsId }); if (!stored) { const candidate: IControllerSettingsDocument = { id: settingsId, controllerId, defaultModels: [], }; applyPatch(candidate); const insertResult = await ControllerSettingsModel.exact.insert(candidate); if (insertResult.status !== 'conflict') { return ControllerSettingsModel.exact.toPersisted(insertResult.document); } // fall through to transition against the concurrently created document } const current = stored ?? await ControllerSettingsModel.exact.findStoredOne({ id: settingsId }); if (!current) { throw new AuthError('ambiguous_write', 'The settings document has an ambiguous state.'); } const result = await ControllerSettingsModel.exact.transition({ current, change: (model) => { applyPatch(model); }, }); if (result.status !== 'transitioned') { throw new AuthError('concurrent_change', 'The settings changed concurrently and were not updated.'); } const persisted = ControllerSettingsModel.exact.toPersisted(result.document); return { ...persisted, defaultModels: persisted.defaultModels.map(cloneModelChoice), ...(persisted.standardProjectDirectories === undefined ? {} : { standardProjectDirectories: [...persisted.standardProjectDirectories] }), }; } private trackedConversationDocumentId( projectIdArg: string, runtimeIdArg: TControllerSessionId, ): string { return controllerTrackedConversationId( this.requireControllerId(), projectIdArg, runtimeIdArg, ); } /** * Every conversation AGL tracks, newest first per page scan. Pass project IDs to scope the * read; the complete set is bounded so one cross-project sidebar read can never be unbounded. */ public async listTrackedConversations( projectIdsArg?: readonly string[], signalArg?: AbortSignal, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if (projectIdsArg !== undefined && projectIdsArg.length === 0) return []; const documents: IControllerTrackedConversationDocument[] = []; let lastId: string | undefined; while (true) { signalArg?.throwIfAborted(); const page = await ControllerTrackedConversationModel.exact.findStored({ filter: { controllerId, ...(projectIdsArg === undefined ? {} : { projectId: { $in: [...projectIdsArg] } }), ...(lastId === undefined ? {} : { id: { $gt: lastId } }), }, sort: { id: 1 }, limit: Math.min( trackedConversationPageLimit, controllerTrackedConversationLimit + 1 - documents.length, ), signal: signalArg, }); for (const entry of page) { documents.push(cloneTrackedConversationDocument( ControllerTrackedConversationModel.exact.toPersisted(entry), )); } if (documents.length > controllerTrackedConversationLimit) { throw new AuthError( 'invalid_project', 'The tracked conversation limit was exceeded.', ); } const last = page.at(-1); if (!last || page.length < trackedConversationPageLimit) break; lastId = last.id; } return documents; } public async getTrackedConversation( projectIdArg: string, runtimeIdArg: TControllerSessionId, ): Promise { this.assertInitialized(); const stored = await ControllerTrackedConversationModel.exact.findStoredOne({ id: this.trackedConversationDocumentId(projectIdArg, runtimeIdArg), }); if (!stored) return undefined; return cloneTrackedConversationDocument( ControllerTrackedConversationModel.exact.toPersisted(stored), ); } /** * Idempotent. An explicit `opened` intent also returns an AGL-archived conversation to the * active list, because opening it again is exactly the user asking for it back. */ public async trackConversation(inputArg: { projectId: string; runtimeId: TControllerSessionId; sessionIdentityId: string; origin: TControllerConversationOrigin; title?: string; }): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const documentId = this.trackedConversationDocumentId(inputArg.projectId, inputArg.runtimeId); const now = new Date(); const titleCache = normalizeTrackedConversationTitle(inputArg.title); const stored = await ControllerTrackedConversationModel.exact.findStoredOne({ id: documentId }); if (!stored) { const projectCount = await ControllerTrackedConversationModel.exact.count({ controllerId, projectId: inputArg.projectId, }); if (projectCount >= controllerTrackedConversationsPerProjectLimit) { throw new AuthError( 'invalid_project', `A project tracks at most ${controllerTrackedConversationsPerProjectLimit} conversations.`, ); } const candidate: IControllerTrackedConversationDocument = { id: documentId, controllerId, projectId: inputArg.projectId, harnessId: inputArg.runtimeId.harnessId, runtimeId: { ...inputArg.runtimeId }, sessionIdentityId: inputArg.sessionIdentityId, origin: inputArg.origin, trackedAt: now, ...(titleCache === undefined ? {} : { titleCache, titleCacheAt: now }), updateId: plugins.crypto.randomBytes(32).toString('base64url'), }; const insertResult = await ControllerTrackedConversationModel.exact.insert(candidate); if (insertResult.status !== 'conflict') { return cloneTrackedConversationDocument( ControllerTrackedConversationModel.exact.toPersisted(insertResult.document), ); } } const current = stored ?? await ControllerTrackedConversationModel.exact.findStoredOne({ id: documentId }); if (!current) { throw new AuthError('ambiguous_write', 'The tracked conversation has an ambiguous state.'); } const result = await ControllerTrackedConversationModel.exact.transition({ current, change: (model) => { model.sessionIdentityId = inputArg.sessionIdentityId; if (titleCache !== undefined) { model.titleCache = titleCache; model.titleCacheAt = now; } if (inputArg.origin === 'opened') delete model.archivedAt; model.updateId = plugins.crypto.randomBytes(32).toString('base64url'); }, }); if (result.status !== 'transitioned') { throw new AuthError( 'concurrent_change', 'The tracked conversation changed concurrently and was not updated.', ); } return cloneTrackedConversationDocument( ControllerTrackedConversationModel.exact.toPersisted(result.document), ); } /** AGL-level archive state only. The harness conversation is never touched here. */ public async setTrackedConversationArchived( projectIdArg: string, runtimeIdArg: TControllerSessionId, archivedArg: boolean, ): Promise { this.assertInitialized(); const documentId = this.trackedConversationDocumentId(projectIdArg, runtimeIdArg); const current = await ControllerTrackedConversationModel.exact.findStoredOne({ id: documentId }); if (!current) { throw new AuthError('invalid_project', 'The conversation is not tracked by AGL.'); } const persisted = ControllerTrackedConversationModel.exact.toPersisted(current); if ((persisted.archivedAt !== undefined) === archivedArg) { return cloneTrackedConversationDocument(persisted); } const archivedAt = new Date(); const result = await ControllerTrackedConversationModel.exact.transition({ current, change: (model) => { if (archivedArg) model.archivedAt = archivedAt; else delete model.archivedAt; model.updateId = plugins.crypto.randomBytes(32).toString('base64url'); }, }); if (result.status !== 'transitioned') { throw new AuthError( 'concurrent_change', 'The tracked conversation changed concurrently and was not updated.', ); } return cloneTrackedConversationDocument( ControllerTrackedConversationModel.exact.toPersisted(result.document), ); } /** Cache the newest observed title so the archive view needs no harness read. */ public async cacheTrackedConversationTitle( projectIdArg: string, runtimeIdArg: TControllerSessionId, titleArg: string, ): Promise { this.assertInitialized(); const titleCache = normalizeTrackedConversationTitle(titleArg); if (titleCache === undefined) return; const documentId = this.trackedConversationDocumentId(projectIdArg, runtimeIdArg); const current = await ControllerTrackedConversationModel.exact.findStoredOne({ id: documentId }); if (!current) return; const persisted = ControllerTrackedConversationModel.exact.toPersisted(current); if (persisted.titleCache === titleCache) return; const now = new Date(); await ControllerTrackedConversationModel.exact.transition({ current, change: (model) => { model.titleCache = titleCache; model.titleCacheAt = now; model.updateId = plugins.crypto.randomBytes(32).toString('base64url'); }, }); } public async untrackConversation( projectIdArg: string, runtimeIdArg: TControllerSessionId, ): Promise { this.assertInitialized(); const documentId = this.trackedConversationDocumentId(projectIdArg, runtimeIdArg); const current = await ControllerTrackedConversationModel.exact.findStoredOne({ id: documentId }); if (!current) return false; const result = await ControllerTrackedConversationModel.exact.delete({ current }); return result.status === 'deleted'; } /** Project removal takes its tracked conversations with it. */ public async untrackProjectConversations(projectIdArg: string): Promise { this.assertInitialized(); const tracked = await this.listTrackedConversations([projectIdArg]); let removed = 0; for (const entry of tracked) { if (await this.untrackConversation(projectIdArg, entry.runtimeId)) removed += 1; } return removed; } public async getSessionLayout(): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const stored = await ControllerSessionGroupsModel.exact.findStoredOne({ id: `${controllerId}:groups`, }); if (!stored) return { groups: [], ungroupedItemIds: [], revision: 0 }; const persisted = ControllerSessionGroupsModel.exact.toPersisted(stored); return { groups: persisted.groups.map((group) => ({ ...group, itemIds: group.itemIds.map(cloneLayoutItemRef), })), ungroupedItemIds: (persisted.ungroupedItemIds ?? []).map(cloneLayoutItemRef), revision: persisted.revision ?? 0, }; } public async updateSessionLayout( groupsArg: IControllerSessionGroup[], ungroupedItemIdsArg?: IControllerSessionGroup['itemIds'], expectedRevisionArg?: number, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const documentId = `${controllerId}:groups`; const stored = await ControllerSessionGroupsModel.exact.findStoredOne({ id: documentId }); const groups = groupsArg.map((group) => ({ id: group.id, name: group.name, itemIds: group.itemIds.map(cloneLayoutItemRef), })); const groupedItemIds = new Set(groups.flatMap( (group) => group.itemIds.map(controllerLayoutItemRefKey), )); const storedPersisted = stored ? ControllerSessionGroupsModel.exact.toPersisted(stored) : undefined; const storedRevision = storedPersisted?.revision ?? 0; if (expectedRevisionArg !== undefined && expectedRevisionArg !== storedRevision) { throw new AuthError('concurrent_change', 'The session layout changed in another client.'); } const persistedUngroupedIds = storedPersisted?.ungroupedItemIds ?? []; const ungroupedItemIds = ungroupedItemIdsArg === undefined ? persistedUngroupedIds.filter( (itemId) => !groupedItemIds.has(controllerLayoutItemRefKey(itemId)), ) : ungroupedItemIdsArg.map(cloneLayoutItemRef); const candidate: IControllerSessionGroupsDocument = { id: documentId, controllerId, scope: 'controller', groups, ungroupedItemIds, revision: storedRevision + 1, }; if (!stored) { const insertResult = await ControllerSessionGroupsModel.exact.insert(candidate); if (insertResult.status !== 'conflict') { return { groups, ungroupedItemIds, revision: candidate.revision! }; } if (expectedRevisionArg !== undefined) { throw new AuthError('concurrent_change', 'The session layout changed in another client.'); } // A legacy client did not supply a revision, so preserve its prior // last-write behavior against the concurrently created document. } const current = stored ?? await ControllerSessionGroupsModel.exact.findStoredOne({ id: documentId }); if (!current) { throw new AuthError('ambiguous_write', 'The session groups document has an ambiguous state.'); } const currentPersisted = ControllerSessionGroupsModel.exact.toPersisted(current); const currentRevision = currentPersisted.revision ?? 0; if (expectedRevisionArg !== undefined && expectedRevisionArg !== currentRevision) { throw new AuthError('concurrent_change', 'The session layout changed in another client.'); } const transitionUngroupedItemIds = ungroupedItemIdsArg === undefined ? (currentPersisted.ungroupedItemIds ?? []).filter( (itemId) => !groupedItemIds.has(controllerLayoutItemRefKey(itemId)), ) : candidate.ungroupedItemIds; const result = await ControllerSessionGroupsModel.exact.transition({ current, change: (model) => { model.groups = candidate.groups; model.ungroupedItemIds = transitionUngroupedItemIds; model.revision = currentRevision + 1; }, }); if (result.status !== 'transitioned') { throw new AuthError('concurrent_change', 'The session groups changed concurrently and were not updated.'); } const persisted = ControllerSessionGroupsModel.exact.toPersisted(result.document); return { groups: persisted.groups.map((group) => ({ ...group, itemIds: group.itemIds.map(cloneLayoutItemRef), })), ungroupedItemIds: (persisted.ungroupedItemIds ?? []).map(cloneLayoutItemRef), revision: persisted.revision ?? 0, }; } public async removeSessionFromLayout( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return this.filterSessionLayout( (itemRef) => itemRef.kind !== 'session' || itemRef.projectId !== projectIdArg || controllerRuntimeIdKey(itemRef.id) !== sessionKey, ); } /** A retired resource must not leave a dangling layout member behind. */ public async removeResourceFromLayout( projectIdArg: string, resourceIdArg: string, ): Promise { return this.filterSessionLayout( (itemRef) => itemRef.kind !== 'resource' || itemRef.projectId !== projectIdArg || itemRef.id !== resourceIdArg, ); } /** A deregistered project must not leave its conversations and resources in the layout. */ public async removeProjectFromLayout(projectIdArg: string): Promise { return this.filterSessionLayout((itemRef) => itemRef.projectId !== projectIdArg); } public async pruneArchivedSessionLayoutEntries( projectIdArg: string, archivedSessionIdsArg: readonly IControllerRuntimeId[], ): Promise { const archivedSessionKeys = new Set(archivedSessionIdsArg.map(controllerRuntimeIdKey)); return this.filterSessionLayout( (itemRef) => itemRef.kind !== 'session' || itemRef.projectId !== projectIdArg || !archivedSessionKeys.has(controllerRuntimeIdKey(itemRef.id)), ); } private async filterSessionLayout( keepItemArg: (itemRefArg: TControllerLayoutItemRef) => boolean, signalArg?: AbortSignal, ): Promise { this.assertInitialized(); const documentId = `${this.requireControllerId()}:groups`; for (let attempt = 0; attempt < 3; attempt += 1) { signalArg?.throwIfAborted(); const current = await ControllerSessionGroupsModel.exact.findStoredOne( { id: documentId }, { signal: signalArg }, ); if (!current) return false; const persisted = ControllerSessionGroupsModel.exact.toPersisted(current); const groups = persisted.groups.map((group) => ({ ...group, itemIds: group.itemIds.filter(keepItemArg), })); const ungroupedItemIds = (persisted.ungroupedItemIds ?? []).filter(keepItemArg); const changed = groups.some((group, index) => ( group.itemIds.length !== persisted.groups[index].itemIds.length )) || ungroupedItemIds.length !== (persisted.ungroupedItemIds ?? []).length; if (!changed) return false; signalArg?.throwIfAborted(); const result = await ControllerSessionGroupsModel.exact.transition({ current, change: (model) => { model.groups = groups; model.ungroupedItemIds = ungroupedItemIds; model.revision = (persisted.revision ?? 0) + 1; }, }); if (result.status === 'transitioned') return true; } throw new AuthError('concurrent_change', 'The session layout changed concurrently and was not pruned.'); } private sessionStateIdentity( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): { controllerId: string; projectId: string; sessionId: IControllerRuntimeId; id: string } { const controllerId = this.requireControllerId(); if ( typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64 || typeof sessionIdArg?.nativeId !== 'string' || sessionIdArg.nativeId.length === 0 || Buffer.byteLength(sessionIdArg.nativeId, 'utf8') > 512 || (sessionIdArg.harnessId !== 'opencode' && sessionIdArg.harnessId !== 'flex' && sessionIdArg.harnessId !== 'codex') ) { throw new AuthError('invalid_project', 'The session-state scope is malformed.'); } const sessionId = { ...sessionIdArg }; return { controllerId, projectId: projectIdArg, sessionId, id: controllerSessionStateId(controllerId, projectIdArg, sessionId), }; } private createEmptySessionStateDocument( projectIdArg: string, sessionIdArg: IControllerRuntimeId, updateIdArg: string, ): IControllerSessionStateDocument { const identity = this.sessionStateIdentity(projectIdArg, sessionIdArg); return { ...identity, scratchpad: { text: '', revision: 0 }, intelligenceExchanges: [], updateId: updateIdArg, }; } private async findStoredSessionState( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { this.assertInitialized(); const { id } = this.sessionStateIdentity(projectIdArg, sessionIdArg); return (await ControllerSessionStateModel.exact.findStoredOne({ id })) ?? undefined; } private async insertSessionState( candidateArg: IControllerSessionStateDocument, ): Promise { try { const result = await ControllerSessionStateModel.exact.insert(candidateArg); if (result.status === 'conflict') { throw new AuthError('concurrent_change', 'The session state was created concurrently.'); } return ControllerSessionStateModel.exact.toPersisted(result.document); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await ControllerSessionStateModel.exact.findStoredOne({ id: candidateArg.id }); if (reconciled) { const document = ControllerSessionStateModel.exact.toPersisted(reconciled); if (document.updateId === candidateArg.updateId) return document; } throw new AuthError( 'ambiguous_write', 'The session-state insertion has an ambiguous outcome.', { cause: errorArg }, ); } } private async transitionSessionState( currentArg: TStoredSessionStateModel, changeArg: (documentArg: IControllerSessionStateDocument) => void, ): Promise { const currentDocument = ControllerSessionStateModel.exact.toPersisted(currentArg); const updateId = randomAuthorityValue(); try { const result = await ControllerSessionStateModel.exact.transition({ current: currentArg, change: (model) => { changeArg(model); model.updateId = updateId; }, }); if (result.status !== 'transitioned') { throw new AuthError('concurrent_change', 'The session state changed concurrently.'); } return ControllerSessionStateModel.exact.toPersisted(result.document); } catch (errorArg) { if (!isAmbiguousWrite(errorArg)) throw errorArg; const reconciled = await ControllerSessionStateModel.exact.findStoredOne({ id: currentDocument.id, }); if (reconciled) { const document = ControllerSessionStateModel.exact.toPersisted(reconciled); if (document.updateId === updateId) return document; } throw new AuthError( 'ambiguous_write', 'The session-state transition has an ambiguous outcome.', { cause: errorArg }, ); } } public async getSessionState( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { const identity = this.sessionStateIdentity(projectIdArg, sessionIdArg); const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) { return { scratchpad: { id: identity.id, text: '', revision: 0 }, intelligenceExchanges: [], }; } return toPublicSessionState(ControllerSessionStateModel.exact.toPersisted(stored)); } public readFlexProjectTasks( projectIdArg: string, sessionIdArg: string, sessionGenerationArg: Readonly, ): Promise { return this.runSessionStateMutation(async () => { assertFlexSessionGeneration(sessionGenerationArg); const stored = await this.findStoredSessionState(projectIdArg, { harnessId: 'flex', nativeId: sessionIdArg, }); if (!stored) return []; const record = stored.flexProjectManagement; if (!record) return []; plugins.flexharness.assertFlexProjectManagementRecord(record); const storageKey = projectManagementStoreKey(projectIdArg, sessionIdArg); if (record.sessionGenerationId === sessionGenerationArg.sessionGenerationId) { if (record.sessionGenerationSequence !== sessionGenerationArg.sessionGenerationSequence) { throw new plugins.flexharness.FlexHarnessStoreConflictError( storageKey, 0, record.revision, ); } if ('deletedAt' in record) { throw new plugins.flexharness.FlexHarnessNotFoundError( 'Project management state', sessionIdArg, ); } return structuredClone(record.tasks); } if ( 'deletedAt' in record && record.sessionGenerationSequence < sessionGenerationArg.sessionGenerationSequence ) return []; throw new plugins.flexharness.FlexHarnessStoreConflictError( storageKey, 0, record.revision, ); }); } public loadFlexProjectManagement( projectIdArg: string, sessionIdArg: string, sessionGenerationArg: Readonly, ): Promise { return this.runSessionStateMutation(async () => { assertFlexProjectManagementSessionContext(sessionGenerationArg, sessionIdArg); const runtimeId = { harnessId: 'flex' as const, nativeId: sessionIdArg }; for (let attempt = 0; attempt < 3; attempt += 1) { const stored = await this.findStoredSessionState(projectIdArg, runtimeId); if (!stored) { const candidate = this.createEmptySessionStateDocument( projectIdArg, runtimeId, randomAuthorityValue(), ); const snapshot: TFlexProjectManagementSnapshot = { schemaVersion: 1, revision: 0, sessionGenerationId: sessionGenerationArg.sessionGenerationId, sessionGenerationSequence: sessionGenerationArg.sessionGenerationSequence, scratchpad: '', tasks: [], }; plugins.flexharness.assertFlexProjectManagementSnapshot(snapshot); candidate.flexProjectManagement = snapshot; try { const inserted = await this.insertSessionState(candidate); return cloneProjectManagementRecord(inserted.flexProjectManagement!); } catch (errorArg) { if (!(errorArg instanceof AuthError) || errorArg.code !== 'concurrent_change') { throw errorArg; } continue; } } const current = ControllerSessionStateModel.exact.toPersisted(stored); if (current.flexProjectManagement) { return cloneProjectManagementRecord(current.flexProjectManagement); } if (current.deletedAt) return undefined; const snapshot: TFlexProjectManagementSnapshot = { schemaVersion: 1, revision: 0, sessionGenerationId: sessionGenerationArg.sessionGenerationId, sessionGenerationSequence: sessionGenerationArg.sessionGenerationSequence, scratchpad: current.scratchpad.text, tasks: [], }; plugins.flexharness.assertFlexProjectManagementSnapshot(snapshot); try { const transitioned = await this.transitionSessionState(stored, (document) => { document.flexProjectManagement = snapshot; }); return cloneProjectManagementRecord(transitioned.flexProjectManagement!); } catch (errorArg) { if (!(errorArg instanceof AuthError) || errorArg.code !== 'concurrent_change') { throw errorArg; } } } throw new AuthError( 'concurrent_change', 'The Flex project-management state remained busy during initialization.', ); }); } public saveFlexProjectManagement( projectIdArg: string, sessionIdArg: string, snapshotArg: TFlexProjectManagementSnapshot, expectedRevisionArg: number, writeContextArg: TFlexProjectManagementWriteContext, ): Promise { return this.runSessionStateMutation(async () => { plugins.flexharness.assertFlexProjectManagementSnapshot(snapshotArg); assertProjectManagementNextRevision(snapshotArg, expectedRevisionArg); assertProjectManagementWriteContext(writeContextArg); const snapshot = cloneProjectManagementRecord(snapshotArg); const runtimeId = { harnessId: 'flex' as const, nativeId: sessionIdArg }; const stored = await this.findStoredSessionState(projectIdArg, runtimeId); if (!stored) { if (expectedRevisionArg !== 0) { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, 0, ); } const candidate = this.createEmptySessionStateDocument( projectIdArg, runtimeId, randomAuthorityValue(), ); candidate.flexProjectManagement = snapshot; if (snapshot.scratchpad !== '') { candidate.scratchpad = { text: snapshot.scratchpad, revision: 1, updatedAt: new Date(), updatedBy: projectManagementUpdater(writeContextArg), }; } try { await this.insertSessionState(candidate); return; } catch (errorArg) { if (errorArg instanceof AuthError && errorArg.code === 'concurrent_change') { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, 0, ); } throw errorArg; } } const current = ControllerSessionStateModel.exact.toPersisted(stored); const currentProjectManagement = current.flexProjectManagement; const actualRevision = currentProjectManagement?.revision ?? 0; const sameGeneration = currentProjectManagement ? sameProjectManagementGeneration(currentProjectManagement, snapshot) : false; const canInitialize = currentProjectManagement ? canInitializeProjectManagementGeneration( currentProjectManagement, snapshot, expectedRevisionArg, ) : expectedRevisionArg === 0; const conflicts = currentProjectManagement ? sameGeneration ? Object.hasOwn(currentProjectManagement, 'deletedAt') || actualRevision !== expectedRevisionArg : !canInitialize : !canInitialize || (!current.deletedAt && current.scratchpad.text !== snapshot.scratchpad); if (conflicts) { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, actualRevision, ); } const reviving = current.deletedAt !== undefined; await this.transitionSessionState(stored, (document) => { if (reviving) { delete document.deletedAt; document.intelligenceExchanges = []; delete document.modelChoice; } if (reviving || document.scratchpad.text !== snapshot.scratchpad) { document.scratchpad = { text: snapshot.scratchpad, revision: document.scratchpad.revision + 1, updatedAt: new Date(), updatedBy: projectManagementUpdater(writeContextArg), }; } document.flexProjectManagement = snapshot; }); }); } public tombstoneFlexProjectManagement( projectIdArg: string, sessionIdArg: string, tombstoneArg: TFlexProjectManagementTombstone, expectedRevisionArg: number, sessionContextArg: Readonly, ): Promise { return this.runSessionStateMutation(async () => { plugins.flexharness.assertFlexProjectManagementTombstone(tombstoneArg); assertFlexProjectManagementSessionContext(sessionContextArg, sessionIdArg); if ( tombstoneArg.sessionGenerationId !== sessionContextArg.sessionGenerationId || tombstoneArg.sessionGenerationSequence !== sessionContextArg.sessionGenerationSequence ) throw new plugins.flexharness.FlexHarnessStoreFormatError( 'The Flex project-management tombstone context generation does not match.', ); assertProjectManagementNextRevision(tombstoneArg, expectedRevisionArg); const tombstone = cloneProjectManagementRecord(tombstoneArg); const runtimeId = { harnessId: 'flex' as const, nativeId: sessionIdArg }; const stored = await this.findStoredSessionState(projectIdArg, runtimeId); const deletedAt = new Date(tombstone.deletedAt); if (!stored) { if (expectedRevisionArg !== 0) { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, 0, ); } const candidate = this.createEmptySessionStateDocument( projectIdArg, runtimeId, randomAuthorityValue(), ); candidate.flexProjectManagement = tombstone; this.applySessionStateTombstone(candidate, deletedAt); try { await this.insertSessionState(candidate); return; } catch (errorArg) { if (errorArg instanceof AuthError && errorArg.code === 'concurrent_change') { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, 0, ); } throw errorArg; } } const current = ControllerSessionStateModel.exact.toPersisted(stored); const currentProjectManagement = current.flexProjectManagement; const actualRevision = currentProjectManagement?.revision ?? 0; if (currentProjectManagement && sameProjectManagementGeneration( currentProjectManagement, tombstone, )) { if (Object.hasOwn(currentProjectManagement, 'deletedAt')) return; if (actualRevision !== expectedRevisionArg) { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, actualRevision, ); } } else if ( currentProjectManagement ? !canInitializeProjectManagementGeneration( currentProjectManagement, tombstone, expectedRevisionArg, ) : expectedRevisionArg !== 0 ) { throw new plugins.flexharness.FlexHarnessStoreConflictError( projectManagementStoreKey(projectIdArg, sessionIdArg), expectedRevisionArg, actualRevision, ); } await this.transitionSessionState(stored, (document) => { document.flexProjectManagement = tombstone; this.applySessionStateTombstone(document, deletedAt); }); }); } public purgeFlexProjectManagementNamespace(projectIdArg: string): Promise { return this.runSessionStateMutation(async () => { this.assertInitialized(); const controllerId = this.requireControllerId(); this.sessionStateIdentity(projectIdArg, { harnessId: 'flex', nativeId: 'validation' }); let lastStoredId: TStoredSessionStateModel['_id'] | undefined; for (let pageIndex = 0; pageIndex < maxSessionStateRetirementPages; pageIndex += 1) { const storedEntries = await ControllerSessionStateModel.exact.findStored({ filter: { controllerId, projectId: projectIdArg, flexProjectManagement: { $exists: true }, ...(lastStoredId === undefined ? {} : { _id: { $gt: lastStoredId } }), }, sort: { _id: 1 }, limit: sessionStateRetirementPageLimit, }); for (const stored of storedEntries) { await this.transitionSessionState(stored, (document) => { delete document.flexProjectManagement; }); } const lastStored = storedEntries.at(-1); if (!lastStored || storedEntries.length < sessionStateRetirementPageLimit) return; lastStoredId = lastStored._id; } throw new Error('Flex project-management namespace purge exceeded its bounded scan.'); }); } public async setSessionModelChoice( projectIdArg: string, sessionIdArg: IControllerRuntimeId, modelChoiceArg: TControllerModelChoice, providerConnectionIdArg?: string, ): Promise { if (modelChoiceArg.harnessId !== sessionIdArg.harnessId) { throw new Error('The session model choice belongs to a different harness.'); } if ( providerConnectionIdArg !== undefined && ( modelChoiceArg.harnessId !== 'flex' || providerConnectionIdArg.length === 0 || Buffer.byteLength(providerConnectionIdArg, 'utf8') > 512 ) ) throw new Error('The provider connection identifier is malformed.'); const modelChoice = cloneModelChoice(modelChoiceArg); const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) { const candidate = this.createEmptySessionStateDocument( projectIdArg, sessionIdArg, randomAuthorityValue(), ); candidate.modelChoice = modelChoice; if (providerConnectionIdArg !== undefined) { candidate.providerConnectionId = providerConnectionIdArg; } return toPublicSessionState(await this.insertSessionState(candidate)); } const current = ControllerSessionStateModel.exact.toPersisted(stored); if (current.deletedAt) { throw new AuthError('concurrent_change', 'The deleted session state cannot be recreated.'); } return toPublicSessionState(await this.transitionSessionState(stored, (document) => { document.modelChoice = modelChoice; if (providerConnectionIdArg === undefined) delete document.providerConnectionId; else document.providerConnectionId = providerConnectionIdArg; })); } private async clearStoredSessionProviderConnection( storedArg: TStoredSessionStateModel, providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise { let stored = storedArg; for (let attempt = 0; attempt < sessionProviderConnectionClearTransitionMaxAttempts; attempt++) { signalArg?.throwIfAborted(); const current = ControllerSessionStateModel.exact.toPersisted(stored); if ( current.deletedAt || current.providerConnectionId !== providerConnectionIdArg ) return false; try { await this.transitionSessionState(stored, (document) => { if (document.providerConnectionId === providerConnectionIdArg) { delete document.providerConnectionId; } }); return true; } catch (errorArg) { if ( !(errorArg instanceof AuthError) || (errorArg.code !== 'concurrent_change' && errorArg.code !== 'ambiguous_write') ) throw errorArg; const latest = await this.findStoredSessionState(current.projectId, current.sessionId); if (!latest) return false; stored = latest; } } throw new AuthError( 'concurrent_change', 'The session provider connection remained busy during provider cleanup.', ); } public async clearSessionProviderConnectionsForConnection( providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if ( typeof providerConnectionIdArg !== 'string' || providerConnectionIdArg.length === 0 || Buffer.byteLength(providerConnectionIdArg, 'utf8') > 512 ) { throw new Error('The provider connection identifier is malformed.'); } let clearedCount = 0; let lastStoredId: TStoredSessionStateModel['_id'] | undefined; while (true) { signalArg?.throwIfAborted(); const storedEntries = await ControllerSessionStateModel.exact.findStored({ filter: { controllerId, deletedAt: { $exists: false }, providerConnectionId: providerConnectionIdArg, ...(lastStoredId === undefined ? {} : { _id: { $gt: lastStoredId } }), }, sort: { _id: 1 }, limit: sessionStateRetirementPageLimit, }); for (let index = 0; index < storedEntries.length; index += 8) { signalArg?.throwIfAborted(); const page = storedEntries.slice(index, index + 8); await Promise.all(page.map(async (storedEntry) => { if (await this.clearStoredSessionProviderConnection( storedEntry, providerConnectionIdArg, signalArg, )) clearedCount += 1; })); } const lastStored = storedEntries.at(-1); if (!lastStored || storedEntries.length < sessionStateRetirementPageLimit) return clearedCount; lastStoredId = lastStored._id; } } public saveSessionScratchpad( projectIdArg: string, sessionIdArg: IControllerRuntimeId, textArg: string, expectedRevisionArg: number, updatedByArg: TControllerScratchpadUpdater = 'user', ): Promise { return this.runSessionStateMutation(() => this.saveSessionScratchpadUnqueued( projectIdArg, sessionIdArg, textArg, expectedRevisionArg, updatedByArg, )); } private async saveSessionScratchpadUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, textArg: string, expectedRevisionArg: number, updatedByArg: TControllerScratchpadUpdater, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); const now = new Date(); if (!stored) { if (expectedRevisionArg !== 0) { throw new AuthError('concurrent_change', 'The scratchpad revision is stale.'); } const candidate = this.createEmptySessionStateDocument( projectIdArg, sessionIdArg, randomAuthorityValue(), ); candidate.scratchpad = { text: textArg, revision: 1, updatedAt: now, updatedBy: updatedByArg, }; return toPublicSessionState(await this.insertSessionState(candidate)); } const current = ControllerSessionStateModel.exact.toPersisted(stored); if (current.deletedAt) { throw new AuthError('concurrent_change', 'The deleted session state cannot be recreated.'); } if (current.scratchpad.revision !== expectedRevisionArg) { throw new AuthError('concurrent_change', 'The scratchpad revision is stale.'); } const next = await this.transitionSessionState(stored, (document) => { document.scratchpad = { text: textArg, revision: expectedRevisionArg + 1, updatedAt: now, updatedBy: updatedByArg, }; this.applyFlexProjectManagementScratchpad(document, textArg); }); return toPublicSessionState(next); } public admitSessionIntelligence( projectIdArg: string, sessionIdArg: IControllerRuntimeId, questionArg: string, ): Promise { return this.runSessionStateMutation(() => this.admitSessionIntelligenceUnqueued( projectIdArg, sessionIdArg, questionArg, )); } private async admitSessionIntelligenceUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, questionArg: string, ): Promise { const exchangeId = plugins.crypto.randomBytes(16).toString('base64url'); const createdAt = new Date(); let stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) { const candidate = this.createEmptySessionStateDocument( projectIdArg, sessionIdArg, randomAuthorityValue(), ); candidate.intelligenceExchanges = [{ id: exchangeId, question: questionArg, status: 'running', scratchpadRevisionAtAdmission: 0, createdAt, }]; const inserted = await this.insertSessionState(candidate); return { exchange: toPublicIntelligenceExchange(inserted.intelligenceExchanges[0]), scratchpadRevision: 0, scratchpadText: '', }; } const current = ControllerSessionStateModel.exact.toPersisted(stored); if (current.deletedAt) { throw new AuthError('concurrent_change', 'The deleted session state cannot be recreated.'); } if (current.intelligenceExchanges.some( (exchange) => exchange.status === 'running' || exchange.temporarySessionId !== undefined, )) { throw new AuthError( 'concurrent_change', 'Session Intelligence is already running or its cleanup is still pending.', ); } const scratchpadRevision = current.scratchpad.revision; const next = await this.transitionSessionState(stored, (document) => { document.intelligenceExchanges = trimSessionStateExchanges([ ...document.intelligenceExchanges, { id: exchangeId, question: questionArg, status: 'running', scratchpadRevisionAtAdmission: scratchpadRevision, createdAt, }, ], exchangeId); }); const exchange = next.intelligenceExchanges.find((entry) => entry.id === exchangeId)!; return { exchange: toPublicIntelligenceExchange(exchange), scratchpadRevision, scratchpadText: current.scratchpad.text, }; } public attachSessionIntelligenceTemporarySession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, temporarySessionIdArg: string, ): Promise { return this.runSessionStateMutation(() => this.attachSessionIntelligenceTemporarySessionUnqueued( projectIdArg, sessionIdArg, exchangeIdArg, temporarySessionIdArg, )); } private async attachSessionIntelligenceTemporarySessionUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, temporarySessionIdArg: string, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) throw new AuthError('concurrent_change', 'The intelligence admission is missing.'); const current = ControllerSessionStateModel.exact.toPersisted(stored); const exchange = current.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg); if (current.deletedAt || exchange?.status !== 'running') { throw new AuthError('concurrent_change', 'The intelligence admission is no longer active.'); } await this.transitionSessionState(stored, (document) => { const target = document.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg)!; target.temporarySessionId = temporarySessionIdArg; }); } public completeSessionIntelligence( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, answerArg: string, scratchpadTextArg: string, modelArg: string, ): Promise { return this.runSessionStateMutation(() => this.completeSessionIntelligenceUnqueued( projectIdArg, sessionIdArg, exchangeIdArg, answerArg, scratchpadTextArg, modelArg, )); } private async completeSessionIntelligenceUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, answerArg: string, scratchpadTextArg: string, modelArg: string, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) return undefined; const current = ControllerSessionStateModel.exact.toPersisted(stored); const exchange = current.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg); if (current.deletedAt || exchange?.status !== 'running') return undefined; const scratchpadConflict = current.scratchpad.revision !== exchange.scratchpadRevisionAtAdmission; const completedAt = new Date(); const next = await this.transitionSessionState(stored, (document) => { const target = document.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg)!; document.intelligenceExchanges = trimSessionStateExchanges( document.intelligenceExchanges.map((entry) => entry.id === exchangeIdArg ? { id: target.id, question: target.question, status: 'completed', answer: answerArg, model: modelArg, ...(scratchpadConflict ? { scratchpadConflict: true as const } : {}), ...(target.temporarySessionId === undefined ? {} : { temporarySessionId: target.temporarySessionId }), createdAt: target.createdAt, completedAt, } : entry), exchangeIdArg, ); if (!scratchpadConflict && document.scratchpad.text !== scratchpadTextArg) { document.scratchpad = { text: scratchpadTextArg, revision: document.scratchpad.revision + 1, updatedAt: completedAt, updatedBy: 'intelligence', }; this.applyFlexProjectManagementScratchpad(document, scratchpadTextArg); } }); return toPublicSessionState(next); } public failSessionIntelligence( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, errorArg: string, modelArg?: string, ): Promise { return this.runSessionStateMutation(() => this.failSessionIntelligenceUnqueued( projectIdArg, sessionIdArg, exchangeIdArg, errorArg, modelArg, )); } private async failSessionIntelligenceUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, errorArg: string, modelArg?: string, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) return undefined; const current = ControllerSessionStateModel.exact.toPersisted(stored); const exchange = current.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg); if (current.deletedAt || exchange?.status !== 'running') return undefined; const completedAt = new Date(); const next = await this.transitionSessionState(stored, (document) => { const target = document.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg)!; document.intelligenceExchanges = trimSessionStateExchanges( document.intelligenceExchanges.map((entry) => entry.id === exchangeIdArg ? { id: target.id, question: target.question, status: 'error', error: errorArg, ...(modelArg === undefined ? {} : { model: modelArg }), ...(target.temporarySessionId === undefined ? {} : { temporarySessionId: target.temporarySessionId }), createdAt: target.createdAt, completedAt, } : entry), exchangeIdArg, ); }); return toPublicSessionState(next); } public recoverInterruptedSessionIntelligence(): Promise { return this.runSessionStateMutation(() => this.recoverInterruptedSessionIntelligenceUnqueued()); } private async recoverInterruptedSessionIntelligenceUnqueued(): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); const storedEntries = await ControllerSessionStateModel.exact.findStored({ filter: { controllerId, deletedAt: { $exists: false }, intelligenceExchanges: { $elemMatch: { status: 'running' } }, }, limit: sessionStateRecoveryLimit, }); const temporarySessionIds: IControllerInterruptedIntelligenceRecovery['temporarySessionIds'] = []; let recoveredCount = 0; for (const stored of storedEntries) { const current = ControllerSessionStateModel.exact.toPersisted(stored); for (const exchange of current.intelligenceExchanges) { if (exchange.status === 'running' && exchange.temporarySessionId) { temporarySessionIds.push({ projectId: current.projectId, sessionId: current.sessionId, exchangeId: exchange.id, nativeId: exchange.temporarySessionId, }); } } const completedAt = new Date(); await this.transitionSessionState(stored, (document) => { document.intelligenceExchanges = document.intelligenceExchanges.map((exchange) => exchange.status === 'running' ? { id: exchange.id, question: exchange.question, status: 'error', error: 'Session Intelligence was interrupted by a controller restart.', ...(exchange.temporarySessionId === undefined ? {} : { temporarySessionId: exchange.temporarySessionId }), createdAt: exchange.createdAt, completedAt, } : exchange); }); recoveredCount += 1; } return { recoveredCount, exhaustive: storedEntries.length < sessionStateRecoveryLimit, temporarySessionIds, }; } public async listProjectSessionIntelligenceCleanupObligations( projectIdArg: string, ): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if ( typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64 ) { throw new AuthError('invalid_project', 'The project identifier is malformed.'); } const storedEntries = await ControllerSessionStateModel.exact.findStored({ filter: { controllerId, projectId: projectIdArg, deletedAt: { $exists: false }, intelligenceExchanges: { $elemMatch: { temporarySessionId: { $exists: true } } }, }, limit: sessionStateRecoveryLimit, }); return storedEntries.flatMap((stored) => { const current = ControllerSessionStateModel.exact.toPersisted(stored); return current.intelligenceExchanges.flatMap((exchange) => exchange.temporarySessionId ? [{ projectId: current.projectId, sessionId: current.sessionId, exchangeId: exchange.id, nativeId: exchange.temporarySessionId, }] : []); }); } public async getSessionIntelligenceCleanupObligations( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) return []; const current = ControllerSessionStateModel.exact.toPersisted(stored); if (current.deletedAt) return []; return current.intelligenceExchanges.flatMap((exchange) => exchange.temporarySessionId ? [{ projectId: current.projectId, sessionId: current.sessionId, exchangeId: exchange.id, nativeId: exchange.temporarySessionId, }] : []); } public async findProjectSessionIntelligenceTemporarySessionIds( projectIdArg: string, candidateIdsArg: string[], signalArg?: AbortSignal, ): Promise> { this.assertInitialized(); const controllerId = this.requireControllerId(); if ( typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64 || candidateIdsArg.length > 512 || candidateIdsArg.some((id) => ( typeof id !== 'string' || id.length === 0 || Buffer.byteLength(id, 'utf8') > 512 )) ) { throw new AuthError('invalid_project', 'The temporary-session lookup is malformed.'); } const candidateIds = [...new Set(candidateIdsArg)]; if (candidateIds.length === 0) return new Set(); const storedEntries = await ControllerSessionStateModel.exact.findStored({ filter: { controllerId, projectId: projectIdArg, deletedAt: { $exists: false }, intelligenceExchanges: { $elemMatch: { temporarySessionId: { $in: candidateIds } }, }, }, limit: candidateIds.length, signal: signalArg, }); const candidateSet = new Set(candidateIds); return new Set(storedEntries.flatMap((stored) => ControllerSessionStateModel.exact.toPersisted(stored).intelligenceExchanges.flatMap( (exchange) => exchange.temporarySessionId && candidateSet.has(exchange.temporarySessionId) ? [exchange.temporarySessionId] : [], ))); } public async isSessionStateTombstoned( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) return false; return ControllerSessionStateModel.exact.toPersisted(stored).deletedAt !== undefined; } public async retireProjectSessionStates( projectIdArg: string, cleanupTemporarySessionArg: (temporarySessionIdArg: string) => Promise, signalArg?: AbortSignal, ): Promise<{ retiredCount: number; temporarySessionIds: string[]; }> { this.assertInitialized(); const controllerId = this.requireControllerId(); if ( typeof projectIdArg !== 'string' || projectIdArg.length === 0 || projectIdArg.length > 64 ) { throw new AuthError('invalid_project', 'The project identifier is malformed.'); } const temporarySessionIds = new Set(); let retiredCount = 0; let lastStoredId: TStoredSessionStateModel['_id'] | undefined; for (let pageIndex = 0; pageIndex < maxSessionStateRetirementPages; pageIndex += 1) { signalArg?.throwIfAborted(); const storedEntries = await ControllerSessionStateModel.exact.findStored({ filter: { controllerId, projectId: projectIdArg, deletedAt: { $exists: false }, ...(lastStoredId === undefined ? {} : { _id: { $gt: lastStoredId } }), }, sort: { _id: 1 }, limit: sessionStateRetirementPageLimit, }); signalArg?.throwIfAborted(); for (const stored of storedEntries) { const current = ControllerSessionStateModel.exact.toPersisted(stored); for (const exchange of current.intelligenceExchanges) { if (!exchange.temporarySessionId || temporarySessionIds.has(exchange.temporarySessionId)) { continue; } signalArg?.throwIfAborted(); await cleanupTemporarySessionArg(exchange.temporarySessionId); temporarySessionIds.add(exchange.temporarySessionId); } signalArg?.throwIfAborted(); const retired = await this.runSessionStateMutation(async () => { const latest = await this.findStoredSessionState(projectIdArg, current.sessionId); if (!latest || ControllerSessionStateModel.exact.toPersisted(latest).deletedAt) return false; await this.transitionSessionState(latest, (document) => { this.applySessionStateTombstone(document, new Date()); }); return true; }); if (retired) retiredCount += 1; } const lastStored = storedEntries.at(-1); if (!lastStored || storedEntries.length < sessionStateRetirementPageLimit) { return { retiredCount, temporarySessionIds: [...temporarySessionIds] }; } lastStoredId = lastStored._id; } throw new Error( `Project session-state retirement cleared ${retiredCount} state(s) before exceeding its bounded scan.`, ); } public clearSessionIntelligenceTemporarySession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, temporarySessionIdArg: string, ): Promise { return this.runSessionStateMutation(() => this.clearSessionIntelligenceTemporarySessionUnqueued( projectIdArg, sessionIdArg, exchangeIdArg, temporarySessionIdArg, )); } private async clearSessionIntelligenceTemporarySessionUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, exchangeIdArg: string, temporarySessionIdArg: string, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); if (!stored) return; const current = ControllerSessionStateModel.exact.toPersisted(stored); const exchange = current.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg); if (current.deletedAt || exchange?.temporarySessionId !== temporarySessionIdArg) return; await this.transitionSessionState(stored, (document) => { const target = document.intelligenceExchanges.find((entry) => entry.id === exchangeIdArg); if (target?.temporarySessionId === temporarySessionIdArg) { delete target.temporarySessionId; } }); } public tombstoneSessionState( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { return this.runSessionStateMutation(() => this.tombstoneSessionStateUnqueued( projectIdArg, sessionIdArg, )); } private async tombstoneSessionStateUnqueued( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise { const stored = await this.findStoredSessionState(projectIdArg, sessionIdArg); const deletedAt = new Date(); if (!stored) { const candidate = this.createEmptySessionStateDocument( projectIdArg, sessionIdArg, randomAuthorityValue(), ); candidate.deletedAt = deletedAt; await this.insertSessionState(candidate); return; } const current = ControllerSessionStateModel.exact.toPersisted(stored); if (current.deletedAt) return; await this.transitionSessionState(stored, (document) => { this.applySessionStateTombstone(document, deletedAt); }); } private applyFlexProjectManagementScratchpad( documentArg: IControllerSessionStateDocument, textArg: string, ): void { const current = documentArg.flexProjectManagement; if (!current || 'deletedAt' in current || current.scratchpad === textArg) return; const snapshot: TFlexProjectManagementSnapshot = { ...current, revision: current.revision + 1, scratchpad: textArg, }; plugins.flexharness.assertFlexProjectManagementSnapshot(snapshot); documentArg.flexProjectManagement = snapshot; } private applySessionStateTombstone( documentArg: IControllerSessionStateDocument, deletedAtArg: Date, ): void { const projectManagement = documentArg.flexProjectManagement; if (projectManagement && !Object.hasOwn(projectManagement, 'deletedAt')) { const tombstone: TFlexProjectManagementTombstone = { schemaVersion: 1, revision: projectManagement.revision + 1, sessionGenerationId: projectManagement.sessionGenerationId, sessionGenerationSequence: projectManagement.sessionGenerationSequence, deletedAt: deletedAtArg.toISOString(), }; plugins.flexharness.assertFlexProjectManagementTombstone(tombstone); documentArg.flexProjectManagement = tombstone; } documentArg.scratchpad = { text: '', revision: documentArg.scratchpad.revision, ...(documentArg.scratchpad.updatedAt === undefined ? {} : { updatedAt: documentArg.scratchpad.updatedAt }), ...(documentArg.scratchpad.updatedBy === undefined ? {} : { updatedBy: documentArg.scratchpad.updatedBy }), }; documentArg.intelligenceExchanges = []; delete documentArg.modelChoice; delete documentArg.providerConnectionId; documentArg.deletedAt = deletedAtArg; } public async createTempPassword(ttlMsArg: number): Promise { this.assertInitialized(); const controllerId = this.requireControllerId(); if ( !Number.isSafeInteger(ttlMsArg) || ttlMsArg < tempPasswordTtlMinMs || ttlMsArg > tempPasswordTtlMaxMs ) { throw new AuthError( 'temp_password_invalid', 'A temporary password lifetime must be between one minute and 24 hours.', ); } const now = Date.now(); const nowDate = new Date(now); const collection = ControllerTempPasswordModel.collection.mongoDbCollection; const expired = await collection .find( { controllerId, expiresAt: { $lte: nowDate } }, { projection: { _id: 1 } }, ) .limit(tempPasswordPruneBatchSize) .toArray(); if (expired.length > 0) { await collection.deleteMany({ _id: { $in: expired.map((document) => document._id) } }); } const activeCount = await ControllerTempPasswordModel.exact.count({ controllerId, expiresAt: { $gt: nowDate }, }); if (activeCount >= maxActiveTempPasswords) { throw new AuthError( 'temp_password_limit', `At most ${maxActiveTempPasswords} temporary passwords can be active; wait for one to expire.`, ); } const password = plugins.crypto.randomBytes(24).toString('base64url'); const id = `${controllerId}:temppass:${randomAuthorityValue()}`; const insertResult = await ControllerTempPasswordModel.exact.insert({ id, controllerId, secretHash: hashSetupCode(password), createdAt: new Date(now), expiresAt: new Date(now + ttlMsArg), }); if (insertResult.status === 'conflict') { throw new AuthError('concurrent_change', 'The temporary password identifier conflicts.'); } const persisted = ControllerTempPasswordModel.exact.toPersisted(insertResult.document); return { password, credentialId: `temp:${persisted.id}`, expiresAt: persisted.expiresAt }; } public async verifyTempPassword( passwordArg: string, ): Promise<{ credentialId: string; expiresAt: Date } | undefined> { this.assertInitialized(); const controllerId = this.requireControllerId(); if (typeof passwordArg !== 'string' || !tempPasswordPattern.test(passwordArg)) { return undefined; } const candidateHash = hashSetupCode(passwordArg); const now = Date.now(); const stored = await ControllerTempPasswordModel.exact.findStored({ filter: { controllerId, expiresAt: { $gt: new Date(now) } }, limit: maxActiveTempPasswords * 4, }); for (const entry of stored) { const persisted = ControllerTempPasswordModel.exact.toPersisted(entry); if (persisted.expiresAt.getTime() <= now) continue; if (hashesEqual(persisted.secretHash, candidateHash)) { return { credentialId: `temp:${persisted.id}`, expiresAt: persisted.expiresAt }; } } return undefined; } private assertRuntimeConfigMatches( storedArg: IControllerRuntimeConfig, requestedArg: IControllerRuntimeConfig, ): void { if (!runtimeConfigsEqual(storedArg, requestedArg)) { throw new AuthError( 'config_mismatch', 'The stored controller runtime configuration is immutable and does not match.', ); } } private auditEventsEqual( leftArg: IControllerAuditEvent, rightArg: IControllerAuditEvent, ): boolean { return leftArg.id === rightArg.id && leftArg.controllerId === rightArg.controllerId && leftArg.timestamp.getTime() === rightArg.timestamp.getTime() && leftArg.type === rightArg.type && leftArg.outcome === rightArg.outcome && leftArg.operationId === rightArg.operationId && leftArg.peerId === rightArg.peerId && leftArg.credentialId === rightArg.credentialId && ( leftArg.sessionId === undefined ? rightArg.sessionId === undefined // Compared as the qualified identity it is: an acting session may be a Claude Code // conversation, which is not an AGL runtime id and has no runtime-id key. : rightArg.sessionId !== undefined && leftArg.sessionId.harnessId === rightArg.sessionId.harnessId && leftArg.sessionId.nativeId === rightArg.sessionId.nativeId ) && ( leftArg.requestId === undefined ? rightArg.requestId === undefined : rightArg.requestId !== undefined && controllerRuntimeIdKey(leftArg.requestId) === controllerRuntimeIdKey(rightArg.requestId) ); } }