import { ControllerBrowserOperationAuthority } from './classes.browseroperationauthority.js'; import { awaitWithAbortSignal } from './functions.abort.js'; import { ControllerBrowserDevToolsTransport } from './classes.browserdevtoolstransport.js'; import { createBrowserDevToolsSurface } from './functions.browserdevtools.js'; import * as devToolsProtocol from '../ts_interfaces/devtools.js'; import type { IReq_ControllerMcpBrowserAction, IReq_ControllerMcpContextResolve } from '../ts_interfaces/mcpworkspacerequests.js'; import { CodexConnections, type ICodexConnectionRuntime } from './classes.codexconnections.js'; import { assertCodexMappingUpdate, assertCodexProfileCreate, assertCodexProfilesList, assertCodexProfileSelection, assertCodexProfileUpdate } from './functions.codexvalidation.js'; import { migrateCodexOriginsV29 } from '../ts_migration/v29_codexorigins.js'; import type * as codexInterfaces from '../ts_interfaces/codex.js'; import { codexDiagnostic } from './functions.codexdiagnostics.js'; import * as plugins from './plugins.js'; import type { IControllerAuthSwitchHarness, IReq_ControllerAuthSwitch, TControllerAuthSwitchLimitsContext, } from '../ts_interfaces/authswitch.js'; import type { IReq_ControllerAccountsImportInventory, IReq_ControllerAccountsSnapshot, } from '../ts_interfaces/requests.accounts.js'; import { AuthorityState } from './classes.authoritystate.js'; import { CodexSupervisor } from './classes.codexsupervisor.js'; import { CodexClientAdapter, CodexCollaborationModeOutcomeUnknownError, } from './classes.codexclientadapter.js'; import { codexArray, codexRecord, codexRuntimeId, codexSession, codexString, codexThreadId } from './classes.codexprojection.js'; import { codexSessionProviderGeneration } from './classes.sessionidentityintegration.js'; import type { IControllerCodexModelChoice, IControllerFailureDetail, IControllerSessionDetail } from '../ts_interfaces/index.js'; import { assertCanonicalDirectory, CanonicalDirectoryError, filesystemIdentitiesEqual, resolveCanonicalDirectory, type ICanonicalDirectoryResolution, } from './functions.canonicaldirectory.js'; import { commitinfo } from './00_commitinfo_data.js'; import { controllerFailureJournal } from './classes.failurejournal.js'; import { controllerPackageName, controllerBuiltinCommands, classifyControllerSlashInput, controllerFailureDetailRequestLimit, controllerOperationFailedErrorCode, controllerMaxToolEventBytes, controllerMaxLiveMessageEventBytes, controllerBrowserTransportEventContentType, controllerBrowserTransportMaximumJsonBytes, controllerBrowserTransportMaximumPendingEventBytes, controllerBrowserTransportMaximumPendingEvents, controllerBrowserTransportMaximumPendingOperations, controllerBrowserTransportMaximumQueuedOperationBytes, controllerBrowserTransportMaximumRecoveryAttempts, controllerBrowserTransportOperationContentType, controllerBrowserTransportPairOpenTimeoutMs, controllerBrowserTransportProtocol, controllerBrowserTransportRecoveryGraceMs, controllerBrowserViewClosedErrorCode, controllerBrowserViewFailedErrorCode, controllerBrowserViewResourceChangedErrorCode, type TControllerBrowserViewCloseCode, decodeControllerBrowserTransportPacket, encodeControllerBrowserTransportPacket, controllerLiveMessageDeltaTargetBytes, controllerInitialMessageBundleLimit, controllerMaximumTranscriptBytes, controllerLiveToolExecutionIsCovered, controllerProtocolVersion, controllerUpgradeManagementVersion, controllerLayoutItemRefKey, controllerRuntimeIdKey, type IControllerBlockedProjectRemoval, type TControllerProjectRemovalBlockedCode, type IControllerEvent, type IControllerChildAttention, type IControllerChildEvent, type IControllerChildSessionDetail, type IControllerSessionDraftUpdate, type IControllerFlexModelOption, type IControllerMessage, type IControllerMessageBundle, type IControllerMessagePage, type IControllerMessageStreamCursor, type IControllerPendingPrompt, type IControllerReasoningDelta, type IControllerReasoningUpdate, type IControllerTextDelta, type IControllerTextUpdate, type IControllerTodo, type TControllerModelChoice, type IControllerModelRefreshJob, type IControllerPermission, type IControllerProject, type IControllerProviderConnection, type IControllerProviderLogin, type IControllerQuestion, type IControllerRuntimeId, type IControllerSession, type IControllerSessionGroup, type IControllerSessionLayout, type IControllerStatus, type IControllerUpgradeStatus, type IControllerSlashCommandDescriptor, type TControllerSlashExecuteResult, type TControllerSlashClientAction, type IControllerSessionMetrics, type IControllerSessionAuxiliary, type IControllerToolExecution, type IControllerToolStreamCursor, type TControllerSessionHarnessId, controllerSessionHarnessIds, type TControllerSessionId, type TControllerLayoutItemRef, type TControllerBuiltinCommand, type IReq_ControllerAuthenticationBegin, type IReq_ControllerAuthenticationFinish, type IReq_ControllerTempPasswordLogin, type IReq_ControllerAuthResume, type IReq_ControllerAuthState, type IReq_ControllerEvent, type IReq_ControllerSlashList, type IReq_ControllerModelList, type IReq_ControllerProviderList, type IReq_ControllerProviderConnectionList, type IReq_ControllerProviderConnectionActivateOpenCode, type IReq_ControllerProviderLoginBegin, type IReq_ControllerProviderLoginGet, type IReq_ControllerProviderLoginCancel, type IReq_ControllerProviderConnectionLogout, type IReq_ControllerProviderConnectionRateLimitsGet, type IReq_ControllerProviderModelRefreshBegin, type IReq_ControllerProviderModelRefreshGet, type IReq_ControllerPermissionReply, type IReq_ControllerQuestionReply, type IReq_ControllerSessionRename, type IReq_ControllerSessionArchive, type IReq_ControllerSessionYolo, type IReq_ControllerSessionGroupsGet, type IReq_ControllerSessionGroupsUpdate, type IReq_ControllerTerminalList, type IReq_ControllerTerminalCreate, type IReq_ControllerTerminalRename, type IReq_ControllerTerminalRemove, type IReq_ControllerTerminalAttach, type IReq_ControllerTerminalDetach, type IReq_ControllerTerminalInput, type IReq_ControllerTerminalResize, type IReq_ControllerTerminalOutput, type IReq_ControllerTerminalDetached, type IReq_ControllerResourceList, type IReq_ControllerResourceCreate, type IReq_ControllerResourceRename, type IReq_ControllerResourceAttach, type IReq_ControllerResourceDetach, type IReq_ControllerResourceStart, type IReq_ControllerResourceStop, type IReq_ControllerResourceRetire, type IReq_ControllerBrowserDevToolsOpen, type IReq_ControllerBrowserDevToolsClose, type IReq_ControllerBrowserViewOpen, type IReq_ControllerBrowserViewActivate, type IReq_ControllerBrowserViewClose, type IReq_ControllerBrowserViewRecover, type IControllerBrowserViewState, type IControllerBrowserOperationPacket, type IControllerBrowserOperationResultPacket, type TControllerBrowserOperationResult, type TControllerBrowserClientPacket, type TControllerBrowserServerPacket, type TControllerBrowserViewOperation, type IReq_ControllerProjectCreate, type IReq_ControllerProjectList, type IReq_ControllerProjectRemove, type IReq_ControllerProjectRemovalRetry, type IReq_ControllerProjectSuggest, type IReq_ControllerSlashExecute, type IReq_ControllerSessionAbort, type IReq_ControllerSessionCreate, type IReq_ControllerSessionDelete, type IReq_ControllerSessionDiscardEmpty, type IReq_ControllerSessionDraftGet, type IReq_ControllerSessionDraftUpdate, type IReq_ControllerSessionGet, type IReq_ControllerSessionChildGet, type IReq_ControllerSessionChildMessagesPage, type IReq_ControllerSessionChildMessageGet, type IReq_ControllerSessionChildPermissionReply, type IReq_ControllerSessionChildQuestionReply, type IReq_ControllerSessionChildEvent, type IReq_ControllerSessionAuxiliaryGet, type IReq_ControllerSessionModelUpdate, type IReq_ControllerSessionScratchpadSave, type IReq_ControllerSessionIntelligenceAsk, type IReq_ControllerConversationList, type IReq_ControllerConversationSearch, type IReq_ControllerConversationOpen, type IReq_ControllerConversationArchive, type IReq_ControllerConversationReopen, type IControllerTrackedConversation, type IControllerConversationSearchResult, type IReq_ControllerSessionMessagesPage, type IReq_ControllerSessionMessageGet, type IReq_ControllerSessionSend, type IReq_ControllerSettingsGet, type IReq_ControllerSettingsUpdate, type IReq_ControllerSetupBegin, type IReq_ControllerSetupFinish, type IReq_ControllerStatus, type IReq_ControllerUpgradeLaunch, type IReq_ControllerUpgradePrepare, type IReq_ControllerUpgradePrepareBegin, type IReq_ControllerUpgradeFinalize, type IReq_ControllerFailureDetailGet, type IReq_ControllerSystemMetrics, type IReq_ControllerSystemMetricsHistory, type IReq_ControllerMcpProjectsList, type IReq_ControllerMcpSessionRead, type IReq_ControllerMcpSessionScratchpadRead, type IReq_ControllerMcpSessionScratchpadUpdate, type IReq_ControllerMcpSessionSend, type IReq_ControllerMcpSessionsList, type IReq_ControllerMcpStatus, type IControllerMcpCaller, type TControllerMcpSubject, type TControllerResource, type TControllerOperationPrincipal, type TControllerResourceAttachmentTarget, } from '../ts_interfaces/index.js'; import { readTrustedProxySecret, resolveInitialRuntimeConfig, resolveStoredRuntimeConfig, } from './classes.config.js'; import { resolveAGLHomePaths } from './classes.aglhome.js'; import { ensureControllerDataRootForStartup } from './functions.controllerdataroot.js'; import { boundLiveToolExecution, boundToolText } from './functions.livetoolbounding.js'; import { SmartDataAuthStore } from './classes.authstore.js'; import { authStoreSessionIdentityCapability, flexSessionProviderGeneration, openCodeSessionIdentitySnapshotCapability, openCodeSessionProviderGeneration, } from './classes.sessionidentityintegration.js'; import { buildControllerFlexCleanupCohort, ControllerSessionIdentityError, type IControllerManagedSessionDeletionObligation, type IControllerSessionManagedObservation, type IControllerSessionCreationObligation, type IControllerSessionSnapshotEntry, type TControllerFlexHostSessionAuthority, } from './classes.sessionidentityservice.js'; import type { IControllerFlexCleanupEntry } from './classes.managedsessionmodels.js'; import { PasskeyManager } from './classes.passkeymanager.js'; import { AuthError, type IRecordAuditEventInput, } from './interfaces.auth.js'; import { OpenCodeSupervisor } from './classes.opencodesupervisor.js'; import { mergeOpenCodeToolExecutionsIntoDetail, OpenCodeClientAdapter, } from './classes.opencodeclient.js'; import type { IOpenCodeMessageLifecycle, IOpenCodeDirectChildObservation, TOpenCodeReasoningUpdate, TOpenCodeTextUpdate, TOpenCodeToolExecution, } from './interfaces.opencode.js'; import { FlexSupervisor, type IFlexSupervisorExit, } from './classes.flexsupervisor.js'; import { FlexStore } from './classes.flexstore.js'; import { FlexPersistenceMigrationRunner } from '../ts_migration/classes.flexmigrationrunner.js'; import { FlexProjectionCursorError, FlexProjectionFormatError, FlexProjectionNotFoundError, FlexProjectionReader, FlexProjectionStaleCursorError, type IFlexProjectedMessage, } from './classes.flexprojectionreader.js'; import { flexDelegatedRunAdmissionContextKeys, FlexServiceError, flexIpcLifecycleTimeoutMs, isFlexDelegatedRunAdmissionLeaseId, type IFlexGitWorktreeDescriptor, type IFlexIntelligenceChatSummary, type IFlexIntelligenceChatTranscript, type IFlexIntelligenceSessionId, type IFlexServiceInit, type IFlexModelChoice, type IFlexOpenCodeOAuthAuth, type IFlexPublicProviderConnection, type IFlexPublicProviderModel, type IFlexRequestMap, type TFlexChildEvent, type TFlexHostRequest, type TFlexHostRequestMethod, type TFlexHostResponse, type TFlexSessionGeneration, } from './interfaces.flexipc.js'; import { SessionIntelligenceReader } from './classes.sessionintelligencereader.js'; import { ControllerTerminalManager, type ITerminalOutputPayload, type TControllerTerminalDetachReason, } from './classes.terminalmanager.js'; import { ControllerProjectHasResourcesError, ControllerResourceCoordinator, ControllerResourceUnavailableError, type TControllerResourceSessionAuthoritySnapshot, type TControllerResourceSubject, } from './classes.resourcecoordinator.js'; import { ControllerMcpCallerRegistry, currentControllerMcpCaller, } from './classes.mcpcallerregistry.js'; import { hasExactSessionAttachment, hasExactTerminalAttachment, hasSessionAttachmentOfHarness, isResourceAttached, resourceSessionEntries, resourceTerminalEntries, } from './functions.resourceattachments.js'; import { ControllerTerminalResourceHost } from './classes.terminalresourcehost.js'; import { ControllerBrowserResourceHost } from './classes.browserresourcehost.js'; import { readControllerProcessIdentity, readProcessGroupMemberPids, type IControllerProcessIdentity, } from './classes.processinspection.js'; import { ControllerMcpHost, type IControllerMcpPrivateApi, } from './classes.mcpcontrollerhost.js'; import { controllerMcpRuntimeIsSupported } from './classes.mcpplatform.js'; type TFlexMessage = plugins.flexharness.IFlexMessage; type TFlexPermissionRequest = plugins.flexharness.IFlexPermissionRequest; type TFlexSession = plugins.flexharness.IFlexSession; const requireFlexSessionGeneration = (sessionArg: TFlexSession): TFlexSessionGeneration => { if ( typeof sessionArg.sessionGenerationId !== 'string' || sessionArg.sessionGenerationId.length === 0 || !Number.isSafeInteger(sessionArg.sessionGenerationSequence) || sessionArg.sessionGenerationSequence === undefined || sessionArg.sessionGenerationSequence < 1 ) throw new Error('Flex returned a session without canonical generation metadata.'); return { sessionGenerationId: sessionArg.sessionGenerationId, sessionGenerationSequence: sessionArg.sessionGenerationSequence, }; }; const requireFlexSessionProviderGeneration = (sessionArg: TFlexSession): string => { const generation = requireFlexSessionGeneration(sessionArg); return flexSessionProviderGeneration( generation.sessionGenerationId, generation.sessionGenerationSequence, ); }; const isManagedRuntimeId = ( runtimeIdArg: IControllerRuntimeId, ): runtimeIdArg is TControllerSessionId => runtimeIdArg.harnessId !== 'controller'; const requireManagedRuntimeId = (runtimeIdArg: IControllerRuntimeId): TControllerSessionId => { if (runtimeIdArg.harnessId === 'controller') { throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions cannot be selected.', { code: 'invalid_request' }, ); } return runtimeIdArg as TControllerSessionId; }; import type { IControllerStartOptions, IControllerStartResult } from './interfaces.lifecycle.js'; import { assertAuthenticationFinishRequest, assertAuthResumeRequest, assertTempPasswordLoginRequest, assertSlashListRequest, assertEmptyRequest, assertFailureDetailGetRequest, assertProviderConnectionListRequest, assertProviderConnectionActivateOpenCodeRequest, assertProviderConnectionLogoutRequest, assertProviderConnectionRateLimitsGetRequest, assertProviderListRequest, assertProviderLoginBeginRequest, assertProviderLoginCancelRequest, assertProviderLoginGetRequest, assertProviderModelRefreshBeginRequest, assertProviderModelRefreshGetRequest, assertPermissionReplyRequest, assertQuestionReplyRequest, assertSessionRenameRequest, assertSessionArchiveRequest, assertSessionYoloRequest, assertSessionGroupsGetRequest, assertSessionGroupsUpdateRequest, assertTerminalListRequest, assertTerminalCreateRequest, assertTerminalRenameRequest, assertTerminalTargetRequest, assertTerminalInputRequest, assertTerminalResizeRequest, assertResourceListRequest, assertResourceCreateRequest, assertResourceRenameRequest, assertResourceAttachRequest, assertResourceDetachRequest, assertResourceTargetRequest, assertBrowserDevToolsOpenRequest, assertBrowserDevToolsCloseRequest, assertBrowserViewOpenRequest, assertBrowserViewActivateRequest, assertBrowserViewCloseRequest, assertBrowserViewRecoverRequest, assertControllerBrowserClientPacket, assertProjectCreateRequest, resolveProjectPathInput, assertProjectRemovalRetryRequest, assertProjectRemoveRequest, assertProjectSuggestRequest, assertSlashExecuteRequest, assertSessionCreateRequest, assertSessionDraftUpdateRequest, assertSessionAbortRequest, assertSessionGetRequest, assertSessionChildGetRequest, assertSessionChildMessagesPageRequest, assertSessionChildMessageGetRequest, assertSessionChildPermissionReplyRequest, assertSessionChildQuestionReplyRequest, assertSessionAuxiliaryGetRequest, assertSessionModelUpdateRequest, assertSessionScratchpadSaveRequest, assertSessionIntelligenceAskRequest, assertSessionListRequest, assertConversationListRequest, assertConversationSearchRequest, assertConversationOpenRequest, assertConversationArchiveRequest, assertConversationReopenRequest, assertStandardProjectDirectories, assertSessionMessagesPageRequest, assertSessionMessageGetRequest, assertSessionSendRequest, assertSettingsUpdateRequest, assertSetupBeginRequest, assertSetupFinishRequest, } from './classes.validation.js'; import type { IControllerProjectDocument, IControllerResourceDocument, IControllerTrackedConversationDocument, IControllerSessionState as IStoredControllerSessionState, } from './interfaces.projects.js'; import { controllerCrossProjectReadFanoutLimit } from './interfaces.projects.js'; import { SystemMetricsSampler } from './classes.systemmetricssampler.js'; import { ControllerHarnessOperationConflictError, ControllerHarnessOperationLimitError, ControllerHarnessOperationManager, type IControllerHarnessOperationBarrier, type TControllerHarnessOperationReservation, } from './classes.harnessoperationmanager.js'; import { ControllerDraftConflictError, ControllerDraftLimitError, } from './classes.draftmanager.js'; import { ControllerGitReversion, ensureGitReversionParentDirectory, GitReversionDirtyWorktreeError, GitReversionDiscoveryLimitError, GitReversionFencedError, } from './classes.gitreversion.js'; import type { IGitReversionWorktreeDescriptor, TGitReversionReference, } from './interfaces.gitreversion.js'; import { UpgradeCoordinator, upgradeCoordinationVersion, upgradePackageTransitionSource, upgradePackageTransitionTarget, upgradePackageTransitionTransactionVersion, upgradeTransactionIsStalled, upgradeTransactionTargetVersion, type UpgradeInstallationLock, type IUpgradeSessionState, type TUpgradeTransaction, type TUpgradeSessionTransitionState, } from './classes.upgradecoordinator.js'; import { assertInheritedUpgradeTransaction, launchDetachedUpgradeWorker, resolveCurrentPnpmGlobalInstallation, resolveInheritedUpgradeTargetInstallation, terminateUpgradeWorkerCandidateUntilDrained, type IDetachedUpgradeWorker, } from './classes.upgradetransaction.js'; import { upgradeCoordinationRootEnvironmentVariable, upgradeTokenEnvironmentVariable, } from './constants.upgradeenvironment.js'; const currentCliName = String(controllerPackageName) === upgradePackageTransitionSource.packageName ? upgradePackageTransitionSource.cliName : upgradePackageTransitionTarget.cliName; const currentRuntimeName = currentCliName === upgradePackageTransitionTarget.cliName ? 'AGL' : currentCliName; const authenticatedPeerStateKey = '__opencodeControllerAuthenticatedCredentialId'; const terminalPeerLifecycleStateKey = '__harnessControllerTerminalPeerLifecycle'; const operationAbortControllerPeerStateKey = '__harnessControllerOperationAbortController'; const trustedProxySecretHeader = 'x-harness-controller-proxy-secret'; // Generous for a local single-user deployment: transcripts and inline // attachments are legitimately multi-megabyte, and an undersized limit // fail-closes the connection (and with it the per-connection auth). // Typed request envelopes still carry transcripts and inline attachments. const websocketPayloadLimit = 16 * 1024 * 1024; const browserViewActivationTimeoutMs = 15_000; const browserViewCleanupRetryMs = 1_000; const browserViewTransportCleanupTimeoutMs = 9_000; const browserStreamAuthorizationMarker = Symbol('controller.browser.stream'); const browserDevToolsAuthorizationMarker = Symbol('controller.browser.devtools.stream'); // Browser session resume: a login mints a single-use random token the client // holds in sessionStorage; after a transport reconnect it re-authenticates // without user interaction. Rotation keeps tokens single-use, and the chain // expiry is fixed at first login — resuming never extends it. const maxResumeTokens = 64; const resumeTokenTtlMs = 12 * 60 * 60 * 1000; const operationDrainBeforeChildStopMs = 2_000; /** * The one harness whose runtime AGL owns across a credential switch: its mutation is coordinated * here, which stops the managed runtime once its work is idle and restarts it afterwards. Every * other harness runs its own mutation, so AGL restarts nothing for it. */ const authSwitchCoordinatedHarnessId = 'opencode'; const projectRemovalInitialRetryMs = 1_000; const projectRemovalMaximumRetryMs = 30_000; /** * Deterministic project-removal failures: repeating the same attempt cannot change their outcome, * so the retry loop stops and names the cause instead of spinning on it forever. Everything else — * Flex IPC timeouts, admission drains, concurrent changes, recovery in progress, a directory that * is momentarily unavailable — stays on the backoff. */ const classifyProjectRemovalBlock = (errorArg: unknown): { code: TControllerProjectRemovalBlockedCode; reason: string; } | undefined => { for ( let cursor: unknown = errorArg, depth = 0; cursor instanceof Error && depth < 8; cursor = cursor.cause, depth += 1 ) { if (cursor instanceof GitReversionDiscoveryLimitError) return { code: 'git.discovery-limit', reason: `Git repository discovery exceeded its ${cursor.limit} limit for this project directory.`, }; if (cursor instanceof GitReversionDirtyWorktreeError) return { code: 'git.dirty-worktree', reason: 'An owned Git worktree has uncommitted changes and was retained.', }; if (cursor instanceof GitReversionFencedError) return { code: 'git.fenced', reason: 'Owned Git state was fenced because the repository changed unexpectedly.', }; const errno = (cursor as NodeJS.ErrnoException).code; if (errno === 'EACCES' || errno === 'EPERM') return { code: 'filesystem.permission', reason: 'A path required by the removal could not be accessed: permission denied.', }; } return undefined; }; const flexProjectReconciliationInitialRetryMs = 100; const flexProjectReconciliationMaximumRetryMs = 5_000; const projectResourceReconciliationAttemptTimeoutMs = 30_000; const projectResourceReconciliationInitialRetryMs = 100; const projectResourceReconciliationMaximumRetryMs = 5_000; // Bounded so a hung OpenCode child cannot stall shutdown indefinitely. const abortConversationsOnStopTimeoutMs = 10_000; const operationDrainAfterChildStopMs = 5_000; const operationDrainAfterServerStopMs = 2_000; const processGroupDrainTimeoutMs = 2_000; const maxActiveControllerOperations = 64; const maxQueuedSessionLayoutOperationsPerProject = 64; /** The single scope key of the controller-wide layout queue. */ const controllerSessionLayoutScopeKey = 'controller'; // The UI legitimately fires ~8 loads in parallel when a project opens // (sessions, detail, groups, terminals, models, settings, commands); the cap // bounds a runaway client, not normal fan-out. const maxActiveControllerOperationsPerPeer = 16; const protectedOperationWindowMs = 60_000; // Sized for event-driven UIs: a busy agent run refreshes the session list // and detail several times a second per tab. Instantaneous load stays // bounded by the concurrency caps above; this window only catches runaways. const maxProtectedOperationsPerWindow = 4096; const maxProtectedOperationsPerPeerPerWindow = 900; const unauthenticatedAuditWindowMs = 60_000; const maxUnauthenticatedAuditsPerWindow = 32; const maxUnauthenticatedAuditsPerPeerPerWindow = 8; const unauthenticatedPreflightWindowMs = 60_000; const maxUnauthenticatedPreflightsPerPeerPerWindow = 16; const maxUnauthenticatedPreflightsPerWindow = 256; const maxTrackedUnauthenticatedPreflightPeers = 1_024; const maxTrackedAutoRepliedPermissions = 4_096; const maxTrackedSessionAdmissionStates = 4_096; const maxFlexProviderLogins = 128; const maxFlexRefreshJobs = 128; const maxPendingFlexPromptFinishes = 128; const maxActiveFlexRuns = 256; const maxFlexDelegatedAuthorities = 256; const flexDelegationTicketTtlMs = 5 * 60 * 1000; const flexRunCleanupRetryMs = 1_000; const maxFlexModelConnections = 512; const maxYoloSessionIds = 512; const maxPendingOpenCodeControllerEvents = 512; const childScopeIdleTtlMs = 5 * 60 * 1000; const childScopeActiveLifetimeMs = 30 * 60 * 1000; const childScopeTerminalTtlMs = 10 * 60 * 1000; const childScopeCleanupIntervalMs = 30 * 1000; const maxChildScopesPerPeerParent = 16; const maxChildScopesPerPeer = 32; const maxControllerChildScopes = 512; const maxPendingChildScopeEvents = 512; const maxPendingChildScopeEventBytes = 16 * 1024 * 1024; const maxPendingEventsPerChildScope = 64; const maxPendingEventBytesPerChildScope = 4 * 1024 * 1024; const maxChildAttentionChildren = 16; const maxChildAttentionItems = 32; const maxChildAttentionBytes = 256 * 1024; const upgradePauseMessage = 'please pause at the earliest convenience for a harness upgrade'; const upgradeContinueMessage = 'harness has been updated, please continue'; const upgradePromptPollMs = 250; const retainedUpgradeRetryInitialMs = 25; const retainedUpgradeRetryMaximumMs = 250; const openCodeAccountSwitchPauseMessage = 'pause at earliest convenience for harness restart'; const openCodeAccountSwitchContinueMessage = 'harness has restarted with the selected OpenAI account, please continue'; const openCodeAccountSwitchTimeoutMs = 5 * 60 * 1000; const sessionArchiveOperationTimeoutMs = 10_000; const sessionArchiveMutationObservationMs = 2_000; const sessionArchivePostDispatchTimeoutMs = 10_000; const sessionArchiveReconciliationPollMs = 250; const maxSessionArchiveReconciliationReads = 3; const maxLatestOpenCodeToolExecutions = 512; const maxLatestOpenCodeMessageUpdates = 512; const maxPendingFlexControllerEvents = 512; const maxLatestFlexToolExecutions = 512; const maxLatestFlexToolExecutionBytes = 8 * 1024 * 1024; const maxLatestFlexMessageUpdateBytes = 16 * 1024 * 1024; const maxPendingFlexAutoAcceptSweeps = 1_024; const maxConcurrentArchivedLayoutReads = 8; const maxArchivedLayoutExactReads = 64; /** Upper bound on one cross-project conversation search response. */ const conversationSearchResultLimit = 512; const maxActiveSessionIntelligenceJobs = 4; const sessionIntelligenceModelId = 'gpt-5.6-luna'; const sessionIntelligenceModel = `openai/${sessionIntelligenceModelId}`; const sessionIntelligenceConnectReason = 'Connect an OpenAI account in Settings to use Session Intelligence.'; const sessionIntelligenceReconnectReason = 'Reconnect an OpenAI account in Settings to use Session Intelligence.'; const sessionIntelligenceTemporarilyUnavailableReason = 'Session Intelligence is temporarily unavailable.'; const sessionIntelligenceSourceSessionLimit = 2_048; const sessionIntelligenceSourceOverfetchLimit = sessionIntelligenceSourceSessionLimit + 1; const sessionIntelligenceHiddenLookupBatchSize = 512; const sessionIntelligencePollMs = 250; const sessionIntelligenceCancellationDrainMs = 15_000; const sessionIntelligenceTranscriptBytes = 240 * 1024; const openCodeOperationRecoveryPollMs = 1_000; const maxOpenCodeOperationRecoveryFailures = 3; const maxOpenCodeOperationRecoveryPages = 8; const openCodeFailClosedRetryMs = 1_000; const openCodeSessionErrorCleanupRetryMs = 1_000; const maxFlexSessionIdentitySnapshotEntries = 2_048; const sessionIntelligenceOrphanGraceMs = 30 * 60 * 1000; const sessionIntelligenceCleanupIntervalMs = 15 * 60 * 1000; const sessionIntelligenceCleanupTimeoutMs = 15_000; const openCodeRestartInitialDelayMs = 1_000; const openCodeRestartMaximumDelayMs = 30_000; const flexRestartInitialDelayMs = 1_000; /** * Boot-time Flex startup may not delay controller readiness beyond this * budget. Detached `agl start` and the upgrade worker wait a bounded time for * readiness, while Flex initialization (reversion maintenance over large * projects) can legitimately take minutes under its 30 minute lifecycle * deadline; past the budget the harness continues through Flex recovery. */ export const controllerFlexBootStartupBudgetMs = 30_000; const flexRestartMaximumDelayMs = 30_000; export const controllerOpenCodeRestartDelayMs = (attemptArg: number): number => Math.min( openCodeRestartInitialDelayMs * (2 ** Math.max(0, attemptArg)), openCodeRestartMaximumDelayMs, ); export const controllerFlexRestartDelayMs = (attemptArg: number): number => Math.min( flexRestartInitialDelayMs * (2 ** Math.max(0, attemptArg)), flexRestartMaximumDelayMs, ); const waitForSignalOrDelay = async ( delayMsArg: number, signalArg: AbortSignal, ): Promise => { if (signalArg.aborted) return; await new Promise((resolve) => { let settled = false; const settle = () => { if (settled) return; settled = true; clearTimeout(timer); signalArg.removeEventListener('abort', settle); resolve(); }; const timer = setTimeout(settle, delayMsArg); signalArg.addEventListener('abort', settle, { once: true }); }); }; type TUnusedSessionState = 'unused' | 'discarding'; interface IUnusedSessionDiscardResult { discarded: boolean; retryable: boolean; } interface IControllerPeer { id: string; context: { headers: Headers; state: Record; }; } interface IUpgradePreparationContext { coordinator: UpgradeCoordinator; token: string; targetVersion: string; gracePeriodMs: number; barrier: IControllerHarnessOperationBarrier; preparationAbortController: AbortController; preparationSignal: AbortSignal; acceptedAt: number; deadline: number; } interface ITerminalPeerLifecycleState { connected: boolean; } const truncateBrowserErrorMessage = (messageArg: string, maximumBytesArg = 512): string => { if (Buffer.byteLength(messageArg, 'utf8') <= maximumBytesArg) return messageArg; const characters: string[] = []; let byteLength = 0; for (const character of messageArg) { const characterBytes = Buffer.byteLength(character, 'utf8'); if (byteLength + characterBytes > maximumBytesArg) break; characters.push(character); byteLength += characterBytes; } return characters.join(''); }; const controllerBrowserError = ( errorArg: { code: string; fatal: boolean; message?: string; tabId?: string }, ): NonNullable => { if ( Buffer.byteLength(errorArg.code, 'utf8') === 0 || Buffer.byteLength(errorArg.code, 'utf8') > 128 ) throw new Error('The browser runtime error code is invalid.'); if ( errorArg.tabId !== undefined && ( Buffer.byteLength(errorArg.tabId, 'utf8') === 0 || Buffer.byteLength(errorArg.tabId, 'utf8') > 512 ) ) throw new Error('The browser runtime error tab ID is invalid.'); const message = truncateBrowserErrorMessage(errorArg.message || errorArg.code); return { code: errorArg.code, message, fatal: errorArg.fatal, ...(errorArg.tabId === undefined ? {} : { tabId: errorArg.tabId }), }; }; type TControllerBrowserViewLifecycle = | 'inactive' | 'activating' | 'active' | 'recoverable' | 'recovering' | 'failing' | 'closing'; type TControllerBrowserTransportLifecycle = | 'opening' | 'ready' | 'active' | 'failed' | 'retiring' | 'retired'; interface IControllerBrowserQueuedOperation { operationId: number; operation: TControllerBrowserViewOperation; byteLength: number; abortController?: AbortController; cancelled: boolean; terminal: boolean; } interface IControllerBrowserQueuedEvent { packet: TControllerBrowserServerPacket; packetBytes: Uint8Array; byteLength: number; terminal: boolean; onSent?: () => void; onSkipped?: (reasonArg: string) => void; resolve: () => void; reject: (reasonArg: unknown) => void; } interface IControllerBrowserViewTransport { readonly identity: symbol; generation: number; lifecycle: TControllerBrowserTransportLifecycle; /** * Set once a close has been announced on this stream. The stream is then * ended gracefully rather than reset, because a reset discards the announce * and leaves the client reading an unexplained stream failure. It belongs to * the stream rather than the view: recovery can give a view a fresh stream * that never carried an announce. */ announcedClose?: boolean; /** * Set once an announced close has actually been written to the stream, which * is not the same as having queued one: the delivery continuation re-checks * its guards at send time and can drop the packet while still resolving. Only * a delivered announce means the client was told, so this -- not * `announcedClose` -- is what decides whether a close is worth reporting. */ announceDelivered?: boolean; /** * Why an announced close never reached the wire, when it did not. The * delivery chain's own rejection handler suppresses faults once a close is * announced -- correctly, since they are that close happening -- but that * also swallowed the one error that explains a missing announce. */ announceFailure?: unknown; /** Set when the delivery continuation skipped the announce, with the guard that skipped it. */ announceSkipped?: string; events: plugins.typedrequestInterfaces.TVirtualStream<'send'>; operations: plugins.typedrequestInterfaces.TVirtualStream<'receive'>; abortController: AbortController; openingTask: Promise; receiveTask?: Promise; drainTask?: Promise; deliveryTail: Promise; eventQueue: IControllerBrowserQueuedEvent[]; eventDeliveryTask?: Promise; pendingEventCount: number; pendingEventBytes: number; deliveryHighWater?: { events: number; bytes: number }; operationQueue: IControllerBrowserQueuedOperation[]; pendingOperationBytes: number; highestOperationId: number; cancellableOperationIds: Set; submittedOperations: Map>; admittingOperations?: boolean; retirementTask?: Promise; retirementSettlement?: Promise; retirementSettled?: boolean; retirementDeadlineExpired?: boolean; failure?: unknown; } interface IControllerBrowserTransportConstructionCleanup { readonly settlement: Promise; readonly reason: unknown; settled: boolean; deadlineExpired: boolean; } interface IControllerBrowserView { id: string; peerId: string; credentialId: string; authenticationGeneration: symbol; transportConnection: plugins.typedsocket.ISmartServeConnectionWrapper; transportPeer: plugins.typedsocket.ISmartServeConnectionWrapper['peer']; resource: IControllerResourceDocument; handle: Awaited>; authority: plugins.browserRuntime.TBrowserRuntimeLeaseAuthority; lifecycle: TControllerBrowserViewLifecycle; activationAbortController: AbortController; activationTimer?: ReturnType; activationPromise?: Promise; renewalTimer?: ReturnType; renewalTask?: Promise; transport?: IControllerBrowserViewTransport; devTools?: ControllerBrowserDevToolsTransport; pendingTransportIdentity?: symbol; transportConstructionCleanup?: IControllerBrowserTransportConstructionCleanup; streamGeneration: number; stateRevision: number; recoveryAttempts: number; recoveryDeadlineAt?: number; recoveryTimer?: ReturnType; closing: boolean; closeDiagnostic?: { code: TControllerBrowserViewCloseCode; message: string }; cleanupTask?: Promise; cleanupRetryTimer?: ReturnType; } interface IControllerBrowserViewSlot { tail: Promise; pendingOperations: number; view?: IControllerBrowserView; } interface IControllerBrowserStreamAuthorization { readonly marker: typeof browserStreamAuthorizationMarker; readonly view: IControllerBrowserView; readonly authority: plugins.browserRuntime.TBrowserRuntimeLeaseAuthority; readonly transportPeer: plugins.typedsocket.ISmartServeConnectionWrapper['peer']; readonly transportIdentity: symbol; readonly streamGeneration: number; readonly creatorDirection: plugins.typedrequestInterfaces.TVirtualStreamDirection; readonly contentType: string; } interface IControllerBrowserDevToolsAuthorization { readonly marker: typeof browserDevToolsAuthorizationMarker; readonly view: IControllerBrowserView; readonly transport: ControllerBrowserDevToolsTransport; readonly primaryIdentity: symbol; readonly authority: plugins.browserRuntime.TBrowserRuntimeLeaseAuthority; readonly transportPeer: plugins.typedsocket.ISmartServeConnectionWrapper['peer']; readonly streamGeneration: number; readonly creatorDirection: plugins.typedrequestInterfaces.TVirtualStreamDirection; readonly contentType: string; } interface IFlexRefreshJobCacheEntry { job: IControllerModelRefreshJob; providerConnectionId: string; } interface IFlexAutoAcceptSweep { projectId: string; sessionId?: string; authority?: TFlexPermissionAuthority; } interface IOpenCodeAccountSwitchSession { projectId: string; sessionId: IControllerRuntimeId & { harnessId: 'opencode' }; directory: string; } interface IControllerOwnedOpenCodeRuntime { supervisor: OpenCodeSupervisor; client: OpenCodeClientAdapter; eventStreamTask?: Promise; } interface IOpenCodeRuntimeInstallationOptions { advanceStreams: boolean; retainPlannedStopUntilCallerCommit?: boolean; } interface ILatestToolExecution { projectId: string; execution: IControllerToolExecution; bytes?: number; } interface ILatestReasoningUpdate { projectId: string; update: IControllerReasoningUpdate; textUtf8Bytes: number; bytes?: number; } interface ILatestTextUpdate { projectId: string; update: IControllerTextUpdate; textUtf8Bytes: number; bytes?: number; } type TControllerMessageDelta = IControllerReasoningDelta | IControllerTextDelta; interface IControllerTextDeltaFragment { delta: string; utf8Bytes: number; } type TControllerToolExecutionSnapshot = Omit< IControllerToolExecution, 'revision' | 'streamEpoch' >; const historyLimitedNotice = '[earlier message content omitted: this transcript exceeds the transfer budget]'; const historyLimitedDigestPrefix = 'history-limited:'; const controllerEventMessageDelta = ( eventArg: IControllerEvent, ): TControllerMessageDelta | undefined => eventArg.reasoningDelta ?? eventArg.textDelta; const controllerTextAfterAppendUtf8Bytes = ( textArg: string, textUtf8BytesArg: number, deltaArg: string, ): number | undefined => { // Separately encoded surrogate halves cost six bytes; after append the pair costs four. const boundaryCorrection = ( textArg.length > 0 && deltaArg.length > 0 && textArg.charCodeAt(textArg.length - 1) >= 0xD800 && textArg.charCodeAt(textArg.length - 1) <= 0xDBFF && deltaArg.charCodeAt(0) >= 0xDC00 && deltaArg.charCodeAt(0) <= 0xDFFF ) ? -2 : 0; const result = textUtf8BytesArg + Buffer.byteLength(deltaArg, 'utf8') + boundaryCorrection; return Number.isSafeInteger(result) && result >= 0 ? result : undefined; }; const fragmentControllerTextDelta = (valueArg: string): IControllerTextDeltaFragment[] => { const fragments: IControllerTextDeltaFragment[] = []; let delta = ''; let utf8Bytes = 0; for (const codePoint of valueArg) { const codePointBytes = Buffer.byteLength(codePoint, 'utf8'); if ( delta !== '' && utf8Bytes + codePointBytes > controllerLiveMessageDeltaTargetBytes ) { fragments.push({ delta, utf8Bytes }); delta = ''; utf8Bytes = 0; } delta += codePoint; utf8Bytes += codePointBytes; } if (delta !== '') fragments.push({ delta, utf8Bytes }); return fragments; }; const controllerMessageDeltaPendingKey = (eventArg: IControllerEvent): string | undefined => { const delta = controllerEventMessageDelta(eventArg); if (!delta) return undefined; return JSON.stringify([ eventArg.type, eventArg.harnessId ?? null, eventArg.projectId ?? null, controllerRuntimeIdKey(delta.sessionId), controllerRuntimeIdKey(delta.partId), ]); }; const controllerMessageDeltaEventsAreContiguous = ( previousEventArg: IControllerEvent, eventArg: IControllerEvent, ): boolean => { const previous = controllerEventMessageDelta(previousEventArg); const delta = controllerEventMessageDelta(eventArg); if (!previous || !delta) return false; return previousEventArg.type === eventArg.type && previousEventArg.harnessId === eventArg.harnessId && previousEventArg.projectId === eventArg.projectId && (previousEventArg.sessionId ? eventArg.sessionId !== undefined && controllerRuntimeIdKey(previousEventArg.sessionId) === controllerRuntimeIdKey(eventArg.sessionId) : eventArg.sessionId === undefined) && controllerRuntimeIdKey(previous.sessionId) === controllerRuntimeIdKey(delta.sessionId) && controllerRuntimeIdKey(previous.messageId) === controllerRuntimeIdKey(delta.messageId) && controllerRuntimeIdKey(previous.partId) === controllerRuntimeIdKey(delta.partId) && previous.streamEpoch === delta.streamEpoch && previous.order?.messageIndex === delta.order?.messageIndex && previous.order?.partIndex === delta.order?.partIndex && previous.textUtf8Bytes === delta.baseTextUtf8Bytes && delta.revision > previous.revision; }; const mergeControllerMessageDeltaEvents = ( previousEventArg: IControllerEvent, eventArg: IControllerEvent, ): IControllerEvent | undefined => { if (!controllerMessageDeltaEventsAreContiguous(previousEventArg, eventArg)) return undefined; const previous = controllerEventMessageDelta(previousEventArg)!; const delta = controllerEventMessageDelta(eventArg)!; const mergedDelta = { ...delta, delta: `${previous.delta}${delta.delta}`, baseTextUtf8Bytes: previous.baseTextUtf8Bytes, }; return eventArg.reasoningDelta ? { ...eventArg, reasoningDelta: mergedDelta } : { ...eventArg, textDelta: mergedDelta }; }; const markHistoryLimitedDigest = (digestArg: string): string => ( digestArg.startsWith(historyLimitedDigestPrefix) ? digestArg : `${historyLimitedDigestPrefix}${digestArg}` ); const cloneMessageBundle = (bundleArg: IControllerMessageBundle): IControllerMessageBundle => ({ ...bundleArg, sourceMessageId: { ...bundleArg.sourceMessageId }, messages: bundleArg.messages.map((message) => ({ ...message, id: { ...message.id }, ...(message.reasoning ? { reasoning: message.reasoning.map((part) => ({ ...part, id: { ...part.id } })) } : {}), ...(message.toolCall ? { toolCall: { ...message.toolCall, id: { ...message.toolCall.id }, ...(message.toolCall.childSessionId ? { childSessionId: { ...message.toolCall.childSessionId } } : {}), }, } : {}), })), }); export const boundControllerMessagePage = ( pageArg: IControllerMessagePage, maximumBundlesArg = 50, protectedMessageCheckArg?: (messageArg: IControllerMessage) => boolean, ): IControllerMessagePage => { const page: IControllerMessagePage = { ...pageArg, bundles: pageArg.bundles.slice(-maximumBundlesArg).map(cloneMessageBundle), ...(pageArg.bundles.length > maximumBundlesArg ? { historyLimited: true } : {}), }; const pageBytes = (): number => Buffer.byteLength(JSON.stringify(page), 'utf8'); const mutateMessagesUntilBound = ( mutationArg: (messageArg: IControllerMessage) => boolean, protectedMessageArg: boolean, ): boolean => { for (const bundle of page.bundles) { for (const message of bundle.messages) { if ((protectedMessageCheckArg?.(message) === true) !== protectedMessageArg) continue; mutationArg(message); } } return pageBytes() <= controllerMaximumTranscriptBytes; }; if (pageBytes() <= controllerMaximumTranscriptBytes) return page; page.historyLimited = true; for (const protectedMessage of [false, true]) { if (mutateMessagesUntilBound((message) => { if (!message.toolCall || !Object.hasOwn(message.toolCall, 'input')) return false; delete message.toolCall.input; return true; }, protectedMessage)) return page; if (mutateMessagesUntilBound((message) => { if (!message.toolCall || !Object.hasOwn(message.toolCall, 'output')) return false; delete message.toolCall.output; return true; }, protectedMessage)) return page; if (mutateMessagesUntilBound((message) => { if (!message.reasoning?.some((reasoning) => reasoning.text !== '')) return false; for (const reasoning of message.reasoning) reasoning.text = ''; return true; }, protectedMessage)) return page; if (mutateMessagesUntilBound((message) => { if (message.text === '') return false; message.text = ''; return true; }, protectedMessage)) return page; } for (const protectedBundle of [false, true]) { for (const bundle of page.bundles) { if ( bundle.messages.some((message) => protectedMessageCheckArg?.(message) === true) !== protectedBundle ) continue; const newest = bundle.messages.at(-1); bundle.structuralDigest = markHistoryLimitedDigest(bundle.structuralDigest); bundle.messages = [{ id: { ...bundle.sourceMessageId }, role: 'system', text: historyLimitedNotice, createdAt: newest?.createdAt ?? 0, }]; if (pageBytes() <= controllerMaximumTranscriptBytes) return page; } } if (pageBytes() > controllerMaximumTranscriptBytes) { throw new Error('The bounded message page shells exceeded the transfer budget.'); } return page; }; export const boundControllerMessageBundle = ( bundleArg: IControllerMessageBundle, ): IControllerMessageBundle => { let page = boundControllerMessagePage({ bundles: [bundleArg] }, 1); let bundle = page.bundles[0]; if (!bundle) throw new Error('The bounded exact message response omitted its bundle.'); if (page.historyLimited === true) { bundle.structuralDigest = markHistoryLimitedDigest(bundle.structuralDigest); page = boundControllerMessagePage({ bundles: [bundle], historyLimited: true }, 1); bundle = page.bundles[0]; if (!bundle) throw new Error('The bounded exact message response omitted its bundle.'); } if (Buffer.byteLength(JSON.stringify(page), 'utf8') > controllerMaximumTranscriptBytes) { throw new Error('The bounded exact message response exceeded the transfer budget.'); } return bundle; }; export const mergeControllerMessagePageSnapshots = ( pageArg: IControllerMessagePage, executionsArg: readonly IControllerToolExecution[], reasoningUpdatesArg: readonly IControllerReasoningUpdate[], textUpdatesArg: readonly IControllerTextUpdate[], ): IControllerMessagePage => { const bundles = pageArg.bundles.map(cloneMessageBundle); const protectedPartKeys = new Set(executionsArg.map((execution) => ( controllerRuntimeIdKey(execution.partId) ))); const protectedOrderKeys = new Set( [...executionsArg, ...reasoningUpdatesArg, ...textUpdatesArg] .flatMap((snapshot) => snapshot.order === undefined ? [] : [`${snapshot.order.messageIndex}\0${snapshot.order.partIndex}`]), ); const messageIsProtected = (messageArg: IControllerMessage): boolean => ( protectedPartKeys.has(controllerRuntimeIdKey(messageArg.id)) || ( messageArg.order !== undefined && protectedOrderKeys.has(`${messageArg.order.messageIndex}\0${messageArg.order.partIndex}`) ) ); const bundleForSource = ( sourceMessageIdArg: IControllerRuntimeId, orderArg?: IControllerMessage['order'], ): IControllerMessageBundle => { const sourceKey = controllerRuntimeIdKey(sourceMessageIdArg); let bundle = bundles.find((candidate) => ( controllerRuntimeIdKey(candidate.sourceMessageId) === sourceKey )); if (!bundle) { bundle = { sourceMessageId: { ...sourceMessageIdArg }, structuralDigest: `live:${sourceKey}`, messages: [], }; const insertionIndex = orderArg === undefined ? -1 : bundles.findIndex((candidate) => { const candidateOrder = candidate.messages.find((message) => message.order)?.order; return candidateOrder !== undefined && candidateOrder.messageIndex > orderArg.messageIndex; }); if (insertionIndex < 0) bundles.push(bundle); else bundles.splice(insertionIndex, 0, bundle); } return bundle; }; for (const update of reasoningUpdatesArg) { const bundle = bundleForSource(update.messageId, update.order); bundle.messages = ( update.sessionId.harnessId === 'opencode' ? mergeOpenCodeReasoningUpdatesIntoDetail : mergeFlexReasoningUpdatesIntoDetail )({ messages: bundle.messages }, [update]).messages; } for (const update of textUpdatesArg) { const bundle = bundleForSource(update.messageId, update.order); bundle.messages = ( update.sessionId.harnessId === 'opencode' ? mergeOpenCodeTextUpdatesIntoDetail : mergeFlexTextUpdatesIntoDetail )({ messages: bundle.messages }, [update]).messages; } for (const execution of executionsArg) { const bundle = bundleForSource(execution.messageId, execution.order); bundle.messages = mergeOpenCodeToolExecutionsIntoDetail( { messages: bundle.messages }, [execution], ).messages; } for (const bundle of bundles) { if (!bundle.messages.every((message) => message.order !== undefined)) continue; bundle.messages.sort((left, right) => ( left.order!.messageIndex - right.order!.messageIndex || left.order!.partIndex - right.order!.partIndex )); } return boundControllerMessagePage( { ...pageArg, bundles }, controllerInitialMessageBundleLimit, messageIsProtected, ); }; /** One truncation primitive for every controller-side payload bound; see `boundToolText`. */ const truncateToolUtf8 = (valueArg: string, byteLimitArg: number): string => boundToolText(valueArg, byteLimitArg).text; export const mergeFlexReasoningUpdatesIntoDetail = < TDetail extends { messages: IControllerMessage[]; session?: IControllerSession }, >( detailArg: TDetail, updatesArg: readonly IControllerReasoningUpdate[], ): TDetail => { if (updatesArg.length === 0) return detailArg; const messages = detailArg.messages.map((message) => ({ ...message, ...(message.reasoning ? { reasoning: message.reasoning.map((part) => ({ ...part })) } : {}), })); for (const update of updatesArg) { const existingMessage = messages.find((message) => ( message.reasoning?.some((part) => ( controllerRuntimeIdKey(part.id) === controllerRuntimeIdKey(update.partId) )) || ( update.order !== undefined && message.order?.messageIndex === update.order.messageIndex && message.order.partIndex === update.order.partIndex ) )); const reasoning = { id: update.partId, text: update.text, startedAt: update.sourceUpdatedAt, ...(update.status === 'running' ? {} : { endedAt: update.sourceUpdatedAt }), }; if (existingMessage) { const existingIndex = existingMessage.reasoning?.findIndex((part) => ( controllerRuntimeIdKey(part.id) === controllerRuntimeIdKey(update.partId) )) ?? -1; if (durableMessageIsTerminal(detailArg, existingMessage)) { if (existingIndex >= 0 && existingMessage.reasoning![existingIndex].text === '') { existingMessage.reasoning![existingIndex] = { ...reasoning, ...existingMessage.reasoning![existingIndex], text: update.text, }; } else if (existingIndex < 0) { existingMessage.reasoning = [...(existingMessage.reasoning ?? []), reasoning]; } continue; } if (existingIndex >= 0) existingMessage.reasoning![existingIndex] = reasoning; else existingMessage.reasoning = [...(existingMessage.reasoning ?? []), reasoning]; existingMessage.streaming = update.status === 'running'; continue; } messages.push({ id: update.partId, role: 'assistant', text: '', createdAt: update.sourceUpdatedAt, order: update.order, streaming: update.status === 'running', reasoning: [reasoning], }); } return { ...detailArg, messages }; }; export const mergeFlexTextUpdatesIntoDetail = < TDetail extends { messages: IControllerMessage[]; session?: IControllerSession }, >( detailArg: TDetail, updatesArg: readonly IControllerTextUpdate[], ): TDetail => { if (updatesArg.length === 0) return detailArg; const messages = detailArg.messages.map((message) => ({ ...message })); for (const update of updatesArg) { const existing = messages.find((message) => ( controllerRuntimeIdKey(message.id) === controllerRuntimeIdKey(update.partId) || ( update.order !== undefined && message.order?.messageIndex === update.order.messageIndex && message.order.partIndex === update.order.partIndex ) )); if (existing) { if (durableMessageIsTerminal(detailArg, existing)) { if (existing.text === '') existing.text = update.text; continue; } existing.text = update.text; existing.streaming = update.status === 'running'; continue; } messages.push({ id: update.partId, role: 'assistant', text: update.text, createdAt: update.sourceUpdatedAt, order: update.order, streaming: update.status === 'running', }); } return { ...detailArg, messages }; }; const durableMessageIsTerminal = ( detailArg: { session?: IControllerSession }, messageArg: IControllerMessage, ): boolean => messageArg.streaming !== true && ( messageArg.updatedAt !== undefined || messageArg.error !== undefined || messageArg.usage !== undefined || detailArg.session?.status === 'idle' ); export const mergeOpenCodeReasoningUpdatesIntoDetail = < TDetail extends { messages: IControllerMessage[]; session?: IControllerSession }, >( detailArg: TDetail, updatesArg: readonly IControllerReasoningUpdate[], ): TDetail => { if (updatesArg.length === 0) return detailArg; const messages = detailArg.messages.map((messageArg) => ({ ...messageArg, ...(messageArg.reasoning ? { reasoning: messageArg.reasoning.map((partArg) => ({ ...partArg })) } : {}), })); for (const update of updatesArg) { const sourceIndex = messages.findIndex((messageArg) => ( messageArg.role === 'assistant' && controllerRuntimeIdKey(messageArg.id) === controllerRuntimeIdKey(update.messageId) )); const partIndex = messages.findIndex((messageArg) => ( messageArg.reasoning?.some((partArg) => ( controllerRuntimeIdKey(partArg.id) === controllerRuntimeIdKey(update.partId) )) )); const targetIndex = sourceIndex >= 0 ? sourceIndex : partIndex; const reasoning = { id: update.partId, text: update.text, startedAt: update.sourceUpdatedAt, ...(update.status === 'running' ? {} : { endedAt: update.sourceUpdatedAt }), }; if (targetIndex >= 0) { const message = messages[targetIndex]; if (durableMessageIsTerminal(detailArg, message)) continue; const existingIndex = message.reasoning?.findIndex((partArg) => ( controllerRuntimeIdKey(partArg.id) === controllerRuntimeIdKey(update.partId) )) ?? -1; if (existingIndex >= 0) message.reasoning![existingIndex] = reasoning; else message.reasoning = [...(message.reasoning ?? []), reasoning]; message.streaming = update.status === 'running' || message.streaming === true; continue; } messages.push({ id: update.messageId, role: 'assistant', text: '', createdAt: update.sourceUpdatedAt, streaming: update.status === 'running', reasoning: [reasoning], }); } return { ...detailArg, messages }; }; export const mergeOpenCodeTextUpdatesIntoDetail = < TDetail extends { messages: IControllerMessage[]; session?: IControllerSession }, >( detailArg: TDetail, updatesArg: readonly IControllerTextUpdate[], ): TDetail => { if (updatesArg.length === 0) return detailArg; const messages = detailArg.messages.map((messageArg) => ({ ...messageArg })); for (const update of updatesArg) { const sourceIndex = messages.findIndex((messageArg) => ( messageArg.role === 'assistant' && controllerRuntimeIdKey(messageArg.id) === controllerRuntimeIdKey(update.messageId) )); const partIndex = messages.findIndex((messageArg) => ( controllerRuntimeIdKey(messageArg.id) === controllerRuntimeIdKey(update.partId) )); const targetIndex = sourceIndex >= 0 ? sourceIndex : partIndex; if (targetIndex >= 0) { const message = messages[targetIndex]; if (durableMessageIsTerminal(detailArg, message)) continue; message.text = update.text; message.streaming = update.status === 'running' || message.streaming === true; continue; } messages.push({ id: update.messageId, role: 'assistant', text: update.text, createdAt: update.sourceUpdatedAt, streaming: update.status === 'running', }); } return { ...detailArg, messages }; }; interface IProjectReconciliationResult { projects: IControllerProjectDocument[]; availableProjects: IControllerProjectDocument[]; flexSynchronized: boolean; } /** * The sidebar layout has to tell a project that is gone from one whose directory is only * temporarily unavailable: the first loses its remembered rows, the second keeps them. */ type TControllerLayoutProjectAvailability = | { status: 'available'; project: IControllerProjectDocument } | { status: 'unavailable' } | { status: 'gone'; error: plugins.typedrequest.TypedResponseError }; interface ISessionIntelligenceJob { abortController: AbortController; exchangeId: string; capabilityToken?: string; flexJobId?: string; temporarySessionId?: string; task?: Promise; } interface ISessionIntelligenceCapability { projectId: string; directory: string; sourceSessionId: IFlexIntelligenceSessionId; flexSupervisor: FlexSupervisor; openCodeSupervisor: OpenCodeSupervisor; openCodeClient: OpenCodeClientAdapter; reader: SessionIntelligenceReader; abortController: AbortController; peerId?: string; sourceState: 'unread' | 'reading' | 'read'; activeRequests: number; closed: boolean; closeTask?: Promise; drained?: Promise; resolveDrained?: () => void; } interface IActiveSessionBackgroundOperation { abortController: AbortController; task: Promise; } interface IProjectSessionIntelligenceAdmissions { count: number; drained: Promise; resolveDrained: () => void; } interface IHarnessSubmissionAdmissions { count: number; drained: Promise; resolveDrained: () => void; } interface IControllerRuntimeExitSignal { signal: AbortSignal; resolve: () => void; sessionIdentityGeneration: string; } interface IOpenCodeManagedSessionScopeAuthority { project: IControllerProjectDocument; session: IControllerSession; sessionIdentityId: string; providerSessionGeneration: string; supervisor: OpenCodeSupervisor; client: OpenCodeClientAdapter; runtimeAuthority: IControllerRuntimeExitSignal; } interface IOpenCodeFamilyAuthority { parent: IOpenCodeManagedSessionScopeAuthority; child?: IOpenCodeDirectChildObservation; } interface IControllerChildScopeRecord { projectId: string; parentSessionId: IControllerRuntimeId & { harnessId: 'opencode' }; childSessionId: IControllerRuntimeId & { harnessId: 'opencode' }; scopeGeneration: string; mode: 'active' | 'terminal'; peerId: string; credentialId: string; parentSessionIdentityId: string; parentProviderSessionGeneration: string; supervisorSessionIdentityGeneration: string; childProviderSessionGeneration: string; issuedAt: number; lastUsedAt: number; expiresAt: number; sequence: number; pendingEvents: IControllerChildEvent[]; pendingEventBytes: number; revoked: boolean; eventDrainTask?: Promise; } interface IOpenCodeCrashRecoveryContext extends IControllerOwnedOpenCodeRuntime { exitSignal: AbortSignal; admissionFenceGeneration: number; temporarySessionIds: string[]; supervisorStopped: boolean; cleanupComplete: boolean; cleanupTask?: Promise; } interface IOpenCodeArchiveRuntime { client: OpenCodeClientAdapter; supervisor: OpenCodeSupervisor; exitSignal: AbortSignal; } type TOpenCodeArchiveMutationOutcome = | { type: 'session'; session: IControllerSession } | { type: 'error'; error: unknown }; interface IOpenCodeArchiveMutationState { outcome?: TOpenCodeArchiveMutationOutcome; task: Promise; } interface IFlexArchiveRuntime { supervisor: FlexSupervisor; exitSignal: AbortSignal; } interface IOpenCodeOperationMonitor { abortController: AbortController; task: Promise; } interface IQueuedOpenCodePromptSubmission { operation: TControllerHarnessOperationReservation; projectDirectory: string; prompt: string; model?: TControllerModelChoice; agent?: string; } interface IFlexConnectionModelOption { harnessId: 'flex'; providerConnectionId: string; providerID: string; providerName: string; modelID: string; modelName: string; variants: string[]; isDefault: boolean; } interface IControllerFlexModelSelection { model: Extract; providerConnectionId: string; } type TControllerSessionSendCoreRequest = | ({ source: 'draft' } & IReq_ControllerSessionSend['request']) | ({ source: 'direct' } & IReq_ControllerMcpSessionSend['request']); interface IPendingFlexRunCleanup { projectId: string; sessionId: string; sessionGenerationId: string; sessionGenerationSequence: number; runId: string; cleanupPromise?: Promise; } interface IActiveFlexRunAuthority { readonly projectId: string; readonly sessionId: string; readonly sessionGenerationId: string; readonly sessionGenerationSequence: number; readonly queueId: string; readonly runId: string; readonly model: Readonly; readonly sessionDepth: number; readonly managedSessionIdentityId: string; readonly providerSessionGeneration: string; readonly supervisor: FlexSupervisor; readonly runtimeAuthority: IControllerRuntimeExitSignal; readonly peerId: string; } interface IFlexDelegationTicketContext { readonly scopeId: string; readonly storageKey: string; readonly sessionId: string; readonly parentSessionId: string; readonly parentSessionGenerationId: string; readonly parentSessionGenerationSequence: number; readonly parentQueueId: string; readonly parentRunId: string; readonly parentToolCallId: string; readonly originParentRunId: string; readonly originParentToolCallId: string; readonly agent: string; readonly depth: number; } interface IFlexDelegationTicket { readonly permissionId: string; readonly permissionKey: string; readonly contextKey: string; readonly context: Readonly; readonly parentAuthority: IActiveFlexRunAuthority; readonly activation: Promise; readonly resolveActivation: (activeArg: boolean) => void; readonly abortController: AbortController; readonly timeout: ReturnType; activationState: 'provisional' | 'active' | 'revoked'; consuming: boolean; } interface IFlexDelegatedRunLease { readonly leaseId: string; readonly context: Readonly>; readonly parentAuthority: IActiveFlexRunAuthority; readonly permissionId: string; readonly model: Readonly; readonly supervisor: FlexSupervisor; readonly runtimeAuthority: IControllerRuntimeExitSignal; readonly peerId: string; readonly abortController: AbortController; state: 'active' | 'terminal-pending' | 'closed'; closeObserved: boolean; terminalObserved: boolean; cleanupCompleted: boolean; cleanupPromise?: Promise; } interface IFlexDelegatedRunLeaseIssuer { readonly supervisor: FlexSupervisor; readonly runtimeAuthority: IControllerRuntimeExitSignal; readonly secret: Buffer; } type TFlexPermissionAuthority = | { readonly type: 'managed'; readonly run: IActiveFlexRunAuthority } | { readonly type: 'delegated'; readonly lease: IFlexDelegatedRunLease }; type TFlexDelegatedHostAuthority = | { readonly type: 'lease'; readonly lease: IFlexDelegatedRunLease; readonly cleanupOnly: boolean; } | { readonly type: 'ticket'; readonly ticket: IFlexDelegationTicket } | { readonly type: 'relationship'; readonly supervisor: FlexSupervisor; readonly runtimeAuthority: IControllerRuntimeExitSignal; readonly peerId: string; readonly projectId: string; readonly parentSessionId: string; }; type TFlexFinishedJobEvent = Extract; type TFlexRunningPromptEvent = Extract; type TFlexFinishedPromptEvent = Omit & { type: 'prompt.finished' }; const isControllerPeer = (valueArg: unknown): valueArg is IControllerPeer => { if (typeof valueArg !== 'object' || valueArg === null) return false; const peer = valueArg as Partial; return typeof peer.id === 'string' && peer.id.length > 0 && typeof peer.context === 'object' && peer.context !== null && peer.context.headers instanceof Headers && typeof peer.context.state === 'object' && peer.context.state !== null; }; const peerFromLocalData = (localDataArg: unknown): IControllerPeer => { if (typeof localDataArg !== 'object' || localDataArg === null) { throw new plugins.typedrequest.TypedResponseError('Browser connection context is unavailable.'); } const peer = (localDataArg as { peer?: unknown }).peer; if (!isControllerPeer(peer)) { throw new plugins.typedrequest.TypedResponseError('Browser connection context is unavailable.'); } return peer; }; const validateRequest = (validatorArg: () => T): T => { try { return validatorArg(); } catch (error) { throw new plugins.typedrequest.TypedResponseError( error instanceof Error ? error.message : 'The request is invalid.', { code: 'invalid_request' }, ); } }; const flexRuntimeId = ( nativeIdArg: string, ): IControllerRuntimeId & { harnessId: 'flex' } => ({ harnessId: 'flex', nativeId: nativeIdArg, }); const flexPromptQueueKey = ( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, queueIdArg: string, ): string => JSON.stringify([ projectIdArg, controllerRuntimeIdKey(flexRuntimeId(sessionIdArg)), generationArg.sessionGenerationId, generationArg.sessionGenerationSequence, queueIdArg, ]); const flexRunKey = ( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, runIdArg: string, ): string => JSON.stringify([ projectIdArg, controllerRuntimeIdKey(flexRuntimeId(sessionIdArg)), generationArg.sessionGenerationId, generationArg.sessionGenerationSequence, runIdArg, ]); const trimOldestMapEntries = ( mapArg: Map, maximumArg: number, ): void => { while (mapArg.size > maximumArg) { const oldest = mapArg.keys().next().value; if (oldest === undefined) return; mapArg.delete(oldest); } }; const toFlexGitWorktreeDescriptor = ( worktreeArg: IGitReversionWorktreeDescriptor, ): IFlexGitWorktreeDescriptor => ({ worktreeId: worktreeArg.worktreeId, projectId: worktreeArg.projectId, sessionId: worktreeArg.sessionId, path: worktreeArg.path, repositoryId: worktreeArg.repositoryId, createdAt: worktreeArg.createdAt, }); const parseModelReference = (referenceArg: string): TControllerModelChoice | undefined => { const separatorIndex = referenceArg.indexOf('/'); if (separatorIndex <= 0 || separatorIndex === referenceArg.length - 1) return undefined; const providerID = referenceArg.slice(0, separatorIndex); const modelID = referenceArg.slice(separatorIndex + 1); if (providerID.length > 256 || modelID.length > 256) return undefined; return { harnessId: 'opencode', providerID, modelID }; }; const openCodeBuiltinSlashCommands: readonly IControllerSlashCommandDescriptor[] = [ { name: 'compact', description: 'Compact the session context.', kind: 'builtin', hints: [], available: true, workspaceReversion: 'not-applicable', metadata: {}, }, { name: 'undo', description: 'Undo the latest session turn and its workspace changes.', kind: 'builtin', hints: [], available: true, workspaceReversion: 'supported', metadata: {}, }, { name: 'redo', description: 'Redo the latest reverted session turn and its workspace changes.', kind: 'builtin', hints: [], available: true, workspaceReversion: 'supported', metadata: {}, }, { name: 'init', description: 'Create or update project agent instructions.', kind: 'builtin', hints: [], available: true, workspaceReversion: 'not-applicable', metadata: {}, }, ]; type TCodexSlashAvailability = 'always' | 'idle'; type TCodexSlashExecution = | { type: 'unsupported' } | { type: 'review' } | { type: 'plan' } | { type: 'fork' } | { type: 'rename' } | { type: 'pwd' } | { type: 'client-action'; action: TControllerSlashClientAction }; interface ICodexSlashCommandDefinition { name: string; description: string; kind: IControllerSlashCommandDescriptor['kind']; hints: string[]; availability: TCodexSlashAvailability; unavailableReason?: string; execution: TCodexSlashExecution; } const codexSlashCommands: readonly ICodexSlashCommandDefinition[] = [ { name: 'model', description: 'Choose the model and reasoning effort for this conversation.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'model' }, }, { name: 'permissions', description: 'Choose what Codex is allowed to do.', kind: 'handler', hints: [], availability: 'always', unavailableReason: 'Codex permissions are not yet configurable from AGL.', execution: { type: 'unsupported' }, }, { name: 'compact', description: 'Summarize the conversation to prevent hitting the context limit.', kind: 'builtin', hints: [], availability: 'idle', unavailableReason: 'Codex 0.155.1 does not return the compaction turn ID, so AGL cannot safely correlate ownership.', execution: { type: 'unsupported' }, }, { name: 'plan', description: 'Switch to Plan mode, optionally starting a planning turn.', kind: 'builtin', hints: ['[prompt]'], availability: 'idle', execution: { type: 'plan' }, }, { name: 'fork', description: 'Fork this conversation into a new conversation.', kind: 'builtin', hints: [], availability: 'idle', execution: { type: 'fork' }, }, { name: 'review', description: 'Review the current changes and find issues.', kind: 'builtin', hints: ['[instructions]'], availability: 'idle', execution: { type: 'review' }, }, { name: 'rename', description: 'Rename the current conversation.', kind: 'builtin', hints: [''], availability: 'always', execution: { type: 'rename' }, }, { name: 'new', description: 'Start a new conversation.', kind: 'handler', hints: [], availability: 'idle', execution: { type: 'client-action', action: 'new' }, }, { name: 'archive', description: 'Archive this conversation.', kind: 'handler', hints: [], availability: 'idle', execution: { type: 'client-action', action: 'archive-confirm' }, }, { name: 'delete', description: 'Permanently delete this conversation.', kind: 'handler', hints: [], availability: 'idle', execution: { type: 'client-action', action: 'delete-confirm' }, }, { name: 'resume', description: 'Open a saved conversation.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'resume' }, }, { name: 'copy', description: 'Copy the latest completed response or plan.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'copy' }, }, { name: 'mention', description: 'Mention a file.', kind: 'handler', hints: [], availability: 'always', unavailableReason: 'AGL\'s server-validated command flow cannot guarantee the direct browser activation required by the file picker yet.', execution: { type: 'unsupported' }, }, { name: 'status', description: 'Show the current session configuration and token usage.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'status' }, }, { name: 'pwd', description: 'Show the current working directory.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'pwd' }, }, { name: 'usage', description: 'View account usage and limits.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'usage' }, }, { name: 'agent', description: 'View and switch between this conversation\'s subagents.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'subagents' }, }, { name: 'subagents', description: 'View and switch between this conversation\'s subagents.', kind: 'handler', hints: [], availability: 'always', execution: { type: 'client-action', action: 'subagents' }, }, { name: 'clear', description: 'Start a new conversation and clear the current view.', kind: 'handler', hints: [], availability: 'idle', execution: { type: 'client-action', action: 'clear' }, }, ]; const codexSlashReversion = { undoAvailable: false, redoAvailable: false, groups: [], } as const; const codexSlashUnavailableReason = ( commandArg: ICodexSlashCommandDefinition, activityArg: ReturnType<CodexClientAdapter['activitySummary']>, ): string | undefined => { if (commandArg.unavailableReason !== undefined) return commandArg.unavailableReason; if (activityArg.writer === 'released') { return 'Resume this conversation in AGL before using composer commands.'; } if (activityArg.writer === 'external') { return 'This conversation is running elsewhere. Wait for that turn to finish or stop it first.'; } if (activityArg.collaborationModeAuthority) { return activityArg.collaborationModeAuthority.status === 'pending' ? 'The Codex collaboration mode is still changing.' : 'The last Codex mode update outcome is unconfirmed. Choose Default or Plan again before continuing.'; } if (commandArg.availability === 'idle' && activityArg.writer !== 'idle') { return 'Requires an idle conversation — wait for the turn or press Stop.'; } return undefined; }; export const settleControllerAttentionLists = async ( permissionsPromiseArg: Promise<IControllerPermission[]>, questionsPromiseArg: Promise<IControllerQuestion[]>, reportErrorArg: (message: string, error: unknown) => void = console.error, ): Promise<{ permissions: IControllerPermission[]; questions: IControllerQuestion[]; }> => { const [permissionsResult, questionsResult] = await Promise.allSettled([ permissionsPromiseArg, questionsPromiseArg, ]); if (permissionsResult.status === 'rejected') { reportErrorArg('Listing pending permissions for session attention failed.', permissionsResult.reason); } if (questionsResult.status === 'rejected') { reportErrorArg('Listing pending questions for session attention failed.', questionsResult.reason); } return { permissions: permissionsResult.status === 'fulfilled' ? permissionsResult.value : [], questions: questionsResult.status === 'fulfilled' ? questionsResult.value : [], }; }; /** * Reduces any failure to the response a client may see. `operationArg` names what failed, for * the failure journal and the log line; it never reaches an unauthenticated client. */ const asTypedResponseError = ( errorArg: unknown, operationArg: string, ): plugins.typedrequest.TypedResponseError => { if (errorArg instanceof plugins.typedrequest.TypedResponseError) return errorArg; if (errorArg instanceof AuthError) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: errorArg.code }); } if (errorArg instanceof ControllerDraftConflictError) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: 'concurrent_change' }); } if (errorArg instanceof ControllerDraftLimitError) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: 'operation_limit' }); } if (errorArg instanceof ControllerHarnessOperationConflictError) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: 'concurrent_change' }); } if (errorArg instanceof ControllerHarnessOperationLimitError) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: 'operation_limit' }); } if (errorArg instanceof ControllerSessionIdentityError) { if (errorArg.code === 'not_found' || errorArg.code === 'tombstoned') { return new plugins.typedrequest.TypedResponseError( 'The requested session was not found.', { code: 'session_not_found' }, ); } if (errorArg.code === 'limit_exceeded') { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: 'operation_limit' }); } if ( errorArg.code === 'concurrent_change' || errorArg.code === 'provider_generation_mismatch' || errorArg.code === 'deleting' ) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: 'concurrent_change' }); } } if ( errorArg instanceof ControllerResourceUnavailableError || errorArg instanceof ControllerProjectHasResourcesError ) { return new plugins.typedrequest.TypedResponseError(errorArg.message, { code: errorArg.code }); } if (errorArg instanceof FlexServiceError) { if (errorArg.code === 'OUTCOME_UNKNOWN') { return new plugins.typedrequest.TypedResponseError( 'The Flex harness operation outcome is unknown. Do not retry it automatically.', { code: 'outcome_unknown' }, ); } return new plugins.typedrequest.TypedResponseError( 'The Flex harness operation failed.', { code: 'flex_operation_failed' }, ); } if (errorArg instanceof FlexProjectionNotFoundError) { return new plugins.typedrequest.TypedResponseError( 'The requested Flex resource was not found.', { code: 'flex_projection_not_found' }, ); } if ( errorArg instanceof FlexProjectionCursorError || errorArg instanceof FlexProjectionStaleCursorError ) { return new plugins.typedrequest.TypedResponseError( 'The Flex projection cursor is invalid or stale.', { code: 'flex_projection_cursor_invalid' }, ); } if (errorArg instanceof FlexProjectionFormatError) { return new plugins.typedrequest.TypedResponseError( 'The Flex projection failed its integrity check.', { code: 'flex_projection_invalid' }, ); } // The wire response stays generic; the cause survives in the log and in the failure journal, // both under the reference the owner UI reads it back with. const reference = controllerFailureJournal.record({ operation: operationArg, cause: errorArg }); console.error(`${currentRuntimeName} operation failed (${operationArg}, ref ${reference}):`, errorArg); return new plugins.typedrequest.TypedResponseError('The controller operation failed.', { code: controllerOperationFailedErrorCode, reference, }); }; const hashResumeToken = (tokenArg: string): string => plugins.crypto.createHash('sha256').update(tokenArg, 'utf8').digest('base64url'); const headerSecretsEqual = (candidateArg: string | null, expectedArg: string): boolean => { if (candidateArg === null) return false; const candidate = Buffer.from(candidateArg, 'utf8'); const expected = Buffer.from(expectedArg, 'utf8'); return candidate.byteLength === expected.byteLength && plugins.crypto.timingSafeEqual(candidate, expected); }; const assertControllerUpgradePreparationRequest: ( requestArg: unknown, ) => asserts requestArg is IReq_ControllerUpgradePrepare['request'] = (requestArg) => { if ( !requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg) || Object.keys(requestArg).length !== 3 || typeof (requestArg as Record<string, unknown>).token !== 'string' || typeof (requestArg as Record<string, unknown>).targetVersion !== 'string' || !Number.isSafeInteger((requestArg as Record<string, unknown>).gracePeriodMs) || ((requestArg as Record<string, unknown>).gracePeriodMs as number) < 1_000 || ((requestArg as Record<string, unknown>).gracePeriodMs as number) > 60 * 60 * 1000 ) throw new plugins.typedrequest.TypedResponseError( 'The upgrade preparation request is invalid.', { code: 'invalid_request' }, ); }; export const isControllerWebSocketRequestAllowed = ( headersArg: Headers, publicOriginArg: string, tlsModeArg: 'local' | 'proxy', trustedProxySecretArg?: string, ): boolean => { const origin = headersArg.get('origin'); if (origin === null) return true; if (origin !== publicOriginArg) return false; if (tlsModeArg === 'local') return true; return headersArg.get('x-forwarded-proto') === 'https' && trustedProxySecretArg !== undefined && headerSecretsEqual( headersArg.get(trustedProxySecretHeader), trustedProxySecretArg, ); }; interface IControllerHarnessSessionEnumeration { openCode: PromiseSettledResult<IControllerSession[]>; flex: PromiseSettledResult<IControllerSession[]>; } interface IControllerHarnessSessionEnumerationTask { task: Promise<IControllerHarnessSessionEnumeration>; invalidated: boolean; settled: boolean; waiterCount: number; } interface ICompleteManagedSessionHarnessScope { harnessId: TControllerSessionHarnessId; liveRuntimeIds: readonly IControllerRuntimeId[]; providerSessionGenerations?: ReadonlyMap<string, string>; flexGenerationFacts?: ReadonlyMap<string, { sessionGenerationId: string; sessionGenerationSequence: number }>; } interface ICompleteFlexSessionListMetadata { liveRuntimeIds: readonly IControllerRuntimeId[]; providerSessionGenerations: ReadonlyMap<string, string>; flexGenerationFacts: ReadonlyMap<string, { sessionGenerationId: string; sessionGenerationSequence: number }>; } interface IRetiredSessionAuthorityToken { readonly key: string; readonly authority: Readonly<{ state: 'definitively_unmanaged' }>; readonly flexEventGeneration?: string; } interface IProjectResourceReconciliationRetry { requestGeneration: number; readonly tokens: Map<string, IRetiredSessionAuthorityToken>; task: Promise<void>; readonly abortController: AbortController; retryWakeController: AbortController; } export type TControllerManagedSessionAuthority = | 'managed' | 'definitively_unmanaged' | 'uncertain'; /** What the controller tells a client about each kind of close it makes itself. */ const controllerBrowserViewCloseReasons: Readonly<Record<TControllerBrowserViewCloseCode, string>> = { [controllerBrowserViewClosedErrorCode]: 'The browser resource is no longer running.', [controllerBrowserViewResourceChangedErrorCode]: 'The browser resource attachment changed.', [controllerBrowserViewFailedErrorCode]: 'The browser resource could not keep its view.', }; export class OpenCodeController { public readonly options: IControllerStartOptions; private readonly startedAt = Date.now(); private lifecycleState: IControllerStatus['lifecycleState'] = 'starting'; private processGroupId = process.pid; private processFingerprint = `unresolved:${process.pid}:${this.startedAt}`; private processIdentity?: IControllerProcessIdentity; private mcpControllerHost?: ControllerMcpHost; private setupRequired = true; private setupCodeExpiresAt?: number; private projectIdsByDirectory = new Map<string, string>(); private readonly managedSessionAuthorities = new Map< string, TControllerResourceSessionAuthoritySnapshot >(); /** Credentials minted for chats this controller generation spawned. Never persisted. */ private readonly mcpCallerRegistry = new ControllerMcpCallerRegistry(); private defaultModels: TControllerModelChoice[] = []; private autoAcceptPermissions = false; private activeBrowserVideoBackend: 'chromium' | 'native' = 'chromium'; // Per-chat yolo switches; deliberately in-memory — the durable default is // the global setting, a chat-level switch lasts for the controller run. private readonly yoloSessionsByProjectKey = new Map<string, { projectId: string; sessionId: IControllerRuntimeId; }>(); private readonly unusedSessionStates = new Map<string, TUnusedSessionState>(); private readonly claimedSessionKeys = new Set<string>(); private claimedSessionEvictionGeneration = 0; /** sha256(token) -> auth payload; memory-only, cleared on stop */ private readonly resumeTokens = new Map<string, { credentialId: string; expiresAt: number }>(); private readonly autoRepliedPermissionIds = new Set<string>(); private readonly flexProviderLogins = new Map<string, IControllerProviderLogin>(); private readonly flexRefreshJobs = new Map<string, IFlexRefreshJobCacheEntry>(); private readonly pendingFlexFinishedJobs = new Map<string, TFlexFinishedJobEvent>(); private readonly pendingFlexFinishedPrompts = new Map<string, TFlexFinishedPromptEvent>(); private readonly activeFlexRuns = new Map<string, IActiveFlexRunAuthority>(); private readonly flexDelegationTicketsByPermission = new Map<string, IFlexDelegationTicket>(); private readonly flexDelegationTicketsByContext = new Map<string, IFlexDelegationTicket>(); private readonly flexDelegatedRunLeases = new Map<string, IFlexDelegatedRunLease>(); private readonly flexDelegatedRunLeasesByRun = new Map<string, IFlexDelegatedRunLease>(); private readonly flexDelegatedRunLeasesBySession = new Map<string, IFlexDelegatedRunLease>(); private flexDelegatedRunLeaseIssuer?: IFlexDelegatedRunLeaseIssuer; private readonly pendingFlexRunCleanups = new Map<string, IPendingFlexRunCleanup>(); private readonly pendingFlexProjectRegistrationOperations = new Map<string, { projectId: string; supervisor: FlexSupervisor; }>(); private readonly flexBrowserChannelAuthorities = new Map<string, { projectId: string; sessionId: string; sessionGenerationId: string; sessionGenerationSequence: number; peerId: string; supervisor: FlexSupervisor; }>(); private readonly currentFlexEventGenerations = new Map<string, string>(); private flexRunAuthorityFenced = false; private flexGlobalCleanupPending = false; private flexGlobalCleanupGeneration = 0; private flexGlobalCleanupPromise?: Promise<void>; private readonly flexModelOptionsByConnection = new Map<string, IFlexConnectionModelOption[]>(); private readonly flexProviderConnectionMutationTails = new Map<string, Promise<void>>(); private readonly flexModelRefreshAdmissionTasks = new Map< string, Promise<IControllerModelRefreshJob> >(); private readonly flexProviderNames = new Map<string, string>(); private readonly pendingOpenCodeControllerEvents = new Map<string, IControllerEvent>(); private readonly childScopesByGeneration = new Map<string, IControllerChildScopeRecord>(); private readonly childScopeGenerationByBinding = new Map<string, string>(); private readonly childScopeOpenTails = new Map<string, Promise<void>>(); private readonly openCodeAutoAcceptExactAdmissions = new Map< string, IHarnessSubmissionAdmissions >(); private readonly openCodeAutoAcceptFamilyAdmissions = new Map< string, IHarnessSubmissionAdmissions >(); private readonly sealedOpenCodeAutoAcceptFamilyKeys = new Set<string>(); private openCodeAutoAcceptAdmissionSealCount = 0; private childScopeCleanupTimer?: ReturnType<typeof setInterval>; private pendingChildScopeEventCount = 0; private pendingChildScopeEventBytes = 0; private readonly latestOpenCodeToolExecutions = new Map< string, ILatestToolExecution >(); private readonly latestOpenCodeReasoningUpdates = new Map<string, ILatestReasoningUpdate>(); private readonly latestOpenCodeTextUpdates = new Map<string, ILatestTextUpdate>(); private readonly latestFlexToolExecutions = new Map<string, ILatestToolExecution>(); private readonly latestFlexReasoningUpdates = new Map<string, ILatestReasoningUpdate>(); private readonly latestFlexTextUpdates = new Map<string, ILatestTextUpdate>(); private readonly pendingFlexControllerEvents = new Map<string, IControllerEvent>(); private readonly pendingFlexAutoAcceptSweeps = new Map<string, IFlexAutoAcceptSweep>(); private openCodeControllerEventDrainTask?: Promise<void>; private openCodeToolStreamEpoch = 0; private openCodeToolStreamRevision = 0; private openCodeMessageStreamEpoch = 0; private openCodeMessageStreamRevision = 0; private flexToolStreamEpoch = 0; private flexToolStreamRevision = 0; private flexMessageStreamEpoch = 0; private flexMessageStreamRevision = 0; private latestFlexToolExecutionBytes = 0; private latestFlexMessageUpdateBytes = 0; private pendingFlexToolEventBytes = 0; private pendingFlexMessageEventBytes = 0; private flexControllerEventDrainTask?: Promise<void>; private flexAutoAcceptDrainTask?: Promise<void>; private runtimeConfig?: Awaited<ReturnType<typeof resolveInitialRuntimeConfig>>; private controllerId?: string; private openCodeRuntimeDirectory?: string; private authStore?: SmartDataAuthStore; private passkeyManager?: PasskeyManager; private openCodeSupervisor?: OpenCodeSupervisor; private openCodeClient?: OpenCodeClientAdapter; private flexSupervisor?: FlexSupervisor; private codexSupervisor?: CodexSupervisor; private codexConnections?: CodexConnections; private codexDefaultProfileId?: string; private codexLocalProfileId?: string; private codexStartupDiagnostic?: codexInterfaces.IControllerCodexDiagnostic; private codexClient?: CodexClientAdapter; private codexCreationAdmissions = 0; private codexRestartTask?: Promise<void>; private codexRestartProfileId?: string; private readonly codexCleanupTasks = new Map<string, Promise<void>>(); private readonly codexRecoveryDiagnostics = new Map<string, codexInterfaces.IControllerCodexDiagnostic>(); private readonly queuedCodexPrompts = new Map<string, IQueuedOpenCodePromptSubmission>(); private readonly codexQueueDrains = new Map<string, Promise<void>>(); private readonly pausedCodexQueues = new Set<string>(); private readonly codexCreationFinalizations = new Map<string, Promise<string | undefined>>(); private readonly codexPendingEvents = new Map<string, IControllerEvent>(); private codexEventDrain?: Promise<void>; private readonly openCodeSupervisorExitSignals = new WeakMap< OpenCodeSupervisor, IControllerRuntimeExitSignal >(); private readonly flexSupervisorExitSignals = new WeakMap< FlexSupervisor, IControllerRuntimeExitSignal >(); private flexProjectionReader?: FlexProjectionReader; private gitReversion?: ControllerGitReversion; private readonly pendingProjectRemovalTasks = new Map<string, Promise<void>>(); private readonly projectResourceReconciliationRetries = new Map< string, IProjectResourceReconciliationRetry >(); private flexProjectionReaderReady = false; private flexServiceInit?: IFlexServiceInit; private flexRecoveryReady = false; private flexUnsupported = false; private flexRestartAttempt = 0; private flexRestartTimer?: ReturnType<typeof setTimeout>; private flexBootStartupBudgetMs = controllerFlexBootStartupBudgetMs; private gitReversionTemporaryRoot?: string; private gitReversionReplacement?: Promise<void>; private gitReversionReplacementTimer?: ReturnType<typeof setTimeout>; private gitReversionReplacementAttempt = 0; private flexRestartTask?: Promise<void>; private flexHarnessAdmissionFenceGeneration?: number; private flexChildExitCleanupPending = false; private flexChildExitCleanupTask?: Promise<void>; private flexGenerationCleanup?: { supervisor: FlexSupervisor; task: Promise<void>; }; private readonly plannedFlexSupervisorStops = new Set<FlexSupervisor>(); private flexMigrationDatabase?: plugins.smartdata.SmartdataDb; private flexMigrationStore?: FlexStore; private flexMigrationCleanupPromise?: Promise<void>; private controllerProjectMutationTail = Promise.resolve(); private flexProjectReconciliationRetryTask?: Promise<void>; private unsubscribeFlexEvents?: () => void; private terminalManager?: ControllerTerminalManager; private terminalResourceHost?: ControllerTerminalResourceHost; private browserResourceHost?: ControllerBrowserResourceHost; private resourceCoordinator?: ControllerResourceCoordinator; private readonly browserViews = new Map<string, IControllerBrowserView>(); private readonly browserViewSlots = new Map<string, IControllerBrowserViewSlot>(); private readonly authenticatedPeerIds = new Set<string>(); private readonly authenticatedPeerGenerations = new WeakMap<IControllerPeer, symbol>(); private openCodeResourceGeneration = 0; private flexResourceGeneration = 0; private typedServer?: plugins.typedserver.TypedServer; private unsubscribeConnectionClose?: () => void; private eventStreamTask?: Promise<void>; private openCodeFailClosedTask?: Promise<void>; private openCodeFailClosedRetryTimer?: ReturnType<typeof setTimeout>; private openCodeRestartAttempt = 0; private openCodeRestartTimer?: ReturnType<typeof setTimeout>; private openCodeRestartTask?: Promise<void>; private openCodeRetainedCleanupAttempt = 0; private openCodeRetainedCleanupTimer?: ReturnType<typeof setTimeout>; private openCodeRetainedCleanupTask?: Promise<void>; private openCodeCrashRecovery?: IOpenCodeCrashRecoveryContext; private openCodeHarnessAdmissionFenceGeneration?: number; private openCodeRuntimeMutationTail = Promise.resolve(); private flexRunCleanupRetryTimer?: ReturnType<typeof setTimeout>; private startPromise?: Promise<IControllerStartResult>; private stopPromise?: Promise<void>; private stopRequested = false; private controllerStartedRecorded = false; private controllerStoppedRecorded = false; private readonly operationAbortController = new AbortController(); private readonly activeOperations = new Set<Promise<unknown>>(); private readonly harnessSessionEnumerationTasks = new Map< string, IControllerHarnessSessionEnumerationTask >(); private readonly completeHarnessSessionRuntimeIdsByList = new WeakMap< IControllerSession[], ICompleteFlexSessionListMetadata >(); private readonly sessionLayoutOperationTails = new Map<string, Promise<void>>(); private readonly sessionLayoutOperationDepths = new Map<string, number>(); private readonly sessionStateMutationTails = new Map<string, Promise<void>>(); private readonly harnessOperationManager: ControllerHarnessOperationManager; private upgradeCoordinator?: UpgradeCoordinator; private upgradeToken?: string; private upgradeStatus?: IControllerUpgradeStatus; private upgradeBarrier?: IControllerHarnessOperationBarrier; private openCodeAccountSwitchBarrier?: IControllerHarnessOperationBarrier; private openCodeAccountSwitchTask?: Promise<unknown>; private authSwitchHarnesses?: readonly plugins.authswitch.IAuthHarness[]; /** Serializes controller-run credential operations; authswitch's store lock is exclusive. */ private authSwitchCredentialTail: Promise<void> = Promise.resolve(); private authSwitchService?: plugins.authswitch.AuthSwitchService; private openCodeAccountSwitchAdmissionSealed = false; private selectedOpenCodeProviderConnectionId?: string; private readonly plannedOpenCodeSupervisorStops = new Set<OpenCodeSupervisor>(); private readonly retainedOpenCodeRuntimeCleanup = new Map< OpenCodeSupervisor, IControllerOwnedOpenCodeRuntime | undefined >(); private openCodeRuntimeAdmissionSealed = false; private sessionIntelligenceFlexAdmissionSealed = true; private openCodeRuntimeLeaseCount = 0; private openCodeRuntimeLeaseDrained?: { promise: Promise<void>; resolve: () => void; }; private readonly openCodeRuntimeLeaseReleases = new Set<() => void>(); private upgradePreparationAbortController?: AbortController; private upgradePreparationTask?: Promise<void>; private upgradePreparationAdmissionTail = Promise.resolve(); private upgradeFinalizeTail = Promise.resolve(); private upgradeWorkerWatchdog?: ReturnType<typeof setInterval>; private upgradeWorkerWatchdogTask?: Promise<void>; private retainedUpgradeOwnershipTask?: Promise<void>; private retainedUpgradeLock?: UpgradeInstallationLock; private retainedUpgradeReleaseTask?: Promise<void>; private readonly activeSessionBackgroundOperations = new Map< string, IActiveSessionBackgroundOperation >(); private readonly openCodeOperationMonitors = new Map<string, IOpenCodeOperationMonitor>(); private readonly queuedOpenCodePromptSubmissions = new Map< string, IQueuedOpenCodePromptSubmission >(); private readonly openCodePromptQueueDrainTasks = new Map<string, Promise<void>>(); private readonly openCodePromptQueueFenceCounts = new Map<string, number>(); private readonly openCodeSessionErrorCleanupTasks = new Map<string, Promise<void>>(); private readonly sessionIntelligenceJobs = new Map<string, ISessionIntelligenceJob>(); private readonly sessionIntelligenceCapabilities = new Map< string, ISessionIntelligenceCapability >(); private readonly projectSessionIntelligenceAdmissions = new Map< string, IProjectSessionIntelligenceAdmissions >(); private readonly sessionIntelligenceAdmissions = new Map< string, IProjectSessionIntelligenceAdmissions >(); private readonly retiringProjectIds = new Set<string>(); private readonly retiringSessionKeys = new Set<string>(); private readonly archivingSessionKeys = new Set<string>(); private readonly archivePreflightReads = new Map<string, Promise<IControllerProjectDocument>>(); private readonly abortingSessionKeys = new Set<string>(); private readonly projectArchiveAdmissions = new Map<string, IHarnessSubmissionAdmissions>(); private readonly projectHarnessSubmissionAdmissions = new Map< string, IHarnessSubmissionAdmissions >(); private readonly flexProjectHostRequestAdmissions = new Map< string, IHarnessSubmissionAdmissions >(); private readonly flexSessionHostRequestAdmissions = new Map< string, IHarnessSubmissionAdmissions >(); private readonly sealedFlexProjectHostRequestAdmissions = new Set<string>(); private readonly sealedFlexSessionHostRequestAdmissions = new Set<string>(); private readonly flexProjectIdentityRecoveryPending = new Set<string>(); private readonly sessionHarnessSubmissionAdmissions = new Map< string, IHarnessSubmissionAdmissions >(); private activeSessionIntelligenceJobCount = 0; private sessionIntelligenceCleanupTimer?: ReturnType<typeof setInterval>; private sessionIntelligenceCleanupTask?: Promise<void>; private readonly systemMetricsSampler = new SystemMetricsSampler(); /** * The account authority view. Supervised from `start()`, so a host whose daemon is not running -- * or a controller that was given no endpoints at all -- holds a named `absent` state instead of * failing startup. Built in the constructor because it reads `options`. */ private readonly authorityState: AuthorityState; private readonly activeOperationCountsByPeer = new Map<string, number>(); private protectedOperationWindowStartedAt = Date.now(); private protectedOperationCount = 0; private readonly protectedOperationCountsByPeer = new Map<string, number>(); private unauthenticatedAuditWindowStartedAt = Date.now(); private unauthenticatedAuditCount = 0; private readonly unauthenticatedAuditCountsByPeer = new Map<string, number>(); private unauthenticatedPreflightWindowStartedAt = Date.now(); private unauthenticatedPreflightCount = 0; private readonly unauthenticatedPreflightCountsByPeer = new Map<string, number>(); constructor(optionsArg: IControllerStartOptions) { this.options = optionsArg; this.authorityState = new AuthorityState({ ...(optionsArg.accountAuthority ? { paths: optionsArg.accountAuthority } : {}), onChange: () => { void this.pushControllerEvent({ type: 'accounts.changed', timestamp: Date.now() }) .catch(() => undefined); }, }); this.harnessOperationManager = new ControllerHarnessOperationManager({ onDraftChanged: (projectIdArg, sessionIdArg, updateArg) => { this.pushSessionDraftChanged(projectIdArg, sessionIdArg, updateArg); }, onQueuedOperationCancelled: (reservationArg) => { this.queuedOpenCodePromptSubmissions.delete(reservationArg.operationId); this.queuedCodexPrompts.delete(reservationArg.operationId); }, onOperationReleased: (reservationArg) => { this.handleHarnessOperationReleased(reservationArg); }, }); } private captureInheritedUpgradeCoordination(): void { try { if (Boolean(this.options.upgradeToken) !== Boolean(this.options.upgradeGlobalRoot)) { throw new Error('Inherited upgrade coordination requires both its token and global root.'); } if (!this.options.upgradeToken || !this.options.upgradeGlobalRoot) return; if (process.env[upgradeTokenEnvironmentVariable] !== this.options.upgradeToken) { throw new Error('The inherited upgrade coordination token changed before controller startup.'); } this.upgradeToken = this.options.upgradeToken; this.upgradeCoordinator = new UpgradeCoordinator(this.options.upgradeGlobalRoot); } finally { delete process.env[upgradeTokenEnvironmentVariable]; delete process.env[upgradeCoordinationRootEnvironmentVariable]; } } public async start(): Promise<IControllerStartResult> { if (this.lifecycleState === 'ready') { return { status: this.getStatus(), publicUrl: this.requireRuntimeConfig().publicOrigin }; } if (this.startPromise) return this.startPromise; if (this.stopRequested || this.lifecycleState === 'stopping') { throw new Error('The controller cannot start after shutdown has begun.'); } const startPromise = this.performStart(); this.startPromise = startPromise; let startError: unknown; try { return await startPromise; } catch (error) { startError = error; } finally { if (this.startPromise === startPromise) this.startPromise = undefined; } try { await this.stop(); } catch (cleanupError) { throw new AggregateError( [startError, cleanupError], 'Controller startup failed and its resources could not be released cleanly.', ); } throw startError; } public async stop(): Promise<void> { this.stopRequested = true; this.mcpControllerHost?.beginShutdown(); if (this.stopPromise) return this.stopPromise; const activeStart = this.startPromise; const stopPromise = (async () => { const errors: unknown[] = []; if (this.mcpControllerHost) { const host = this.mcpControllerHost; try { await host.close(); if (this.mcpControllerHost === host) { this.mcpControllerHost = undefined; } } catch (errorArg) { errors.push(errorArg); } } if (!this.operationAbortController.signal.aborted) { this.operationAbortController.abort(new Error('Controller shutdown has begun.')); } try { await this.closeFlexMigrationResources(); } catch (errorArg) { errors.push(errorArg); } if (activeStart) await activeStart.catch(() => undefined); try { await this.performStop(); } catch (errorArg) { errors.push(errorArg); } if (errors.length === 1) throw errors[0]; if (errors.length > 1) { throw new AggregateError(errors, 'Controller migration and runtime cleanup failed.'); } })(); this.stopPromise = stopPromise; try { await stopPromise; } finally { if (this.stopPromise === stopPromise) this.stopPromise = undefined; } } public getStatus(): IControllerStatus { const openCodeStatus = this.openCodeSupervisor?.getStatus() ?? { state: 'stopped' as const, healthy: false, }; const flexStatus = this.flexSupervisor?.getStatus() ?? { state: 'stopped' as const, ready: false, }; const flexOperational = flexStatus.state === 'ready' && flexStatus.ready && this.flexRecoveryReady; return { packageName: controllerPackageName, packageVersion: commitinfo.version, protocolVersion: controllerProtocolVersion, upgradeManagementVersion: controllerUpgradeManagementVersion, controllerPid: process.pid, processGroupId: this.processGroupId, processFingerprint: this.processFingerprint, processMode: this.options.processMode, lifecycleState: this.lifecycleState, startedAt: this.startedAt, setupRequired: this.setupRequired, ...(this.upgradeStatus ? { upgrade: { ...this.upgradeStatus } } : {}), harnesses: [ { harnessId: 'opencode', state: openCodeStatus.state, healthy: openCodeStatus.healthy, ...(openCodeStatus.pid ? { pid: openCodeStatus.pid } : {}), ...(openCodeStatus.version ? { version: openCodeStatus.version } : {}), }, { harnessId: 'flex', state: flexStatus.state === 'unsupported' ? 'failed' : flexStatus.state === 'ready' && !this.flexRecoveryReady ? 'starting' : flexStatus.state, healthy: flexOperational, ...(flexStatus.pid ? { pid: flexStatus.pid } : {}), }, { harnessId: 'codex', ...(this.codexStartupDiagnostic ? { state: 'failed' as const, healthy: false, diagnostic: this.codexStartupDiagnostic } : this.codexSupervisor?.getStatus() ?? { state: 'stopped' as const, healthy: false }), supportsLocalAttachments: this.codexSupervisor?.supportsLocalAttachments === true }, ], }; } private statusForPrincipal(principalArg: TControllerOperationPrincipal): IControllerStatus { this.assertControllerOperationPrincipal(principalArg); return this.getStatus(); } private async listProjectsForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, signalArg: AbortSignal, ): Promise<IReq_ControllerMcpProjectsList['response']> { signalArg.throwIfAborted(); return this.runAudited( { type: 'project.list', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => ({ projects: (await this.requireAuthStore().listProjects()) .map((project) => this.toPublicProject(project)), }), ); } private requireCodexClient(nativeIdArg?: string): CodexClientAdapter { if (this.codexStartupDiagnostic) throw new Error(this.codexStartupDiagnostic.message); if (nativeIdArg && this.codexConnections) return this.codexConnections.clientForSession(nativeIdArg); const client = this.codexClient; if (!client || !this.codexSupervisor?.getStatus().healthy) throw new plugins.typedrequest.TypedResponseError( this.codexSupervisor?.getStatus().diagnostic?.message ?? 'Codex is unavailable. Open Settings → Codex and restart the connection.', { code: 'harness_unavailable' }); client.supervisor.signal.throwIfAborted(); return client; } private codexDirectory(nativeIdArg: string, legacyDirectoryArg: string): string { return this.codexConnections ? this.codexConnections.origin(nativeIdArg).remoteDirectory : legacyDirectoryArg; } private async codexConnectionForProject(projectIdArg: string): Promise<{ client: CodexClientAdapter; profileId: string; remoteDirectory: string }> { const project = await this.resolveProject(projectIdArg); if (!this.codexConnections || !this.codexDefaultProfileId) throw new Error('Codex connection profiles are not initialized.'); const store = this.requireAuthStore().requireCodexConnections(); let mapping = await store.mapping(projectIdArg); if (!mapping) mapping = await store.setMapping(projectIdArg, this.codexDefaultProfileId, project.directory); const profile = await store.get(mapping.profileId); if (profile.state !== 'active') throw new Error('Select an active Codex connection for this project before creating a conversation.'); const runtime = await this.codexConnections.connect(mapping.profileId); return { client: runtime.client, profileId: mapping.profileId, remoteDirectory: profile.mode === 'local' ? project.directory : mapping.remoteDirectory }; } private async releaseCodexRuntimeOperations(profileIdArg: string): Promise<void> { for (const operation of this.harnessOperationManager.listHarnessActive('codex')) { if (operation.state === 'queued' || this.codexConnections?.origin(operation.reservation.sessionId.nativeId).profileId !== profileIdArg) continue; await this.harnessOperationManager.complete(operation.reservation); } } private async codexComposerContext(projectIdArg?: string, sessionIdArg?: IControllerRuntimeId): Promise<{ context: codexInterfaces.IControllerCodexContext; client?: CodexClientAdapter }> { if (!this.codexDefaultProfileId || !this.codexConnections) throw new Error('Codex connection profiles are unavailable.'); const store = this.requireAuthStore().requireCodexConnections(); let profileId = this.codexDefaultProfileId; let remoteDirectory = this.requireProjectsRoot(); if (projectIdArg) { const project = await this.resolveProject(projectIdArg); if (sessionIdArg?.harnessId === 'codex') { const origin = await this.requireAuthStore().getCodexOrigin(projectIdArg, sessionIdArg.nativeId, this.operationAbortController.signal); this.codexConnections.bind(projectIdArg, sessionIdArg.nativeId, origin); profileId = origin.profileId; remoteDirectory = origin.remoteDirectory; } else { const mapping = await store.mapping(projectIdArg); profileId = mapping?.profileId ?? profileId; remoteDirectory = mapping?.remoteDirectory ?? project.directory; } } const profile = await store.get(profileId); try { if (sessionIdArg?.harnessId !== 'codex' && profile.state !== 'active') throw new Error('Select an active Codex connection for new conversations.'); const runtime = await this.codexConnections.connect(profileId); return { client: runtime.client, context: { profileId, remoteDirectory, status: { harnessId: 'codex', ...runtime.supervisor.getStatus(), supportsLocalAttachments: runtime.supervisor.supportsLocalAttachments } } }; } catch (error) { return { context: { profileId, remoteDirectory, status: { harnessId: 'codex', state: 'failed', healthy: false, connectionMode: profile.mode, supportsLocalAttachments: profile.mode === 'local', diagnostic: codexDiagnostic(error, 'connection') } } }; } } private handleCodexUnavailable(runtimeArg: ICodexConnectionRuntime): void { if (!this.codexConnections?.isCurrent(runtimeArg)) return; void this.pushControllerEvent({ type: 'harness.changed', harnessId: 'codex', timestamp: Date.now() }).catch(() => undefined); if (this.lifecycleState !== 'ready' || this.stopRequested || this.codexRestartProfileId === runtimeArg.profile.id) return; const cleanup = this.trackOperationPromise(Promise.resolve().then(async () => { await runtimeArg.supervisor.stop(); await this.releaseCodexRuntimeOperations(runtimeArg.profile.id); })); this.codexCleanupTasks.set(runtimeArg.profile.id, cleanup); void cleanup.finally(() => { if (this.codexCleanupTasks.get(runtimeArg.profile.id) === cleanup) this.codexCleanupTasks.delete(runtimeArg.profile.id); }).catch(() => console.error('Codex generation cleanup is incomplete.')); } private queueCodexPromptIfBlocked(submissionArg: IQueuedOpenCodePromptSubmission): boolean { this.harnessOperationManager.assertCurrentHarnessAdmission(submissionArg.operation); if (!this.harnessOperationManager.queuePromptIfBlocked(submissionArg.operation)) return false; this.queuedCodexPrompts.set(submissionArg.operation.operationId, { ...submissionArg, ...(submissionArg.model ? { model: { ...submissionArg.model } } : {}) }); try { this.harnessOperationManager.acceptQueued(submissionArg.operation); } catch (error) { this.queuedCodexPrompts.delete(submissionArg.operation.operationId); throw error; } this.queueHarnessPromptChanged(submissionArg.operation.projectId, submissionArg.operation.sessionId); this.scheduleQueuedCodexPrompt(submissionArg.operation.projectId, submissionArg.operation.sessionId); return true; } private codexQueueCanDispatch(projectIdArg: string, sessionIdArg: IControllerRuntimeId): boolean { const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); return this.lifecycleState === 'ready' && !this.stopRequested && !this.codexRestartTask && (this.codexConnections ? this.codexConnections.runtime(this.codexConnections.origin(sessionIdArg.nativeId).profileId)?.supervisor.getStatus().healthy === true : this.codexSupervisor?.getStatus().healthy === true) && !this.operationAbortController.signal.aborted && !this.retiringProjectIds.has(projectIdArg) && !this.retiringSessionKeys.has(key) && !this.archivingSessionKeys.has(key) && !this.abortingSessionKeys.has(key) && !this.pausedCodexQueues.has(key) && this.requireCodexClient(sessionIdArg.nativeId).canDispatchModeDependent(sessionIdArg.nativeId); } private scheduleQueuedCodexPrompt(projectIdArg: string, sessionIdArg: IControllerRuntimeId): void { const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (!this.codexQueueCanDispatch(projectIdArg, sessionIdArg) || this.codexQueueDrains.has(key)) return; const head = this.harnessOperationManager.getActive(projectIdArg, sessionIdArg); if (head?.state !== 'queued') return; const queued = this.queuedCodexPrompts.get(head.reservation.operationId); if (!queued) return; const task = this.trackOperationPromise(this.dispatchQueuedCodexPrompt(queued)); this.codexQueueDrains.set(key, task); void task.finally(() => { if (this.codexQueueDrains.get(key) !== task) return; this.codexQueueDrains.delete(key); this.scheduleQueuedCodexPrompt(projectIdArg, sessionIdArg); }).catch(() => console.error('Codex queued prompt dispatch failed.')); } private async dispatchQueuedCodexPrompt(queuedArg: IQueuedOpenCodePromptSubmission): Promise<void> { const { operation, prompt, model } = queuedArg; const { projectId, sessionId } = operation; const key = this.sessionStateMutationKey(projectId, sessionId); let begun = false; let observeOutcomeUnknown: (() => void) | undefined; try { await this.withHarnessSubmissionAdmission(projectId, sessionId, this.operationAbortController.signal, async () => { if (!this.codexQueueCanDispatch(projectId, sessionId)) return; const client = this.requireCodexClient(sessionId.nativeId); observeOutcomeUnknown = () => client.observeSubmissionOutcomeUnknown(sessionId.nativeId, `agl_${operation.operationId}`); if (model && model.harnessId !== 'codex') throw new Error('The queued model belongs to another harness.'); this.harnessOperationManager.assertCurrentHarnessAdmission(operation); this.harnessOperationManager.beginQueuedSubmission(operation); begun = true; const arm = await client.startTurn(sessionId.nativeId, prompt, `agl_${operation.operationId}`, model, async () => { this.assertSessionMutationAdmission(projectId, sessionId); await this.requireAuthStore().prepareCodexTurn(projectId, sessionId.nativeId); }, () => { void this.trackOperationPromise(this.harnessOperationManager.complete(operation)).catch(() => console.error('Codex queued prompt cleanup failed.')); }, this.operationAbortController.signal, id => this.harnessOperationManager.bindCodexMessage(operation, codexRuntimeId(id)), () => this.requireAuthStore().cancelUndispatchedCodexTurn(projectId, sessionId.nativeId)); await this.requireAuthStore().markCodexMaterialized(projectId, sessionId.nativeId); this.harnessOperationManager.accept(operation); this.queuedCodexPrompts.delete(operation.operationId); arm(); this.queueHarnessPromptChanged(projectId, sessionId); }); } catch (error) { // Accepted queue text and upload leases stay owned if no native frame left. this.pausedCodexQueues.add(key); if (begun && this.harnessOperationManager.getOperation(operation)) { if (error instanceof plugins.crossharness.CodexAppServerRequestError && !error.dispatched) this.harnessOperationManager.returnUndispatchedToQueue(operation); else { this.harnessOperationManager.markOutcomeUnknown(operation); observeOutcomeUnknown?.(); } } this.queueHarnessPromptChanged(projectId, sessionId); } } private restartCodexRuntime(profileIdArg = this.codexDefaultProfileId, updateArg?: () => Promise<void>): Promise<void> { if (this.codexRestartTask) { if (updateArg || this.codexRestartProfileId !== profileIdArg) throw new Error('Another Codex connection is restarting. Wait for it to finish.'); return this.codexRestartTask; } if (this.upgradeBarrier || this.codexCreationAdmissions > 0 || this.stopRequested) throw new Error('Wait for conversation creation or the current controller lifecycle operation before restarting Codex.'); if (updateArg && (!this.codexConnections || this.codexStartupDiagnostic)) throw new Error('Restore Codex connections before replacing a credential.'); const barrier = this.harnessOperationManager.beginOperationBarrier('codex'); const connections = this.codexConnections; const task = Promise.resolve().then(async () => { if (!connections || this.codexStartupDiagnostic) { await this.runControllerProjectMutation(async () => { await this.initializeCodexRuntime(); this.codexConnections!.require(profileIdArg ?? this.codexDefaultProfileId); }); return; } if (!profileIdArg) throw new Error('Select a Codex connection to restart.'); // A failed cleanup stays owned by the supervisor; explicit restart retries it. await this.codexCleanupTasks.get(profileIdArg)?.catch(() => undefined); await connections.disconnect(profileIdArg); await Promise.allSettled([...this.codexQueueDrains.values()]); await this.releaseCodexRuntimeOperations(profileIdArg); await updateArg?.(); await connections.connect(profileIdArg); if (profileIdArg === this.codexDefaultProfileId) { this.codexSupervisor = connections.require(profileIdArg).supervisor; this.codexClient = connections.require(profileIdArg).client; } await this.recoverCodexProfile(profileIdArg); for (const queued of this.queuedCodexPrompts.values()) if (connections.origin(queued.operation.sessionId.nativeId).profileId === profileIdArg) { this.pausedCodexQueues.delete(this.sessionStateMutationKey(queued.operation.projectId, queued.operation.sessionId)); } }); this.codexRestartTask = task; this.codexRestartProfileId = profileIdArg; void task.finally(() => { if (profileIdArg === this.codexDefaultProfileId && !this.codexStartupDiagnostic) { const runtime = this.codexConnections?.runtime(profileIdArg); this.codexSupervisor = runtime?.supervisor; this.codexClient = runtime?.client; } this.harnessOperationManager.endOperationBarrier(barrier); if (this.codexRestartTask === task) { this.codexRestartTask = undefined; this.codexRestartProfileId = undefined; } for (const queued of this.queuedCodexPrompts.values()) this.scheduleQueuedCodexPrompt(queued.operation.projectId, queued.operation.sessionId); void this.pushControllerEvent({ type: 'harness.changed', harnessId: 'codex', timestamp: Date.now() }).catch(() => undefined); }).catch(() => undefined); return task; } private queueCodexEvent(eventArg: IControllerEvent): void { const key = JSON.stringify([eventArg.type, eventArg.projectId, eventArg.sessionId, eventArg.toolExecution?.partId ?? eventArg.textUpdate?.partId ?? eventArg.reasoningUpdate?.partId]); this.codexPendingEvents.set(key, eventArg); const bytes = [...this.codexPendingEvents.values()].reduce((sum, event) => sum + Buffer.byteLength(JSON.stringify(event), 'utf8'), 0); if (this.codexPendingEvents.size > 512 || bytes > 8 * 1024 * 1024) { this.codexPendingEvents.clear(); if (eventArg.sessionId) this.requireCodexClient(eventArg.sessionId.nativeId).supervisor.requireClient().close('Codex event delivery exceeded its bounded capacity.'); return; } if (this.codexEventDrain) return; const task = this.trackOperationPromise((async () => { while (this.codexPendingEvents.size > 0 && !this.operationAbortController.signal.aborted) { const [nextKey, next] = this.codexPendingEvents.entries().next().value!; this.codexPendingEvents.delete(nextKey); await this.pushControllerEvent(next); } })()); this.codexEventDrain = task; void task.finally(() => { if (this.codexEventDrain === task) this.codexEventDrain = undefined; const next = this.codexPendingEvents.values().next().value; if (next && !this.operationAbortController.signal.aborted) this.queueCodexEvent(next); }).catch(() => console.error('Codex event delivery failed.')); } private async initializeCodexRuntime(projectsArg?: IControllerProjectDocument[], pendingRemovalsArg?: IControllerProjectDocument[]): Promise<void> { try { await this.codexConnections?.close(); this.codexConnections = undefined; this.codexSupervisor = undefined; this.codexClient = undefined; this.codexStartupDiagnostic = undefined; await this.startCodexRuntime(projectsArg, pendingRemovalsArg); this.codexStartupDiagnostic = undefined; } catch (error) { this.codexStartupDiagnostic = codexDiagnostic(error, 'recovery'); this.codexSupervisor = undefined; this.codexClient = undefined; try { await this.codexConnections?.close(); this.codexConnections = undefined; } catch { this.codexStartupDiagnostic = codexDiagnostic(new Error('Codex cleanup is incomplete.'), 'cleanup'); } this.operationAbortController.signal.throwIfAborted(); throw new Error(this.codexStartupDiagnostic.message); } } private async startCodexRuntime(projectsArg?: IControllerProjectDocument[], pendingRemovalsArg?: IControllerProjectDocument[]): Promise<void> { const store = this.requireAuthStore(); const projects = projectsArg ?? await store.listProjects(); const pendingRemovals = pendingRemovalsArg ?? await store.listPendingProjectRemovals(); const profiles = await migrateCodexOriginsV29({ connections: store.requireCodexConnections(), projects, pendingRemovalProjects: pendingRemovals, legacyServerUrl: process.env.HARNESS_CONTROLLER_CODEX_SERVER_URL, legacyToken: process.env.HARNESS_CONTROLLER_CODEX_SERVER_TOKEN, signal: this.operationAbortController.signal }); this.codexDefaultProfileId = profiles.defaultProfileId; this.codexLocalProfileId = profiles.localProfileId; const connections = new CodexConnections({ store: store.requireCodexConnections(), directory: this.requireProjectsRoot(), executable: process.env.HARNESS_CONTROLLER_CODEX_EXECUTABLE, defaultProfileId: profiles.defaultProfileId, onUnavailable: runtime => this.handleCodexUnavailable(runtime), onEvent: event => { if (event.projectId) this.invalidateHarnessSessionEnumeration(event.projectId); this.queueCodexEvent(event); if (event.type === 'session.changed' && event.projectId && event.sessionId?.harnessId === 'codex') { this.scheduleQueuedCodexPrompt(event.projectId, event.sessionId); } if (event.type === 'permissions.changed' && event.projectId && event.sessionId && this.isProjectAutoAcceptedSession(event.projectId, event.sessionId)) this.queueCodexAutoAccept(event.projectId, event.sessionId); }, }); this.codexConnections = connections; const required = new Set([profiles.localProfileId, profiles.defaultProfileId]); const pendingIds = new Set(pendingRemovals.map(project => project.id)); for (const project of [...projects, ...pendingRemovals]) { const pending = pendingIds.has(project.id); const mapping = pending ? undefined : await store.requireCodexConnections().mapping(project.id); if (mapping) required.add(mapping.profileId); const memberships = await (pending ? store.listCodexMembershipsForProjectRemoval(project.id, this.operationAbortController.signal) : store.listCodexMemberships(project.id, this.operationAbortController.signal)); for (const membership of memberships) { if (!membership.codexOrigin) throw new Error('Codex origin migration left an active conversation unbound.'); connections.bind(project.id, membership.runtimeId.nativeId, membership.codexOrigin); required.add(membership.codexOrigin.profileId); } const intents = await (pending ? store.listCodexCreationsForProjectRemoval(project.id, this.operationAbortController.signal) : store.listCodexCreations(project.id, this.operationAbortController.signal)); for (const intent of intents) { if (intent.connection) required.add(intent.connection.profileId); if (intent.nativeId && intent.codexOrigin) connections.bind(project.id, intent.nativeId, intent.codexOrigin); } } const ids = [...required]; for (let index = 0; index < ids.length; index += 4) await Promise.allSettled(ids.slice(index, index + 4).map(id => connections.connect(id))); const defaultRuntime = connections.runtime(profiles.defaultProfileId); this.codexSupervisor = defaultRuntime?.supervisor; this.codexClient = defaultRuntime?.client; for (const runtime of connections.all()) if (runtime.supervisor.getStatus().healthy) { try { await this.recoverCodexProfile(runtime.profile.id); } catch { this.operationAbortController.signal.throwIfAborted(); } } } private async recoverCodexProfile(profileIdArg: string): Promise<void> { const runtime = this.codexConnections!.require(profileIdArg); try { await this.recoverCodexRuntimeObligations(profileIdArg); } catch (error) { if (runtime.supervisor.getStatus().healthy) runtime.supervisor.requireClient().close('Codex conversation authority recovery failed.'); try { await runtime.supervisor.stop(); } catch { throw new Error(codexDiagnostic(new Error('Codex recovery cleanup is incomplete.'), 'cleanup').message); } throw new Error(codexDiagnostic(error, 'recovery').message); } } private async recoverCodexRuntimeObligations(profileIdArg: string): Promise<void> { const runtime = this.codexConnections!.require(profileIdArg); for (const project of await this.requireAuthStore().listProjects()) { const deletions = await this.requireAuthStore().listManagedSessionDeletionObligations({ projectIdentityId: project.id, harnessId: 'codex', signal: this.operationAbortController.signal }); for (const deletion of deletions) { if (this.codexConnections!.origin(deletion.runtimeId.nativeId).profileId !== profileIdArg) continue; const key = this.projectRuntimeIdKey(project.id, deletion.runtimeId); try { if (deletion.dispatchStartedAt === undefined) { await this.requireAuthStore().cancelManagedSessionDeletion({ projectIdentityId: project.id, runtimeId: deletion.runtimeId, operationId: deletion.operationId, cancelledAt: new Date() }); } else { if (await this.assertManagedDeletionProviderGenerationCurrent(project.id, project.directory, deletion, this.operationAbortController.signal)) { await runtime.client.deleteThread(this.codexDirectory(deletion.runtimeId.nativeId, project.directory), deletion.runtimeId.nativeId, this.operationAbortController.signal); } await this.completeRecoveredManagedSessionDeletion(project, deletion, this.operationAbortController.signal); } this.codexRecoveryDiagnostics.delete(key); } catch (error) { this.operationAbortController.signal.throwIfAborted(); runtime.supervisor.signal.throwIfAborted(); this.codexRecoveryDiagnostics.set(key, codexDiagnostic(error, 'recovery')); } } await this.recoverCodexCreations(project.id, project.directory, this.operationAbortController.signal, false, true, profileIdArg); } } private queueCodexAutoAccept(projectIdArg: string, sessionIdArg: IControllerRuntimeId): void { const client = this.requireCodexClient(sessionIdArg.nativeId); for (const permission of client.attention(sessionIdArg.nativeId).permissions) { const key = JSON.stringify(['codex', projectIdArg, sessionIdArg.nativeId, permission.id.nativeId]); if (this.autoRepliedPermissionIds.has(key)) continue; this.autoRepliedPermissionIds.add(key); const task = this.withHarnessSubmissionAdmission(projectIdArg, sessionIdArg, this.operationAbortController.signal, async () => this.runAudited( { type: 'permission.reply', requestId: permission.id, sessionId: sessionIdArg }, async () => { this.operationAbortController.signal.throwIfAborted(); this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); if (this.requireCodexClient(sessionIdArg.nativeId) !== client || !this.isProjectAutoAcceptedSession(projectIdArg, sessionIdArg)) return; if (client.attention(sessionIdArg.nativeId).permissions.some((entry) => entry.id.nativeId === permission.id.nativeId)) { client.replyPermission(sessionIdArg.nativeId, permission.id.nativeId, 'once'); } }, )); void this.trackOperationPromise(task).finally(() => this.autoRepliedPermissionIds.delete(key)).catch(() => undefined); } } private async recoverCodexCreations(projectIdArg: string, directoryArg: string, signalArg: AbortSignal, projectRemovalArg = false, tolerateConflictsArg = false, profileIdArg?: string): Promise<void> { const store = this.requireAuthStore(); const intents = projectRemovalArg ? await store.listCodexCreationsForProjectRemoval(projectIdArg, signalArg) : await store.listCodexCreations(projectIdArg, signalArg); if (intents.length === 0) return; const retire = projectRemovalArg ? store.retireCodexCreationForProjectRemoval.bind(store) : store.retireCodexCreation.bind(store); const admit = projectRemovalArg ? store.admitCodexCreationForProjectRemoval.bind(store) : store.admitCodexCreation.bind(store); for (const intent of intents) { if (profileIdArg && intent.connection?.profileId !== profileIdArg) continue; signalArg.throwIfAborted(); try { if (!intent.nativeId) { // A server-assigned create whose response was lost cannot be replayed. await retire(intent, CodexSupervisor.assertRuntimeTerminated); continue; } if (await store.isSessionStateTombstoned(projectIdArg, codexRuntimeId(intent.nativeId))) { await store.completeCodexCreationDeletion(projectIdArg, intent.nativeId, projectRemovalArg); continue; } const client = this.requireCodexClient(intent.nativeId); const directory = this.codexDirectory(intent.nativeId, directoryArg); let session: IControllerSession; try { session = await client.readSession(directory, intent.nativeId, signalArg); } catch (error) { if (!(error instanceof plugins.crossharness.CodexAppServerRequestError) || error.code !== -32600 || !/thread.*(not loaded|not found|does not exist|no rollout)|no rollout.*thread/i.test(error.message)) throw error; await retire(intent, CodexSupervisor.assertRuntimeTerminated); continue; } if (session.createdAt !== intent.providerCreatedAt) throw new Error('Codex recovery observed another provider generation.'); const managed = await admit(intent, client.supervisor.generation); client.register(projectIdArg, directory, session); this.setManagedSessionAuthority(projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId); if (!projectRemovalArg) await this.finalizeCodexCreation(projectIdArg, intent.nativeId, signalArg); if (intent.turnDispatchedAt && !projectRemovalArg) await store.markCodexMaterialized(projectIdArg, intent.nativeId); this.codexRecoveryDiagnostics.delete(intent.id); } catch (error) { if (!tolerateConflictsArg) throw error; signalArg.throwIfAborted(); this.codexRecoveryDiagnostics.set(intent.id, codexDiagnostic(error, 'recovery')); } } } private finalizeCodexCreation(projectIdArg: string, nativeIdArg: string, signalArg?: AbortSignal): Promise<string | undefined> { const key = this.projectRuntimeIdKey(projectIdArg, codexRuntimeId(nativeIdArg)); const pending = this.codexCreationFinalizations.get(key); if (pending) return pending; if (this.codexCreationFinalizations.size >= 128) throw new Error('Too many Codex creations are being finalized.'); const task = Promise.resolve().then(async () => { const store = this.requireAuthStore(); const intent = await store.findUnmaterializedCodexCreation(projectIdArg, nativeIdArg); if (!intent || intent.metadataFinalizedAt) return undefined; if (intent.state !== 'admitted') throw new Error('Codex creation is awaiting managed admission.'); const client = this.requireCodexClient(nativeIdArg); signalArg?.throwIfAborted(); // These setters are idempotent. A lost acknowledgement is recovered on the // same assigned thread, before later user edits or the first turn. await client.rename(nativeIdArg, intent.title, signalArg); if (intent.model) await store.setSessionModelChoice(projectIdArg, codexRuntimeId(nativeIdArg), intent.model); client.supervisor.signal.throwIfAborted(); await store.finalizeCodexCreationMetadata(intent); return intent.title; }); this.codexCreationFinalizations.set(key, task); void task.finally(() => { if (this.codexCreationFinalizations.get(key) === task) this.codexCreationFinalizations.delete(key); }).catch(() => undefined); return task; } private async createCodexSession( projectIdArg: string, titleArg: string | undefined, modelArg: TControllerModelChoice | undefined, operationIdArg: string, signalArg: AbortSignal, claimEvictionGenerationArg: number, ): Promise<IReq_ControllerSessionCreate['response']> { modelArg ??= this.defaultModelForHarness('codex'); if (modelArg && modelArg.harnessId !== 'codex') throw new Error('The selected model belongs to another harness.'); if (this.retiringProjectIds.has(projectIdArg) || this.upgradeBarrier || this.codexRestartTask) throw new Error('Project changes or an AGL upgrade currently prevent Codex creation.'); if (this.codexCreationAdmissions >= 16) throw new Error('Too many Codex conversations are being created.'); const release = this.reserveAdmissionEntry(this.projectHarnessSubmissionAdmissions, projectIdArg); this.codexCreationAdmissions += 1; const scope = { projectIdentityId: projectIdArg, operationId: operationIdArg }; try { const connection = await this.codexConnectionForProject(projectIdArg); const { client, profileId, remoteDirectory } = connection; if (modelArg) await client.validateModel(modelArg, signalArg); const title = titleArg?.trim() || 'New Codex conversation'; await this.requireAuthStore().beginCodexCreation({ ...scope, title, connection: { profileId, remoteDirectory }, ...(modelArg ? { model: modelArg } : {}) }); await client.supervisor.observeOwnedMembers(); await this.requireAuthStore().dispatchCodexCreation(scope, client.supervisor.runtime); signalArg.throwIfAborted(); const result = await client.request('thread/start', { cwd: remoteDirectory, approvalPolicy: 'on-request', sandbox: 'workspace-write', ...(modelArg ? { model: modelArg.modelID, ...(modelArg.variant ? { config: { model_reasoning_effort: modelArg.variant } } : {}) } : {}) }, signalArg); const session = codexSession(result.thread, remoteDirectory); await this.requireAuthStore().bindCodexCreation(scope, client.supervisor.generation, session.id.nativeId, session.createdAt); const managed = await this.requireAuthStore().admitCodexCreation(scope, client.supervisor.generation); client.supervisor.signal.throwIfAborted(); this.codexConnections?.bind(projectIdArg, session.id.nativeId, { version: 2, profileId, remoteDirectory, rawThreadId: client.rawThreadId(session.id.nativeId) }); client.register(projectIdArg, remoteDirectory, session); client.markCreatedLoaded(session.id.nativeId); client.observeSettings(session.id.nativeId, result); this.setManagedSessionAuthority(projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId); await this.trackCreatedConversation(projectIdArg, session, managed.identity.sessionIdentityId); let creationPending = false; try { const finalizedTitle = await this.finalizeCodexCreation(projectIdArg, session.id.nativeId, signalArg); if (finalizedTitle !== undefined) session.title = finalizedTitle; } catch { // Admission has already created a real managed conversation. Return its // ID even when metadata is unfinished so the composer retains its draft // and retries finalization on that conversation, never thread/start. creationPending = true; } this.invalidateHarnessSessionEnumeration(projectIdArg); this.markUnusedSession(projectIdArg, session.id, claimEvictionGenerationArg); void this.pushControllerEvent({ type: 'sessions.changed', harnessId: 'codex', projectId: projectIdArg, timestamp: Date.now() }).catch(() => undefined); return { session, ...(creationPending ? { creationPending: true } : {}) }; } finally { this.codexCreationAdmissions -= 1; release(); } } private async codexModeModelChoice( projectIdArg: string, sessionIdArg: IControllerRuntimeId, clientArg: CodexClientAdapter, signalArg: AbortSignal, requestedModelArg?: TControllerModelChoice, ): Promise<IControllerCodexModelChoice> { if (sessionIdArg.harnessId !== 'codex') throw new Error('Mode update requires a Codex conversation.'); await clientArg.ensureLoaded(sessionIdArg.nativeId, signalArg); const state = await this.requireAuthStore().getSessionState(projectIdArg, sessionIdArg); const selected = requestedModelArg ?? state.modelChoice ?? this.defaultModelForHarness('codex'); if (selected) { if (selected.harnessId !== 'codex') throw new Error('The selected model belongs to another harness.'); return selected; } const activity = clientArg.activity(sessionIdArg.nativeId); if (!activity.model) throw new Error('Codex did not report the conversation model required to change mode.'); return { harnessId: 'codex', providerID: 'codex', modelID: activity.model, ...(activity.effort ? { variant: activity.effort } : {}), }; } private async forkCodexSession( projectIdArg: string, sourceSessionIdArg: IControllerRuntimeId, operationIdArg: string, modelArg: IControllerCodexModelChoice | undefined, signalArg: AbortSignal, claimEvictionGenerationArg: number, beforeProviderDispatchArg: () => void, ): Promise<{ session: IControllerSession; creationPending?: true }> { if (sourceSessionIdArg.harnessId !== 'codex') throw new Error('Fork requires a Codex conversation.'); if (!this.codexConnections) throw new Error('Codex connection routing is unavailable.'); if (this.retiringProjectIds.has(projectIdArg) || this.upgradeBarrier || this.codexRestartTask) { throw new Error('Project changes or an AGL upgrade currently prevent Codex creation.'); } if (this.codexCreationAdmissions >= 16) throw new Error('Too many Codex conversations are being created.'); const release = this.reserveAdmissionEntry(this.projectHarnessSubmissionAdmissions, projectIdArg); this.codexCreationAdmissions += 1; const scope = { projectIdentityId: projectIdArg, operationId: operationIdArg }; try { const origin = this.codexConnections.origin(sourceSessionIdArg.nativeId, projectIdArg); const runtime = this.codexConnections.require(origin.profileId); const client = runtime.client; const source = await client.readSession(origin.remoteDirectory, sourceSessionIdArg.nativeId, signalArg); if (source.status !== 'idle') throw new Error('A Codex turn is already running. Wait for it to finish before forking.'); if (modelArg && modelArg.harnessId !== 'codex') throw new Error('The selected model belongs to another harness.'); await this.requireAuthStore().beginCodexCreation({ ...scope, title: source.title, connection: { profileId: origin.profileId, remoteDirectory: origin.remoteDirectory }, ...(modelArg ? { model: modelArg } : {}), }); await client.supervisor.observeOwnedMembers(); await this.requireAuthStore().dispatchCodexCreation(scope, client.supervisor.runtime); signalArg.throwIfAborted(); client.supervisor.signal.throwIfAborted(); beforeProviderDispatchArg(); const result = await client.request('thread/fork', { threadId: sourceSessionIdArg.nativeId, excludeTurns: true, }, signalArg); const thread = codexRecord(result.thread); if (thread.forkedFromId !== sourceSessionIdArg.nativeId) { throw new Error('Codex returned a fork with a different source conversation.'); } const session = codexSession(thread, origin.remoteDirectory); if (session.id.nativeId === sourceSessionIdArg.nativeId) throw new Error('Codex returned the source conversation as its own fork.'); await this.requireAuthStore().bindCodexCreation( scope, client.supervisor.generation, session.id.nativeId, session.createdAt, ); const managed = await this.requireAuthStore().admitCodexCreation(scope, client.supervisor.generation); client.supervisor.signal.throwIfAborted(); this.codexConnections.bind(projectIdArg, session.id.nativeId, { version: 2, profileId: origin.profileId, remoteDirectory: origin.remoteDirectory, rawThreadId: client.rawThreadId(session.id.nativeId), }); client.register(projectIdArg, origin.remoteDirectory, session); client.markForkedLoaded(session.id.nativeId); client.observeSettings(session.id.nativeId, result); this.setManagedSessionAuthority(projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId); await this.trackCreatedConversation(projectIdArg, session, managed.identity.sessionIdentityId); let creationPending = false; try { const finalizedTitle = await this.finalizeCodexCreation(projectIdArg, session.id.nativeId, signalArg); if (finalizedTitle !== undefined) session.title = finalizedTitle; } catch { creationPending = true; } this.invalidateHarnessSessionEnumeration(projectIdArg); this.markUnusedSession(projectIdArg, session.id, claimEvictionGenerationArg); void this.pushControllerEvent({ type: 'sessions.changed', harnessId: 'codex', projectId: projectIdArg, timestamp: Date.now(), }).catch(() => undefined); return { session, ...(creationPending ? { creationPending: true as const } : {}) }; } finally { this.codexCreationAdmissions -= 1; release(); } } private async readManagedCodexSession(projectIdArg: string, directoryArg: string, nativeIdArg: string, signalArg?: AbortSignal): Promise<IControllerSession> { if (this.codexConnections) this.codexConnections.bind(projectIdArg, nativeIdArg, await this.requireAuthStore().getCodexOrigin(projectIdArg, nativeIdArg, signalArg)); directoryArg = this.codexDirectory(nativeIdArg, directoryArg); const client = this.requireCodexClient(nativeIdArg); const session = await client.readSession(directoryArg, nativeIdArg, signalArg); const managed = await this.requireAuthStore().observeManagedCodexSession({ projectIdentityId: projectIdArg, runtimeId: codexRuntimeId(nativeIdArg), supervisorGeneration: client.supervisor.generation, providerSessionGeneration: codexSessionProviderGeneration(nativeIdArg, session.createdAt), signal: signalArg }); client.supervisor.signal.throwIfAborted(); if (!managed) throw new plugins.typedrequest.TypedResponseError('The Codex conversation is not managed by AGL.', { code: 'session_not_found' }); const canonical = client.register(projectIdArg, directoryArg, session); this.setManagedSessionAuthority(projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId); return { ...canonical, ...(client.attention(nativeIdArg).permissions.length || client.attention(nativeIdArg).questions.length ? { attention: true } : {}) }; } private async listManagedCodexSessions(projectIdArg: string, directoryArg: string, signalArg: AbortSignal, discoverFolderSessionsArg = true): Promise<IControllerSession[]> { if (this.codexConnections) { const connections = this.codexConnections; const store = this.requireAuthStore(); const memberships = await store.listCodexMemberships(projectIdArg, signalArg); const activeMemberships = memberships.filter((membership) => membership.state === 'active'); const known = new Map(activeMemberships.map((membership) => [membership.runtimeId.nativeId, membership])); const scopes = new Map<string, { profileId: string; remoteDirectory: string; discover: boolean }>(); const scopeKey = (profileId: string, remoteDirectory: string) => JSON.stringify([profileId, remoteDirectory]); for (const membership of activeMemberships) { if (!membership.codexOrigin) throw new Error('Codex conversation origin is missing.'); connections.bind(projectIdArg, membership.runtimeId.nativeId, membership.codexOrigin); const { profileId, remoteDirectory } = membership.codexOrigin; scopes.set(scopeKey(profileId, remoteDirectory), { profileId, remoteDirectory, discover: false }); } const profileStore = store.requireCodexConnections(); const mapping = await profileStore.mapping(projectIdArg) ?? await profileStore.setMapping(projectIdArg, this.codexDefaultProfileId!, directoryArg); const profile = await profileStore.get(mapping.profileId); if (profile.state === 'active') { const remoteDirectory = profile.mode === 'local' ? directoryArg : mapping.remoteDirectory; scopes.set(scopeKey(profile.id, remoteDirectory), { profileId: profile.id, remoteDirectory, discover: discoverFolderSessionsArg }); } const result: IControllerSession[] = []; for (const scope of scopes.values()) { signalArg.throwIfAborted(); let client: CodexClientAdapter; let sessions: IControllerSession[]; try { client = (await connections.connect(scope.profileId)).client; sessions = await client.listSessions(scope.remoteDirectory, signalArg); } catch (error) { signalArg.throwIfAborted(); for (const membership of activeMemberships) { if (membership.codexOrigin!.profileId !== scope.profileId || membership.codexOrigin!.remoteDirectory !== scope.remoteDirectory) continue; result.push({ id: { ...membership.runtimeId }, title: 'Codex conversation · connection unavailable', createdAt: membership.managedAt.getTime(), updatedAt: membership.managedAt.getTime(), status: 'error' }); this.codexRecoveryDiagnostics.set(this.projectRuntimeIdKey(projectIdArg, membership.runtimeId), codexDiagnostic(error, 'connection')); } continue; } for (let index = 0; index < sessions.length; index += 8) { signalArg.throwIfAborted(); const admitted = await Promise.all(sessions.slice(index, index + 8).map(async (session) => { const membership = known.get(session.id.nativeId); // A mapping change must never move an existing conversation to a new folder/server. if (membership && (membership.codexOrigin!.profileId !== scope.profileId || membership.codexOrigin!.remoteDirectory !== scope.remoteDirectory)) return undefined; const input = { projectIdentityId: projectIdArg, runtimeId: codexRuntimeId(session.id.nativeId), supervisorGeneration: client.supervisor.generation, providerSessionGeneration: codexSessionProviderGeneration(session.id.nativeId, session.createdAt), signal: signalArg }; try { const origin = membership?.codexOrigin ?? { version: 2 as const, profileId: scope.profileId, remoteDirectory: scope.remoteDirectory, rawThreadId: client.rawThreadId(session.id.nativeId) }; const managed = membership ? await store.observeManagedCodexSession(input) : scope.discover && session.archivedAt === undefined && session.parentId === undefined ? await this.withCodexDiscoveryAdmission(projectIdArg, session.id, client, () => store.admitManagedSession({ ...input, codexOrigin: origin, admissionSource: 'project-folder-discovery' })) : undefined; client.supervisor.signal.throwIfAborted(); if (!managed) return undefined; connections.bind(projectIdArg, session.id.nativeId, origin); const canonical = client.register(projectIdArg, scope.remoteDirectory, session); this.setManagedSessionAuthority(projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId); this.codexRecoveryDiagnostics.delete(this.projectRuntimeIdKey(projectIdArg, session.id)); const attention = client.attention(session.id.nativeId); if (canonical.status === 'busy' && !client.activitySummary(session.id.nativeId).canInterrupt) { await client.refreshActivity(session.id.nativeId, signalArg).catch(() => undefined); } return { ...canonical, codexActivity: client.activitySummary(session.id.nativeId), ...(attention.permissions.length || attention.questions.length ? { attention: true } : {}) }; } catch (error) { if (!(error instanceof ControllerSessionIdentityError)) throw error; this.setManagedSessionAuthority(projectIdArg, session.id, 'uncertain'); return undefined; } })); for (const session of admitted) if (session) result.push(session); } } return result; } const client = this.requireCodexClient(); const sessions = await client.listSessions(directoryArg, signalArg); const result: IControllerSession[] = []; for (const session of sessions) { const managed = await this.requireAuthStore().observeManagedCodexSession({ projectIdentityId: projectIdArg, runtimeId: codexRuntimeId(session.id.nativeId), supervisorGeneration: client.supervisor.generation, providerSessionGeneration: codexSessionProviderGeneration(session.id.nativeId, session.createdAt), signal: signalArg }); client.supervisor.signal.throwIfAborted(); if (!managed) continue; const canonical = client.register(projectIdArg, directoryArg, session); this.setManagedSessionAuthority(projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId); const attention = client.attention(session.id.nativeId); if (canonical.status === 'busy' && !client.activitySummary(session.id.nativeId).canInterrupt) { await client.refreshActivity(session.id.nativeId, signalArg).catch(() => undefined); } result.push({ ...canonical, codexActivity: client.activitySummary(session.id.nativeId), ...(attention.permissions.length || attention.questions.length ? { attention: true } : {}) }); } return result; } private async withCodexDiscoveryAdmission<T>( projectIdArg: string, sessionIdArg: IControllerRuntimeId, clientArg: CodexClientAdapter, operationArg: () => Promise<T>, ): Promise<T> { clientArg.supervisor.signal.throwIfAborted(); const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.retiringProjectIds.has(projectIdArg) || this.upgradeBarrier || this.codexRestartTask || this.retiringSessionKeys.has(key) || this.archivingSessionKeys.has(key) || this.codexCreationAdmissions >= 16) { throw new ControllerSessionIdentityError('concurrent_change', 'Codex discovery admission is temporarily fenced.'); } const release = this.reserveAdmissionEntry(this.projectHarnessSubmissionAdmissions, projectIdArg); this.codexCreationAdmissions += 1; try { return await operationArg(); } finally { this.codexCreationAdmissions -= 1; release(); } } private async readCodexSessionDetail(projectIdArg: string, nativeIdArg: string, signalArg: AbortSignal): Promise<IControllerSessionDetail> { const project = await this.resolveProject(projectIdArg); const client = this.requireCodexClient(nativeIdArg); const session = await this.readManagedCodexSession(projectIdArg, project.directory, nativeIdArg, signalArg); // Capture the cursor before the asynchronous snapshot. Events arriving during the read remain replayable. const cursor = client.cursor; if (session.status === 'busy' && !client.activitySummary(nativeIdArg).canInterrupt) { await client.refreshActivity(nativeIdArg, signalArg).catch(() => undefined); } const messagePage = boundControllerMessagePage(await client.listMessagePage(nativeIdArg, {}, signalArg), controllerInitialMessageBundleLimit); const state = await this.requireAuthStore().getSessionState(projectIdArg, session.id); const authSwitchLimitsContext = await this.authSwitchLimitsContext( projectIdArg, session.id, state.modelChoice, ); return { session, messagePage, ...client.attention(nativeIdArg), todos: client.activity(nativeIdArg).plan?.steps ?? [], codexActivity: { ...client.activity(nativeIdArg), queuePaused: this.pausedCodexQueues.has(this.sessionStateMutationKey(projectIdArg, session.id)) }, scratchpad: state.scratchpad, intelligenceExchanges: state.intelligenceExchanges, authSwitchLimitsContext, pendingPrompts: this.pendingPromptsForSession(projectIdArg, session.id, messagePage.bundles.flatMap((bundle) => bundle.messages)), toolStreamCursor: cursor, messageStreamCursor: cursor, ...(client.activity(nativeIdArg).model ? { model: `codex/${client.activity(nativeIdArg).model}` } : {}), ...(client.activity(nativeIdArg).effort ? { effort: client.activity(nativeIdArg).effort } : {}), ...(state.modelChoice ? { modelChoice: state.modelChoice } : {}), ...(this.isProjectAutoAcceptedSession(projectIdArg, session.id) ? { autoAcceptPermissions: true } : {}) }; } /** Selects only identities that authswitch itself can prove from the session's credential owner. */ private async authSwitchLimitsContext( projectIdArg: string, sessionIdArg: IControllerRuntimeId, modelChoiceArg: TControllerModelChoice | undefined, ): Promise<TControllerAuthSwitchLimitsContext> { if (sessionIdArg.harnessId === 'codex') { if (this.codexConnections) { const origin = this.codexConnections.origin(sessionIdArg.nativeId, projectIdArg); const runtime = this.codexConnections.runtime(origin.profileId); const profile = runtime?.profile ?? await this.requireAuthStore().requireCodexConnections().get(origin.profileId); if (profile.mode === 'remote') return { available: false, reason: 'remote_codex_profile', message: 'This conversation uses a remote Codex profile whose account limits are owned by that remote server.', }; return { available: true, harnessId: 'codex', profileId: origin.profileId }; } return { available: false, reason: 'codex_runtime_account_unresolved', message: 'The running Codex account cannot yet be matched exactly to an authswitch account.', }; } if (sessionIdArg.harnessId === 'opencode') { const choice = modelChoiceArg ?? this.defaultModelForHarness('opencode'); if (choice?.harnessId !== 'opencode' || !choice.providerID) return { available: false, reason: 'opencode_provider_unresolved', message: 'This conversation has no exact OpenCode provider slot for account limits.', }; return { available: true, harnessId: 'opencode', slotId: choice.providerID }; } return { available: false, reason: 'flex_account_unavailable', message: 'This Flex account is not represented by an authswitch harness identity.', }; } private async assertCodexComposerMutationAllowed( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal, allowModeMutationArg = false, ): Promise<void> { if (sessionIdArg.harnessId !== 'codex') return; await this.resolveProject(projectIdArg); const client = this.requireCodexClient(sessionIdArg.nativeId); const current = client.activitySummary(sessionIdArg.nativeId); const activity = current.writer === 'external' && !current.canInterrupt ? await client.refreshActivity(sessionIdArg.nativeId, signalArg).catch(() => current) : current; if (activity.writer === 'external') { throw new plugins.typedrequest.TypedResponseError( 'This Codex conversation is running elsewhere. Wait for that turn to finish or stop it first.', { code: 'concurrent_change' }, ); } if (!allowModeMutationArg && activity.collaborationModeAuthority) { throw new plugins.typedrequest.TypedResponseError( activity.collaborationModeAuthority.status === 'pending' ? 'The Codex collaboration mode is still changing.' : 'The last Codex mode update outcome is unconfirmed. Choose Default or Plan again before continuing.', { code: 'concurrent_change' }, ); } } /** * Projects that hold at least one tracked conversation and whose directory is available. * Bounded, because one browser read must never fan out over every registered project. */ private async trackedConversationProjectScope( signalArg: AbortSignal, ): Promise<{ projects: IControllerProjectDocument[]; projectsById: Map<string, IControllerProjectDocument>; tracked: IControllerTrackedConversationDocument[]; truncated: boolean; }> { const tracked = await this.requireAuthStore().listTrackedConversations(undefined, signalArg); const orderedProjectIds: string[] = []; const seenProjectIds = new Set<string>(); for (const entry of tracked) { if (seenProjectIds.has(entry.projectId)) continue; seenProjectIds.add(entry.projectId); orderedProjectIds.push(entry.projectId); } const truncated = orderedProjectIds.length > controllerCrossProjectReadFanoutLimit; const selectedProjectIds = orderedProjectIds.slice(0, controllerCrossProjectReadFanoutLimit); const projectsById = new Map<string, IControllerProjectDocument>(); for (const projectId of selectedProjectIds) { signalArg.throwIfAborted(); if (this.retiringProjectIds.has(projectId)) continue; const project = await this.requireAuthStore().getProject(projectId); if (!project) continue; projectsById.set(project.id, project); } const available = await this.resolveAvailableProjects([...projectsById.values()]); const availableById = new Map(available.map((project) => [project.id, project])); return { projects: available, projectsById: availableById, tracked: tracked.filter((entry) => availableById.has(entry.projectId)), truncated, }; } /** * Live harness state for the tracked conversations of one project, without admitting anything: * a conversation AGL does not already track stays invisible here. */ private async resolveTrackedProjectSessions( projectArg: IControllerProjectDocument, trackedArg: readonly IControllerTrackedConversationDocument[], signalArg: AbortSignal, ): Promise<{ sessions: Map<string, IControllerSession>; /** Harnesses that answered exhaustively for this project in this exact read. */ authoritativeHarnessIds: Set<TControllerSessionHarnessId>; /** Tracked conversations the harness still listed, before membership confirmation. */ observedKeys: Set<string>; }> { const wanted = new Map(trackedArg.map((entry) => [ controllerRuntimeIdKey(entry.runtimeId), entry, ])); const authoritativeHarnessIds = new Set<TControllerSessionHarnessId>(); const observedKeys = new Set<string>(); if (wanted.size === 0) return { sessions: new Map(), authoritativeHarnessIds, observedKeys }; const candidates: IControllerSession[] = []; const needsOpenCodeOrFlex = trackedArg.some((entry) => entry.harnessId !== 'codex'); if (needsOpenCodeOrFlex) { const enumeration = await this.enumerateHarnessSessions( projectArg.id, projectArg.directory, signalArg, ); if (enumeration.openCode.status === 'rejected') throw enumeration.openCode.reason; if (enumeration.flex.status === 'rejected') throw enumeration.flex.reason; authoritativeHarnessIds.add('opencode'); // Flex answers exhaustively only while its projection reader and runtime are live; a // restarting Flex must never look like a conversation that no longer exists. if (this.flexProjectionReader !== undefined && this.flexSupervisor?.getStatus().ready) { authoritativeHarnessIds.add('flex'); } for (const session of [...enumeration.openCode.value, ...enumeration.flex.value]) { const key = controllerRuntimeIdKey(session.id); if (!wanted.has(key)) continue; observedKeys.add(key); candidates.push(session); } } if ( trackedArg.some((entry) => entry.harnessId === 'codex') && (this.codexConnections || this.codexSupervisor?.getStatus().healthy) ) { authoritativeHarnessIds.add('codex'); for (const session of await this.listManagedCodexSessions( projectArg.id, projectArg.directory, signalArg, false, )) { const key = controllerRuntimeIdKey(session.id); if (!wanted.has(key)) continue; observedKeys.add(key); candidates.push(session); } } // Lookup-only membership confirmation: discovery stays off for every browser read. const managed = await this.filterManagedSessions(projectArg.id, candidates, [], signalArg); return { sessions: new Map(managed.map((session) => [controllerRuntimeIdKey(session.id), session])), authoritativeHarnessIds, observedKeys, }; } /** A conversation archived in AGL renders from its durable cache, never from a harness read. */ private trackedConversationFromCache( entryArg: IControllerTrackedConversationDocument, ): IControllerSession { return { id: { ...entryArg.runtimeId }, title: entryArg.titleCache ?? entryArg.runtimeId.nativeId, createdAt: entryArg.trackedAt.getTime(), updatedAt: (entryArg.titleCacheAt ?? entryArg.trackedAt).getTime(), status: 'idle', }; } private toTrackedConversation( entryArg: IControllerTrackedConversationDocument, projectArg: IControllerProjectDocument, sessionArg: IControllerSession, ): IControllerTrackedConversation { return { projectId: projectArg.id, projectName: projectArg.name, session: sessionArg, origin: entryArg.origin, trackedAt: entryArg.trackedAt.getTime(), ...(entryArg.archivedAt === undefined ? {} : { archivedAt: entryArg.archivedAt.getTime() }), }; } private async listTrackedConversationsForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, signalArg: AbortSignal, ): Promise<IReq_ControllerConversationList['response']> { return this.runAudited( { type: 'conversation.list', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const scope = await this.trackedConversationProjectScope(signalArg); const trackedByProject = new Map<string, IControllerTrackedConversationDocument[]>(); for (const entry of scope.tracked) { const entries = trackedByProject.get(entry.projectId) ?? []; entries.push(entry); trackedByProject.set(entry.projectId, entries); } const conversations: IControllerTrackedConversation[] = []; for (const project of scope.projects) { signalArg.throwIfAborted(); const entries = trackedByProject.get(project.id) ?? []; const active = entries.filter((entry) => entry.archivedAt === undefined); const resolved = active.length === 0 ? { sessions: new Map<string, IControllerSession>(), authoritativeHarnessIds: new Set<TControllerSessionHarnessId>(), observedKeys: new Set<string>(), } : await this.resolveTrackedProjectSessions(project, active, signalArg); const { sessions, authoritativeHarnessIds, observedKeys } = resolved; for (const entry of entries) { if (entry.archivedAt !== undefined) { conversations.push(this.toTrackedConversation( entry, project, this.trackedConversationFromCache(entry), )); continue; } const entryKey = controllerRuntimeIdKey(entry.runtimeId); const session = sessions.get(entryKey); if (!session) { // Only a harness that answered exhaustively here may prove a conversation gone: // absence from that exhaustive list, or a definitive loss of managed membership. // Anything less is a temporary runtime condition, and durable sidebar state is // never deleted by one — the entry renders from its cache until the harness answers. const gone = authoritativeHarnessIds.has(entry.harnessId) && ( !observedKeys.has(entryKey) || this.managedSessionAuthority(entry.projectId, entry.runtimeId).state === 'definitively_unmanaged' ); if (!gone) { conversations.push(this.toTrackedConversation( entry, project, this.trackedConversationFromCache(entry), )); continue; } try { await this.requireAuthStore() .untrackConversation(entry.projectId, entry.runtimeId); } catch (errorArg) { console.error('Untracking a vanished conversation failed.', errorArg); } continue; } try { await this.requireAuthStore() .cacheTrackedConversationTitle(entry.projectId, entry.runtimeId, session.title); } catch (errorArg) { console.error('Caching the conversation title failed.', errorArg); } conversations.push(this.toTrackedConversation(entry, project, session)); } } // Active conversations order by live harness activity; AGL-archived ones order by when // they were archived, because their session timestamps come from a cache. conversations.sort((left, right) => { if ((left.archivedAt === undefined) !== (right.archivedAt === undefined)) { return left.archivedAt === undefined ? -1 : 1; } return left.archivedAt === undefined || right.archivedAt === undefined ? right.session.updatedAt - left.session.updatedAt : right.archivedAt - left.archivedAt; }); return { conversations, ...(scope.truncated ? { truncated: true as const } : {}), }; }, ); } /** * Directories the conversation search covers: registered projects first, then the immediate * subdirectories of the configured standard directories that are not registered yet. */ private async resolveConversationSearchCandidates( projectIdsArg: readonly string[] | undefined, signalArg: AbortSignal, ): Promise<{ candidates: Array<{ project?: IControllerProjectDocument; directory: string; name: string }>; truncated: boolean; }> { const registered = await this.resolveAvailableProjects( await this.requireAuthStore().listProjects(), ); const selected = projectIdsArg === undefined ? registered : registered.filter((project) => projectIdsArg.includes(project.id)); const candidates: Array<{ project?: IControllerProjectDocument; directory: string; name: string; }> = selected.map((project) => ({ project, directory: project.directory, name: project.name, })); const registeredDirectories = new Set(registered.map((project) => project.directory)); let truncated = candidates.length > controllerCrossProjectReadFanoutLimit; if (projectIdsArg === undefined) { const settings = await this.requireAuthStore().getSettings(); for (const standardDirectory of settings.standardProjectDirectories ?? []) { signalArg.throwIfAborted(); let entries: plugins.fs.Dirent[]; let standardRoot: string; try { standardRoot = await plugins.fs.promises.realpath(standardDirectory); entries = await plugins.fs.promises.readdir(standardDirectory, { withFileTypes: true }); } catch { continue; } for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) { if (!entry.isDirectory() && !entry.isSymbolicLink()) continue; if (entry.name.startsWith('.')) continue; const directory = plugins.path.join(standardDirectory, entry.name); // A symlink is a candidate only while it stays inside the configured directory. // One that points outside it would silently widen the search to a location the // operator never configured, so it is skipped instead of followed. if (entry.isSymbolicLink()) { let realDirectory: string; try { realDirectory = await plugins.fs.promises.realpath(directory); } catch { continue; } if ( realDirectory !== standardRoot && !realDirectory.startsWith(`${standardRoot}${plugins.path.sep}`) ) { console.debug( `Conversation search skips ${directory}: it resolves outside ${standardDirectory}.`, ); continue; } } if (registeredDirectories.has(directory)) continue; registeredDirectories.add(directory); if (candidates.length >= controllerCrossProjectReadFanoutLimit) { truncated = true; break; } candidates.push({ directory, name: entry.name }); } if (truncated) break; } } return { candidates: candidates.slice(0, controllerCrossProjectReadFanoutLimit), truncated, }; } private async searchConversationsForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerConversationSearch['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerConversationSearch['response']> { const { query, projectIds, harnessIds } = validateRequest( () => assertConversationSearchRequest(requestArg), ); return this.runAudited( { type: 'conversation.search', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const searchHarnessIds = harnessIds ?? controllerSessionHarnessIds; const { candidates, truncated: candidatesTruncated } = await this .resolveConversationSearchCandidates(projectIds, signalArg); const tracked = await this.requireAuthStore().listTrackedConversations(undefined, signalArg); const trackedKeys = new Set(tracked.map( (entry) => `${entry.projectId}:${controllerRuntimeIdKey(entry.runtimeId)}`, )); const loweredQuery = query.toLowerCase(); const results: IControllerConversationSearchResult[] = []; let truncated = candidatesTruncated; for (const candidate of candidates) { signalArg.throwIfAborted(); if (results.length >= conversationSearchResultLimit) { truncated = true; break; } const found: IControllerSession[] = []; if (searchHarnessIds.includes('opencode')) { // A registered project was explicitly asked for, so its failure fails the search. // An unregistered standard-directory candidate is a speculative probe: a failure // there narrows the result set, which is reported as a truncated response. let openCodeResult: Awaited<ReturnType<OpenCodeClientAdapter['searchSessions']>>; try { openCodeResult = await this.requireOpenCodeClient().searchSessions( candidate.directory, query, signalArg, ); } catch (errorArg) { if (candidate.project !== undefined) throw errorArg; signalArg.throwIfAborted(); truncated = true; continue; } if (openCodeResult.truncated) truncated = true; const hidden = candidate.project ? await this.requireAuthStore() .findProjectSessionIntelligenceTemporarySessionIds( candidate.project.id, openCodeResult.sessions.map((session) => session.id.nativeId), signalArg, ) : new Set<string>(); found.push(...openCodeResult.sessions.filter( (session) => !hidden.has(session.id.nativeId), )); } // Flex and Codex conversations are addressed through a registered project: their // storage scope and connection mapping do not exist for an unregistered directory. if (candidate.project && searchHarnessIds.includes('flex')) { found.push(...(await this.listFlexSessions(candidate.project.id, signalArg)).filter( (session) => session.title.toLowerCase().includes(loweredQuery), )); } if ( candidate.project && searchHarnessIds.includes('codex') && (this.codexConnections !== undefined || this.codexClient !== undefined) && !this.codexStartupDiagnostic ) { try { const connection = await this.codexConnectionForProject(candidate.project.id); found.push(...(await connection.client.listSessions( connection.remoteDirectory, signalArg, )).filter((session) => session.title.toLowerCase().includes(loweredQuery))); } catch { // A Codex connection that is unavailable narrows the search rather than failing // it, and the narrowing is reported instead of hidden. truncated = true; } } for (const session of found) { if (session.parentId !== undefined) continue; if (results.length >= conversationSearchResultLimit) { truncated = true; break; } results.push({ ...(candidate.project === undefined ? {} : { projectId: candidate.project.id }), projectName: candidate.name, projectDirectory: candidate.directory, session, tracked: candidate.project !== undefined && trackedKeys.has( `${candidate.project.id}:${controllerRuntimeIdKey(session.id)}`, ), }); } } results.sort((left, right) => right.session.updatedAt - left.session.updatedAt); return { results, ...(truncated ? { truncated: true as const } : {}) }; }, ); } /** * Admit an existing harness conversation into AGL's managed set. Unlike folder discovery this * is an explicit user intent, so it admits archived roots too, but never a subagent. */ private async admitExistingConversation( projectArg: IControllerProjectDocument, sessionIdArg: TControllerSessionId, signalArg: AbortSignal, ): Promise<{ session: IControllerSession; sessionIdentityId: string }> { const notFound = () => new plugins.typedrequest.TypedResponseError( 'The requested conversation cannot be opened in this project.', { code: 'session_not_found' }, ); if (sessionIdArg.harnessId === 'opencode') { await this.assertSessionIsNotIntelligenceTemporary(projectArg.id, sessionIdArg, signalArg); const supervisor = this.openCodeSupervisor; const client = this.requireOpenCodeClient(); if (!supervisor) throw new Error('The OpenCode runtime is unavailable.'); const authority = this.requireOpenCodeRuntimeExitAuthority(supervisor); const signal = AbortSignal.any([signalArg, authority.signal]); const candidate = await client.getEnrollableSession( projectArg.directory, sessionIdArg.nativeId, signal, ); if (!candidate) throw notFound(); this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, authority); const observation = await this.requireAuthStore().admitManagedSession({ projectIdentityId: projectArg.id, runtimeId: sessionIdArg, supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration: candidate.providerSessionGeneration, observedAt: new Date(), admissionSource: 'explicit-enrollment', signal, }); this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, authority); this.setManagedSessionAuthority( projectArg.id, sessionIdArg, 'managed', observation.identity.sessionIdentityId, ); return { session: candidate.session, sessionIdentityId: observation.identity.sessionIdentityId, }; } if (sessionIdArg.harnessId === 'flex') { const supervisor = this.flexSupervisor; if (!supervisor?.getStatus().ready) { throw new plugins.typedrequest.TypedResponseError( 'FlexHarness is unavailable.', { code: 'harness_unavailable' }, ); } const authority = this.requireFlexRuntimeExitAuthority(supervisor); const sessions = await this.listFlexSessions(projectArg.id, signalArg); const metadata = this.completeHarnessSessionRuntimeIdsByList.get(sessions); const session = sessions.find( (entry) => entry.id.nativeId === sessionIdArg.nativeId && entry.parentId === undefined, ); const providerSessionGeneration = metadata?.providerSessionGenerations .get(sessionIdArg.nativeId); const flexFacts = metadata?.flexGenerationFacts.get(sessionIdArg.nativeId); if (!session || providerSessionGeneration === undefined || flexFacts === undefined) { throw notFound(); } const observation = await this.requireAuthStore().admitManagedSession({ projectIdentityId: projectArg.id, runtimeId: sessionIdArg, supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration, ...flexFacts, observedAt: new Date(), admissionSource: 'explicit-enrollment', signal: signalArg, }); this.assertFlexSessionIdentityGenerationCurrent(supervisor, authority); this.setManagedSessionAuthority( projectArg.id, sessionIdArg, 'managed', observation.identity.sessionIdentityId, ); return { session, sessionIdentityId: observation.identity.sessionIdentityId }; } const connection = await this.codexConnectionForProject(projectArg.id); const sessions = await connection.client.listSessions(connection.remoteDirectory, signalArg); const session = sessions.find( (entry) => entry.id.nativeId === sessionIdArg.nativeId && entry.parentId === undefined, ); if (!session) throw notFound(); const origin = { version: 2 as const, profileId: connection.profileId, remoteDirectory: connection.remoteDirectory, rawThreadId: connection.client.rawThreadId(session.id.nativeId), }; const observation = await this.requireAuthStore().admitManagedSession({ projectIdentityId: projectArg.id, runtimeId: sessionIdArg, supervisorGeneration: connection.client.supervisor.generation, providerSessionGeneration: codexSessionProviderGeneration( session.id.nativeId, session.createdAt, ), codexOrigin: origin, observedAt: new Date(), admissionSource: 'explicit-enrollment', signal: signalArg, }); connection.client.supervisor.signal.throwIfAborted(); this.codexConnections?.bind(projectArg.id, session.id.nativeId, origin); const canonical = connection.client.register( projectArg.id, connection.remoteDirectory, session, ); this.setManagedSessionAuthority( projectArg.id, sessionIdArg, 'managed', observation.identity.sessionIdentityId, ); return { session: canonical, sessionIdentityId: observation.identity.sessionIdentityId }; } private async openConversationForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerConversationOpen['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerConversationOpen['response']> { const { projectDirectory, sessionId } = validateRequest( () => assertConversationOpenRequest(requestArg), ); return this.runAudited( { type: 'conversation.open', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId }, async () => { // Registration is idempotent, so opening a conversation under a standard directory // registers its project exactly once and never needs a separate step. const registration = await this.createProjectAndReconcile(projectDirectory); if (registration.created) { // Other clients learn about the new project exactly as they do from project.create. void this.pushControllerEvent({ type: 'projects.changed', projectId: registration.project.id, timestamp: Date.now(), }).catch(() => undefined); } let project: IControllerProjectDocument; let admitted: { session: IControllerSession; sessionIdentityId: string }; try { project = await this.resolveProject(registration.project.id); admitted = await this.admitExistingConversation(project, sessionId, signalArg); } catch (errorArg) { // Nothing was admitted into the project, so a registration this call created is // rolled back rather than left behind as an empty project. A project that already // existed is never touched, and a failing rollback never replaces the real error. if (registration.created) { try { await this.removeRegisteredProject(registration.project, signalArg); } catch (rollbackErrorArg) { console.error( 'Rolling back the implicitly registered project failed.', rollbackErrorArg, ); } } throw errorArg; } const entry = await this.requireAuthStore().trackConversation({ projectId: project.id, runtimeId: sessionId, sessionIdentityId: admitted.sessionIdentityId, origin: 'opened', title: admitted.session.title, }); this.invalidateHarnessSessionEnumeration(project.id); this.pushTrackedConversationsChanged(project.id, sessionId.harnessId); return { conversation: this.toTrackedConversation(entry, project, admitted.session), }; }, ); } private async setConversationArchivedForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerConversationArchive['request'], archivedArg: boolean, signalArg: AbortSignal, ): Promise<IReq_ControllerConversationArchive['response']> { const { projectId, sessionId } = validateRequest( () => (archivedArg ? assertConversationArchiveRequest(requestArg) : assertConversationReopenRequest(requestArg)), ); return this.runAudited( { type: archivedArg ? 'conversation.archive' : 'conversation.reopen', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId, }, async () => { const project = await this.resolveProject(projectId); const existing = await this.requireAuthStore().getTrackedConversation(projectId, sessionId); if (!existing) { throw new plugins.typedrequest.TypedResponseError( 'The conversation is not tracked by AGL.', { code: 'session_not_found' }, ); } const entry = await this.requireAuthStore().setTrackedConversationArchived( projectId, sessionId, archivedArg, ); this.pushTrackedConversationsChanged(projectId, sessionId.harnessId); if (archivedArg) { return { conversation: this.toTrackedConversation( entry, project, this.trackedConversationFromCache(entry), ), }; } const { sessions } = await this.resolveTrackedProjectSessions(project, [entry], signalArg); const session = sessions.get(controllerRuntimeIdKey(entry.runtimeId)); if (!session) { await this.requireAuthStore() .untrackConversation(projectId, sessionId) .catch(() => undefined); throw new plugins.typedrequest.TypedResponseError( 'The conversation is no longer available in its harness.', { code: 'session_not_found' }, ); } return { conversation: this.toTrackedConversation(entry, project, session) }; }, ); } /** Track a conversation AGL just created. Tracking never gates the creation itself. */ private async trackCreatedConversation( projectIdArg: string, sessionArg: IControllerSession, sessionIdentityIdArg: string, ): Promise<void> { if (!isManagedRuntimeId(sessionArg.id)) return; try { await this.requireAuthStore().trackConversation({ projectId: projectIdArg, runtimeId: sessionArg.id, sessionIdentityId: sessionIdentityIdArg, origin: 'created', title: sessionArg.title, }); } catch (errorArg) { console.error('Tracking the created conversation failed.', errorArg); } } private pushTrackedConversationsChanged( projectIdArg: string, harnessIdArg: TControllerSessionHarnessId, ): void { void this.pushControllerEvent({ type: 'sessions.changed', harnessId: harnessIdArg, projectId: projectIdArg, timestamp: Date.now(), }).catch(() => undefined); } /** Absolute existing directories only; a missing one is rejected instead of stored. */ private async resolveStandardProjectDirectories( directoriesArg: readonly string[], ): Promise<string[]> { const directories = validateRequest( () => assertStandardProjectDirectories([...directoriesArg]), ); const resolved: string[] = []; for (const directory of directories) { const existing = await this.resolveExistingProjectDirectory(directory); if (resolved.includes(existing.directory)) { throw new plugins.typedrequest.TypedResponseError( 'Standard project directories must resolve to distinct directories.', { code: 'invalid_request' }, ); } resolved.push(existing.directory); } return resolved; } private async listSessionsForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerMcpSessionsList['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerMcpSessionsList['response']> { const { projectId } = requestArg; return this.runAudited( { type: 'session.list', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const project = await this.resolveProject(projectId); const openCodeClient = this.requireOpenCodeClient(); const enumeration = await this.enumerateHarnessSessions( projectId, project.directory, signalArg, ); if (enumeration.openCode.status === 'rejected') throw enumeration.openCode.reason; if (enumeration.flex.status === 'rejected') throw enumeration.flex.reason; const listedOpenCodeSessions = enumeration.openCode.value; const flexSessions = enumeration.flex.value; const hiddenOpenCodeSessionIds = await this.requireAuthStore() .findProjectSessionIntelligenceTemporarySessionIds( projectId, listedOpenCodeSessions.map((session) => session.id.nativeId), signalArg, ); const visibleOpenCodeSessions = listedOpenCodeSessions.filter( (session) => !hiddenOpenCodeSessionIds.has(session.id.nativeId), ); const completeFlexMetadata = this.completeHarnessSessionRuntimeIdsByList.get(flexSessions); const sessions = await this.filterManagedSessions( projectId, [...visibleOpenCodeSessions, ...flexSessions], [ { harnessId: 'opencode', liveRuntimeIds: listedOpenCodeSessions.map((session) => session.id), }, ...(completeFlexMetadata === undefined ? [] : [{ harnessId: 'flex' as const, liveRuntimeIds: completeFlexMetadata.liveRuntimeIds, providerSessionGenerations: completeFlexMetadata.providerSessionGenerations, flexGenerationFacts: completeFlexMetadata.flexGenerationFacts, }]), ], signalArg, true, ); const { permissions, questions } = await settleControllerAttentionLists( openCodeClient.listPermissions(project.directory, undefined, signalArg), openCodeClient.listQuestions(project.directory, undefined, signalArg), ); const attentionSessionIds = await this.resolveAttentionRootSessionIds( openCodeClient, project.directory, sessions, [ ...permissions.map((permission) => permission.sessionId), ...questions.map((question) => question.sessionId), ], signalArg, ); const codexSessions = this.codexConnections || this.codexSupervisor?.getStatus().healthy ? await this.listManagedCodexSessions(projectId, project.directory, signalArg) : []; return { sessions: [...sessions, ...codexSessions].map((session) => ({ ...session, ...(attentionSessionIds.has(controllerRuntimeIdKey(session.id)) ? { attention: true } : {}), })).sort((left, right) => right.updatedAt - left.updatedAt), }; }, ); } private async readSessionForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerMcpSessionRead['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerMcpSessionRead['response']> { const { projectId, sessionId } = requestArg; return this.runAudited( { type: 'session.read', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId, }, async () => { const project = await this.resolveProject(projectId); await this.assertSessionIsNotIntelligenceTemporary(projectId, sessionId, signalArg); await this.assertSessionExists(projectId, sessionId, signalArg); if (sessionId.harnessId === 'codex') return this.readCodexSessionDetail(projectId, sessionId.nativeId, signalArg); const [detail, sessionState] = await Promise.all([ sessionId.harnessId === 'opencode' ? this.requireOpenCodeClient().getSessionDetail( project.directory, sessionId.nativeId, signalArg, ) : this.getFlexSessionDetail(projectId, sessionId.nativeId, signalArg), this.requireAuthStore().getSessionState(projectId, sessionId), ]); let messagePage: IControllerMessagePage; let toolStreamCursor: IControllerToolStreamCursor; let messageStreamCursor: IControllerMessageStreamCursor; if (sessionId.harnessId === 'opencode') { toolStreamCursor = this.getOpenCodeToolStreamCursor(); messageStreamCursor = this.getOpenCodeMessageStreamCursor(); const executions = this.openCodeToolExecutionsForSession( projectId, sessionId, toolStreamCursor, ); const reasoningUpdates = this.openCodeReasoningUpdatesForSession( projectId, sessionId, messageStreamCursor, ); const textUpdates = this.openCodeTextUpdatesForSession( projectId, sessionId, messageStreamCursor, ); const corePage = await this.prepareCoreMessagePageForLiveSnapshots( detail.messagePage, [...executions, ...reasoningUpdates, ...textUpdates], async (limitArg) => { const page = await this.requireOpenCodeClient().listMessagePage( project.directory, sessionId.nativeId, { limit: limitArg }, signalArg, ); return { ...page, bundles: page.bundles.map(({ sourceRole: _sourceRole, parentMessageId: _parentMessageId, terminal: _terminal, ...bundle }) => bundle), }; }, ); const boundedLive = this.boundLiveSnapshotsForCorePage( corePage, executions, reasoningUpdates, textUpdates, ); messagePage = mergeControllerMessagePageSnapshots( corePage, boundedLive.executions, boundedLive.reasoningUpdates, boundedLive.textUpdates, ); } else { toolStreamCursor = this.getFlexToolStreamCursor(); messageStreamCursor = this.getFlexMessageStreamCursor(); const { executions, reasoningUpdates, textUpdates } = this.flexSnapshotsForSession( projectId, sessionId, toolStreamCursor, messageStreamCursor, ); const corePage = await this.prepareCoreMessagePageForLiveSnapshots( detail.messagePage, [...executions, ...reasoningUpdates, ...textUpdates], async (limitArg) => { const page = await this.requireFlexProjectionReader().listMessagePage( projectId, sessionId.nativeId, { limit: limitArg }, signalArg, ); return { bundles: page.messages.map((message) => this.toFlexMessageBundle(message)), ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }), ...(page.truncated ? { truncated: true } : {}), }; }, ); const boundedCorePage = boundControllerMessagePage( corePage, controllerInitialMessageBundleLimit, ); const boundedLive = this.boundLiveSnapshotsForCorePage( boundedCorePage, executions, reasoningUpdates, textUpdates, ); messagePage = mergeControllerMessagePageSnapshots( boundedCorePage, boundedLive.executions, boundedLive.reasoningUpdates, boundedLive.textUpdates, ); this.retireCoveredFlexSnapshots( projectId, boundedCorePage, executions, reasoningUpdates, textUpdates, ); if (executions.some((execution) => ( !this.flexCorePageRepresentsToolExecution(messagePage, execution) ))) { const advancedBothStreams = this.advanceFlexToolStreamEpoch(true); toolStreamCursor = this.getFlexToolStreamCursor(); if (advancedBothStreams) messageStreamCursor = this.getFlexMessageStreamCursor(); } if ( reasoningUpdates.some((update) => ( !this.flexCorePageRepresentsReasoningUpdate(messagePage, update) )) || textUpdates.some((update) => ( !this.flexCorePageRepresentsTextUpdate(messagePage, update) )) ) { const advancedBothStreams = this.advanceFlexMessageStreamEpoch(true); messageStreamCursor = this.getFlexMessageStreamCursor(); if (advancedBothStreams) toolStreamCursor = this.getFlexToolStreamCursor(); } } const messages = messagePage.bundles.flatMap((bundle) => bundle.messages); const authSwitchLimitsContext = await this.authSwitchLimitsContext( projectId, sessionId, sessionState.modelChoice, ); return { ...detail, messagePage, authSwitchLimitsContext, pendingPrompts: this.pendingPromptsForSession(projectId, sessionId, messages), scratchpad: sessionState.scratchpad, intelligenceExchanges: sessionState.intelligenceExchanges, ...(sessionState.modelChoice === undefined ? {} : { modelChoice: sessionState.modelChoice }), ...(sessionState.providerConnectionId === undefined ? {} : { providerConnectionId: sessionState.providerConnectionId }), toolStreamCursor, messageStreamCursor, ...(this.isProjectAutoAcceptedSession(projectId, sessionId) ? { autoAcceptPermissions: true } : {}), }; }, ); } private async readSessionScratchpadForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerMcpSessionScratchpadRead['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerMcpSessionScratchpadRead['response']> { const { projectId, sessionId } = requestArg; if (sessionId.harnessId === 'controller') { throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions do not have a scratchpad.', { code: 'invalid_request' }, ); } return this.runAudited( { type: 'session.read', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId, }, async () => { await this.resolveProject(projectId); await this.assertSessionIsNotIntelligenceTemporary(projectId, sessionId, signalArg); await this.assertSessionExists(projectId, sessionId, signalArg); const state = await this.requireAuthStore().getSessionState(projectId, sessionId); return { scratchpad: state.scratchpad }; }, ); } private async updateSessionScratchpadForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerMcpSessionScratchpadUpdate['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerMcpSessionScratchpadUpdate['response']> { const { projectId, sessionId, text, expectedRevision } = requestArg; if (sessionId.harnessId === 'controller') { throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions do not have a scratchpad.', { code: 'invalid_request' }, ); } const harnessId = sessionId.harnessId; return this.runAudited( { type: 'session.scratchpad.save', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId, }, async () => this.withActiveSessionMutation( projectId, sessionId, signalArg, async () => { const state = await this.requireAuthStore().saveSessionScratchpad( projectId, sessionId, text, expectedRevision, principalArg === 'mcp' ? 'agent' : 'user', ); void this.pushControllerEvent({ type: 'session.changed', projectId, harnessId, sessionId, timestamp: Date.now(), }).catch(() => undefined); return { scratchpad: state.scratchpad }; }, ), ); } private async sendSessionForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: TControllerSessionSendCoreRequest, signalArg: AbortSignal, ): Promise<IReq_ControllerMcpSessionSend['response']> { const { projectId, sessionId, model, providerConnectionId } = requestArg; return this.runAudited( { type: 'session.send', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId, }, async () => this.withHarnessSubmissionAdmission( projectId, sessionId, signalArg, async () => { let operation: TControllerHarnessOperationReservation | undefined; let submissionStarted = false; let submissionAccepted = false; let observeCodexOutcomeUnknown: (() => void) | undefined; try { await this.assertCodexComposerMutationAllowed(projectId, sessionId, signalArg); if (requestArg.source === 'direct') { if (model !== undefined) { if (model.harnessId !== sessionId.harnessId) { throw new plugins.typedrequest.TypedResponseError( 'The selected model belongs to another session harness.', { code: 'invalid_request' }, ); } if (model.harnessId === 'opencode') { await this.validateControllerModelChoice(model, signalArg); } } operation = await this.harnessOperationManager.reserveDirect( projectId, sessionId, requestArg.text, model, providerConnectionId, ); } else { const delivery = requestArg.delivery === 'steer' ? 'steer' : 'ordinary'; operation = await this.harnessOperationManager.reserve( 'send', projectId, sessionId, requestArg.draftRevision, { allowAttachments: sessionId.harnessId !== 'codex' || this.requireCodexClient(sessionId.nativeId).supervisor.supportsLocalAttachments, delivery, }, ); } this.claimUnusedSession(projectId, sessionId); const project = await this.resolveProject(projectId); const prompt = operation.source === 'direct' ? operation.direct.text : `${operation.draft.text.trim()}${operation.promptSuffix}`; const harnessId = requireManagedRuntimeId(sessionId).harnessId; const operationModel = operation.source === 'direct' ? operation.direct.model === undefined ? undefined : { ...operation.direct.model } as TControllerModelChoice : model; const operationProviderConnectionId = operation.source === 'direct' ? operation.direct.providerConnectionId : providerConnectionId; const sessionState = await this.requireAuthStore().getSessionState(projectId, sessionId); const selectedModel = operationModel ?? sessionState.modelChoice ?? this.defaultModelForHarness(harnessId); const selectedProviderConnectionId = operationProviderConnectionId ?? sessionState.providerConnectionId; this.assertSessionMutationAdmission(projectId, sessionId); if (sessionId.harnessId === 'opencode') { if (this.queueOpenCodePromptIfBlocked({ operation, projectDirectory: project.directory, prompt, ...(selectedModel === undefined ? {} : { model: selectedModel }), })) return { accepted: true as const }; this.harnessOperationManager.assertCurrentHarnessAdmission(operation); submissionStarted = true; const messageId: IControllerRuntimeId = { harnessId: 'opencode', nativeId: `msg_hcon_${operation.operationId}`, }; this.harnessOperationManager.bindOpenCodeMessage(operation, messageId); await this.requireOpenCodeClient().sendMessage( project.directory, sessionId.nativeId, prompt, selectedModel, undefined, signalArg, messageId.nativeId, ); submissionAccepted = true; } else if (sessionId.harnessId === 'codex') { if (selectedModel && selectedModel.harnessId !== 'codex') throw new Error('The selected model belongs to another harness.'); const steering = operation.delivery === 'steer'; if (!steering && this.queueCodexPromptIfBlocked({ operation, projectDirectory: project.directory, prompt, ...(selectedModel ? { model: selectedModel } : {}) })) return { accepted: true as const }; this.harnessOperationManager.assertCurrentHarnessAdmission(operation); const reservation = operation; const complete = () => { void this.trackOperationPromise(this.harnessOperationManager.complete(reservation)) .then(() => this.queueHarnessPromptChanged(projectId, sessionId)).catch(() => console.error('Codex prompt cleanup is incomplete.')); }; const client = this.requireCodexClient(sessionId.nativeId); observeCodexOutcomeUnknown = () => client.observeSubmissionOutcomeUnknown(sessionId.nativeId, `agl_${reservation.operationId}`); let arm: () => void; if (steering) { submissionStarted = true; arm = await client.steer(sessionId.nativeId, prompt, `agl_${operation.operationId}`, id => this.harnessOperationManager.bindCodexMessage(reservation, codexRuntimeId(id)), complete, requestArg.source === 'draft' ? requestArg.expectedTurnId : undefined, signalArg); submissionAccepted = true; this.harnessOperationManager.accept(operation); } else { arm = await client.startTurn(sessionId.nativeId, prompt, `agl_${operation.operationId}`, selectedModel, async () => { this.assertSessionMutationAdmission(projectId, sessionId); await this.requireAuthStore().prepareCodexTurn(projectId, sessionId.nativeId); submissionStarted = true; }, complete, signalArg, id => this.harnessOperationManager.bindCodexMessage(reservation, codexRuntimeId(id)), () => this.requireAuthStore().cancelUndispatchedCodexTurn(projectId, sessionId.nativeId)); submissionAccepted = true; } await this.requireAuthStore().markCodexMaterialized(projectId, sessionId.nativeId); if (!steering) this.harnessOperationManager.accept(operation); arm(); this.queueHarnessPromptChanged(projectId, sessionId); return { accepted: true as const }; } else { const flexSessionId = flexRuntimeId(sessionId.nativeId); const dispatch = async (flexModel: IFlexModelChoice): Promise<void> => { signalArg.throwIfAborted(); this.harnessOperationManager.assertCurrentHarnessAdmission(operation!); submissionStarted = true; const admission = await this.requireFlexSupervisor().request( 'prompt.start', { scopeId: projectId, sessionId: flexSessionId.nativeId, prompt, model: flexModel, ...(operation!.source === 'draft' && operation!.uploadDirectory ? { upload: { operationId: operation!.operationId, directory: operation!.uploadDirectory, }, } : {}), }, signalArg, ); submissionAccepted = true; this.harnessOperationManager.bindFlex( operation!, admission.queueId, admission.runId, flexModel, ); await this.acknowledgeFlexRun( projectId, sessionId.nativeId, admission.queueId, admission.runId, flexModel, ); }; await this.withEffectiveFlexModelChoice( projectId, selectedModel, selectedProviderConnectionId, signalArg, dispatch, ); } this.harnessOperationManager.accept(operation); this.queueHarnessPromptChanged(projectId, sessionId); if (sessionId.harnessId === 'opencode') { this.startOpenCodeOperationMonitor(projectId, sessionId, project.directory); } return { accepted: true as const }; } catch (errorArg) { let outcomeUnknown = false; if (operation) { if ( submissionAccepted || ( submissionStarted && ( operation.sessionId.harnessId === 'opencode' || (operation.sessionId.harnessId === 'codex' && !(errorArg instanceof plugins.crossharness.CodexAppServerRequestError && !errorArg.dispatched)) || this.isHarnessSubmissionOutcomeUnknown(errorArg, 'flex') ) ) ) { if ( !submissionAccepted && operation.sessionId.harnessId === 'flex' && this.isFlexPromptAdmissionTerminated(errorArg) ) { await this.harnessOperationManager.fail(operation); } else { outcomeUnknown = true; const terminalObserved = this.harnessOperationManager.markOutcomeUnknown(operation); observeCodexOutcomeUnknown?.(); if (!terminalObserved && operation.sessionId.harnessId === 'opencode') { this.failClosedOpenCodeHarness(); } } } else { await this.harnessOperationManager.fail(operation); } } if (outcomeUnknown && (requestArg.source === 'direct' || sessionId.harnessId === 'codex')) { throw new plugins.typedrequest.TypedResponseError( 'The session-send outcome is unknown. Do not retry it automatically.', { code: 'outcome_unknown' }, ); } throw asTypedResponseError(errorArg, 'controller.session.send'); } }, ), ); } private async createProjectForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerProjectCreate['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerProjectCreate['response']> { const { path } = validateRequest(() => assertProjectCreateRequest(requestArg)); const requestedDirectory = validateRequest( () => resolveProjectPathInput(path, this.requireProjectsRoot()), ); return this.runAudited( { type: 'project.create', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const { project } = await this.createProjectAndReconcile(requestedDirectory); void this.pushControllerEvent({ type: 'projects.changed', projectId: project.id, timestamp: Date.now(), }).catch(() => undefined); return { project: this.toPublicProject(project) }; }, ); } private async listModelsForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerModelList['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerModelList['response']> { const contextInput: { projectId?: string; sessionId?: IControllerRuntimeId } = validateRequest(() => { const value = codexRecord(requestArg); if (Object.keys(value).some(key => !['projectId', 'sessionId'].includes(key))) throw new Error('Invalid model context.'); return value.sessionId !== undefined ? assertSessionGetRequest(value) : assertCodexProfilesList(value); }); return this.runAudited( { type: 'model.list', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const codex = this.codexConnections && !this.codexStartupDiagnostic ? await this.codexComposerContext(contextInput.projectId, contextInput.sessionId) : undefined; const openCodeModels = await this.requireOpenCodeClient() .listModelOptions(signalArg) .catch(() => { console.error('OpenCode model listing failed; cached Flex models remain available.'); return []; }); return { models: [ ...openCodeModels, ...this.aggregateFlexModelOptions(), ...(codex?.client ? await codex.client.listModels(signalArg) : []), ], ...(codex ? { codexContext: codex.context } : {}), }; }, ); } private async createSessionForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerSessionCreate['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerSessionCreate['response']> { const { projectId, harnessId, title, model, providerConnectionId } = validateRequest( () => assertSessionCreateRequest(requestArg), ); const claimEvictionGeneration = this.claimedSessionEvictionGeneration; return this.runAudited( { type: 'session.create', peerId: this.auditPeerId(principalArg, peerIdArg) }, async (operationId) => { if (harnessId === 'codex') return this.createCodexSession(projectId, title, model, operationId, signalArg, claimEvictionGeneration); const soleDefault = model === undefined && harnessId === 'flex' ? await this.soleActiveFlexDefaultModelChoice(signalArg) : undefined; const initialModel = model ?? soleDefault?.model; const initialProviderConnectionId = model?.harnessId === 'flex' ? providerConnectionId : soleDefault?.providerConnectionId; let resolvedInitialProviderConnectionId = initialProviderConnectionId; const create = async () => { const project = await this.resolveProject(projectId); if (initialModel?.harnessId === 'opencode') { await this.validateControllerModelChoice(initialModel, signalArg); } const runtimeId: TControllerSessionId = { harnessId, nativeId: harnessId === 'opencode' ? `ses_hcon_${operationId}` : `hcon_${operationId}`, }; const requestedAt = new Date(); const expectedFlexSessionGenerationId = harnessId === 'flex' ? plugins.crypto.randomBytes(32).toString('base64url') : undefined; return this.withSessionCreationAdmission(projectId, runtimeId, async () => { await this.requireAuthStore().beginManagedSessionCreation({ projectIdentityId: projectId, runtimeId, operationId, requestedAt, ...(expectedFlexSessionGenerationId === undefined ? {} : { expectedFlexSessionGenerationId }), }); let dispatchStarted = false; try { if (harnessId === 'flex') signalArg.throwIfAborted(); const dispatchStartedAt = new Date(); await this.requireAuthStore().markManagedSessionCreationDispatched({ projectIdentityId: projectId, runtimeId, operationId, dispatchStartedAt, }); dispatchStarted = true; let session: IControllerSession; let providerSessionGeneration: string; let supervisorGeneration: string; let flexSessionGeneration: { sessionGenerationId: string; sessionGenerationSequence: number; } | undefined; if (harnessId === 'opencode') { const supervisor = this.openCodeSupervisor; const client = this.requireOpenCodeClient(); if (!supervisor) throw new Error('The OpenCode runtime is unavailable.'); const authority = this.requireOpenCodeRuntimeExitAuthority(supervisor); const observation = await client.createSessionWithId( project.directory, runtimeId.nativeId, title, AbortSignal.any([signalArg, authority.signal]), ); this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, authority); session = observation.session; providerSessionGeneration = observation.providerSessionGeneration; supervisorGeneration = authority.sessionIdentityGeneration; } else { const supervisor = this.requireFlexSupervisor(); const authority = this.requireFlexRuntimeExitAuthority(supervisor); const raw = await supervisor.request('session.create', { scopeId: projectId, options: { sessionId: runtimeId.nativeId, sessionGenerationId: expectedFlexSessionGenerationId!, ...(title === undefined ? {} : { title }), }, }, AbortSignal.any([signalArg, authority.signal])); this.assertFlexSessionIdentityGenerationCurrent(supervisor, authority); if (raw.sessionId !== runtimeId.nativeId) { throw new Error('Flex returned a different preallocated session ID.'); } session = this.toFlexSession(raw); providerSessionGeneration = requireFlexSessionProviderGeneration(raw); supervisorGeneration = authority.sessionIdentityGeneration; flexSessionGeneration = requireFlexSessionGeneration(raw); } const managedObservation = await this.requireAuthStore() .completeManagedSessionCreationAdmission({ projectIdentityId: projectId, runtimeId, operationId, supervisorGeneration, providerSessionGeneration, ...flexSessionGeneration, terminalAt: new Date(), signal: signalArg, }); if (initialModel) { await this.requireAuthStore().setSessionModelChoice( projectId, session.id, initialModel, resolvedInitialProviderConnectionId, ); } this.setManagedSessionAuthority( projectId, runtimeId, 'managed', managedObservation.identity.sessionIdentityId, ); await this.trackCreatedConversation( projectId, session, managedObservation.identity.sessionIdentityId, ); this.invalidateHarnessSessionEnumeration(projectId); const changedEvent: IControllerEvent = { type: 'sessions.changed', harnessId, projectId, timestamp: Date.now(), }; if (harnessId === 'opencode') this.queueOpenCodeControllerEvent(changedEvent); else this.queueFlexControllerEvent(changedEvent); this.markUnusedSession(projectId, session.id, claimEvictionGeneration); return { session }; } catch (errorArg) { this.setManagedSessionAuthority(projectId, runtimeId, 'uncertain'); if (!dispatchStarted) { await this.requireAuthStore().retireManagedSessionCreation({ projectIdentityId: projectId, runtimeId, operationId, terminalAt: new Date(), }).then(() => { this.deleteManagedSessionAuthorityCacheEntry(projectId, runtimeId); }).catch(() => undefined); } throw errorArg; } }); }; return initialModel?.harnessId === 'flex' ? this.withResolvedFlexModelConnection( initialModel, initialProviderConnectionId, signalArg, async (_choice, exactProviderConnectionId) => { resolvedInitialProviderConnectionId = exactProviderConnectionId; return create(); }, ) : create(); }, ); } private async updateSessionModelForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerSessionModelUpdate['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerSessionModelUpdate['response']> { const { projectId, sessionId, model, providerConnectionId } = validateRequest( () => assertSessionModelUpdateRequest(requestArg), ); return this.runAudited( { type: 'session.model.update', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId }, async () => { const update = async (resolvedProviderConnectionId?: string) => this.withActiveSessionMutation( projectId, sessionId, signalArg, async () => { await this.assertCodexComposerMutationAllowed(projectId, sessionId, signalArg); if (model.harnessId !== 'flex') { await this.validateControllerModelChoice(model, signalArg, sessionId.harnessId === 'codex' ? sessionId.nativeId : undefined); } if (sessionId.harnessId === 'codex') await this.finalizeCodexCreation(projectId, sessionId.nativeId, signalArg); const state = await this.requireAuthStore().setSessionModelChoice( projectId, sessionId, model, resolvedProviderConnectionId, ); void this.pushControllerEvent({ type: 'session.changed', projectId, harnessId: model.harnessId, sessionId, timestamp: Date.now(), }).catch(() => undefined); return { model: state.modelChoice!, ...(state.providerConnectionId === undefined ? {} : { providerConnectionId: state.providerConnectionId }), }; }, ); return model.harnessId === 'flex' ? this.withResolvedFlexModelConnection( model, providerConnectionId, signalArg, async (_choice, resolvedProviderConnectionId) => ( update(resolvedProviderConnectionId) ), ) : update(); }, ); } private async renameSessionForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerSessionRename['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerSessionRename['response']> { const { projectId, sessionId, title } = validateRequest( () => assertSessionRenameRequest(requestArg), ); this.claimUnusedSession(projectId, sessionId); return this.runAudited( { type: 'session.rename', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId }, async () => this.withActiveSessionMutation( projectId, sessionId, signalArg, async () => { const project = await this.resolveProject(projectId); if (sessionId.harnessId === 'codex') { await this.finalizeCodexCreation(projectId, sessionId.nativeId, signalArg); await this.requireCodexClient(sessionId.nativeId).rename(sessionId.nativeId, title, signalArg); return { session: await this.requireCodexClient(sessionId.nativeId).readSession(this.codexDirectory(sessionId.nativeId, project.directory), sessionId.nativeId, signalArg) }; } if (sessionId.harnessId === 'flex') { signalArg.throwIfAborted(); return { session: this.toFlexSession(await this.requireFlexSupervisor().request( 'session.update', { scopeId: projectId, sessionId: sessionId.nativeId, options: { title }, }, )), }; } return { session: await this.requireOpenCodeClient().updateSession( project.directory, sessionId.nativeId, { title }, signalArg, ), }; }, ), ); } private async archiveSessionForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerSessionArchive['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerSessionArchive['response']> { const { projectId, sessionId } = validateRequest( () => assertSessionArchiveRequest(requestArg), ); this.claimUnusedSession(projectId, sessionId); return this.runAudited( { type: 'session.archive', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId }, async () => ({ session: await this.archiveSession(projectId, sessionId, signalArg), }), ); } private async abortSessionForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerSessionAbort['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerSessionAbort['response']> { const { projectId, sessionId, expectedTurnId } = validateRequest( () => assertSessionAbortRequest(requestArg), ); this.claimUnusedSession(projectId, sessionId); return this.runAudited( { type: 'session.abort', peerId: this.auditPeerId(principalArg, peerIdArg), sessionId }, async () => this.withActiveSessionMutation(projectId, sessionId, signalArg, async () => { const project = await this.resolveProject(projectId); const sessionKey = this.sessionStateMutationKey(projectId, sessionId); this.abortingSessionKeys.add(sessionKey); try { await this.waitForSessionHarnessSubmissionAdmissions(sessionKey); if (sessionId.harnessId === 'codex') { const client = this.requireCodexClient(sessionId.nativeId); const interrupted = await client.abort( sessionId.nativeId, expectedTurnId, signalArg, ); await client.waitForTurnTerminal(sessionId.nativeId, interrupted.turnId, signalArg); if (interrupted.writer === 'agl') { await this.harnessOperationManager.releaseSession(projectId, sessionId); this.pausedCodexQueues.delete(sessionKey); } this.queueHarnessPromptChanged(projectId, sessionId); return { success: true }; } if (sessionId.harnessId === 'opencode') { const success = await this.requireOpenCodeClient().abortSession( project.directory, sessionId.nativeId, signalArg, ); if (success) { await this.harnessOperationManager.releaseSession(projectId, sessionId); this.queueHarnessPromptChanged(projectId, sessionId); } else if ( this.harnessOperationManager.listSessionActive(projectId, sessionId).length > 0 ) { this.failClosedOpenCodeHarness(); } return { success }; } const active = this.harnessOperationManager.listSessionActive(projectId, sessionId); let success = false; signalArg.throwIfAborted(); for (const operation of active) { if (!operation.flexQueueId) continue; const result = await this.requireFlexSupervisor().request('prompt.cancel', { scopeId: projectId, sessionId: sessionId.nativeId, queueId: operation.flexQueueId, }); success ||= result.accepted; } return { success }; } finally { this.abortingSessionKeys.delete(sessionKey); } }), ); } /** Internal lifecycle work observes an exact native turn before issuing its interrupt. */ private async abortObservedCodexTurn( clientArg: CodexClientAdapter, nativeIdArg: string, signalArg: AbortSignal, ): Promise<{ turnId: string; writer: 'agl' | 'external' }> { const observed = await clientArg.refreshActivity(nativeIdArg, signalArg); if (!observed.turnId || !observed.canInterrupt) { throw new Error('Codex has no exact interruptible turn on this connection.'); } return clientArg.abort(nativeIdArg, observed.turnId, signalArg); } private async listResourcesForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceList['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceList['response']> { const { projectId } = validateRequest(() => assertResourceListRequest(requestArg)); return this.runAudited( { type: 'resource.list', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { if (projectId !== undefined) { await this.resolveProject(projectId); return { resources: await this.requireResourceCoordinator().listResources(projectId, signalArg), }; } // Cross-project listing follows the tracked conversations: a project the sidebar // never shows must not contribute resources to it. const scope = await this.trackedConversationProjectScope(signalArg); const resources: TControllerResource[] = []; for (const project of scope.projects) { signalArg.throwIfAborted(); resources.push( ...await this.requireResourceCoordinator().listResources(project.id, signalArg), ); } return { resources, ...(scope.truncated ? { truncated: true as const } : {}) }; }, ); } private async createResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceCreate['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceCreate['response']> { const { projectId, kind, title, agent } = validateRequest(() => assertResourceCreateRequest(requestArg)); return this.runAudited( { type: 'resource.create', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { await this.resolveProject(projectId); const createInput = kind === 'terminal' ? { projectId, kind, ...await this.requireTerminalManager().resolveTerminalResourceMetadata( projectId, title, agent, ), } : { projectId, kind, title: title ?? 'Browser' }; const resource = await this.requireResourceCoordinator().createResource( createInput, signalArg, ); this.pushResourceChanged(projectId); return { resource }; }, ); } private async renameResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceRename['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceRename['response']> { const { projectId, resourceId, title } = validateRequest(() => assertResourceRenameRequest(requestArg)); return this.runAudited( { type: 'resource.rename', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const resource = await this.requireResourceCoordinator() .renameResource(projectId, resourceId, title); this.pushResourceChanged(projectId); return { resource }; }, ); } private async attachResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceAttach['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceAttach['response']> { const parsed = validateRequest(() => assertResourceAttachRequest(requestArg)); const sessionId = parsed.sessionId; return this.runAudited( { type: 'resource.attach', peerId: this.auditPeerId(principalArg, peerIdArg), ...(sessionId === undefined ? {} : { sessionId }), }, async () => { if (sessionId !== undefined) { await this.assertSessionExists(parsed.projectId, sessionId, signalArg); this.assertSessionMutationAdmission(parsed.projectId, sessionId); } // Attaching adds a membership. A `replace` makes it a Move in one step, so the resource // is never momentarily unattached. const resource = await this.requireResourceCoordinator().changeAttachment({ projectId: parsed.projectId, resourceId: parsed.resourceId, expectedAttachmentRevision: parsed.expectedAttachmentRevision, op: parsed.replace === undefined ? 'add' : 'replace', target: sessionId === undefined ? { kind: 'terminal', resourceId: parsed.terminalResourceId! } : { kind: 'session', sessionId }, ...(parsed.replace === undefined ? {} : { replaces: parsed.replace }), signal: signalArg, }); this.pushResourceChanged(parsed.projectId); return { resource }; }, ); } private async detachResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceDetach['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceDetach['response']> { const parsed = validateRequest(() => assertResourceDetachRequest(requestArg)); return this.runAudited( { type: 'resource.detach', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { // Detaching removes one membership, or clears the set when no entry is named. const resource = await this.requireResourceCoordinator().changeAttachment({ projectId: parsed.projectId, resourceId: parsed.resourceId, expectedAttachmentRevision: parsed.expectedAttachmentRevision, op: 'remove', target: parsed.entry ?? null, signal: signalArg, }); this.pushResourceChanged(parsed.projectId); return { resource }; }, ); } private async startResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceStart['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceStart['response']> { const { projectId, resourceId } = validateRequest(() => assertResourceTargetRequest( requestArg, 'resource start request', )); return this.runAudited( { type: 'resource.start', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const resource = await this.requireResourceCoordinator() .startResource(projectId, resourceId, signalArg); this.pushResourceChanged(projectId); return { resource }; }, ); } private async stopResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceStop['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceStop['response']> { const { projectId, resourceId } = validateRequest(() => assertResourceTargetRequest( requestArg, 'resource stop request', )); return this.runAudited( { type: 'resource.stop', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { const resource = await this.requireResourceCoordinator() .stopResource(projectId, resourceId, signalArg); this.pushResourceChanged(projectId); return { resource }; }, ); } private publicSettings(settings: Awaited<ReturnType<SmartDataAuthStore['getSettings']>>): IReq_ControllerSettingsGet['response']['settings'] { return { defaultModels: settings.defaultModels, standardProjectDirectories: [...(settings.standardProjectDirectories ?? [])], ...(settings.lastSessionHarnessId === undefined ? {} : { lastSessionHarnessId: settings.lastSessionHarnessId }), ...(settings.autoAcceptPermissions === true ? { autoAcceptPermissions: true } : {}), ...(settings.browserVideoBackend === 'native' ? { browserVideoBackend: 'native' as const } : {}), ...(this.activeBrowserVideoBackend === 'native' ? { activeBrowserVideoBackend: 'native' as const } : {}), }; } private readControllerSettings(peerId: string): Promise<IReq_ControllerSettingsGet['response']> { return this.runAudited({ type: 'settings.read', peerId }, async () => ({ settings: this.publicSettings(await this.requireAuthStore().getSettings()), projectsRoot: this.requireProjectsRoot(), })); } private updateControllerSettings(peerId: string, patch: IReq_ControllerSettingsUpdate['request'], signalArg: AbortSignal): Promise<IReq_ControllerSettingsUpdate['response']> { return this.runAudited({ type: 'settings.update', peerId }, async () => { const previousAutoAccept = this.autoAcceptPermissions; let sweepOnEnable = false; const updateSettings = async () => { if (patch.defaultModels) { for (const modelChoice of patch.defaultModels) { await this.validateControllerModelChoice(modelChoice, signalArg); } } // A standard directory that does not exist would silently produce an empty // Open search and a failing New, so it is rejected at the boundary. const standardProjectDirectories = patch.standardProjectDirectories === undefined ? undefined : await this.resolveStandardProjectDirectories(patch.standardProjectDirectories); const settings = await this.requireAuthStore().updateSettings({ ...patch, ...(standardProjectDirectories === undefined ? {} : { standardProjectDirectories }), }); this.defaultModels = settings.defaultModels; const autoAcceptEnabled = settings.autoAcceptPermissions === true; sweepOnEnable = autoAcceptEnabled && !previousAutoAccept; this.autoAcceptPermissions = autoAcceptEnabled; void this.pushControllerEvent({ type: 'settings.changed', timestamp: Date.now(), }).catch(() => undefined); return { settings: this.publicSettings(settings), }; }; const disablingAutoAccept = patch.autoAcceptPermissions === false && previousAutoAccept; const releaseAutoAcceptSeal = disablingAutoAccept ? this.acquireOpenCodeAutoAcceptAdmissionSeal() : undefined; try { if (disablingAutoAccept) { await this.waitForAllOpenCodeAutoAcceptAdmissions(signalArg); } const response = await updateSettings(); return response; } finally { releaseAutoAcceptSeal?.(); if (sweepOnEnable) { // Persist enablement before admitting replies that were already waiting. this.sweepAutoAcceptAcrossProjects(); } } }); } private async retireResourceForPrincipal( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerResourceRetire['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerResourceRetire['response']> { const { projectId, resourceId } = validateRequest(() => assertResourceTargetRequest( requestArg, 'resource retire request', )); return this.runAudited( { type: 'resource.retire', peerId: this.auditPeerId(principalArg, peerIdArg) }, async () => { await this.requireResourceCoordinator().retireResource(projectId, resourceId, signalArg); // A retired resource is a peer of the conversations in the ordered sidebar, so it must // not be left behind as a dangling layout member. if (await this.requireAuthStore().removeResourceFromLayout(projectId, resourceId)) { this.pushSessionLayoutChanged(); } this.pushResourceChanged(projectId); return { retired: true as const }; }, ); } private async prepareMcpSessionTarget( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal, ): Promise<void> { if (this.managedSessionAuthority(projectIdArg, sessionIdArg).state !== 'managed') { await this.listSessionsForPrincipal('mcp', 'mcp', { projectId: projectIdArg }, signalArg); } // Discovery records a native root; every operation still revalidates its // exact project, origin, runtime generation and deletion/archive fences. await this.assertSessionExists(projectIdArg, sessionIdArg, signalArg); } private async resolveMcpContext( principalArg: TControllerOperationPrincipal, peerIdArg: string, requestArg: IReq_ControllerMcpContextResolve['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerMcpContextResolve['response']> { const directory = await this.resolveExistingProjectDirectory(requestArg.directory); const projects = await this.listProjectsForPrincipal(principalArg, peerIdArg, signalArg); const project = projects.projects.find((candidate) => candidate.directory === directory.directory); if (!project) throw new plugins.typedrequest.TypedResponseError( 'Register this exact directory with project_add before resolving its context.', { code: 'project_not_found' }, ); await this.resolveProject(project.id); if (!requestArg.sessionId) return { project }; const target = requestArg.sessionId; const { sessions } = await this.listSessionsForPrincipal( principalArg, peerIdArg, { projectId: project.id }, signalArg, ); const matches = sessions.filter((session) => { if (session.archivedAt !== undefined || session.parentId !== undefined || session.id.harnessId !== target.harnessId) return false; if (session.id.nativeId === target.nativeId) return true; return target.harnessId === 'codex' && this.codexConnections !== undefined && this.codexConnections.origin(session.id.nativeId, project.id).rawThreadId === target.nativeId; }); if (matches.length !== 1) throw new plugins.typedrequest.TypedResponseError( matches.length ? 'This native ID exists on multiple connections; select its qualified AGL ID.' : 'The requested native task is unavailable in this project and its configured connection.', { code: matches.length ? 'ambiguous_session' : 'session_not_found' }, ); const session = await this.readManagedSession(project.id, matches[0]!.id, signalArg); return { project, session }; } /** The chat this MCP call runs as, proven by the credential on the request envelope. */ private mcpCaller(): IControllerMcpCaller { return currentControllerMcpCaller(); } private mcpIdentityRequired(): plugins.typedrequest.TypedResponseError { return new plugins.typedrequest.TypedResponseError( 'This operation is available only to an AGL-launched chat; this MCP host has no AGL identity.', { code: 'identity_required' }, ); } private mcpSubjectForbidden(): plugins.typedrequest.TypedResponseError { return new plugins.typedrequest.TypedResponseError( 'This operation is limited to the tasks and resources of the calling chat.', { code: 'identity_forbidden' }, ); } /** Discovery and mutation both require an identity; only the reach differs. */ private requireMcpSubject(): Exclude<TControllerMcpSubject, { kind: 'anonymous' }> { const { subject } = this.mcpCaller(); if (subject.kind === 'anonymous') throw this.mcpIdentityRequired(); return subject; } /** * Resource operations act inside one project. A task or terminal subject owns exactly its own * project; a shared OpenCode runtime is scoped to the project named in the request. */ private requireMcpResourceSubject(projectIdArg: string): TControllerResourceSubject { const subject = this.requireMcpSubject(); if (subject.kind === 'runtime') return { kind: 'runtime' }; if (subject.projectId !== projectIdArg) throw this.mcpSubjectForbidden(); if (subject.kind === 'terminal') { return { kind: 'terminal', resourceId: subject.resourceId, // The conversation, not just the terminal, is the subject: a terminal restarted into a // different conversation must not inherit the previous conversation's attachments. ...(subject.agentSessionId === undefined ? {} : { agentSessionId: subject.agentSessionId }), }; } return { kind: 'session', sessionId: subject.sessionId, sessionIdentityId: subject.sessionIdentityId, }; } /** * Task *mutations* are limited to the caller's own task; a terminal subject owns no task and an * unidentified host owns nothing. Task reads are deliberately not gated here: a cross-harness * chat outside any AGL terminal still reads AGL-managed tasks with the descriptor token alone. * * The gate also binds the task to the project named in the request, so a shared harness runtime * — which owns no single task — cannot reach a task outside the project it addressed. */ private async requireMcpSessionMutationTarget( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal, ): Promise<void> { const subject = this.requireMcpSubject(); if (subject.kind === 'terminal') throw this.mcpSubjectForbidden(); if (subject.kind === 'runtime') { if (sessionIdArg.harnessId !== 'opencode') throw this.mcpSubjectForbidden(); } else if ( subject.projectId !== projectIdArg || subject.sessionId.harnessId !== sessionIdArg.harnessId || subject.sessionId.nativeId !== sessionIdArg.nativeId ) throw this.mcpSubjectForbidden(); await this.prepareMcpSessionTarget(projectIdArg, sessionIdArg, signalArg); } /** A caller sees what is attached to it, plus what is attached to nothing. */ private filterMcpVisibleResources( resourcesArg: TControllerResource[], subjectArg: TControllerMcpSubject, ): TControllerResource[] { return resourcesArg.filter((resource) => { const entries = resource.attachment.entries; if (entries.length === 0) return true; // Another chat's entry shows this caller nothing: only its own membership counts. if (subjectArg.kind === 'runtime') { return entries.some((entry) => ( entry.kind === 'session' && (entry.id as TControllerSessionId).harnessId === 'opencode' )); } if (subjectArg.kind === 'terminal') { // The same fence the gates use, so a restarted terminal is never shown an attachment made // by a conversation it can no longer act for. return entries.some((entry) => ( entry.kind === 'terminal' && entry.id === subjectArg.resourceId && entry.agentSessionId === subjectArg.agentSessionId )); } if (subjectArg.kind === 'session') { return entries.some((entry) => ( entry.kind === 'session' && (entry.id as TControllerSessionId).harnessId === subjectArg.sessionId.harnessId && (entry.id as TControllerSessionId).nativeId === subjectArg.sessionId.nativeId )); } return false; }); } private mcpAttachmentTargetFor( projectIdArg: string, requestedSessionIdArg: TControllerSessionId | undefined, ): TControllerResourceAttachmentTarget { const subject = this.requireMcpSubject(); if (subject.kind === 'runtime') { // A shared runtime has no single chat, so it must name which of its own tasks to attach to. if (!requestedSessionIdArg) throw this.mcpSubjectForbidden(); if (requestedSessionIdArg.harnessId !== 'opencode') throw this.mcpSubjectForbidden(); return { kind: 'session', sessionId: requestedSessionIdArg }; } if (requestedSessionIdArg) throw this.mcpSubjectForbidden(); if (subject.projectId !== projectIdArg) throw this.mcpSubjectForbidden(); return subject.kind === 'terminal' ? { kind: 'terminal', resourceId: subject.resourceId } : { kind: 'session', sessionId: subject.sessionId }; } /** * The membership an MCP caller may remove: its own. A shared runtime owns no single chat, so it * must name which of its own tasks to release, the same rule attach applies. */ private mcpDetachEntryFor( projectIdArg: string, requestedSessionIdArg: TControllerSessionId | undefined, ): TControllerResourceAttachmentTarget { const subject = this.requireMcpSubject(); if (subject.kind === 'runtime') { // A shared runtime owns no single chat, so it must name which of its own tasks to release. if (!requestedSessionIdArg) throw this.mcpSubjectForbidden(); if (requestedSessionIdArg.harnessId !== 'opencode') throw this.mcpSubjectForbidden(); return { kind: 'session', sessionId: requestedSessionIdArg }; } if (requestedSessionIdArg) throw this.mcpSubjectForbidden(); if (subject.projectId !== projectIdArg) throw this.mcpSubjectForbidden(); return subject.kind === 'terminal' ? { kind: 'terminal', resourceId: subject.resourceId } : { kind: 'session', sessionId: subject.sessionId }; } /** Detaching is an ownership operation: only a subject may release its own resource. */ private async assertMcpResourceOwnership( projectIdArg: string, resourceIdArg: string, allowUnattachedArg: boolean, ): Promise<void> { const subject = this.requireMcpResourceSubject(projectIdArg); const resource = await this.requireAuthStore().getResource(projectIdArg, resourceIdArg); if (!resource) throw new ControllerResourceUnavailableError('The resource does not exist.'); if (!isResourceAttached(resource)) { if (allowUnattachedArg) return; throw this.mcpSubjectForbidden(); } if (subject.kind === 'runtime') { if (!hasSessionAttachmentOfHarness(resource, 'opencode')) throw this.mcpSubjectForbidden(); return; } if (subject.kind === 'terminal') { if (!hasExactTerminalAttachment(resource, subject.resourceId, subject.agentSessionId)) { throw this.mcpSubjectForbidden(); } return; } // Another chat's membership authorizes nothing here: this caller needs its own. if (!hasExactSessionAttachment(resource, subject.sessionId, subject.sessionIdentityId)) { throw this.mcpSubjectForbidden(); } } private async executeMcpBrowserAction( requestArg: IReq_ControllerMcpBrowserAction['request'], signalArg: AbortSignal, ): Promise<IReq_ControllerMcpBrowserAction['response']> { const subject = this.requireMcpResourceSubject(requestArg.projectId); if (subject.kind === 'session') { await this.prepareMcpSessionTarget(requestArg.projectId, subject.sessionId, signalArg); } signalArg.throwIfAborted(); const resource = await this.requireResourceCoordinator().requireSubjectBrowserResource({ projectId: requestArg.projectId, resourceId: requestArg.resourceId, attachmentRevision: requestArg.expectedAttachmentRevision, subject, }); signalArg.throwIfAborted(); const host = this.browserResourceHost; if (!host?.isAvailable()) throw new ControllerResourceUnavailableError('The browser runtime is unavailable.'); // BrowserRuntime issues capabilities to conversations, so the acting identity must be a // conversation this caller's *own* admission rests on — never an arbitrary member of a shared // set. A chat acts as itself. A shared OpenCode runtime was admitted because the browser is // attached to one of its own OpenCode tasks, so it acts as that task and never as some other // harness's chat that happens to share the browser. A terminal subject acts as the // conversation that owns the terminal, which BrowserRuntime names as a `claude` session: it is // taken from the resource's own terminal entry for this exact terminal and conversation — the // fence the resource gate above already proved — and never from the request. const actingSessionId: plugins.browserRuntime.TQualifiedBrowserSessionId | undefined = subject.kind === 'session' ? subject.sessionId : subject.kind === 'runtime' ? resourceSessionEntries(resource).find((entry) => entry.id.harnessId === 'opencode')?.id : this.terminalActingSessionId(resource, subject); if (actingSessionId === undefined) { // What lands here is a caller that holds no conversation in this set: a shared runtime whose // own harness has no task attached, or a plain shell terminal, which owns no conversation at // all. Neither has authority to borrow, because a set of other people's chats is not // authority this caller holds. throw new ControllerResourceUnavailableError( 'The browser is not attached to a task this caller can drive.', ); } return { result: await host.executeMcpAction({ resource, actingSessionId, action: requestArg.action, signal: signalArg, }), }; } /** * The conversation a terminal subject drives as: the coding agent's own chat, carried by the * resource's terminal entry for exactly this terminal and conversation. A plain shell terminal * carries none and therefore drives nothing — matching the binding, which admits a terminal's * conversation only when the entry names one. */ private terminalActingSessionId( resourceArg: IControllerResourceDocument, subjectArg: { resourceId: string; agentSessionId?: string }, ): plugins.browserRuntime.TQualifiedBrowserSessionId | undefined { const agentSessionId = resourceTerminalEntries(resourceArg).find((entry) => ( entry.id === subjectArg.resourceId && entry.agentSessionId === subjectArg.agentSessionId ))?.agentSessionId; return agentSessionId === undefined ? undefined : { harnessId: 'claude', nativeId: agentSessionId }; } private createControllerMcpApi( controllerPortArg: number, lifecycleGenerationArg: string, ): IControllerMcpPrivateApi { const run = <T>( signalArg: AbortSignal, operationArg: (operationSignalArg: AbortSignal) => Promise<T>, ): Promise<T> => this.runPrincipalOperation( 'mcp', 'mcp', operationArg, signalArg, undefined, true, ); return { getSettings: signal => run(signal, () => this.readControllerSettings('mcp')), updateSettings: (request, signal) => run(signal, operationSignal => this.updateControllerSettings( 'mcp', validateRequest(() => assertSettingsUpdateRequest(request)), operationSignal)), authswitch: (requestArg, signalArg) => run(signalArg, async () => ({ operation: await this.getAuthSwitchService().request(requestArg), })), workspace: { 'agl.mcp.browser.action': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { return this.executeMcpBrowserAction(requestArg, operationSignalArg); }), 'agl.mcp.project.create': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { this.requireMcpSubject(); return this.createProjectForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.model.list': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { if (requestArg.sessionId && requestArg.projectId) { await this.prepareMcpSessionTarget(requestArg.projectId, requestArg.sessionId, operationSignalArg); } return this.listModelsForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.session.create': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { this.requireMcpSubject(); return this.createSessionForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.session.model.update': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.requireMcpSessionMutationTarget( requestArg.projectId, requestArg.sessionId, operationSignalArg, ); return this.updateSessionModelForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.session.rename': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.requireMcpSessionMutationTarget( requestArg.projectId, requestArg.sessionId, operationSignalArg, ); return this.renameSessionForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.session.archive': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.requireMcpSessionMutationTarget( requestArg.projectId, requestArg.sessionId, operationSignalArg, ); return this.archiveSessionForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.session.abort': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.requireMcpSessionMutationTarget( requestArg.projectId, requestArg.sessionId, operationSignalArg, ); return this.abortSessionForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.resource.list': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { const { subject } = this.mcpCaller(); const listed = await this.listResourcesForPrincipal( 'mcp', 'mcp', requestArg, operationSignalArg, ); return { resources: this.filterMcpVisibleResources(listed.resources, subject) }; }), 'agl.mcp.resource.create': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { this.requireMcpResourceSubject(requestArg.projectId); return this.createResourceForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.resource.rename': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.assertMcpResourceOwnership(requestArg.projectId, requestArg.resourceId, true); return this.renameResourceForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.resource.attach': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { const target = this.mcpAttachmentTargetFor(requestArg.projectId, requestArg.sessionId); if (target.kind === 'session') { await this.prepareMcpSessionTarget( requestArg.projectId, target.sessionId, operationSignalArg, ); } return this.attachResourceForPrincipal('mcp', 'mcp', { projectId: requestArg.projectId, resourceId: requestArg.resourceId, expectedAttachmentRevision: requestArg.expectedAttachmentRevision, ...(target.kind === 'session' ? { sessionId: target.sessionId } : { terminalResourceId: target.resourceId }), }, operationSignalArg); }), 'agl.mcp.resource.detach': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.assertMcpResourceOwnership(requestArg.projectId, requestArg.resourceId, false); // An MCP caller removes its own membership and never clears the set: other chats // attached to the same resource keep theirs. The subject comes from the caller's // credential, exactly as it does for attach, never from the request. const entry = this.mcpDetachEntryFor(requestArg.projectId, requestArg.sessionId); return this.detachResourceForPrincipal('mcp', 'mcp', { projectId: requestArg.projectId, resourceId: requestArg.resourceId, expectedAttachmentRevision: requestArg.expectedAttachmentRevision, entry, }, operationSignalArg); }), 'agl.mcp.resource.start': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.assertMcpResourceOwnership(requestArg.projectId, requestArg.resourceId, true); return this.startResourceForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.resource.stop': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.assertMcpResourceOwnership(requestArg.projectId, requestArg.resourceId, true); return this.stopResourceForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.resource.retire': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { await this.assertMcpResourceOwnership(requestArg.projectId, requestArg.resourceId, true); return this.retireResourceForPrincipal('mcp', 'mcp', requestArg, operationSignalArg); }), 'agl.mcp.context.resolve': async (requestArg, signalArg) => run(signalArg, async (operationSignalArg) => { return this.resolveMcpContext('mcp', 'mcp', requestArg, operationSignalArg); }), }, status: async (principalArg, signalArg) => run(signalArg, async () => ({ status: this.statusForPrincipal(principalArg), controllerPort: controllerPortArg, lifecycleGeneration: lifecycleGenerationArg, })), listProjects: async (principalArg, signalArg) => run( signalArg, async (operationSignalArg) => this.listProjectsForPrincipal( principalArg, 'mcp', operationSignalArg, ), ), listSessions: async (principalArg, requestArg, signalArg) => run( signalArg, async (operationSignalArg) => this.listSessionsForPrincipal( principalArg, 'mcp', requestArg, operationSignalArg, ), ), readSession: async (principalArg, requestArg, signalArg) => run( signalArg, async (operationSignalArg) => { await this.prepareMcpSessionTarget(requestArg.projectId, requestArg.sessionId, operationSignalArg); return this.readSessionForPrincipal( principalArg, 'mcp', requestArg, operationSignalArg, ); }, ), sendSession: async (principalArg, requestArg, signalArg) => run( signalArg, async (operationSignalArg) => { await this.requireMcpSessionMutationTarget( requestArg.projectId, requestArg.sessionId, operationSignalArg, ); return this.sendSessionForPrincipal( principalArg, 'mcp', { source: 'direct', ...requestArg }, operationSignalArg, ); }, ), readSessionScratchpad: async (principalArg, requestArg, signalArg) => run( signalArg, async (operationSignalArg) => { await this.prepareMcpSessionTarget(requestArg.projectId, requestArg.sessionId, operationSignalArg); return this.readSessionScratchpadForPrincipal( principalArg, 'mcp', requestArg, operationSignalArg, ); }, ), updateSessionScratchpad: async (principalArg, requestArg, signalArg) => run( signalArg, async (operationSignalArg) => { await this.requireMcpSessionMutationTarget( requestArg.projectId, requestArg.sessionId, operationSignalArg, ); return this.updateSessionScratchpadForPrincipal( principalArg, 'mcp', requestArg, operationSignalArg, ); }, ), }; } private async performStart(): Promise<IControllerStartResult> { process.env.AGL_HOME = resolveAGLHomePaths().root; this.lifecycleState = 'starting'; this.captureInheritedUpgradeCoordination(); const identity = await readControllerProcessIdentity(process.pid); this.assertStartActive(); if (identity) { this.processIdentity = identity; this.processGroupId = identity.processGroupId; this.processFingerprint = identity.fingerprint; } let upgradeCliPath: string | undefined; if (this.options.upgradeToken && this.options.upgradeGlobalRoot) { const inheritedCoordinator = this.upgradeCoordinator; if (!inheritedCoordinator || this.upgradeToken !== this.options.upgradeToken) { throw new Error('The inherited upgrade coordination was not captured before startup.'); } const transaction = await inheritedCoordinator.readTransaction(this.options.upgradeToken); const installation = await resolveInheritedUpgradeTargetInstallation(transaction); const cliPath = await plugins.fs.promises.realpath( plugins.url.fileURLToPath(new URL('../cli.js', import.meta.url)), ); if ( installation.globalRoot !== this.options.upgradeGlobalRoot || installation.cliPath !== cliPath ) throw new Error('The inherited upgrade target is not the active pnpm-global installation.'); assertInheritedUpgradeTransaction(transaction, { packageName: String(controllerPackageName), packageVersion: commitinfo.version, managementVersion: Number(controllerUpgradeManagementVersion), cliName: currentCliName, cliRelativePath: './cli.js', }); this.upgradeStatus = await inheritedCoordinator.readSanitizedUpgradeStatus( this.options.upgradeToken, ); upgradeCliPath = cliPath; await inheritedCoordinator.recordController(this.options.upgradeToken, { pid: process.pid, processGroupId: this.processGroupId, fingerprint: this.processFingerprint, cliPath, command: this.options.processMode === 'detached' ? '__serve' : 'foreground', }); this.upgradeBarrier = this.harnessOperationManager.beginUpgradeBarrier(); this.upgradeBarrier.sessions = transaction.sessions.map((session) => ({ projectId: session.projectId, sessionId: { ...session.sessionId }, })); this.startUpgradeWorkerWatchdog(); } const retainMigrationAuthority = Boolean( this.options.upgradeToken && this.options.upgradeGlobalRoot, ); let startupBlockedPublicationTask: Promise<void> | undefined; let startupWasBlocked = false; let latestStartupBlockedError: Error | undefined; const publishStartupBlocked = this.options.onStartupBlocked ? (errorArg: Error): Promise<void> => { startupWasBlocked = true; latestStartupBlockedError = errorArg; if (!startupBlockedPublicationTask) { let task: Promise<void>; task = (async () => { let notificationError: unknown; try { await this.options.onStartupBlocked!(errorArg); } catch (callbackErrorArg) { notificationError = callbackErrorArg; } let retentionError: unknown; if (retainMigrationAuthority && upgradeCliPath) { try { await this.retainUpgradeOwnership(upgradeCliPath); } catch (ownershipErrorArg) { retentionError = ownershipErrorArg; } } if (notificationError !== undefined && retentionError !== undefined) { throw new AggregateError( [notificationError, retentionError], 'Retained startup publication and upgrade ownership both failed.', ); } if (notificationError !== undefined) throw notificationError; if (retentionError !== undefined) throw retentionError; })().catch((publicationErrorArg) => { if (startupBlockedPublicationTask === task) { startupBlockedPublicationTask = undefined; } throw publicationErrorArg; }); startupBlockedPublicationTask = task; } return startupBlockedPublicationTask; } : undefined; const retainedStartupTimer = retainMigrationAuthority && publishStartupBlocked ? setTimeout(() => { void publishStartupBlocked(new Error( 'Controller data-root migration is still in progress.', )).catch((errorArg) => { console.error('Publishing retained controller startup failed.', errorArg); }); }, 30_000) : undefined; let controllerDataRoot: Awaited<ReturnType<typeof ensureControllerDataRootForStartup>> | undefined; let migrationError: unknown; try { controllerDataRoot = await ensureControllerDataRootForStartup({ signal: this.operationAbortController.signal, retainOnFailure: retainMigrationAuthority, ...(publishStartupBlocked ? { onBlocked: publishStartupBlocked } : {}), }); } catch (errorArg) { migrationError = errorArg; } finally { if (retainedStartupTimer) clearTimeout(retainedStartupTimer); } let publicationError: unknown; try { if ( migrationError === undefined && startupWasBlocked && !startupBlockedPublicationTask && latestStartupBlockedError && publishStartupBlocked ) { await publishStartupBlocked(latestStartupBlockedError); } await startupBlockedPublicationTask; } catch (errorArg) { publicationError = errorArg; } if ( migrationError !== undefined && publicationError !== undefined && migrationError !== publicationError ) { throw new AggregateError( [migrationError, publicationError], 'Controller data-root migration and retained ownership both failed.', ); } if (migrationError !== undefined) throw migrationError; if (publicationError !== undefined) throw publicationError; if (!controllerDataRoot) throw new Error('Controller data-root migration returned no result.'); this.assertStartActive(); const databaseConfig = controllerDataRoot.databaseConfig; const dataDirectory = controllerDataRoot.directoryPath; await ensureGitReversionParentDirectory(dataDirectory); this.assertStartActive(); const authStore = new SmartDataAuthStore(databaseConfig); this.authStore = authStore; await authStore.initForController(dataDirectory); this.assertStartActive(); const storedConfig = await authStore.getRuntimeConfig( this.options.runtimeOverrides.controllerPort, ); this.assertStartActive(); const runtimeConfig = storedConfig ? await resolveStoredRuntimeConfig(storedConfig, this.options.runtimeOverrides) : await resolveInitialRuntimeConfig(this.options.runtimeOverrides); this.assertStartActive(); const authDocument = await authStore.resolveOrCreateRuntimeConfig(runtimeConfig); this.assertStartActive(); this.runtimeConfig = runtimeConfig; this.controllerId = authDocument.id; this.openCodeRuntimeDirectory = plugins.path.join( dataDirectory, 'runtime', 'opencode', plugins.crypto.createHash('sha256').update(authDocument.id, 'utf8').digest('hex'), ); await plugins.fs.promises.mkdir(this.openCodeRuntimeDirectory, { recursive: true, mode: 0o700, }); await plugins.fs.promises.chmod(this.openCodeRuntimeDirectory, 0o700); const passkeyManager = new PasskeyManager({ store: authStore }); this.passkeyManager = passkeyManager; const authState = await passkeyManager.getAuthState(); this.setupRequired = authState.state === 'setupRequired'; this.assertStartActive(); const router = this.createRouter(runtimeConfig.publicOrigin); const trustedProxySecret = readTrustedProxySecret(runtimeConfig.tlsMode); const publicHostname = new URL(runtimeConfig.publicOrigin).hostname; const hostnames = [...new Set([publicHostname, 'localhost', '127.0.0.1'])]; const serveDirectory = plugins.path.resolve( plugins.path.dirname(plugins.url.fileURLToPath(import.meta.url)), '..', 'dist_serve', ); const typedServer = new plugins.typedserver.TypedServer({ port: runtimeConfig.controllerPort, // local mode has no transport secret and its admission rests on // browser-supplied headers, which any network client can forge — so the // listener itself is the boundary and must stay loopback-only. Proxy mode // binds wide by design; the readme requires backend isolation there. listenHostname: runtimeConfig.tlsMode === 'local' ? '127.0.0.1' : '0.0.0.0', cors: false, websocketMaxPayloadBytes: websocketPayloadLimit, connectionTimeout: 120_000, headersTimeout: 10_000, requestTimeout: 30_000, virtualStreamAuthorizationAdapter: { bind: (authorizationArg, contextArg) => this.bindBrowserStreamAuthorization( authorizationArg, contextArg, ), }, surfaces: [ createBrowserDevToolsSurface(hostnames), { name: 'controller', match: { hostnames, pathPrefixes: ['/'], websocketPathPrefixes: ['/'] }, websocketTypedRouter: router, typedRequestPath: false, includeBuiltinTypedHandlers: false, serveDir: serveDirectory, spaFallback: true, healthzEndpoint: false, cors: false, noCache: true, requestAdmission: (contextArg) => { if (contextArg.method === 'GET' || contextArg.method === 'HEAD') return true; return new Response('Method not allowed', { status: 405, headers: { Allow: 'GET, HEAD' }, }); }, websocketAdmission: (contextArg) => isControllerWebSocketRequestAllowed( contextArg.headers, runtimeConfig.publicOrigin, runtimeConfig.tlsMode, trustedProxySecret, ), securityHeaders: { csp: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'blob:'], fontSrc: ["'self'", 'data:'], connectSrc: ["'self'"], objectSrc: ["'none'"], frameAncestors: ["'none'"], baseUri: ["'none'"], formAction: ["'self'"], }, xFrameOptions: 'DENY', xContentTypeOptions: true, referrerPolicy: 'no-referrer', crossOriginOpenerPolicy: 'same-origin', crossOriginResourcePolicy: 'same-origin', hstsMaxAge: runtimeConfig.tlsMode === 'local' ? undefined : 31_536_000, permissionsPolicy: { camera: [], microphone: [], geolocation: [], }, }, }, ], }); this.typedServer = typedServer; await typedServer.start(); this.assertStartActive(); this.unsubscribeConnectionClose = typedServer.smartServe.subscribeWebSocketConnectionClose( async (peerArg) => this.handleWebSocketConnectionClose(peerArg, authStore), ); // Owning the public listen port before rotating setup prevents concurrent // starters from invalidating the code printed by the process that wins. const setupAuthority = await authStore.rotateSetupAuthority(this.options.setupCode); this.setupCodeExpiresAt = setupAuthority?.expiresAt.getTime(); this.assertStartActive(); const pendingProjectRemovals = await this.registerExplicitInitialProject( this.options.runtimeOverrides.initialProjectDirectory, ); const startupProjectState = await this.reconcileProjectState(false); for (const project of pendingProjectRemovals) this.retiringProjectIds.add(project.id); const startupSettings = await authStore.getSettings(); this.activeBrowserVideoBackend = startupSettings.browserVideoBackend ?? 'chromium'; this.defaultModels = startupSettings.defaultModels; this.autoAcceptPermissions = startupSettings.autoAcceptPermissions === true; this.selectedOpenCodeProviderConnectionId = startupSettings.selectedOpenCodeProviderConnectionId; this.assertStartActive(); const flexDatabase = authStore.getDatabaseDescriptor(); const controllerId = this.requireControllerId(); this.gitReversionTemporaryRoot = plugins.path.join( dataDirectory, 'git-reversion', plugins.crypto.createHash('sha256').update(controllerId, 'utf8').digest('hex'), ); const gitReversion = this.createGitReversion(); this.gitReversion = gitReversion; const availablePendingProjectRemovals = await this.resolveAvailableProjects( pendingProjectRemovals, ); const gitProjects = [ ...startupProjectState.availableProjects, ...availablePendingProjectRemovals, ].map((project) => ({ projectId: project.id, directory: project.directory, directoryIdentity: structuredClone(project.directoryIdentity!), })); for (const project of gitProjects) { gitReversion.registerProject(project.projectId, project.directory, project.directoryIdentity); } await gitReversion.recover(this.operationAbortController.signal); this.assertStartActive(); const migrationProjects = gitProjects.map((project) => ({ projectId: project.projectId, directory: project.directory, })); const projects = startupProjectState.availableProjects.map((project) => ({ projectId: project.id, directory: project.directory, directoryIdentity: structuredClone(project.directoryIdentity!), })); this.assertStartActive(); const flexMigrationDatabase = new plugins.smartdata.SmartdataDb(flexDatabase); this.flexMigrationDatabase = flexMigrationDatabase; try { await flexMigrationDatabase.init(); this.assertStartActive(); const flexMigrationStore = new FlexStore({ database: flexMigrationDatabase, controllerId, projectManagementHost: { load: (storageKey, sessionId, sessionGeneration) => authStore.loadFlexProjectManagement(storageKey, sessionId, sessionGeneration), save: (storageKey, sessionId, snapshot, expectedRevision, writeContext) => authStore.saveFlexProjectManagement( storageKey, sessionId, snapshot, expectedRevision, writeContext, ), tombstoneSession: ( storageKey, sessionId, tombstone, expectedRevision, sessionContext, ) => authStore.tombstoneFlexProjectManagement( storageKey, sessionId, tombstone, expectedRevision, sessionContext, ), purgeNamespace: (storageKey) => authStore.purgeFlexProjectManagementNamespace(storageKey), }, }); this.flexMigrationStore = flexMigrationStore; await flexMigrationStore.init(); this.assertStartActive(); await new FlexPersistenceMigrationRunner({ store: flexMigrationStore, storageKeys: migrationProjects.map((project) => project.projectId), }).run(this.operationAbortController.signal); } catch (errorArg) { this.operationAbortController.signal.throwIfAborted(); console.error('Flex projection migration failed; the Flex child will retry it.'); } finally { await this.closeFlexMigrationResources(); } this.assertStartActive(); const startupFlexAdmissionFenceGeneration = this.sealFlexRuntimeAuthority(true); const flexSupervisor = new FlexSupervisor({ onChildExitObserved: () => { this.flexSupervisorExitSignals.get(flexSupervisor)?.resolve(); this.handleFlexChildExitObserved(flexSupervisor); }, onChildExit: (exitArg) => { this.handleFlexChildExit(flexSupervisor, exitArg); }, handleHostRequest: (methodArg, payloadArg, peerIdArg, signalArg) => this.handleFlexHostRequest( methodArg, payloadArg, peerIdArg, signalArg, ), handleUndeliveredHostSuccess: (methodArg, payloadArg, resultArg, peerIdArg) => ( this.handleUndeliveredFlexHostSuccess(methodArg, payloadArg, resultArg, peerIdArg) ), handleBrowserFrame: async (channelIdArg, bytesArg, peerIdArg) => { const browserResourceHost = this.browserResourceHost; if (!browserResourceHost || !flexSupervisor.ownsBrowserPeer(peerIdArg)) { throw new Error('The Flex browser host is unavailable.'); } await browserResourceHost.receiveFlexFrame(channelIdArg, bytesArg, peerIdArg); }, }); const startupFlexExitSignal = this.createRuntimeExitSignal(); this.flexSupervisorExitSignals.set(flexSupervisor, startupFlexExitSignal); this.flexSupervisor = flexSupervisor; this.unsubscribeFlexEvents = flexSupervisor.subscribe((eventArg) => { this.handleFlexEvent(eventArg, flexSupervisor); }); const flexServiceInit: IFlexServiceInit = { controllerId, database: flexDatabase, flexCredentialDirectory: plugins.path.join( dataDirectory, 'credentials', 'flex-provider-credentials', plugins.crypto.createHash('sha256').update(controllerId, 'utf8').digest('hex'), ), projects, }; this.flexServiceInit = flexServiceInit; const flexChildReady = await this.startFlexWithinBootBudget(flexSupervisor, flexServiceInit); this.assertStartActive(); // The reader also owns the metadata-only provider fallback and therefore // remains useful when the child or a public projection is unavailable. try { await this.ensureFlexProjectionReader(); } catch (errorArg) { console.error('Flex projection reader startup failed; OpenCode remains available.', errorArg); } const flexStartupCandidate = flexChildReady && this.flexProjectionReaderReady; this.assertStartActive(); const openCodeSupervisor = this.createOpenCodeSupervisor(); const startupOpenCodeExitSignal = this.openCodeSupervisorExitSignals.get(openCodeSupervisor); if (!startupOpenCodeExitSignal) throw new Error('OpenCode startup exit authority is unavailable.'); this.openCodeSupervisor = openCodeSupervisor; const openCodeStatus = await openCodeSupervisor.start(this.operationAbortController.signal); if ( startupOpenCodeExitSignal.signal.aborted || openCodeStatus.state !== 'ready' || !openCodeStatus.healthy ) throw new Error('The OpenCode runtime exited during controller startup.'); this.assertStartActive(); let openCodeClient: OpenCodeClientAdapter | undefined = this.createOpenCodeClient(openCodeSupervisor); this.openCodeClient = openCodeClient; if (this.selectedOpenCodeProviderConnectionId) { try { await this.reconcileSelectedOpenCodeAccount(this.operationAbortController.signal); } catch (errorArg) { console.error('The OpenCode account association could not be reconciled; OpenCode is fail-closed.', errorArg); this.openCodeAccountSwitchAdmissionSealed = true; this.sealOpenCodeRuntimeAdmission(); this.plannedOpenCodeSupervisorStops.add(openCodeSupervisor); try { await openCodeSupervisor.stop(); } finally { this.plannedOpenCodeSupervisorStops.delete(openCodeSupervisor); } if (this.openCodeSupervisor === openCodeSupervisor) this.openCodeSupervisor = undefined; if (this.openCodeClient === openCodeClient) this.openCodeClient = undefined; openCodeClient = undefined; } } await this.recoverSessionIntelligence( controllerId, startupProjectState.projects, this.operationAbortController.signal, openCodeClient !== undefined, ); if (openCodeClient) { await this.withCurrentSessionIdentityProjects( this.operationAbortController.signal, async (identityProjects) => { await this.reconcileOpenCodeSessionIdentities( openCodeSupervisor, openCodeClient, identityProjects, this.operationAbortController.signal, ); }, startupProjectState.projects, ); } this.assertStartActive(); let terminalResourceHost!: ControllerTerminalResourceHost; const terminalManager = new ControllerTerminalManager({ resolveProjectDirectory: async (projectIdArg) => ( await this.resolveProject(projectIdArg) ).directory, sendOutput: async (peerIdArg, payloadArg) => { await this.sendTerminalOutput(peerIdArg, payloadArg); }, onPeerDetached: (peerIdArg, terminalIdArg, reasonArg) => { void this.sendTerminalDetached(peerIdArg, terminalIdArg, reasonArg) .catch(() => undefined); }, mintCallerCredential: (inputArg) => { this.mcpCallerRegistry.revokeTerminal(inputArg.projectId, inputArg.resourceId); return this.mcpCallerRegistry.mint({ kind: 'terminal', projectId: inputArg.projectId, resourceId: inputArg.resourceId, ...(inputArg.agentSessionId === undefined ? {} : { agentSessionId: inputArg.agentSessionId }), }); }, onTerminalsChanged: (projectIdArg) => { void this.pushControllerEvent({ type: 'terminals.changed', projectId: projectIdArg, timestamp: Date.now(), }).catch(() => undefined); }, onTerminalStopped: async (projectIdArg, terminalIdArg, exitCodeArg, reasonArg) => { // The chat that held this credential is gone; a restart mints a fresh one. this.mcpCallerRegistry.revokeTerminal(projectIdArg, terminalIdArg.nativeId); await terminalResourceHost.recordTerminalStopped( projectIdArg, terminalIdArg.nativeId, exitCodeArg, reasonArg, ); void this.pushControllerEvent({ type: 'resources.changed', projectId: projectIdArg, timestamp: Date.now(), }).catch(() => undefined); }, }); this.terminalManager = terminalManager; await terminalManager.init(); this.assertStartActive(); terminalResourceHost = new ControllerTerminalResourceHost(terminalManager, authStore); this.terminalResourceHost = terminalResourceHost; let browserResourceHost!: ControllerBrowserResourceHost; const browserOperationAuthority = new ControllerBrowserOperationAuthority({ store: authStore, isRetiring: operation => this.resourceCoordinator?.isResourceRetiring(operation.projectId, operation.browserResourceId) ?? false, assertHuman: (operation, signal) => { this.assertHumanBrowserLiveAuthority(operation, signal); }, authorizeFlex: operation => browserResourceHost.authorizeFlexCapability(operation), authorizeMcp: operation => browserResourceHost.authorizeMcpCapability(operation), }); browserResourceHost = new ControllerBrowserResourceHost({ videoBackend: this.activeBrowserVideoBackend, runtimeDirectory: plugins.path.join( dataDirectory, 'runtime', 'browser', plugins.crypto.createHash('sha256').update(controllerId).digest('hex'), ), closeResourceViews: (projectIdArg, resourceIdArg, closeCodeArg, signalArg) => ( this.closeBrowserViewsForResource(projectIdArg, resourceIdArg, closeCodeArg, signalArg) ), authorizeCapability: (requestArg) => ( ( requestArg.role === 'human' && requestArg.source === 'human' && this.authenticatedPeerIds.has(requestArg.peerId) && !this.resourceCoordinator?.isResourceRetiring(requestArg.projectId, requestArg.browserResourceId) ) || browserResourceHost.authorizeFlexCapability(requestArg) || browserResourceHost.authorizeMcpCapability(requestArg) ), isFlexRunActive: (bindingArg, runIdArg) => { const channelAuthority = this.flexBrowserChannelAuthorities.get(bindingArg.channelId); return channelAuthority !== undefined && channelAuthority.projectId === bindingArg.projectId && channelAuthority.sessionId === bindingArg.sessionId.nativeId && channelAuthority.sessionGenerationId === bindingArg.sessionGenerationId && channelAuthority.sessionGenerationSequence === bindingArg.sessionGenerationSequence && channelAuthority.peerId === bindingArg.peerId && this.ownsFlexRun( bindingArg.projectId, bindingArg.sessionId.nativeId, channelAuthority, runIdArg, ); }, beforeOperation: browserOperationAuthority.beforeOperation, audit: browserOperationAuthority.audit, }); this.browserResourceHost = browserResourceHost; await browserResourceHost.start(this.operationAbortController.signal); this.assertStartActive(); const resourceCoordinator = new ControllerResourceCoordinator({ store: authStore, terminalHost: terminalResourceHost, browserHost: browserResourceHost, beforeResourceRetirement: async (resourceArg, signalArg) => { if (resourceArg.kind === 'browser') { await this.closeBrowserViewsForResource( resourceArg.projectId, resourceArg.id, controllerBrowserViewClosedErrorCode, signalArg, ); } }, isFlexHarnessAvailable: () => this.flexIsOperational(), resolveSessionAuthority: (projectIdArg, sessionIdArg) => ( this.managedSessionAuthority(projectIdArg, sessionIdArg) ), resolveRetiredSessionIdentity: ( projectIdArg, sessionIdArg, sessionIdentityIdArg, signalArg, ) => authStore.resolveRetiredManagedSessionIdentity({ projectIdentityId: projectIdArg, runtimeId: sessionIdArg, sessionIdentityId: sessionIdentityIdArg, signal: signalArg, }), }); this.resourceCoordinator = resourceCoordinator; if (flexStartupCandidate) { await this.withCurrentSessionIdentityProjects( this.operationAbortController.signal, async (identityProjects) => { try { await this.reconcileFlexSessionIdentities( flexSupervisor, identityProjects, this.operationAbortController.signal, ); } catch (errorArg) { console.error( 'Flex session identity reconciliation failed; automatic recovery remains fenced.', errorArg, ); return; } const cleanupResults = await Promise.allSettled([ browserResourceHost.closeAllFlexChannels(), this.harnessOperationManager.releaseHarness('flex'), ]); this.assertStartActive(); const cleanupErrors = cleanupResults .filter((result): result is PromiseRejectedResult => result.status === 'rejected') .map((result) => result.reason); if (cleanupErrors.length > 0) { console.error( 'Flex startup authority cleanup failed; automatic recovery remains fenced.', new AggregateError(cleanupErrors, 'Flex startup cleanup is incomplete.'), ); return; } this.assertFlexSessionIdentityGenerationCurrent( flexSupervisor, startupFlexExitSignal, ); if (this.reopenFlexRuntimeAuthority(startupFlexAdmissionFenceGeneration)) { this.flexResourceGeneration += 1; this.advanceFlexToolStreamEpoch(false); void this.trackOperationPromise(this.recoverDeletingFlexProviderConnections( this.operationAbortController.signal, )).catch(() => { console.error('Pending Flex provider logout recovery failed; it will retry on restart.'); }); } }, startupProjectState.projects, ); } try { await this.initializeCodexRuntime(startupProjectState.projects, pendingProjectRemovals); } catch { this.operationAbortController.signal.throwIfAborted(); console.error('Codex initialization failed. Codex remains unavailable until its recovery succeeds.'); } this.assertStartActive(); await resourceCoordinator.recover(this.operationAbortController.signal); this.assertStartActive(); try { const agentTerminalFailures = await resourceCoordinator.restartAgentTerminals( this.operationAbortController.signal, ); for (const failure of agentTerminalFailures) { console.error( `The agent terminal ${failure.resourceId} could not be restarted.`, failure.error, ); } } catch (errorArg) { this.operationAbortController.signal.throwIfAborted(); console.error('Agent terminal restart failed. Existing chats remain stopped.', errorArg); } this.assertStartActive(); if (controllerMcpRuntimeIsSupported()) { const processIdentity = this.processIdentity; if (!processIdentity?.startTicks) { throw new Error('The private controller process identity is unavailable.'); } const lifecycleGeneration = plugins.crypto.randomBytes(24).toString('base64url'); const mcpControllerHost = new ControllerMcpHost({ controllerPort: runtimeConfig.controllerPort, lifecycleGeneration, processIdentity, api: this.createControllerMcpApi(runtimeConfig.controllerPort, lifecycleGeneration), resolveCaller: (credentialArg) => this.mcpCallerRegistry.resolve(credentialArg), }); this.mcpControllerHost = mcpControllerHost; await mcpControllerHost.start(); this.assertStartActive(); } this.assertStartActive(); if (openCodeClient) { this.assertOpenCodeStartupRuntimeReady( openCodeSupervisor, openCodeClient, startupOpenCodeExitSignal.signal, ); } await authStore.recordAuditEvent({ type: 'controller.started', outcome: 'succeeded' }); this.controllerStartedRecorded = true; this.assertStartActive(); if (openCodeClient) { this.assertOpenCodeStartupRuntimeReady( openCodeSupervisor, openCodeClient, startupOpenCodeExitSignal.signal, ); this.assertOpenCodeSessionIdentityGenerationCurrent( openCodeSupervisor, openCodeClient, startupOpenCodeExitSignal, ); } this.lifecycleState = 'ready'; this.systemMetricsSampler.startRecording({ record: (metricsArg) => authStore.recordSystemMetricsHistory(metricsArg), prune: () => authStore.pruneSystemMetricsHistory(), }); // Returns immediately; a missing account authority is a named state, never a startup failure. this.authorityState.start(); this.mcpControllerHost?.openAdmission(); for (const project of pendingProjectRemovals) this.schedulePendingProjectRemoval(project, 0); if (this.flexIsOperational()) { void this.trackOperationPromise(this.hydrateFlexProviderCatalogs(true)).catch(() => { console.error('Flex provider catalog hydration failed; provider refresh remains available.'); }); } else { this.scheduleFlexRestart(); } if (openCodeClient) this.startSessionIntelligenceCleanupTimer(); if (this.autoAcceptPermissions) this.sweepAutoAcceptAcrossProjects(); if (openCodeClient) void this.startOpenCodeEventStream(openCodeClient).catch(() => undefined); return { status: this.getStatus(), publicUrl: runtimeConfig.publicOrigin, ...(setupAuthority ? { setupCode: setupAuthority.setupCode } : {}), }; } private coordinatorForUpgradeToken( tokenArg: string, globalRootArg: string, ): UpgradeCoordinator { if (this.upgradeToken === tokenArg && this.upgradeCoordinator) { if (this.upgradeCoordinator.canonicalGlobalRoot !== globalRootArg) { throw new Error('The inherited upgrade coordination global root changed unexpectedly.'); } return this.upgradeCoordinator; } return new UpgradeCoordinator(globalRootArg); } private startOpenCodeEventStream( openCodeClientArg: OpenCodeClientAdapter, skipInitialStreamAdvanceArg = false, ): Promise<void> { const hasCurrentRuntimeAuthority = (): boolean => { const supervisor = this.openCodeSupervisor; return this.openCodeClient === openCodeClientArg && supervisor !== undefined && this.openCodeSupervisorExitSignals.get(supervisor)?.signal.aborted === false; }; let initialConnection = true; let resolveInitialConnection!: () => void; let rejectInitialConnection!: (reasonArg: unknown) => void; const initialConnectionTask = new Promise<void>((resolve, reject) => { resolveInitialConnection = resolve; rejectInitialConnection = reject; }); const eventStreamTask = openCodeClientArg.startEventStream({ onConnected: async () => { if (initialConnection) { initialConnection = false; if (!skipInitialStreamAdvanceArg && hasCurrentRuntimeAuthority()) { this.advanceOpenCodeStreams(false); } resolveInitialConnection(); return; } if (!hasCurrentRuntimeAuthority()) return; this.revokeAllChildScopes(true); this.advanceOpenCodeStreams(false); }, onToolStreamGap: async () => { if (!hasCurrentRuntimeAuthority()) return; this.revokeAllChildScopes(true); this.advanceOpenCodeToolStreamEpoch(true); }, onMessageStreamGap: async () => { if (!hasCurrentRuntimeAuthority()) return; this.revokeAllChildScopes(true); this.advanceOpenCodeMessageStreamEpoch(true); }, onStreamGap: async () => { if (!hasCurrentRuntimeAuthority()) return; this.revokeAllChildScopes(true); this.advanceOpenCodeStreams(true); }, onToolExecution: async (executionArg, directoryArg, timestampArg) => { if (!hasCurrentRuntimeAuthority()) return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; if (!await this.sessionEventIsManaged(projectId, executionArg.sessionId)) { this.queueChildScopeEventForSession( projectId, executionArg.sessionId, 'transcript.changed', ); return; } this.handleOpenCodeToolExecution(executionArg, directoryArg, timestampArg); }, onReasoningUpdate: async (updateArg, directoryArg) => { if (!hasCurrentRuntimeAuthority()) return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; if (!await this.sessionEventIsManaged(projectId, updateArg.sessionId)) { this.queueChildScopeEventForSession(projectId, updateArg.sessionId, 'transcript.changed'); return; } this.handleOpenCodeReasoningUpdate(updateArg, directoryArg); }, onTextUpdate: async (updateArg, directoryArg) => { if (!hasCurrentRuntimeAuthority()) return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; if (!await this.sessionEventIsManaged(projectId, updateArg.sessionId)) { this.queueChildScopeEventForSession(projectId, updateArg.sessionId, 'transcript.changed'); return; } this.handleOpenCodeTextUpdate(updateArg, directoryArg); }, onMessageLifecycle: async (lifecycleArg, directoryArg) => { if (!hasCurrentRuntimeAuthority()) return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; if (!await this.sessionEventIsManaged(projectId, lifecycleArg.sessionId)) { this.queueChildScopeEventForSession( projectId, lifecycleArg.sessionId, 'transcript.changed', ); return; } this.handleOpenCodeMessageLifecycle(lifecycleArg, directoryArg); }, onEvent: async (eventArg, directoryArg) => { if (!hasCurrentRuntimeAuthority()) return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) { // Events for directories outside the registry are not ours to relay. return; } if (eventArg.sessionId && !await this.sessionEventIsManaged(projectId, eventArg.sessionId)) { this.queueChildScopeEventForSession( projectId, eventArg.sessionId, eventArg.type === 'permissions.changed' ? 'attention.changed' : eventArg.type === 'session.changed' ? 'session.changed' : 'transcript.changed', ); if ( eventArg.type === 'permissions.changed' && (this.autoAcceptPermissions || this.yoloSessionsByProjectKey.size > 0) ) this.scheduleAutoAcceptSweep(directoryArg); return; } if ( eventArg.sessionError && eventArg.sessionId?.harnessId === 'opencode' ) { void this.releaseOpenCodeOperationsAfterSessionError(projectId, eventArg.sessionId) .catch(() => undefined); } if ( projectId !== undefined && eventArg.type === 'permissions.changed' && (this.autoAcceptPermissions || this.yoloSessionsByProjectKey.size > 0) ) { this.scheduleAutoAcceptSweep(directoryArg); } this.queueOpenCodeControllerEvent({ ...eventArg, ...(projectId !== undefined ? { projectId } : {}), }); }, }); this.eventStreamTask = eventStreamTask; void eventStreamTask.then( () => { if (initialConnection) { rejectInitialConnection(new Error( 'The OpenCode event stream ended before its initial connection.', )); } }, (errorArg) => { if (initialConnection) rejectInitialConnection(errorArg); }, ); void eventStreamTask.catch(() => { if (this.lifecycleState === 'ready' && hasCurrentRuntimeAuthority()) { console.error('OpenCode event streaming failed; the controller remains available.'); this.queueOpenCodeControllerEvent({ type: 'harness.changed', harnessId: 'opencode', timestamp: Date.now(), }); } }); return initialConnectionTask; } private acquireOpenCodeRuntimeLease(clientArg: OpenCodeClientAdapter): () => void { if (this.openCodeRuntimeAdmissionSealed || this.openCodeClient !== clientArg) { throw new Error('OpenCode is restarting and is temporarily unavailable.'); } this.openCodeRuntimeLeaseCount += 1; if (!this.openCodeRuntimeLeaseDrained) { let resolve!: () => void; const promise = new Promise<void>((resolveArg) => { resolve = resolveArg; }); this.openCodeRuntimeLeaseDrained = { promise, resolve }; } let released = false; let release!: () => void; release = () => { if (released) return; released = true; this.openCodeRuntimeLeaseReleases.delete(release); this.openCodeRuntimeLeaseCount -= 1; if (this.openCodeRuntimeLeaseCount === 0) { this.openCodeRuntimeLeaseDrained?.resolve(); this.openCodeRuntimeLeaseDrained = undefined; } }; this.openCodeRuntimeLeaseReleases.add(release); return release; } private abortOpenCodeRuntimeLeases( clientArg: OpenCodeClientAdapter, reasonArg: Error, ): void { clientArg.abortRuntimeRequests(reasonArg); for (const release of [...this.openCodeRuntimeLeaseReleases]) release(); } private sealOpenCodeRuntimeAdmission(): void { this.openCodeRuntimeAdmissionSealed = true; } private async waitForOpenCodeRuntimeLeases(signalArg: AbortSignal): Promise<void> { signalArg.throwIfAborted(); const drained = this.openCodeRuntimeLeaseDrained?.promise; if (!drained) return; await Promise.race([ drained, new Promise<never>((_resolve, reject) => { const onAbort = () => reject(signalArg.reason); signalArg.addEventListener('abort', onAbort, { once: true }); void drained.finally(() => signalArg.removeEventListener('abort', onAbort)); }), ]); } private createOpenCodeSupervisor(): OpenCodeSupervisor { const runtimeConfig = this.requireRuntimeConfig(); if (!this.openCodeRuntimeDirectory) { throw new Error('The OpenCode runtime directory is unavailable.'); } let supervisor!: OpenCodeSupervisor; supervisor = new OpenCodeSupervisor({ directory: this.openCodeRuntimeDirectory, port: runtimeConfig.opencodePort, mintCallerCredential: () => { const generation = this.openCodeSupervisorExitSignals .get(supervisor)?.sessionIdentityGeneration; if (generation === undefined) return undefined; this.mcpCallerRegistry.revokeRuntime(generation); return this.mcpCallerRegistry.mint({ kind: 'runtime', harnessId: 'opencode', runtimeGeneration: generation, }); }, onChildExitObserved: () => { const generation = this.openCodeSupervisorExitSignals .get(supervisor)?.sessionIdentityGeneration; // The runtime that presented this credential is gone; the next start mints a new one. if (generation !== undefined) this.mcpCallerRegistry.revokeRuntime(generation); this.openCodeSupervisorExitSignals.get(supervisor)?.resolve(); this.handleOpenCodeChildExitObserved(supervisor); }, onChildExit: () => this.handleOpenCodeChildExitCleanup(supervisor), }); this.openCodeSupervisorExitSignals.set(supervisor, this.createRuntimeExitSignal()); return supervisor; } private handleOpenCodeChildExitCleanup(supervisorArg: OpenCodeSupervisor): void { if (this.retainedOpenCodeRuntimeCleanup.has(supervisorArg)) { if (this.openCodeCrashRecovery) this.scheduleOpenCodeRestart(); else this.scheduleRetainedOpenCodeRuntimeCleanup(); } else if (this.plannedOpenCodeSupervisorStops.has(supervisorArg)) { this.plannedOpenCodeSupervisorStops.delete(supervisorArg); } else if (this.openCodeCrashRecovery?.supervisor === supervisorArg) { this.scheduleOpenCodeRestart(); } } private createRuntimeExitSignal(): IControllerRuntimeExitSignal { const abortController = new AbortController(); return { signal: abortController.signal, resolve: () => abortController.abort(new Error('The owning harness runtime exited.')), sessionIdentityGeneration: plugins.crypto.randomBytes(32).toString('base64url'), }; } private createOpenCodeClient(supervisorArg: OpenCodeSupervisor): OpenCodeClientAdapter { let client!: OpenCodeClientAdapter; client = new OpenCodeClientAdapter({ ...supervisorArg.getConnectionConfig(), acquireRuntimeLease: () => this.acquireOpenCodeRuntimeLease(client), }); return client; } private assertOpenCodeStartupRuntimeReady( supervisorArg: OpenCodeSupervisor, clientArg: OpenCodeClientAdapter, exitSignalArg: AbortSignal, ): void { const status = supervisorArg.getStatus(); if ( exitSignalArg.aborted || this.openCodeSupervisor !== supervisorArg || this.openCodeClient !== clientArg || status.state !== 'ready' || !status.healthy ) throw new Error('The OpenCode runtime exited before controller readiness.'); } private sealOpenCodeRuntimeAuthority(replaceAdmissionFenceArg: boolean): number { const existingFence = this.openCodeHarnessAdmissionFenceGeneration; const admissionFence = replaceAdmissionFenceArg || existingFence === undefined ? this.harnessOperationManager.sealHarnessAdmission('opencode') : existingFence; this.openCodeHarnessAdmissionFenceGeneration = admissionFence; this.sealOpenCodeRuntimeAdmission(); this.revokeAllChildScopes(true); this.stopSessionIntelligenceCleanupTimer(); return admissionFence; } private reopenOpenCodeRuntimeAuthority(admissionFenceArg: number): boolean { if ( this.openCodeHarnessAdmissionFenceGeneration !== admissionFenceArg || !this.harnessOperationManager.reopenHarnessAdmission('opencode', admissionFenceArg) ) return false; this.openCodeHarnessAdmissionFenceGeneration = undefined; this.openCodeRuntimeAdmissionSealed = false; this.startSessionIntelligenceCleanupTimer(); return true; } private withOpenCodeRuntimeMutation<T>(operationArg: () => Promise<T>): Promise<T> { const operation = this.openCodeRuntimeMutationTail.catch(() => undefined).then(operationArg); this.openCodeRuntimeMutationTail = operation.then(() => undefined, () => undefined); return operation; } private async closeFlexMigrationResources(): Promise<void> { if (this.flexMigrationCleanupPromise) return this.flexMigrationCleanupPromise; const cleanupPromise = (async () => { const store = this.flexMigrationStore; if (store) { await store.close(); if (this.flexMigrationStore === store) this.flexMigrationStore = undefined; } const database = this.flexMigrationDatabase; if (database) { await database.close(); if (this.flexMigrationDatabase === database) this.flexMigrationDatabase = undefined; } })(); this.flexMigrationCleanupPromise = cleanupPromise; try { await cleanupPromise; } finally { if (this.flexMigrationCleanupPromise === cleanupPromise) { this.flexMigrationCleanupPromise = undefined; } } } private flexIsOperational(): boolean { const status = this.flexSupervisor?.getStatus(); return this.flexRecoveryReady === true && status?.state === 'ready' && status.ready === true; } private sealFlexRuntimeAuthority(replaceAdmissionFenceArg: boolean): number { const existingFence = this.flexHarnessAdmissionFenceGeneration; const admissionFence = replaceAdmissionFenceArg || existingFence === undefined ? this.harnessOperationManager.sealHarnessAdmission('flex') : existingFence; this.flexHarnessAdmissionFenceGeneration = admissionFence; this.flexRecoveryReady = false; this.sessionIntelligenceFlexAdmissionSealed = true; this.flexRunAuthorityFenced = true; this.fenceFlexDelegatedAuthority('The Flex runtime authority was fenced.'); this.activeFlexRuns.clear(); this.pendingFlexFinishedJobs.clear(); this.pendingFlexFinishedPrompts.clear(); this.pendingFlexProjectRegistrationOperations.clear(); this.flexBrowserChannelAuthorities.clear(); this.currentFlexEventGenerations.clear(); return admissionFence; } private reopenFlexRuntimeAuthority(admissionFenceArg: number): boolean { if ( this.flexHarnessAdmissionFenceGeneration !== admissionFenceArg || !this.harnessOperationManager.reopenHarnessAdmission('flex', admissionFenceArg) ) return false; this.flexHarnessAdmissionFenceGeneration = undefined; this.flexRunAuthorityFenced = false; this.flexRecoveryReady = true; this.sessionIntelligenceFlexAdmissionSealed = false; return true; } private async resolveSessionIdentityProjects( signalArg: AbortSignal, storedProjectsArg?: readonly IControllerProjectDocument[], ): Promise<IControllerProjectDocument[]> { signalArg.throwIfAborted(); const stored = storedProjectsArg ? [...storedProjectsArg] : await this.requireAuthStore().listProjects(); const available = await this.resolveAvailableProjects(stored); const currentProjects: IControllerProjectDocument[] = []; for (const project of available) { signalArg.throwIfAborted(); const current = await this.resolveCurrentReconciliationProject(project); if (current) currentProjects.push(current); } currentProjects.sort((leftArg, rightArg) => leftArg.id.localeCompare(rightArg.id)); return currentProjects; } private withCurrentSessionIdentityProjects<TResult>( signalArg: AbortSignal, operationArg: (projectsArg: readonly IControllerProjectDocument[]) => Promise<TResult>, storedProjectsArg?: readonly IControllerProjectDocument[], ): Promise<TResult> { return this.runControllerProjectMutation(async () => { const projects = await this.resolveSessionIdentityProjects(signalArg, storedProjectsArg); return operationArg(projects); }); } private assertOpenCodeSessionIdentitySourceFenced(): void { if ( this.lifecycleState === 'ready' && !this.openCodeRuntimeAdmissionSealed && !this.openCodeAccountSwitchAdmissionSealed ) { throw new Error('OpenCode session identity reconciliation requires sealed runtime admission.'); } } private assertFlexSessionIdentitySourceFenced(): void { if ( this.flexHarnessAdmissionFenceGeneration === undefined || this.flexRecoveryReady || !this.flexRunAuthorityFenced ) throw new Error('Flex session identity reconciliation requires sealed runtime authority.'); } private assertFlexProjectSessionIdentitySourceFenced(projectIdArg: string): void { if (!this.flexProjectIdentityRecoveryPending.has(projectIdArg)) { throw new Error('Flex session identity reconciliation requires sealed project authority.'); } } private requireOpenCodeRuntimeExitAuthority( supervisorArg: OpenCodeSupervisor, ): IControllerRuntimeExitSignal { const authority = this.openCodeSupervisorExitSignals.get(supervisorArg); if (!authority) throw new Error('OpenCode session identity authority is unavailable.'); return authority; } private requireFlexRuntimeExitAuthority( supervisorArg: FlexSupervisor, ): IControllerRuntimeExitSignal { const authority = this.flexSupervisorExitSignals.get(supervisorArg); if (!authority) throw new Error('Flex session identity authority is unavailable.'); return authority; } private assertOpenCodeSessionIdentityGenerationCurrent( supervisorArg: OpenCodeSupervisor, clientArg: OpenCodeClientAdapter, authorityArg: IControllerRuntimeExitSignal, ): void { const status = supervisorArg.getStatus(); if ( this.openCodeSupervisor !== supervisorArg || this.openCodeClient !== clientArg || this.openCodeSupervisorExitSignals.get(supervisorArg) !== authorityArg || authorityArg.signal.aborted || status.state !== 'ready' || !status.healthy ) throw new Error('The OpenCode session identity generation is no longer current.'); } private assertFlexSessionIdentityGenerationCurrent( supervisorArg: FlexSupervisor, authorityArg: IControllerRuntimeExitSignal, ): void { const status = supervisorArg.getStatus(); if ( this.flexSupervisor !== supervisorArg || this.flexSupervisorExitSignals.get(supervisorArg) !== authorityArg || authorityArg.signal.aborted || status.state !== 'ready' || !status.ready ) throw new Error('The Flex session identity generation is no longer current.'); } private async readFlexSessionIdentitySnapshot( supervisorArg: FlexSupervisor, projectIdArg: string, signalArg: AbortSignal, ): Promise<IControllerSessionSnapshotEntry[]> { const pageLimit = 50; const sessions: TFlexSession[] = []; let expectedTotal: number | undefined; while (true) { signalArg.throwIfAborted(); const page = await supervisorArg.request('session.list', { scopeId: projectIdArg, offset: sessions.length, limit: pageLimit, }, signalArg); if ( !Number.isSafeInteger(page.total) || page.total < 0 || page.total > maxFlexSessionIdentitySnapshotEntries || (expectedTotal !== undefined && page.total !== expectedTotal) || page.sessions.length > pageLimit ) throw new Error('Flex returned an invalid bounded identity session snapshot.'); expectedTotal ??= page.total; if (page.sessions.length === 0 && sessions.length < page.total) { throw new Error('Flex returned an incomplete identity session snapshot page.'); } sessions.push(...page.sessions); const truncated = sessions.length < page.total; if (page.truncated !== truncated || sessions.length > page.total) { throw new Error('Flex identity session snapshot pagination changed during its fence.'); } if (!truncated) break; } return sessions.map((sessionArg) => { if ( typeof sessionArg.sessionId !== 'string' || sessionArg.sessionId.length === 0 || typeof sessionArg.sessionGenerationId !== 'string' || sessionArg.sessionGenerationId.length === 0 || typeof sessionArg.sessionGenerationSequence !== 'number' || !Number.isSafeInteger(sessionArg.sessionGenerationSequence) || sessionArg.sessionGenerationSequence < 1 ) throw new Error('Flex returned a session without canonical generation metadata.'); return { nativeId: sessionArg.sessionId, sessionGenerationId: sessionArg.sessionGenerationId, sessionGenerationSequence: sessionArg.sessionGenerationSequence, providerSessionGeneration: flexSessionProviderGeneration( sessionArg.sessionGenerationId, sessionArg.sessionGenerationSequence, ), ...(sessionArg.parentSessionId === undefined ? {} : { parentNativeId: sessionArg.parentSessionId }), }; }).sort((leftArg, rightArg) => leftArg.nativeId.localeCompare(rightArg.nativeId)); } private async captureFlexCleanupCohort( projectIdArg: string, targetRootsArg: readonly IControllerFlexCleanupEntry[], signalArg: AbortSignal, ): Promise<IControllerFlexCleanupEntry[]> { if (targetRootsArg.length === 0) return []; const supervisor = this.requireFlexSupervisor(); const authority = this.requireFlexRuntimeExitAuthority(supervisor); const signal = AbortSignal.any([signalArg, authority.signal]); const snapshot = await this.readFlexSessionIdentitySnapshot( supervisor, projectIdArg, signal, ); this.assertFlexSessionIdentityGenerationCurrent(supervisor, authority); const snapshotById = new Map(snapshot.map((entry) => [entry.nativeId, entry])); const currentTargets = targetRootsArg.filter((target) => ( snapshotById.get(target.sessionId)?.providerSessionGeneration === target.providerSessionGeneration )); return buildControllerFlexCleanupCohort(snapshot, currentTargets); } private async reconcileOpenCodeSessionIdentities( supervisorArg: OpenCodeSupervisor, clientArg: OpenCodeClientAdapter, projectsArg: readonly IControllerProjectDocument[], signalArg: AbortSignal, ): Promise<void> { this.assertOpenCodeSessionIdentitySourceFenced(); const authority = this.requireOpenCodeRuntimeExitAuthority(supervisorArg); const signal = AbortSignal.any([signalArg, authority.signal]); for (const project of projectsArg) { signal.throwIfAborted(); this.markManagedSessionAuthorityScopeUncertain(project.id, 'opencode'); await this.recoverOpenCodeManagedSessionDeletions(project, clientArg, signal); const snapshot = await clientArg[openCodeSessionIdentitySnapshotCapability]( project.directory, signal, ); await this.recoverOpenCodeManagedSessionCreations( project, clientArg, authority.sessionIdentityGeneration, signal, ); const observedAt = new Date(); const assertRuntimeAuthority = () => { this.assertOpenCodeSessionIdentitySourceFenced(); this.assertOpenCodeSessionIdentityGenerationCurrent( supervisorArg, clientArg, authority, ); }; await this.requireAuthStore().migrateLegacyManagedSessions({ projectId: project.id, harnessId: 'opencode', supervisorGeneration: authority.sessionIdentityGeneration, sourceAdmissionFenced: true, snapshotComplete: true, sessions: snapshot.sessions, observedAt, signal, assertRuntimeAuthorityBeforeCompletion: assertRuntimeAuthority, assertRuntimeAuthorityAfterCompletion: assertRuntimeAuthority, }); const result = await this.requireAuthStore()[authStoreSessionIdentityCapability]({ projectIdentityId: project.id, harnessId: 'opencode', supervisorGeneration: authority.sessionIdentityGeneration, sourceAdmissionFenced: true, snapshotComplete: true, managedMembershipsOnly: true, sessions: snapshot.sessions, observedAt, signal, }); if (result.status === 'blocked_by_deletion') { throw new Error( `OpenCode session identity deletion recovery is required for project ${project.id}.`, ); } for (const observation of result.observations) { if (observation.managed) { this.setManagedSessionAuthority( project.id, observation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); } else { this.setManagedSessionAuthority( project.id, observation.runtimeId, 'definitively_unmanaged', ); } } for (const observation of result.observations) { if (this.managedSessionAuthorities.has( this.managedSessionAuthorityKey(project.id, observation.runtimeId), )) continue; if (observation.managed) { this.setManagedSessionAuthority( project.id, observation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); } else { this.setManagedSessionAuthority( project.id, observation.runtimeId, 'definitively_unmanaged', ); } } this.assertOpenCodeSessionIdentityGenerationCurrent( supervisorArg, clientArg, authority, ); this.pruneManagedSessionAuthorityCacheScope( project.id, 'opencode', new Set(result.observations.map((observation) => ( controllerRuntimeIdKey(observation.runtimeId) ))), ); } this.assertOpenCodeSessionIdentitySourceFenced(); this.assertOpenCodeSessionIdentityGenerationCurrent( supervisorArg, clientArg, authority, ); } private async reconcileFlexSessionIdentities( supervisorArg: FlexSupervisor, projectsArg: readonly IControllerProjectDocument[], signalArg: AbortSignal, ): Promise<void> { this.assertFlexSessionIdentitySourceFenced(); const authority = this.requireFlexRuntimeExitAuthority(supervisorArg); const signal = AbortSignal.any([signalArg, authority.signal]); for (const project of projectsArg) { await this.reconcileFlexProjectSessionIdentities( project, supervisorArg, authority, signal, ); } this.assertFlexSessionIdentitySourceFenced(); this.assertFlexSessionIdentityGenerationCurrent(supervisorArg, authority); } private async reconcileFlexProjectSessionIdentities( projectArg: IControllerProjectDocument, supervisorArg: FlexSupervisor, authorityArg: IControllerRuntimeExitSignal, signalArg: AbortSignal, projectRegisteredArg = false, ): Promise<void> { signalArg.throwIfAborted(); const assertSourceFenced = projectRegisteredArg ? () => this.assertFlexProjectSessionIdentitySourceFenced(projectArg.id) : () => this.assertFlexSessionIdentitySourceFenced(); assertSourceFenced(); if (!projectRegisteredArg) { await this.registerFlexProjectForReconciliation(projectArg, supervisorArg, signalArg); } this.markManagedSessionAuthorityScopeUncertain(projectArg.id, 'flex'); await this.recoverFlexManagedSessionDeletions(projectArg, supervisorArg, signalArg); const sessions = await this.readFlexSessionIdentitySnapshot( supervisorArg, projectArg.id, signalArg, ); await this.recoverFlexManagedSessionCreations( projectArg, supervisorArg, authorityArg.sessionIdentityGeneration, signalArg, ); const observedAt = new Date(); const assertRuntimeAuthority = () => { assertSourceFenced(); this.assertFlexSessionIdentityGenerationCurrent(supervisorArg, authorityArg); }; await this.requireAuthStore().migrateLegacyManagedSessions({ projectId: projectArg.id, harnessId: 'flex', supervisorGeneration: authorityArg.sessionIdentityGeneration, sourceAdmissionFenced: true, snapshotComplete: true, sessions, observedAt, signal: signalArg, assertRuntimeAuthorityBeforeCompletion: assertRuntimeAuthority, assertRuntimeAuthorityAfterCompletion: assertRuntimeAuthority, }); const result = await this.requireAuthStore()[authStoreSessionIdentityCapability]({ projectIdentityId: projectArg.id, harnessId: 'flex', supervisorGeneration: authorityArg.sessionIdentityGeneration, sourceAdmissionFenced: true, snapshotComplete: true, managedMembershipsOnly: true, sessions, observedAt, signal: signalArg, }); if (result.status === 'blocked_by_deletion') { throw new Error( `Flex session identity deletion recovery is required for project ${projectArg.id}.`, ); } for (const observation of result.observations) { if (observation.managed) { this.setManagedSessionAuthority( projectArg.id, observation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); } else { this.setManagedSessionAuthority( projectArg.id, observation.runtimeId, 'definitively_unmanaged', ); } } for (const observation of result.observations) { if (this.managedSessionAuthorities.has( this.managedSessionAuthorityKey(projectArg.id, observation.runtimeId), )) continue; if (observation.managed) { this.setManagedSessionAuthority( projectArg.id, observation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); } else { this.setManagedSessionAuthority( projectArg.id, observation.runtimeId, 'definitively_unmanaged', ); } } this.assertFlexSessionIdentityGenerationCurrent(supervisorArg, authorityArg); this.pruneManagedSessionAuthorityCacheScope( projectArg.id, 'flex', new Set(result.observations.map((observation) => ( controllerRuntimeIdKey(observation.runtimeId) ))), ); } private async registerFlexProjectForReconciliation( projectArg: IControllerProjectDocument, supervisorArg: FlexSupervisor, signalArg: AbortSignal, ): Promise<void> { const registrationOperationId = plugins.crypto.randomBytes(24).toString('base64url'); this.pendingFlexProjectRegistrationOperations.set(registrationOperationId, { projectId: projectArg.id, supervisor: supervisorArg, }); try { await supervisorArg.request('project.register', { project: { projectId: projectArg.id, directory: projectArg.directory, directoryIdentity: structuredClone(projectArg.directoryIdentity!), }, registrationOperationId, }, signalArg); } finally { this.pendingFlexProjectRegistrationOperations.delete(registrationOperationId); } } private async recoverOpenCodeManagedSessionDeletions( projectArg: IControllerProjectDocument, clientArg: OpenCodeClientAdapter, signalArg: AbortSignal, ): Promise<void> { const obligations = await this.requireAuthStore().listManagedSessionDeletionObligations({ projectIdentityId: projectArg.id, harnessId: 'opencode', signal: signalArg, }); for (let obligation of obligations) { signalArg.throwIfAborted(); const current = await clientArg.getSessionIfPresent( projectArg.directory, obligation.runtimeId.nativeId, signalArg, ); if ( current && openCodeSessionProviderGeneration(current.id.nativeId, current.createdAt) !== obligation.providerSessionGeneration ) throw new Error('OpenCode reused a managed session ID during deletion recovery.'); if (obligation.dispatchStartedAt === undefined && current) { const observation = await this.requireAuthStore().cancelManagedSessionDeletion({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, cancelledAt: new Date(), }); this.setManagedSessionAuthority( projectArg.id, obligation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); continue; } if (obligation.dispatchStartedAt === undefined) { obligation = await this.requireAuthStore().markManagedSessionDeletionDispatched({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, dispatchStartedAt: new Date(), }); } if (current) { await clientArg.deleteSessionIfPresent( projectArg.directory, obligation.runtimeId.nativeId, signalArg, ); } if (await clientArg.getSessionIfPresent( projectArg.directory, obligation.runtimeId.nativeId, signalArg, )) throw new Error('OpenCode deletion recovery did not remove the managed session.'); await this.completeRecoveredManagedSessionDeletion(projectArg, obligation, signalArg); } } private async recoverFlexManagedSessionDeletions( projectArg: IControllerProjectDocument, supervisorArg: FlexSupervisor, signalArg: AbortSignal, ): Promise<void> { const obligations = await this.requireAuthStore().listManagedSessionDeletionObligations({ projectIdentityId: projectArg.id, harnessId: 'flex', signal: signalArg, }); for (let obligation of obligations) { signalArg.throwIfAborted(); if (obligation.dispatchStartedAt === undefined) { const observation = await this.requireAuthStore().cancelManagedSessionDeletion({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, cancelledAt: new Date(), }); this.setManagedSessionAuthority( projectArg.id, obligation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); continue; } const cleanupCohort = obligation.flexCleanupCohort; const cleanupRoot = cleanupCohort?.find((entry) => entry.cleanupRoot); if ( !cleanupCohort || !cleanupRoot || cleanupRoot.sessionId !== obligation.runtimeId.nativeId || cleanupRoot.providerSessionGeneration !== obligation.providerSessionGeneration ) throw new Error('The Flex deletion obligation has no exact cleanup cohort.'); await supervisorArg.request('session.delete', { scopeId: projectArg.id, root: { sessionId: cleanupRoot.sessionId, sessionGenerationId: cleanupRoot.sessionGenerationId, sessionGenerationSequence: cleanupRoot.sessionGenerationSequence, }, authorizedCohort: cleanupCohort.map((entry) => ({ sessionId: entry.sessionId, sessionGenerationId: entry.sessionGenerationId, sessionGenerationSequence: entry.sessionGenerationSequence, })), }, signalArg); await this.completeRecoveredManagedSessionDeletion(projectArg, obligation, signalArg); } } private async completeRecoveredManagedSessionDeletion( projectArg: IControllerProjectDocument, obligationArg: IControllerManagedSessionDeletionObligation, signalArg: AbortSignal, projectRemovalArg = false, ): Promise<void> { const cleanupObligations = await this.requireAuthStore().getSessionIntelligenceCleanupObligations( projectArg.id, obligationArg.runtimeId, ); for (const cleanup of cleanupObligations) { await this.requireOpenCodeClient().deleteSessionIntelligenceSession( projectArg.directory, cleanup.nativeId, signalArg, ); await this.requireAuthStore().clearSessionIntelligenceTemporarySession( cleanup.projectId, cleanup.sessionId, cleanup.exchangeId, cleanup.nativeId, ); } if (!await this.requireAuthStore().isSessionStateTombstoned( projectArg.id, obligationArg.runtimeId, )) { await this.requireAuthStore().tombstoneSessionState(projectArg.id, obligationArg.runtimeId); } await this.pruneSessionFromLayout(projectArg.id, obligationArg.runtimeId); await this.harnessOperationManager.purgeSession(projectArg.id, obligationArg.runtimeId); const flexSessionKey = obligationArg.runtimeId.harnessId === 'flex' ? this.sessionStateMutationKey(projectArg.id, obligationArg.runtimeId) : undefined; if (flexSessionKey !== undefined) { this.sealedFlexSessionHostRequestAdmissions.add(flexSessionKey); } try { if (flexSessionKey !== undefined) { await this.waitForFlexSessionHostRequestAdmissions(flexSessionKey, signalArg); } const complete = projectRemovalArg ? this.requireAuthStore().completeManagedSessionDeletionForProjectRemoval.bind( this.requireAuthStore(), ) : this.requireAuthStore().completeManagedSessionDeletion.bind(this.requireAuthStore()); await complete({ projectIdentityId: projectArg.id, runtimeId: obligationArg.runtimeId, operationId: obligationArg.operationId, retiredAt: new Date(), }); if (obligationArg.runtimeId.harnessId === 'codex') await this.requireAuthStore().completeCodexCreationDeletion(projectArg.id, obligationArg.runtimeId.nativeId, projectRemovalArg); const retiredAuthorityToken = this.markRetiredSessionAuthority( projectArg.id, obligationArg.runtimeId, ); if (projectRemovalArg) { this.clearRetiredSessionAuthorityIfCurrent(retiredAuthorityToken); } else if (this.resourceCoordinator) { try { await this.resourceCoordinator.reconcileProjectResources( projectArg.id, signalArg, ); } catch (errorArg) { this.scheduleProjectResourceReconciliationRetry( projectArg.id, retiredAuthorityToken, ); throw errorArg; } this.clearRetiredSessionAuthorityIfCurrent(retiredAuthorityToken); } } finally { if (flexSessionKey !== undefined) { this.sealedFlexSessionHostRequestAdmissions.delete(flexSessionKey); } } } private async recoverOpenCodeManagedSessionCreations( projectArg: IControllerProjectDocument, clientArg: OpenCodeClientAdapter, supervisorGenerationArg: string, signalArg: AbortSignal, ): Promise<void> { const obligations = await this.requireAuthStore().listPendingSessionCreationObligations({ projectIdentityId: projectArg.id, harnessId: 'opencode', signal: signalArg, }); for (const obligation of obligations) { signalArg.throwIfAborted(); const current = obligation.dispatchStartedAt === undefined ? undefined : await clientArg.getEnrollableSession( projectArg.directory, obligation.runtimeId.nativeId, signalArg, ); if ( !current || obligation.dispatchStartedAt === undefined || current.session.createdAt < obligation.dispatchStartedAt ) { await this.requireAuthStore().retireManagedSessionCreation({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, terminalAt: new Date(), }); this.deleteManagedSessionAuthorityCacheEntry(projectArg.id, obligation.runtimeId); continue; } const observation = await this.requireAuthStore().completeManagedSessionCreationAdmission({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, supervisorGeneration: supervisorGenerationArg, providerSessionGeneration: current.providerSessionGeneration, terminalAt: new Date(), signal: signalArg, }); this.setManagedSessionAuthority( projectArg.id, obligation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); } } private async recoverFlexManagedSessionCreations( projectArg: IControllerProjectDocument, supervisorArg: FlexSupervisor, supervisorGenerationArg: string, signalArg: AbortSignal, ): Promise<void> { const obligations = await this.requireAuthStore().listPendingSessionCreationObligations({ projectIdentityId: projectArg.id, harnessId: 'flex', signal: signalArg, }); for (const obligation of obligations) { signalArg.throwIfAborted(); let current: TFlexSession | undefined; if (obligation.dispatchStartedAt !== undefined) { try { current = await supervisorArg.request('session.get', { scopeId: projectArg.id, sessionId: obligation.runtimeId.nativeId, }, signalArg); } catch (errorArg) { if (!(errorArg instanceof FlexServiceError) || errorArg.code !== 'NOT_FOUND') throw errorArg; } } if ( !current || obligation.dispatchStartedAt === undefined || obligation.expectedFlexSessionGenerationId === undefined || current.sessionGenerationId !== obligation.expectedFlexSessionGenerationId ) { await this.requireAuthStore().retireManagedSessionCreation({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, terminalAt: new Date(), }); this.deleteManagedSessionAuthorityCacheEntry(projectArg.id, obligation.runtimeId); continue; } const observation = await this.requireAuthStore().completeManagedSessionCreationAdmission({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, supervisorGeneration: supervisorGenerationArg, providerSessionGeneration: requireFlexSessionProviderGeneration(current), sessionGenerationId: current.sessionGenerationId, sessionGenerationSequence: current.sessionGenerationSequence, terminalAt: new Date(), signal: signalArg, }); this.setManagedSessionAuthority( projectArg.id, obligation.runtimeId, 'managed', observation.identity.sessionIdentityId, ); } } private async ensureFlexProjectionReader(): Promise<void> { if (this.flexProjectionReader) { if (this.flexProjectionReaderReady) return; const retainedReader = this.flexProjectionReader; await retainedReader.close(); if (this.flexProjectionReader === retainedReader) this.flexProjectionReader = undefined; } const init = this.flexServiceInit; if (!init) throw new Error('Flex service initialization data is unavailable.'); const reader = new FlexProjectionReader({ database: init.database, controllerId: init.controllerId, }); this.flexProjectionReader = reader; try { await reader.init(); this.flexProjectionReaderReady = true; } catch (errorArg) { this.flexProjectionReaderReady = false; try { await reader.close(); } catch (closeErrorArg) { throw new AggregateError( [errorArg, closeErrorArg], 'Flex projection reader startup and cleanup both failed.', ); } if (this.flexProjectionReader === reader) { this.flexProjectionReader = undefined; } throw errorArg; } } private canRestartOpenCode(): boolean { return this.lifecycleState === 'ready' && !this.stopRequested && !this.operationAbortController.signal.aborted && this.openCodeCrashRecovery !== undefined && this.openCodeAccountSwitchTask === undefined && this.openCodeAccountSwitchBarrier === undefined && !this.openCodeAccountSwitchAdmissionSealed && this.openCodeFailClosedTask === undefined && this.openCodeFailClosedRetryTimer === undefined; } private scheduleOpenCodeRestart(): void { if ( !this.canRestartOpenCode() || this.openCodeRestartTimer || this.openCodeRestartTask ) return; const delayMs = controllerOpenCodeRestartDelayMs(this.openCodeRestartAttempt); this.openCodeRestartAttempt = Math.min(this.openCodeRestartAttempt + 1, 30); this.openCodeRestartTimer = setTimeout(() => { this.openCodeRestartTimer = undefined; this.startOpenCodeRestartAttempt(); }, delayMs); this.openCodeRestartTimer.unref?.(); } private startOpenCodeRestartAttempt(): void { if (!this.canRestartOpenCode() || this.openCodeRestartTask) return; let task: Promise<void>; task = this.withOpenCodeRuntimeMutation(() => this.restartOpenCode()).catch((errorArg) => { if (!this.stopRequested) console.error('OpenCode harness recovery failed.', errorArg); }).finally(() => { if (this.openCodeRestartTask === task) this.openCodeRestartTask = undefined; if (this.openCodeCrashRecovery) this.scheduleOpenCodeRestart(); }); this.openCodeRestartTask = task; } private scheduleRetainedOpenCodeRuntimeCleanup(): void { if ( this.retainedOpenCodeRuntimeCleanup.size === 0 || this.openCodeCrashRecovery || this.stopRequested || this.operationAbortController.signal.aborted || this.openCodeRetainedCleanupTimer || this.openCodeRetainedCleanupTask ) return; const delayMs = controllerOpenCodeRestartDelayMs(this.openCodeRetainedCleanupAttempt); this.openCodeRetainedCleanupAttempt = Math.min( this.openCodeRetainedCleanupAttempt + 1, 30, ); this.openCodeRetainedCleanupTimer = setTimeout(() => { this.openCodeRetainedCleanupTimer = undefined; this.startRetainedOpenCodeRuntimeCleanupAttempt(); }, delayMs); this.openCodeRetainedCleanupTimer.unref?.(); } private startRetainedOpenCodeRuntimeCleanupAttempt(): void { if ( this.retainedOpenCodeRuntimeCleanup.size === 0 || this.openCodeCrashRecovery || this.stopRequested || this.operationAbortController.signal.aborted || this.openCodeRetainedCleanupTask ) return; let task: Promise<void>; task = this.withOpenCodeRuntimeMutation( () => this.retryRetainedOpenCodeRuntimeCleanup(), ).catch((errorArg) => { if (!this.stopRequested) { console.error('Retained OpenCode runtime cleanup failed.', errorArg); } }).finally(() => { if (this.openCodeRetainedCleanupTask === task) { this.openCodeRetainedCleanupTask = undefined; } this.scheduleRetainedOpenCodeRuntimeCleanup(); }); this.openCodeRetainedCleanupTask = task; } private retainOpenCodeRuntimeCleanup( supervisorArg: OpenCodeSupervisor, runtimeArg: IControllerOwnedOpenCodeRuntime | undefined, ): void { this.retainedOpenCodeRuntimeCleanup.set(supervisorArg, runtimeArg); if (this.openCodeCrashRecovery) this.scheduleOpenCodeRestart(); else this.scheduleRetainedOpenCodeRuntimeCleanup(); } private forgetOpenCodeRuntimeCleanup(supervisorArg: OpenCodeSupervisor): void { this.retainedOpenCodeRuntimeCleanup.delete(supervisorArg); if (this.retainedOpenCodeRuntimeCleanup.size > 0) return; this.openCodeRetainedCleanupAttempt = 0; if (this.openCodeRetainedCleanupTimer) { clearTimeout(this.openCodeRetainedCleanupTimer); this.openCodeRetainedCleanupTimer = undefined; } } private async closeSessionIntelligenceCapabilitiesForOpenCodeSupervisor( supervisorArg: OpenCodeSupervisor, ): Promise<void> { const closeResults = await Promise.allSettled( [...this.sessionIntelligenceCapabilities.entries()].flatMap( ([capabilityToken, capability]) => capability.openCodeSupervisor === supervisorArg ? [this.closeSessionIntelligenceCapability(capabilityToken)] : [], ), ); const closeErrors = closeResults.flatMap((result) => ( result.status === 'rejected' ? [result.reason] : [] )); if (closeErrors.length === 1) throw closeErrors[0]; if (closeErrors.length > 1) { throw new AggregateError( closeErrors, 'Closing OpenCode Session Intelligence capabilities failed.', ); } if ( [...this.sessionIntelligenceCapabilities.values()].some( (capability) => capability.openCodeSupervisor === supervisorArg, ) ) throw new Error('OpenCode Session Intelligence capability cleanup is incomplete.'); } private completeOpenCodeCrashCleanup( recoveryArg: IOpenCodeCrashRecoveryContext, ): Promise<void> { if (recoveryArg.cleanupComplete) return Promise.resolve(); if (recoveryArg.cleanupTask) return recoveryArg.cleanupTask; let task: Promise<void>; task = (async () => { await this.waitForAllProjectSessionIntelligenceAdmissions(); for (const job of this.sessionIntelligenceJobs.values()) { job.abortController.abort(new Error('The OpenCode runtime generation exited.')); } await this.waitForSessionIntelligenceOperations(); await this.closeSessionIntelligenceCapabilitiesForOpenCodeSupervisor( recoveryArg.supervisor, ); await this.drainSessionIntelligenceOperations(); await this.harnessOperationManager.releaseHarness('opencode'); recoveryArg.cleanupComplete = true; })().finally(() => { if (recoveryArg.cleanupTask === task) recoveryArg.cleanupTask = undefined; }); recoveryArg.cleanupTask = task; return task; } private async cleanupOpenCodeCrashGeneration( recoveryArg: IOpenCodeCrashRecoveryContext, ): Promise<void> { if (recoveryArg.cleanupComplete) return; await recoveryArg.client.stopEventStream(); if ( this.eventStreamTask === recoveryArg.eventStreamTask || this.openCodeClient === recoveryArg.client ) this.eventStreamTask = undefined; await this.drainOpenCodeOperationMonitors(); await this.waitForAllProjectSessionIntelligenceAdmissions(); for (const job of this.sessionIntelligenceJobs.values()) { job.abortController.abort(new Error('The OpenCode runtime generation exited.')); } await this.waitForSessionIntelligenceOperations(); await this.waitForOpenCodeRuntimeLeases(this.operationAbortController.signal); await this.stopOwnedOpenCodeRuntime(recoveryArg); if (!recoveryArg.cleanupComplete) await this.completeOpenCodeCrashCleanup(recoveryArg); } private async retryRetainedOpenCodeRuntimeCleanup(): Promise<void> { for (const [supervisor, runtime] of [...this.retainedOpenCodeRuntimeCleanup]) { if (runtime) await this.stopOwnedOpenCodeRuntime(runtime); else await this.stopUninstalledOpenCodeSupervisor(supervisor); } } private async restartOpenCode(): Promise<void> { const recovery = this.openCodeCrashRecovery; if (!recovery) return; await this.cleanupOpenCodeCrashGeneration(recovery); await this.retryRetainedOpenCodeRuntimeCleanup(); if ( this.openCodeCrashRecovery !== recovery || this.openCodeAccountSwitchTask || this.openCodeAccountSwitchBarrier || this.openCodeAccountSwitchAdmissionSealed || this.stopRequested || this.lifecycleState !== 'ready' ) return; if (this.openCodeSupervisor || this.openCodeClient) { throw new Error('The failed OpenCode runtime still owns controller authority.'); } const signal = this.operationAbortController.signal; signal.throwIfAborted(); await this.reconcileSelectedOpenCodeAccount(signal); let candidate: IControllerOwnedOpenCodeRuntime | undefined; let installed = false; try { candidate = await this.startStagedOpenCodeRuntime( undefined, recovery.temporarySessionIds, signal, ); if ( this.openCodeCrashRecovery !== recovery || this.openCodeAccountSwitchTask || this.stopRequested ) { await this.stopUninstalledOpenCodeSupervisor(candidate.supervisor); candidate = undefined; return; } await this.installOpenCodeRuntime(candidate, signal, { advanceStreams: false, retainPlannedStopUntilCallerCommit: true, }); installed = true; const installedCandidate = candidate; await this.withCurrentSessionIdentityProjects(signal, async (identityProjects) => { await this.reconcileOpenCodeSessionIdentities( installedCandidate.supervisor, installedCandidate.client, identityProjects, signal, ); const candidateAuthority = this.requireOpenCodeRuntimeExitAuthority( installedCandidate.supervisor, ); const status = installedCandidate.supervisor.getStatus(); if ( this.openCodeCrashRecovery !== recovery || this.openCodeAccountSwitchTask || candidateAuthority.signal.aborted || status.state !== 'ready' || !status.healthy ) throw new Error('The recovered OpenCode runtime exited before authority could reopen.'); this.assertOpenCodeSessionIdentityGenerationCurrent( installedCandidate.supervisor, installedCandidate.client, candidateAuthority, ); if (!this.reopenOpenCodeRuntimeAuthority(recovery.admissionFenceGeneration)) { throw new Error('A newer OpenCode runtime generation superseded this recovery attempt.'); } }); this.openCodeCrashRecovery = undefined; this.openCodeRestartAttempt = 0; this.plannedOpenCodeSupervisorStops.delete(candidate.supervisor); candidate = undefined; } catch (errorArg) { if (candidate) { try { if ( installed || this.openCodeSupervisor === candidate.supervisor || this.openCodeClient === candidate.client ) { await this.stopOwnedOpenCodeRuntime(candidate); } else { await this.stopUninstalledOpenCodeSupervisor(candidate.supervisor); } } catch (cleanupErrorArg) { throw new AggregateError( [errorArg, cleanupErrorArg], 'OpenCode recovery and candidate cleanup both failed.', ); } } throw errorArg; } } private createGitReversion(): ControllerGitReversion { const temporaryRoot = this.gitReversionTemporaryRoot; if (!temporaryRoot) throw new Error('The Git reversion temporary root is not configured.'); const engine: ControllerGitReversion = new ControllerGitReversion({ ownerId: this.requireControllerId(), temporaryRoot, onSealedForCleanup: (errorArg) => { if (this.gitReversion !== engine) return; if (!this.stopRequested) { console.error('Git reversion engine sealed for process cleanup; scheduling replacement.', errorArg); } this.scheduleGitReversionReplacement(); }, }); return engine; } private canReplaceGitReversion(): boolean { return this.lifecycleState === 'ready' && !this.stopRequested && !this.operationAbortController.signal.aborted; } private scheduleGitReversionReplacement(): void { if ( !this.canReplaceGitReversion() || !this.gitReversion?.requiresReplacement || this.gitReversionReplacementTimer || this.gitReversionReplacement ) return; const delayMs = controllerFlexRestartDelayMs(this.gitReversionReplacementAttempt); this.gitReversionReplacementAttempt = Math.min(this.gitReversionReplacementAttempt + 1, 30); this.gitReversionReplacementTimer = setTimeout(() => { this.gitReversionReplacementTimer = undefined; void this.replaceSealedGitReversion(this.operationAbortController.signal) .catch((errorArg) => { if (!this.stopRequested) console.error('Git reversion replacement failed.', errorArg); }) .finally(() => { if (this.gitReversion?.requiresReplacement) this.scheduleGitReversionReplacement(); }); }, delayMs); this.gitReversionReplacementTimer.unref?.(); } /** * Replaces a Git reversion engine that sealed itself after incomplete Git * process cleanup. The sealed engine must confirm its child processes closed * before a fresh engine takes over project registration and recovery, so a * single unconfirmed child can no longer disable reversion until restart. */ private async replaceSealedGitReversion(signalArg: AbortSignal): Promise<void> { const sealed = this.gitReversion; if (!sealed?.requiresReplacement) return; if (this.gitReversionReplacement) return this.gitReversionReplacement; const task = (async () => { await sealed.close(); if (this.gitReversion !== sealed) return; signalArg.throwIfAborted(); if (this.stopRequested) throw new Error('The controller is stopping.'); const replacement = this.createGitReversion(); this.gitReversion = replacement; await this.reconcileProjectState(false, signalArg); await replacement.recover(signalArg); this.gitReversionReplacementAttempt = 0; })(); this.gitReversionReplacement = task; try { await task; } finally { if (this.gitReversionReplacement === task) this.gitReversionReplacement = undefined; } } /** * Starts the Flex child at controller boot under the boot budget. When the * budget expires the start is aborted, the supervisor stops the child, and * Flex recovery brings the harness up after the controller is ready, where * only the lifecycle deadline applies. Readiness therefore never depends on * how long Flex initialization takes. */ private async startFlexWithinBootBudget( supervisorArg: FlexSupervisor, initArg: IFlexServiceInit, ): Promise<boolean> { const budget = new AbortController(); const budgetTimer = setTimeout(() => { budget.abort(new Error('Flex startup exceeded the controller boot budget.')); }, this.flexBootStartupBudgetMs); try { const flexStatus = await supervisorArg.start( initArg, AbortSignal.any([this.operationAbortController.signal, budget.signal]), ); if (flexStatus.state === 'unsupported') { this.flexUnsupported = true; console.error('Flex runtime is unsupported; OpenCode remains available.'); return false; } return flexStatus.state === 'ready'; } catch (errorArg) { if (budget.signal.aborted && !this.operationAbortController.signal.aborted) { console.error( 'Flex harness startup exceeded the boot budget; recovery continues after the controller is ready.', ); } else { console.error('Flex harness startup failed; OpenCode remains available.', errorArg); } return false; } finally { clearTimeout(budgetTimer); } } private canRestartFlex(): boolean { return this.lifecycleState === 'ready' && !this.stopRequested && !this.operationAbortController.signal.aborted && !this.flexUnsupported && this.flexSupervisor !== undefined && this.flexServiceInit !== undefined; } private scheduleFlexRestart(): void { if ( !this.canRestartFlex() || this.flexIsOperational() || this.flexRestartTimer || this.flexRestartTask ) return; const delayMs = controllerFlexRestartDelayMs(this.flexRestartAttempt); this.flexRestartAttempt = Math.min(this.flexRestartAttempt + 1, 30); this.flexRestartTimer = setTimeout(() => { this.flexRestartTimer = undefined; this.startFlexRestartAttempt(); }, delayMs); this.flexRestartTimer.unref?.(); } private startFlexRestartAttempt(): void { if (!this.canRestartFlex() || this.flexRestartTask || this.flexIsOperational()) return; let task: Promise<void>; task = this.restartFlex().catch((errorArg) => { if (!this.stopRequested) console.error('Flex harness recovery failed.', errorArg); }).finally(() => { if (this.flexRestartTask === task) this.flexRestartTask = undefined; if (!this.flexIsOperational()) this.scheduleFlexRestart(); }); this.flexRestartTask = task; } private async stopOwnedFlexSupervisor(supervisorArg: FlexSupervisor): Promise<void> { this.plannedFlexSupervisorStops.add(supervisorArg); try { await supervisorArg.stop(); } finally { this.plannedFlexSupervisorStops.delete(supervisorArg); } } private failClosedFlexAfterSessionIntelligenceCleanup( supervisorArg: FlexSupervisor, errorArg: unknown, ): void { if ( this.flexSupervisor !== supervisorArg || this.lifecycleState !== 'ready' || this.stopRequested || this.operationAbortController.signal.aborted ) return; this.flexRecoveryReady = false; this.sessionIntelligenceFlexAdmissionSealed = true; if (!this.flexChildExitCleanupPending) this.handleHarnessChildExit('flex'); console.error('Session Intelligence cleanup failed; Flex recovery is scheduled.', errorArg); this.scheduleFlexRestart(); } private async closeSessionIntelligenceCapabilitiesForFlexSupervisor( supervisorArg: FlexSupervisor, ): Promise<void> { const closeResults = await Promise.allSettled( [...this.sessionIntelligenceCapabilities.entries()].flatMap( ([capabilityToken, capability]) => capability.flexSupervisor === supervisorArg ? [this.closeSessionIntelligenceCapability(capabilityToken)] : [], ), ); const closeErrors = closeResults.flatMap((result) => ( result.status === 'rejected' ? [result.reason] : [] )); if (closeErrors.length === 1) throw closeErrors[0]; if (closeErrors.length > 1) { throw new AggregateError( closeErrors, 'Closing Session Intelligence read capabilities failed.', ); } if ( [...this.sessionIntelligenceCapabilities.values()].some( (capability) => capability.flexSupervisor === supervisorArg, ) ) throw new Error('Session Intelligence capability cleanup is incomplete.'); } private cleanupFlexGeneration(supervisorArg: FlexSupervisor): Promise<void> { const activeCleanup = this.flexGenerationCleanup; if (activeCleanup) { if (activeCleanup.supervisor !== supervisorArg) { return Promise.reject(new Error('A different Flex generation is still cleaning up.')); } return activeCleanup.task; } let task: Promise<void>; task = (async () => { await this.waitForAllProjectSessionIntelligenceAdmissions(); for (const job of this.sessionIntelligenceJobs.values()) { job.abortController.abort(new Error('The Flex runtime generation is being replaced.')); } await this.waitForSessionIntelligenceOperations(); await this.stopOwnedFlexSupervisor(supervisorArg); this.clearFlexDelegatedAuthority(); if (this.flexChildExitCleanupTask) await this.flexChildExitCleanupTask; this.flexChildExitCleanupPending = true; await this.cleanupFlexAfterChildExit(); await this.closeSessionIntelligenceCapabilitiesForFlexSupervisor(supervisorArg); await this.drainSessionIntelligenceOperations(); await supervisorArg.settleReadyHostRequests(); this.flexChildExitCleanupPending = true; await this.cleanupFlexAfterChildExit(); })().finally(() => { if (this.flexGenerationCleanup?.task === task) this.flexGenerationCleanup = undefined; }); this.flexGenerationCleanup = { supervisor: supervisorArg, task }; return task; } private async waitForFlexGenerationCleanup(cleanupArg: Promise<void>): Promise<void> { const timeoutSignal = AbortSignal.timeout(sessionIntelligenceCancellationDrainMs * 2); await Promise.race([ cleanupArg, new Promise<never>((_resolve, reject) => { const onAbort = () => reject(new Error('Flex generation cleanup exceeded its deadline.')); timeoutSignal.addEventListener('abort', onAbort, { once: true }); void cleanupArg.then( () => timeoutSignal.removeEventListener('abort', onAbort), () => timeoutSignal.removeEventListener('abort', onAbort), ); }), ]); } private cleanupFlexAfterChildExit(): Promise<void> { if (!this.flexChildExitCleanupPending) return Promise.resolve(); if (this.flexChildExitCleanupTask) return this.flexChildExitCleanupTask; this.queueFlexGlobalCleanup('Flex child-exit browser channel cleanup failed.'); let task: Promise<void>; task = (async () => { const results = await Promise.allSettled([ this.drainFlexGlobalCleanup(), this.harnessOperationManager.releaseHarness('flex'), ]); const errors = results .filter((result): result is PromiseRejectedResult => result.status === 'rejected') .map((result) => result.reason); if (errors.length > 0) { throw new AggregateError(errors, 'Flex child-exit cleanup is incomplete.'); } this.flexChildExitCleanupPending = false; })().finally(() => { if (this.flexChildExitCleanupTask === task) this.flexChildExitCleanupTask = undefined; }); this.flexChildExitCleanupTask = task; return task; } private async restartFlex(): Promise<void> { const supervisor = this.flexSupervisor; const retainedInit = this.flexServiceInit; if (!supervisor || !retainedInit) return; this.sealFlexRuntimeAuthority(false); await this.waitForFlexGenerationCleanup(this.cleanupFlexGeneration(supervisor)); if (!this.canRestartFlex() || this.flexSupervisor !== supervisor) return; await this.replaceSealedGitReversion(this.operationAbortController.signal); if (!this.canRestartFlex() || this.flexSupervisor !== supervisor) return; const recoveryAdmissionFence = this.flexHarnessAdmissionFenceGeneration; if (recoveryAdmissionFence === undefined) { throw new Error('Flex recovery lost its operation-admission fence.'); } const startupProjectState = await this.reconcileProjectState( false, this.operationAbortController.signal, ); const init: IFlexServiceInit = { ...retainedInit, projects: startupProjectState.availableProjects.map((project) => ({ projectId: project.id, directory: project.directory, directoryIdentity: structuredClone(project.directoryIdentity!), })), }; this.flexServiceInit = init; let childStarted = false; try { this.plannedFlexSupervisorStops.add(supervisor); const recoveryExitSignal = this.createRuntimeExitSignal(); this.flexSupervisorExitSignals.set(supervisor, recoveryExitSignal); let status: Awaited<ReturnType<FlexSupervisor['start']>>; try { status = await supervisor.start(init, this.operationAbortController.signal); } finally { this.plannedFlexSupervisorStops.delete(supervisor); } if (status.state === 'unsupported') { this.flexUnsupported = true; console.error('Flex runtime is unsupported; automatic recovery is disabled.'); return; } if (status.state !== 'ready' || !status.ready) { throw new Error('The recovered Flex child did not become ready.'); } childStarted = true; await this.ensureFlexProjectionReader(); const postStartReconciliation = await this.reconcileProjectState( true, this.operationAbortController.signal, ); if (!postStartReconciliation.flexSynchronized) { throw new Error('The recovered Flex child could not synchronize authoritative projects.'); } const synchronizedStatus = supervisor.getStatus(); if ( this.flexSupervisorExitSignals.get(supervisor) !== recoveryExitSignal || recoveryExitSignal.signal.aborted || synchronizedStatus.state !== 'ready' || !synchronizedStatus.ready ) throw new Error('The recovered Flex child exited before authority could reopen.'); await this.withCurrentSessionIdentityProjects( this.operationAbortController.signal, async (identityProjects) => { await this.reconcileFlexSessionIdentities( supervisor, identityProjects, this.operationAbortController.signal, ); this.operationAbortController.signal.throwIfAborted(); if (!this.canRestartFlex() || this.flexSupervisor !== supervisor) { throw new Error('The recovered Flex child lost controller ownership before reopen.'); } const finalStatus = supervisor.getStatus(); if ( this.flexSupervisorExitSignals.get(supervisor) !== recoveryExitSignal || recoveryExitSignal.signal.aborted || finalStatus.state !== 'ready' || !finalStatus.ready ) throw new Error('The recovered Flex child exited before authority could reopen.'); this.assertFlexSessionIdentityGenerationCurrent( supervisor, recoveryExitSignal, ); if (!this.reopenFlexRuntimeAuthority(recoveryAdmissionFence)) { throw new Error('A newer Flex runtime generation superseded this recovery attempt.'); } }, postStartReconciliation.projects, ); this.flexRestartAttempt = 0; this.flexResourceGeneration += 1; this.advanceFlexStreams(true); void this.trackOperationPromise(this.recoverDeletingFlexProviderConnections( this.operationAbortController.signal, )).catch(() => { console.error('Pending Flex provider logout recovery failed; it will retry on restart.'); }); void this.trackOperationPromise(this.hydrateFlexProviderCatalogs(true)).catch(() => { console.error('Flex provider catalog hydration failed; provider refresh remains available.'); }); } catch (errorArg) { this.flexRecoveryReady = false; if (childStarted || supervisor.getStatus().state !== 'stopped') { try { await this.stopOwnedFlexSupervisor(supervisor); } catch (stopErrorArg) { throw new AggregateError( [errorArg, stopErrorArg], 'Flex recovery and cleanup both failed.', ); } } throw errorArg; } } private async handleWebSocketConnectionClose( peerArg: IControllerPeer, authStoreArg: SmartDataAuthStore, ): Promise<void> { const peerOperationAbortController = peerArg.context.state[ operationAbortControllerPeerStateKey ]; if (peerOperationAbortController instanceof AbortController) { peerOperationAbortController.abort(new Error('The browser connection closed.')); } const terminalPeerLifecycle = peerArg.context.state[terminalPeerLifecycleStateKey]; if ( terminalPeerLifecycle && typeof terminalPeerLifecycle === 'object' && typeof (terminalPeerLifecycle as Partial<ITerminalPeerLifecycleState>).connected === 'boolean' ) { (terminalPeerLifecycle as ITerminalPeerLifecycleState).connected = false; } this.revokeChildScopesForPeer(peerArg.id); this.authenticatedPeerIds.delete(peerArg.id); this.authenticatedPeerGenerations.delete(peerArg); const cleanupResults = await Promise.allSettled([ this.terminalManager?.detachPeer(peerArg.id) ?? Promise.resolve(), this.closeBrowserViewsForPeer(peerArg.id), this.browserResourceHost?.disconnectPeer(peerArg.id) ?? Promise.resolve(), ]); const cleanupErrors = cleanupResults .filter((result): result is PromiseRejectedResult => result.status === 'rejected') .map((result) => result.reason); if (cleanupErrors.length > 0) { console.error( `Browser connection ${peerArg.id} cleanup remains pending.`, new AggregateError(cleanupErrors, 'Browser connection cleanup is incomplete.'), ); } if (this.lifecycleState !== 'ready') return; const credentialId = peerArg.context.state[authenticatedPeerStateKey]; if (typeof credentialId !== 'string') return; if (this.activeOperations.size >= maxActiveControllerOperations) return; try { this.recordProtectedOperation(peerArg.id); } catch { return; } await this.trackOperationPromise(authStoreArg.recordAuditEvent({ type: 'authentication.logout', outcome: 'succeeded', peerId: peerArg.id, credentialId, })); } private async performStop(): Promise<void> { this.lifecycleState = 'stopping'; this.revokeAllChildScopes(false); if (this.childScopeCleanupTimer) { clearInterval(this.childScopeCleanupTimer); this.childScopeCleanupTimer = undefined; } this.fenceFlexDelegatedAuthority('Controller shutdown fenced the Flex runtime authority.'); // Caller credentials are generation-scoped: nothing minted by this controller may survive it. this.mcpCallerRegistry.clear(); this.activeFlexRuns.clear(); this.flexRecoveryReady = false; this.sessionIntelligenceFlexAdmissionSealed = true; const errors: unknown[] = []; const stopPart = async (operationArg: () => Promise<void>) => { try { await operationArg(); } catch (error) { errors.push(error); } }; await stopPart(() => this.systemMetricsSampler.stop()); await stopPart(() => this.authorityState.stop()); if (this.codexConnections || this.codexSupervisor) { await stopPart(async () => this.codexConnections ? this.codexConnections.close() : this.codexSupervisor!.stop()); } if (this.mcpControllerHost) { await stopPart(async () => { const host = this.mcpControllerHost!; host.beginShutdown(); await host.close(); if (this.mcpControllerHost === host) { this.mcpControllerHost = undefined; } }); } await stopPart(async () => { await Promise.all( [...this.projectResourceReconciliationRetries.values()].map((entry) => entry.task), ); }); if (this.openCodeRestartTimer) { clearTimeout(this.openCodeRestartTimer); this.openCodeRestartTimer = undefined; } if (this.openCodeRestartTask) { await stopPart(async () => this.openCodeRestartTask!); } if (this.openCodeRetainedCleanupTimer) { clearTimeout(this.openCodeRetainedCleanupTimer); this.openCodeRetainedCleanupTimer = undefined; } if (this.flexRestartTimer) { clearTimeout(this.flexRestartTimer); this.flexRestartTimer = undefined; } if (this.gitReversionReplacementTimer) { clearTimeout(this.gitReversionReplacementTimer); this.gitReversionReplacementTimer = undefined; } if (this.gitReversionReplacement) { await stopPart(async () => this.gitReversionReplacement!); } if (this.flexRestartTask) { await stopPart(async () => this.flexRestartTask!); } if (this.flexGenerationCleanup) { await stopPart(async () => this.flexGenerationCleanup!.task); } if (this.flexChildExitCleanupPending) { await stopPart(async () => this.cleanupFlexAfterChildExit()); } for (const timer of this.autoAcceptSweepTimers.values()) { clearTimeout(timer); } this.autoAcceptSweepTimers.clear(); await stopPart(async () => this.drainAutoAcceptSweepTasksForShutdown()); if (this.openCodeFailClosedRetryTimer) { clearTimeout(this.openCodeFailClosedRetryTimer); this.openCodeFailClosedRetryTimer = undefined; } if (this.flexRunCleanupRetryTimer) { clearTimeout(this.flexRunCleanupRetryTimer); this.flexRunCleanupRetryTimer = undefined; } if (this.upgradeWorkerWatchdog) { clearInterval(this.upgradeWorkerWatchdog); this.upgradeWorkerWatchdog = undefined; } this.upgradePreparationAbortController?.abort(new Error('Controller shutdown has begun.')); await Promise.allSettled([ this.upgradePreparationAdmissionTail, ...(this.upgradePreparationTask ? [this.upgradePreparationTask] : []), ...(this.upgradeWorkerWatchdogTask ? [this.upgradeWorkerWatchdogTask] : []), this.upgradeFinalizeTail, ]); if (this.sessionIntelligenceCleanupTimer) { clearInterval(this.sessionIntelligenceCleanupTimer); this.sessionIntelligenceCleanupTimer = undefined; } if (this.openCodeAccountSwitchTask) { await Promise.allSettled([this.openCodeAccountSwitchTask]); } await stopPart(async () => this.authSwitchService?.close()); this.authSwitchService = undefined; if (this.openCodeRetainedCleanupTask) { await stopPart(async () => this.openCodeRetainedCleanupTask!); } for (const job of this.sessionIntelligenceJobs.values()) { job.abortController.abort(new Error('Controller shutdown has begun.')); } await stopPart(async () => { await this.waitForAllProjectSessionIntelligenceAdmissions(); for (const job of this.sessionIntelligenceJobs.values()) { job.abortController.abort(new Error('Controller shutdown has begun.')); } await this.waitForSessionIntelligenceOperations(); }); const cleanupControllerId = this.controllerId; if (this.openCodeClient && this.authStore && cleanupControllerId) { await stopPart(async () => this.cleanupSessionIntelligenceOrphans( cleanupControllerId, Number.MAX_SAFE_INTEGER, AbortSignal.timeout(sessionIntelligenceCleanupTimeoutMs), )); } this.resumeTokens.clear(); this.unsubscribeFlexEvents?.(); this.unsubscribeFlexEvents = undefined; this.pendingOpenCodeControllerEvents.clear(); this.latestOpenCodeToolExecutions.clear(); this.latestOpenCodeReasoningUpdates.clear(); this.latestOpenCodeTextUpdates.clear(); this.clearPendingFlexControllerEvents(); this.latestFlexToolExecutions.clear(); this.latestFlexReasoningUpdates.clear(); this.latestFlexTextUpdates.clear(); this.latestFlexToolExecutionBytes = 0; this.latestFlexMessageUpdateBytes = 0; this.pendingFlexAutoAcceptSweeps.clear(); for (const monitor of this.openCodeOperationMonitors.values()) { monitor.abortController.abort(); } await Promise.allSettled( [...this.openCodeOperationMonitors.values()].map((monitor) => monitor.task), ); this.openCodeOperationMonitors.clear(); // Bound cooperative drainage so stopping the runtime can force transport settlement. await this.waitForOpenCodePromptQueueDrains(operationDrainBeforeChildStopMs); this.openCodePromptQueueDrainTasks.clear(); this.queuedOpenCodePromptSubmissions.clear(); this.openCodePromptQueueFenceCounts.clear(); this.openCodeSessionErrorCleanupTasks.clear(); if (this.terminalManager) { await stopPart(async () => { const terminalManager = this.terminalManager!; await terminalManager.closeAll(); await this.terminalResourceHost?.flushPendingStoppedResources(); if (this.terminalManager === terminalManager) this.terminalManager = undefined; this.terminalResourceHost = undefined; }); } await stopPart(async () => { const results = await Promise.allSettled([...this.browserViewSlots.values()].flatMap( (slot) => slot.view ? [slot.view] : [], ).map((view) => ( this.closeBrowserView(view.id, view.peerId) ))); const errors = results .filter((result): result is PromiseRejectedResult => result.status === 'rejected') .map((result) => result.reason); if (errors.length > 0) { throw new AggregateError(errors, 'Browser view cleanup is incomplete.'); } }); if (this.openCodeClient || this.eventStreamTask) { await stopPart(async () => { await this.openCodeClient?.stopEventStream(); this.eventStreamTask = undefined; }); } this.pendingOpenCodeControllerEvents.clear(); this.latestOpenCodeToolExecutions.clear(); this.latestOpenCodeReasoningUpdates.clear(); this.latestOpenCodeTextUpdates.clear(); this.latestFlexToolExecutions.clear(); this.latestFlexToolExecutionBytes = 0; // Busy conversations are aborted before the child is torn down so no // agent run is killed mid-flight by the process exit itself. await stopPart(async () => this.abortBusySessionsBeforeExit()); await this.waitForActiveOperations(operationDrainBeforeChildStopMs); if (this.flexSupervisor) { await stopPart(async () => { const flexSupervisor = this.flexSupervisor!; await this.cleanupFlexGeneration(flexSupervisor); if (this.flexSupervisor === flexSupervisor) this.flexSupervisor = undefined; }); } await stopPart(async () => this.drainSessionIntelligenceOperations()); if (this.browserResourceHost) { if ([...this.browserViewSlots.values()].some((slot) => slot.view !== undefined)) { errors.push(new Error('Browser views remain owned after shutdown cleanup.')); } else { await stopPart(async () => { const browserResourceHost = this.browserResourceHost!; await browserResourceHost.stop(); this.flexGlobalCleanupPending = false; this.pendingFlexRunCleanups.clear(); if (this.browserResourceHost === browserResourceHost) this.browserResourceHost = undefined; }); } } this.resourceCoordinator = undefined; this.authenticatedPeerIds.clear(); await Promise.allSettled([ ...(this.openCodeControllerEventDrainTask ? [this.openCodeControllerEventDrainTask] : []), ...(this.flexControllerEventDrainTask ? [this.flexControllerEventDrainTask] : []), ]); if (this.flexProjectionReader) { await stopPart(async () => { const flexProjectionReader = this.flexProjectionReader!; this.flexProjectionReaderReady = false; await flexProjectionReader.close(); if (this.flexProjectionReader === flexProjectionReader) { this.flexProjectionReader = undefined; } }); } this.flexProviderLogins.clear(); this.flexRefreshJobs.clear(); this.pendingFlexFinishedJobs.clear(); this.pendingFlexFinishedPrompts.clear(); this.flexModelOptionsByConnection.clear(); this.flexModelRefreshAdmissionTasks.clear(); this.flexProviderNames.clear(); this.flexServiceInit = undefined; this.flexUnsupported = false; this.flexRestartAttempt = 0; if (this.openCodeSupervisor) { await stopPart(async () => { const openCodeSupervisor = this.openCodeSupervisor!; await openCodeSupervisor.stop(); if (this.openCodeSupervisor === openCodeSupervisor) { this.openCodeSupervisor = undefined; } }); } for (const [retainedSupervisor, retainedRuntime] of [ ...this.retainedOpenCodeRuntimeCleanup, ]) { await stopPart(async () => { if (retainedRuntime) await this.stopOwnedOpenCodeRuntime(retainedRuntime); else await this.stopUninstalledOpenCodeSupervisor(retainedSupervisor); }); } if (this.openCodeFailClosedTask) { await stopPart(async () => this.openCodeFailClosedTask!); } if ( !this.openCodeSupervisor && !this.openCodeClient && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.openCodeRestartTask ) { this.openCodeCrashRecovery = undefined; this.openCodeHarnessAdmissionFenceGeneration = undefined; this.openCodeRestartAttempt = 0; } await this.waitForActiveOperations(operationDrainAfterChildStopMs); if ( !this.openCodeSupervisor && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.flexSupervisor && !this.browserResourceHost ) { await stopPart(async () => this.harnessOperationManager.close()); } if (this.gitReversion) { await stopPart(async () => { const gitReversion = this.gitReversion!; await gitReversion.close(); if (this.gitReversion === gitReversion) this.gitReversion = undefined; }); } if ( this.typedServer && !this.terminalManager && !this.openCodeSupervisor && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.flexSupervisor && !this.browserResourceHost && !this.flexProjectionReader && !this.eventStreamTask ) { await stopPart(async () => { this.unsubscribeConnectionClose?.(); this.unsubscribeConnectionClose = undefined; await this.typedServer!.stop(); this.typedServer = undefined; }); } const operationsDrained = await this.waitForActiveOperations( operationDrainAfterServerStopMs, ); if (!operationsDrained) { errors.push(new Error('Controller operations did not settle during shutdown.')); } if ( operationsDrained && !this.typedServer && !this.terminalManager && !this.openCodeSupervisor && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.flexSupervisor && !this.browserResourceHost && !this.flexProjectionReader && !this.eventStreamTask && this.authStore ) { const authStore = this.authStore; if (this.controllerStartedRecorded && !this.controllerStoppedRecorded) { await stopPart(async () => { await authStore.recordAuditEvent({ type: 'controller.stopped', outcome: 'attempted', }); this.controllerStoppedRecorded = true; }); } await stopPart(async () => { await authStore.close(); if (this.authStore === authStore) { this.authStore = undefined; this.passkeyManager = undefined; this.controllerId = undefined; this.openCodeRuntimeDirectory = undefined; } }); } let processGroupDrained = this.options.processMode !== 'detached'; if ( this.options.processMode === 'detached' && !this.typedServer && !this.terminalManager && !this.openCodeSupervisor && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.flexSupervisor && !this.browserResourceHost && !this.flexProjectionReader && !this.eventStreamTask && !this.authStore ) { processGroupDrained = await this.waitForOwnedProcessGroupChildren( processGroupDrainTimeoutMs, ); if (!processGroupDrained) { errors.push(new Error('Owned controller process-group children did not exit during shutdown.')); } } if ( operationsDrained && !this.terminalManager && !this.openCodeSupervisor && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.flexSupervisor && !this.browserResourceHost && !this.flexProjectionReader && !this.eventStreamTask ) { this.openCodeClient = undefined; } if (errors.length === 0) { await stopPart(async () => this.releaseRetainedUpgradeOwnership(true)); } if ( errors.length === 0 && !this.mcpControllerHost && !this.typedServer && !this.terminalManager && !this.openCodeSupervisor && this.retainedOpenCodeRuntimeCleanup.size === 0 && !this.flexSupervisor && !this.browserResourceHost && !this.flexProjectionReader && !this.eventStreamTask && !this.authStore && processGroupDrained ) { this.lifecycleState = 'stopped'; } if (errors.length > 0) { throw new AggregateError(errors, 'The controller did not release every owned resource cleanly.'); } } /** * Composes the three auth scopes. A handler is guarded by the scope router it is * registered on and by nothing else, so each `register*Handlers` method is handed * exactly one scope here, and test/test.routerscopes.node.ts freezes the resulting * method-to-scope table. */ private createRouter(publicOriginArg: string): plugins.typedrequest.TypedRouter { const rootRouter = new plugins.typedrequest.TypedRouter(); const publicRouter = new plugins.typedrequest.TypedRouter(); const browserAuthRouter = new plugins.typedrequest.TypedRouter(); const protectedRouter = new plugins.typedrequest.TypedRouter(); rootRouter.addTypedRouter(publicRouter); rootRouter.addTypedRouter(browserAuthRouter); rootRouter.addTypedRouter(protectedRouter); browserAuthRouter.addMiddleware(async (requestArg) => { const peer = peerFromLocalData(requestArg.localData); if (peer.context.headers.get('origin') !== publicOriginArg) { throw new plugins.typedrequest.TypedResponseError( 'Passkey operations require the configured browser origin.', { code: 'origin_mismatch' }, ); } }); protectedRouter.addMiddleware(async (requestArg) => { const peer = peerFromLocalData(requestArg.localData); if ( peer.context.headers.get('origin') !== publicOriginArg || typeof peer.context.state[authenticatedPeerStateKey] !== 'string' ) { throw new plugins.typedrequest.TypedResponseError( 'Passkey authentication is required on this connection.', { code: 'authentication_required' }, ); } }); this.registerStatusHandlers(publicRouter); this.registerUpgradeHandlers(publicRouter); this.registerAuthHandlers(browserAuthRouter, publicOriginArg); this.registerDiagnosticsHandlers(protectedRouter); this.registerProjectHandlers(protectedRouter); this.registerSlashHandlers(protectedRouter); this.registerAuthSwitchHandlers(protectedRouter); this.registerAccountsHandlers(protectedRouter); this.registerProviderHandlers(protectedRouter); this.registerCodexHandlers(protectedRouter); this.registerModelHandlers(protectedRouter); this.registerSettingsHandlers(protectedRouter); this.registerConversationHandlers(protectedRouter); this.registerSessionHandlers(protectedRouter); this.registerPromptReplyHandlers(protectedRouter); this.registerResourceHandlers(protectedRouter); this.registerBrowserHandlers(protectedRouter); this.registerTerminalHandlers(protectedRouter); return rootRouter; } /** Public scope: a browser reads the status before it holds a passkey session. */ private registerStatusHandlers(publicRouterArg: plugins.typedrequest.TypedRouter): void { publicRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerStatus>( 'controller.status', async (requestArg) => { validateRequest(() => assertEmptyRequest(requestArg)); return this.statusForPrincipal('browser'); }, )); } /** * Public scope: the caller is the upgrade command, which holds no passkey session. * Every handler here authorises itself against the upgrade token it is handed. */ private registerUpgradeHandlers(publicRouterArg: plugins.typedrequest.TypedRouter): void { publicRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerUpgradeLaunch>( 'controller.upgrade.launch', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); this.recordUnauthenticatedPreflightOperation(peer.id); if ( !requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg) || Object.keys(requestArg).length !== 1 || typeof requestArg.token !== 'string' ) { throw new plugins.typedrequest.TypedResponseError( 'The upgrade launch request is invalid.', { code: 'invalid_request' }, ); } return this.runOperation(peer, async (signalArg) => { let coordinator: UpgradeCoordinator | undefined; let worker: IDetachedUpgradeWorker | undefined; try { const installation = await resolveCurrentPnpmGlobalInstallation(); const status = this.getStatus(); coordinator = this.coordinatorForUpgradeToken( requestArg.token, installation.globalRoot, ); await coordinator.consumeLaunchGrant({ token: requestArg.token, port: this.requireRuntimeConfig().controllerPort, packageName: controllerPackageName, packageVersion: commitinfo.version, cliPath: installation.cliPath, controller: { pid: status.controllerPid, processGroupId: status.processGroupId, processFingerprint: status.processFingerprint, }, }); const transaction = await coordinator.readTransaction(requestArg.token); worker = await launchDetachedUpgradeWorker({ installation, payload: { version: upgradeCoordinationVersion, token: requestArg.token, port: this.requireRuntimeConfig().controllerPort, gracePeriodMs: transaction.gracePeriodMs, continueSessions: transaction.continueSessions, expectedController: { pid: status.controllerPid, processGroupId: status.processGroupId, processFingerprint: status.processFingerprint, }, }, }); await coordinator.recordWorkerLaunch( requestArg.token, worker.pid, installation.cliPath, worker.logFilePath, ); return { accepted: true as const, workerPid: worker.pid, logFilePath: worker.logFilePath, }; } catch (errorArg) { let reportedError: unknown = errorArg; if (worker && coordinator) { try { await terminateUpgradeWorkerCandidateUntilDrained(coordinator, worker); } catch (cleanupErrorArg) { reportedError = new AggregateError( [errorArg, cleanupErrorArg], `${currentCliName} upgrade worker launch failed and its candidate did not drain.`, ); } } console.error(`${currentCliName} upgrade worker launch failed:`, reportedError); throw new plugins.typedrequest.TypedResponseError( `The ${currentCliName} upgrade launch was not authorized or could not be started.`, { code: 'upgrade_launch_failed' }, ); } }); }, )); publicRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerUpgradePrepare>( 'controller.upgrade.prepare', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); this.recordUnauthenticatedPreflightOperation(peer.id); assertControllerUpgradePreparationRequest(requestArg); return this.runOperation(peer, async (signalArg) => ( this.runUpgradePreparationAdmission(async () => { const installation = await resolveCurrentPnpmGlobalInstallation(); const coordinator = this.coordinatorForUpgradeToken( requestArg.token, installation.globalRoot, ); const status = this.getStatus(); await coordinator.consumeControllerActionGrant({ token: requestArg.token, action: 'prepare', packageVersion: commitinfo.version, controller: { pid: status.controllerPid, processGroupId: status.processGroupId, processFingerprint: status.processFingerprint, }, }); return await this.prepareUpgrade( coordinator, requestArg.token, requestArg.targetVersion, requestArg.gracePeriodMs, signalArg, ); }) )); }, )); publicRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerUpgradePrepareBegin>( 'controller.upgrade.prepare.begin', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); this.recordUnauthenticatedPreflightOperation(peer.id); assertControllerUpgradePreparationRequest(requestArg); return this.runOperation(peer, async () => ( this.runUpgradePreparationAdmission(async () => { const installation = await resolveCurrentPnpmGlobalInstallation(); const coordinator = this.coordinatorForUpgradeToken( requestArg.token, installation.globalRoot, ); if (await this.upgradePreparationReplayIsAccepted( coordinator, requestArg.token, requestArg.targetVersion, requestArg.gracePeriodMs, )) return { accepted: true as const }; const status = this.getStatus(); await coordinator.consumeControllerActionGrant({ token: requestArg.token, action: 'prepare', packageVersion: commitinfo.version, controller: { pid: status.controllerPid, processGroupId: status.processGroupId, processFingerprint: status.processFingerprint, }, }); return await this.beginUpgradePreparation( coordinator, requestArg.token, requestArg.targetVersion, requestArg.gracePeriodMs, ); }) )); }, ), ); publicRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerUpgradeFinalize>( 'controller.upgrade.finalize', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); this.recordUnauthenticatedPreflightOperation(peer.id); if ( !requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg) || Object.keys(requestArg).length !== 2 || typeof requestArg.token !== 'string' || !['continue', 'compensate', 'reopen'].includes(requestArg.mode) ) throw new plugins.typedrequest.TypedResponseError( 'The upgrade finalization request is invalid.', { code: 'invalid_request' }, ); return this.runOperation(peer, async () => { const installation = await resolveCurrentPnpmGlobalInstallation(); const coordinator = this.coordinatorForUpgradeToken( requestArg.token, installation.globalRoot, ); const status = this.getStatus(); await coordinator.consumeControllerActionGrant({ token: requestArg.token, action: 'finalize', mode: requestArg.mode, packageVersion: commitinfo.version, controller: { pid: status.controllerPid, processGroupId: status.processGroupId, processFingerprint: status.processFingerprint, }, }); await this.abortAndWaitForUpgradePreparation( new Error('Upgrade finalization superseded active preparation.'), ); await coordinator.readTransaction(requestArg.token); return await this.finalizeUpgrade(coordinator, requestArg.token, requestArg.mode); }); }, )); } /** * Passkey ceremonies and session resume. The passkey manager binds every ceremony * step to the configured origin, so this is the one group that is handed it. */ private registerAuthHandlers( browserAuthRouterArg: plugins.typedrequest.TypedRouter, publicOriginArg: string, ): void { browserAuthRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerAuthState>( 'controller.auth.state', async (requestArg, toolsArg) => { validateRequest(() => assertEmptyRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => { return { state: this.setupRequired ? 'setupRequired' as const : 'ready' as const, setupCodeExpired: this.setupRequired && this.setupCodeExpiresAt !== undefined && this.setupCodeExpiresAt <= Date.now(), authenticated: typeof peer.context.state[authenticatedPeerStateKey] === 'string', }; }); }, )); browserAuthRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSetupBegin>( 'controller.auth.setup.begin', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const validated = validateRequest(() => assertSetupBeginRequest(requestArg)); return this.runOperation(peer, async () => { try { return await this.requirePasskeyManager().beginSetup({ peerId: peer.id, origin: publicOriginArg, ...validated, }); } catch (error) { throw asTypedResponseError(error, 'controller.auth.setup.begin'); } }); }, )); browserAuthRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSetupFinish>( 'controller.auth.setup.finish', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const validated = validateRequest(() => assertSetupFinishRequest(requestArg)); return this.runOperation(peer, async () => { this.recordUnauthenticatedPreflightOperation(peer.id); try { await this.requirePasskeyManager().preflightSetupFinish({ peerId: peer.id, origin: publicOriginArg, ...validated, }); } catch (error) { throw asTypedResponseError(error, 'controller.auth.setup.finish'); } this.recordUnauthenticatedAuditOperation(peer.id); const result = await this.runAudited( { type: 'setup.finish', peerId: peer.id }, async () => this.requirePasskeyManager().finishSetup({ peerId: peer.id, origin: publicOriginArg, ...validated, }), ); this.setAuthenticatedPeerCredential(peer, result.credentialId); this.setupRequired = false; this.setupCodeExpiresAt = undefined; return { authenticated: true as const, ...this.mintResumeToken(result.credentialId) }; }); }, )); browserAuthRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerAuthenticationBegin>( 'controller.auth.authentication.begin', async (requestArg, toolsArg) => { validateRequest(() => assertEmptyRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => { try { return await this.requirePasskeyManager().beginAuthentication({ peerId: peer.id, origin: publicOriginArg, }); } catch (error) { throw asTypedResponseError(error, 'controller.auth.authentication.begin'); } }); }, ), ); browserAuthRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerAuthenticationFinish>( 'controller.auth.authentication.finish', async (requestArg, toolsArg) => { const validated = validateRequest(() => assertAuthenticationFinishRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => { this.recordUnauthenticatedPreflightOperation(peer.id); try { await this.requirePasskeyManager().preflightAuthenticationFinish({ peerId: peer.id, origin: publicOriginArg, ...validated, }); } catch (error) { throw asTypedResponseError(error, 'controller.auth.authentication.finish'); } this.recordUnauthenticatedAuditOperation(peer.id); const result = await this.runAudited( { type: 'authentication.finish', peerId: peer.id }, async () => this.requirePasskeyManager().finishAuthentication({ peerId: peer.id, origin: publicOriginArg, ...validated, }), ); this.setAuthenticatedPeerCredential(peer, result.credentialId); return { authenticated: true as const, ...this.mintResumeToken(result.credentialId) }; }); }, ), ); browserAuthRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTempPasswordLogin>( 'controller.auth.temppassword.login', async (requestArg, toolsArg) => { const validated = validateRequest(() => assertTempPasswordLoginRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => { this.recordUnauthenticatedPreflightOperation(peer.id); this.recordUnauthenticatedAuditOperation(peer.id); const result = await this.runAudited( { type: 'temppassword.login', peerId: peer.id }, async () => { const verified = await this.requireAuthStore().verifyTempPassword( validated.password, ); if (!verified) { throw new plugins.typedrequest.TypedResponseError( 'The temporary password is invalid or expired.', { code: 'temp_password_invalid' }, ); } return verified; }, ); this.setAuthenticatedPeerCredential(peer, result.credentialId); return { authenticated: true as const, expiresAt: result.expiresAt.getTime(), ...this.mintResumeToken(result.credentialId, result.expiresAt.getTime()), }; }); }, ), ); browserAuthRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerAuthResume>( 'controller.auth.resume', async (requestArg, toolsArg) => { const validated = validateRequest(() => assertAuthResumeRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => { this.recordUnauthenticatedPreflightOperation(peer.id); this.recordUnauthenticatedAuditOperation(peer.id); const result = await this.runAudited( { type: 'auth.resume', peerId: peer.id }, async () => { const consumed = this.consumeResumeToken(validated.token); if (!consumed) { throw new plugins.typedrequest.TypedResponseError( 'The resume token is invalid or expired.', { code: 'resume_invalid' }, ); } return consumed; }, ); this.setAuthenticatedPeerCredential(peer, result.credentialId); // The rotated token inherits the chain expiry set at first login. return { authenticated: true as const, ...this.mintResumeToken(result.credentialId, result.expiresAt), }; }); }, ), ); } /** * Process-local diagnostics reads. They change nothing and the owner opens them * while errors pile up, so they stay outside the audited pipeline. */ private registerDiagnosticsHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSystemMetrics>( 'controller.system.metrics', async (requestArg, toolsArg) => { validateRequest(() => assertEmptyRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => this.systemMetricsSampler.sample()); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSystemMetricsHistory>( 'controller.system.metrics.history', async (requestArg, toolsArg) => { validateRequest(() => assertEmptyRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => ({ points: await this.requireAuthStore().listSystemMetricsHistory(), })); }, ), ); // Diagnostics read, so it stays outside the audited pipeline like the metrics sample: it // reads process-local memory, changes nothing, and the owner opens it while errors pile up. protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerFailureDetailGet>( 'controller.failure.detail.get', async (requestArg, toolsArg) => { const { references } = validateRequest(() => assertFailureDetailGetRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => ({ failures: references .map((reference) => controllerFailureJournal.find(reference)) .filter((failure): failure is IControllerFailureDetail => failure !== undefined), })); }, ), ); } private registerProjectHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerProjectList>( 'controller.project.list', async (requestArg, toolsArg) => { validateRequest(() => assertEmptyRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => ({ ...await this.listProjectsForPrincipal('browser', peer.id, signalArg), // Blocked removals are deliberately kept out of `projects`: they are gone from the // workspace, and only the operator surface below can act on them. blockedRemovals: await this.listBlockedProjectRemovals(), })); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerProjectCreate>( 'controller.project.create', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.createProjectForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerProjectSuggest>( 'controller.project.suggest', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const parsed = validateRequest(() => assertProjectSuggestRequest(requestArg)); // Deliberately unaudited: suggestions fire per keystroke and read only // bounded directory names. return this.runOperation(peer, async () => ({ suggestions: parsed.unmatchable ? [] : await this.listProjectPathSuggestions( parsed.directoryPath, parsed.prefix, parsed.absolute, ), })); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerProjectRemove>( 'controller.project.remove', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId } = validateRequest(() => assertProjectRemoveRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'project.remove', peerId: peer.id }, async () => { const project = await this.resolveProjectIdentity(projectId); if (this.retiringProjectIds.has(projectId)) { throw new plugins.typedrequest.TypedResponseError( 'The project is already being removed.', { code: 'concurrent_change' }, ); } await this.removeRegisteredProject(project, signalArg); return { success: true }; }, )); }, )); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProjectRemovalRetry>( 'controller.project.removal.retry', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId } = validateRequest( () => assertProjectRemovalRetryRequest(requestArg), ); return this.runOperation(peer, async () => this.runAudited( { type: 'project.removal.retry', peerId: peer.id }, async () => { await this.retryBlockedProjectRemoval(projectId); return { success: true }; }, )); }, ), ); } private registerSlashHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSlashList>( 'controller.slash.list', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId } = validateRequest(() => assertSlashListRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'slash.list', peerId: peer.id, sessionId }, async () => { const project = await this.resolveProject(projectId); await this.assertSessionExists(projectId, sessionId, signalArg); if (sessionId.harnessId === 'codex') { const client = this.requireCodexClient(sessionId.nativeId); const activity = await client.refreshActivity(sessionId.nativeId, signalArg); return { commands: codexSlashCommands.map((command): IControllerSlashCommandDescriptor => { const unavailableReason = codexSlashUnavailableReason(command, activity); return { name: command.name, description: command.description, kind: command.kind, hints: [...command.hints], available: unavailableReason === undefined, ...(unavailableReason === undefined ? {} : { unavailableReason }), workspaceReversion: 'not-applicable', metadata: {}, }; }), reversion: { ...codexSlashReversion, groups: [] }, }; } if (sessionId.harnessId === 'flex') { const [catalog, reversion] = await Promise.all([ this.requireFlexSupervisor().request('slash.list', { scopeId: projectId, sessionId: sessionId.nativeId, }, signalArg), this.requireFlexSupervisor().request('session.reversion.info', { scopeId: projectId, sessionId: sessionId.nativeId, }, signalArg), ]); return { commands: catalog.commands.map((command): IControllerSlashCommandDescriptor => ({ ...command, metadata: {}, })), reversion: { undoAvailable: reversion.undoAvailable, redoAvailable: reversion.redoAvailable, groups: reversion.groups.map((group) => ({ ...group, affectedWorkspaces: group.affectedWorkspaces.map((workspace) => ({ ...workspace })), })), }, }; } const [commands, reversion] = await Promise.all([ this.requireOpenCodeClient().listCommands(project.directory, signalArg), this.requireOpenCodeClient().getSessionReversionInfo( project.directory, sessionId.nativeId, signalArg, ), ]); return { commands: [ ...openCodeBuiltinSlashCommands.map((command) => { const available = command.name === 'undo' ? reversion.undoAvailable : command.name === 'redo' ? reversion.redoAvailable : true; return { ...command, available, ...(available ? {} : { unavailableReason: command.name === 'undo' ? 'No session turn is available to undo.' : 'No reverted session turn is available to redo.', }), }; }), ...commands .filter((command) => !(controllerBuiltinCommands as readonly string[]).includes(command.name)) .map((command): IControllerSlashCommandDescriptor => ({ name: command.name, description: command.description ?? '', kind: 'template', hints: [], available: true, workspaceReversion: 'not-applicable', metadata: {}, })), ], reversion, }; }, )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSlashExecute>( 'controller.slash.execute', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, draftRevision, model, providerConnectionId } = validateRequest( () => assertSlashExecuteRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'slash.execute', peerId: peer.id, sessionId }, async () => this.withHarnessSubmissionAdmission(projectId, sessionId, signalArg, async () => { let operation: TControllerHarnessOperationReservation | undefined; let submissionStarted = false; let submissionAccepted = false; let openCodePromptSubmissionInvoked = false; let flexQueueId: string | undefined; let observeCodexOutcomeUnknown: (() => void) | undefined; let codexOutcomeUnknown = false; let codexNonTurnOutcomeUnknown = false; let codexNonTurnDefinitiveFailure = false; try { await this.assertCodexComposerMutationAllowed(projectId, sessionId, signalArg); operation = await this.harnessOperationManager.reserve( 'slash', projectId, sessionId, draftRevision, { allowAttachments: false }, ); this.claimUnusedSession(projectId, sessionId); const project = await this.resolveProject(projectId); if (sessionId.harnessId === 'codex') { const parsed = classifyControllerSlashInput(operation.draft.text); if (parsed.type !== 'command') { throw new plugins.typedrequest.TypedResponseError( 'The composer draft does not contain a valid slash command.', { code: 'invalid_request' }, ); } const command = codexSlashCommands.find((entry) => entry.name === parsed.name); if (!command) { throw new plugins.typedrequest.TypedResponseError( `Unknown command /${parsed.name}.`, { code: 'invalid_request' }, ); } const client = this.requireCodexClient(sessionId.nativeId); const unavailableReason = codexSlashUnavailableReason( command, await client.refreshActivity(sessionId.nativeId, signalArg), ); if (unavailableReason !== undefined) { throw new plugins.typedrequest.TypedResponseError( unavailableReason, { code: command.unavailableReason === undefined ? 'concurrent_change' : 'invalid_request' }, ); } this.assertSessionMutationAdmission(projectId, sessionId); if (command.execution.type === 'unsupported') { throw new plugins.typedrequest.TypedResponseError( command.unavailableReason ?? `/${command.name} is unavailable.`, { code: 'invalid_request' }, ); } if (command.execution.type === 'client-action') { if (parsed.arguments) { throw new plugins.typedrequest.TypedResponseError( `/${command.name} does not accept arguments.`, { code: 'invalid_request' }, ); } const result: TControllerSlashExecuteResult = { type: 'client-action', name: command.name, action: command.execution.action, }; // The browser clears this exact revision only after the requested UI opened. await this.harnessOperationManager.complete(operation); operation = undefined; return { accepted: true as const, result }; } if (command.execution.type === 'pwd') { if (parsed.arguments) { throw new plugins.typedrequest.TypedResponseError( 'Usage: /pwd', { code: 'invalid_request' }, ); } submissionAccepted = true; this.harnessOperationManager.accept(operation); await this.harnessOperationManager.complete(operation); operation = undefined; return { accepted: true as const, result: { type: 'handler-result', name: command.name, result: this.codexDirectory(sessionId.nativeId, project.directory), }, }; } if (command.execution.type === 'rename') { if (!parsed.arguments) { throw new plugins.typedrequest.TypedResponseError( 'Usage: /rename <title>', { code: 'invalid_request' }, ); } await client.rename(sessionId.nativeId, parsed.arguments, signalArg); submissionAccepted = true; this.harnessOperationManager.accept(operation); await this.harnessOperationManager.complete(operation); operation = undefined; void this.pushControllerEvent({ type: 'session.changed', projectId, harnessId: 'codex', sessionId, timestamp: Date.now(), }).catch(() => undefined); return { accepted: true as const, result: { type: 'operation', name: command.name }, }; } if (command.execution.type === 'fork') { if (parsed.arguments) { throw new plugins.typedrequest.TypedResponseError( 'Usage: /fork', { code: 'invalid_request' }, ); } const state = await this.requireAuthStore().getSessionState(projectId, sessionId); const selectedModel = model ?? state.modelChoice ?? this.defaultModelForHarness('codex'); if (selectedModel && selectedModel.harnessId !== 'codex') { throw new Error('The selected model belongs to another harness.'); } const claimEvictionGeneration = this.claimedSessionEvictionGeneration; let created: { session: IControllerSession; creationPending?: true }; try { created = await this.forkCodexSession( projectId, sessionId, operation.operationId, selectedModel, signalArg, claimEvictionGeneration, () => { submissionStarted = true; }, ); } catch (errorArg) { if (submissionStarted) { if (errorArg instanceof plugins.crossharness.CodexAppServerRequestError && errorArg.code !== undefined) codexNonTurnDefinitiveFailure = true; else codexNonTurnOutcomeUnknown = true; } throw errorArg; } await this.harnessOperationManager.complete(operation); operation = undefined; return { accepted: true as const, result: { type: 'session-created', name: command.name, ...created, }, }; } if (command.execution.type === 'plan') { const selectedModel = await this.codexModeModelChoice( projectId, sessionId, client, signalArg, model, ); if (!parsed.arguments) { let collaborationMode: codexInterfaces.IControllerCodexCollaborationMode; try { collaborationMode = await client.setCollaborationMode( sessionId.nativeId, 'plan', selectedModel, signalArg, () => { submissionStarted = true; }, ); } catch (errorArg) { if (submissionStarted) { if (errorArg instanceof CodexCollaborationModeOutcomeUnknownError) { codexNonTurnOutcomeUnknown = true; } else { codexNonTurnDefinitiveFailure = true; } } throw errorArg; } submissionAccepted = true; this.harnessOperationManager.accept(operation); await this.harnessOperationManager.complete(operation); operation = undefined; return { accepted: true as const, result: { type: 'mode-updated', name: command.name, collaborationMode }, }; } const preparedMode = await client.prepareCollaborationMode( 'plan', selectedModel, signalArg, ); const reservation = operation; const admissionId = `agl_slash_${reservation.operationId}`; const complete = () => { void this.trackOperationPromise(this.harnessOperationManager.complete(reservation)) .then(() => this.queueHarnessPromptChanged(projectId, sessionId)) .catch(() => console.error('Codex slash operation cleanup is incomplete.')); }; observeCodexOutcomeUnknown = () => client.observeSubmissionOutcomeUnknown( sessionId.nativeId, admissionId, ); const arm = await client.startTurn( sessionId.nativeId, parsed.arguments, admissionId, selectedModel, async () => { this.assertSessionMutationAdmission(projectId, sessionId); await this.requireAuthStore().prepareCodexTurn(projectId, sessionId.nativeId); submissionStarted = true; }, complete, signalArg, undefined, () => this.requireAuthStore().cancelUndispatchedCodexTurn(projectId, sessionId.nativeId), preparedMode, ); submissionAccepted = true; await this.requireAuthStore().markCodexMaterialized(projectId, sessionId.nativeId); this.harnessOperationManager.accept(operation); arm(); this.queueHarnessPromptChanged(projectId, sessionId); return { accepted: true as const, result: { type: 'operation', name: command.name }, }; } const reservation = operation; const admissionId = `agl_slash_${reservation.operationId}`; const complete = () => { void this.trackOperationPromise(this.harnessOperationManager.complete(reservation)) .then(() => this.queueHarnessPromptChanged(projectId, sessionId)) .catch(() => console.error('Codex slash operation cleanup is incomplete.')); }; observeCodexOutcomeUnknown = () => client.observeSubmissionOutcomeUnknown( sessionId.nativeId, admissionId, ); const arm = await client.startReview( sessionId.nativeId, parsed.arguments || undefined, admissionId, async () => { this.assertSessionMutationAdmission(projectId, sessionId); await this.requireAuthStore().prepareCodexTurn(projectId, sessionId.nativeId); submissionStarted = true; }, complete, signalArg, () => this.requireAuthStore().cancelUndispatchedCodexTurn(projectId, sessionId.nativeId), ); submissionAccepted = true; await this.requireAuthStore().markCodexMaterialized(projectId, sessionId.nativeId); this.harnessOperationManager.accept(operation); arm(); this.queueHarnessPromptChanged(projectId, sessionId); return { accepted: true as const, result: { type: 'operation', name: command.name }, }; } const harnessId = sessionId.harnessId === 'flex' ? 'flex' : 'opencode'; const sessionState = await this.requireAuthStore().getSessionState(projectId, sessionId); const selectedModel = model ?? sessionState.modelChoice ?? this.defaultModelForHarness(harnessId); const selectedProviderConnectionId = providerConnectionId ?? sessionState.providerConnectionId; this.assertSessionMutationAdmission(projectId, sessionId); if (sessionId.harnessId === 'flex') { const reservedOperation = operation; const executeFlex = async ( flexModel: IFlexModelChoice, ): Promise<IReq_ControllerSlashExecute['response']> => { this.harnessOperationManager.assertCurrentHarnessAdmission(reservedOperation); submissionStarted = true; const result = await this.requireFlexSupervisor().request('slash.execute', { scopeId: projectId, sessionId: sessionId.nativeId, input: reservedOperation.draft.text, model: flexModel, }, signalArg); if (result.type === 'not-command' || result.type === 'malformed') { throw new plugins.typedrequest.TypedResponseError( result.type === 'malformed' ? result.reason : 'The composer draft does not contain a slash command.', { code: 'invalid_request' }, ); } let publicResult: TControllerSlashExecuteResult; if (result.type === 'unknown') { if (result.input !== reservedOperation.draft.text) { throw new Error('The Flex slash authority returned a mismatched unknown input.'); } this.harnessOperationManager.assertCurrentHarnessAdmission(reservedOperation); const admission = await this.requireFlexSupervisor().request('prompt.start', { scopeId: projectId, sessionId: sessionId.nativeId, prompt: reservedOperation.draft.text, model: flexModel, }, signalArg); flexQueueId = admission.queueId; this.harnessOperationManager.bindFlex( reservedOperation, admission.queueId, admission.runId, flexModel, ); submissionAccepted = true; await this.acknowledgeFlexRun( projectId, sessionId.nativeId, admission.queueId, admission.runId, flexModel, ); publicResult = { type: 'literal-prompt', name: result.name }; } else if (result.type === 'prompt-admission') { flexQueueId = result.admission.queueId; this.harnessOperationManager.bindFlex( reservedOperation, result.admission.queueId, result.admission.runId, flexModel, ); submissionAccepted = true; await this.acknowledgeFlexRun( projectId, sessionId.nativeId, result.admission.queueId, result.admission.runId, flexModel, ); publicResult = { type: 'prompt-admission', name: result.name, queueId: result.admission.queueId, runId: result.admission.runId, }; } else { publicResult = result; } submissionAccepted = true; this.harnessOperationManager.accept(reservedOperation); if (!flexQueueId) await this.harnessOperationManager.complete(reservedOperation); this.queueHarnessPromptChanged(projectId, sessionId); return { accepted: true as const, result: publicResult }; }; return this.withEffectiveFlexModelChoice( projectId, selectedModel, selectedProviderConnectionId, signalArg, executeFlex, ); } const parsedCommand = classifyControllerSlashInput(operation.draft.text); if (parsedCommand.type !== 'command') { throw new plugins.typedrequest.TypedResponseError( 'The composer draft does not contain a valid slash command.', { code: 'invalid_request' }, ); } if ((controllerBuiltinCommands as readonly string[]).includes(parsedCommand.name)) { if (parsedCommand.arguments) { throw new plugins.typedrequest.TypedResponseError( 'The requested built-in command does not accept arguments.', { code: 'invalid_request' }, ); } this.harnessOperationManager.assertExclusive(operation); const command = parsedCommand.name as TControllerBuiltinCommand; const pushChanged = () => { void this.pushControllerEvent({ type: command === 'undo' || command === 'redo' ? 'session.history.changed' : 'session.changed', projectId, harnessId: 'opencode', sessionId, timestamp: Date.now(), }).catch(() => undefined); }; if (command === 'compact' || command === 'init') { this.harnessOperationManager.assertCurrentHarnessAdmission(operation); submissionStarted = true; const commandOperation = this.startSessionBackgroundOperation( projectId, sessionId, async (commandSignalArg) => this.requireOpenCodeClient().runBuiltinSessionCommand( project.directory, sessionId.nativeId, command, selectedModel, commandSignalArg, ), ); submissionAccepted = true; this.harnessOperationManager.accept(operation); void commandOperation .catch((errorArg) => { console.error(`Builtin session command ${command} failed.`, errorArg); }) .finally(async () => { pushChanged(); await this.harnessOperationManager.complete(operation!).catch((errorArg) => { console.error('Builtin command resource cleanup failed.', errorArg); }); }) .catch(() => undefined); pushChanged(); return { accepted: true as const, result: { type: 'operation', name: command } as const, }; } this.harnessOperationManager.assertCurrentHarnessAdmission(operation); submissionStarted = true; await this.requireOpenCodeClient().runBuiltinSessionCommand( project.directory, sessionId.nativeId, command, selectedModel, signalArg, ); submissionAccepted = true; this.harnessOperationManager.accept(operation); await this.harnessOperationManager.complete(operation); pushChanged(); return { accepted: true as const, result: { type: 'operation', name: command } as const, }; } const commands = await this.requireOpenCodeClient().listCommands( project.directory, signalArg, ); this.assertSessionMutationAdmission(projectId, sessionId); const command = commands.find((entry) => entry.name === parsedCommand.name); if (!command) { if (this.queueOpenCodePromptIfBlocked({ operation, projectDirectory: project.directory, prompt: operation.draft.text, ...(selectedModel === undefined ? {} : { model: selectedModel }), })) { return { accepted: true as const, result: { type: 'literal-prompt', name: parsedCommand.name } as const, }; } const messageId: IControllerRuntimeId = { harnessId: 'opencode', nativeId: `msg_hcon_${operation.operationId}`, }; this.harnessOperationManager.bindOpenCodeMessage(operation, messageId); this.harnessOperationManager.assertCurrentHarnessAdmission(operation); openCodePromptSubmissionInvoked = true; submissionStarted = true; await this.requireOpenCodeClient().sendMessage( project.directory, sessionId.nativeId, operation.draft.text, selectedModel, undefined, signalArg, messageId.nativeId, ); submissionAccepted = true; this.harnessOperationManager.accept(operation); this.queueHarnessPromptChanged(projectId, sessionId); this.startOpenCodeOperationMonitor(projectId, sessionId, project.directory); return { accepted: true as const, result: { type: 'literal-prompt', name: parsedCommand.name } as const, }; } const expanded = command.template.includes('$ARGUMENTS') ? command.template.replaceAll('$ARGUMENTS', parsedCommand.arguments) : (parsedCommand.arguments ? `${command.template}\n${parsedCommand.arguments}` : command.template); const pinnedModel = command.model !== undefined ? parseModelReference(command.model) : undefined; const prompt = `${expanded}${operation.promptSuffix}`; const promptModel = pinnedModel ?? selectedModel; if (this.queueOpenCodePromptIfBlocked({ operation, projectDirectory: project.directory, prompt, ...(promptModel === undefined ? {} : { model: promptModel }), ...(command.agent === undefined ? {} : { agent: command.agent }), })) { return { accepted: true as const, result: { type: 'prompt-admission', name: parsedCommand.name } as const, }; } const messageId: IControllerRuntimeId = { harnessId: 'opencode', nativeId: `msg_hcon_${operation.operationId}`, }; this.harnessOperationManager.bindOpenCodeMessage(operation, messageId); this.harnessOperationManager.assertCurrentHarnessAdmission(operation); openCodePromptSubmissionInvoked = true; submissionStarted = true; await this.requireOpenCodeClient().sendMessage( project.directory, sessionId.nativeId, prompt, promptModel, command.agent, signalArg, messageId.nativeId, ); submissionAccepted = true; this.harnessOperationManager.accept(operation); this.queueHarnessPromptChanged(projectId, sessionId); this.startOpenCodeOperationMonitor(projectId, sessionId, project.directory); return { accepted: true as const, result: { type: 'prompt-admission', name: parsedCommand.name } as const, }; } catch (errorArg) { if (operation && codexNonTurnDefinitiveFailure) { await this.harnessOperationManager.fail(operation); operation = undefined; throw asTypedResponseError(errorArg, 'controller.slash.execute'); } if (operation && codexNonTurnOutcomeUnknown) { await this.harnessOperationManager.fail(operation); operation = undefined; throw new plugins.typedrequest.TypedResponseError( 'The slash command outcome is unknown. Do not retry it automatically.', { code: 'outcome_unknown' }, ); } if (operation) { if ( !submissionAccepted && operation.sessionId.harnessId === 'flex' && this.isFlexPromptAdmissionTerminated(errorArg) ) { await this.harnessOperationManager.fail(operation); } else if ( submissionAccepted || ( submissionStarted && ( openCodePromptSubmissionInvoked || ( operation.sessionId.harnessId === 'codex' && !(errorArg instanceof plugins.crossharness.CodexAppServerRequestError && !errorArg.dispatched) ) || this.isHarnessSubmissionOutcomeUnknown( errorArg, operation.sessionId.harnessId === 'flex' ? 'flex' : 'opencode', ) ) ) ) { const terminalObserved = this.harnessOperationManager.markOutcomeUnknown(operation); if (operation.sessionId.harnessId === 'codex') { codexOutcomeUnknown = true; observeCodexOutcomeUnknown?.(); } if ( !terminalObserved && operation.sessionId.harnessId === 'opencode' ) { this.failClosedOpenCodeHarness(); } } else { await this.harnessOperationManager.fail(operation); } } if (codexOutcomeUnknown) { throw new plugins.typedrequest.TypedResponseError( 'The slash command outcome is unknown. Do not retry it automatically.', { code: 'outcome_unknown' }, ); } throw asTypedResponseError(errorArg, 'controller.slash.execute'); } }), )); }, )); } private registerAuthSwitchHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerAuthSwitch>( 'controller.authswitch.request', async (requestArg, toolsArg) => { validateRequest(() => plugins.authswitch.assertAuthSwitchServiceRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => this.runAudited( { type: 'authswitch.request', peerId: peer.id }, async () => ({ operation: await this.getAuthSwitchService().request(requestArg), harnesses: this.authSwitchHarnessBehaviour(), }), )); }, )); } /** * The account authority view. One read, no polling companion: a change is announced with the * `accounts.changed` controller event, which is the same push channel every other live controller * state uses, and the client answers it by reading this method. */ private registerAccountsHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerAccountsSnapshot>( 'controller.accounts.snapshot', async (_requestArg, toolsArg) => this.runOperation( peerFromLocalData(toolsArg?.localData), async () => ({ authority: this.authorityState.getProjection() }), ), )); // The import report, read on demand rather than projected: it opens legacy stores, so it is // taken when someone looks at it and never held as state that could go stale unnoticed. protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerAccountsImportInventory>( 'controller.accounts.import.inventory', async (_requestArg, toolsArg) => this.runOperation( peerFromLocalData(toolsArg?.localData), async (signalArg) => ({ result: await this.authorityState.readImportInventory(signalArg) }), ), )); } private registerProviderHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerProviderList>( 'controller.provider.list', async (requestArg, toolsArg) => { validateRequest(() => assertProviderListRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.list', peerId: peer.id }, async () => { signalArg.throwIfAborted(); this.flexProviderNames.set('openai', 'OpenAI'); return { providers: [{ id: 'openai', name: 'OpenAI', loginFlows: ['device'] }], }; }, )); }, )); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderConnectionList>( 'controller.provider.connection.list', async (requestArg, toolsArg) => { validateRequest(() => assertProviderConnectionListRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.connection.list', peerId: peer.id }, async () => { const response = await this.listFlexProviderConnections(signalArg); return { connections: response.connections .filter((connection) => ( connection.status === 'active' || connection.status === 'reauthRequired' )) .map((connection) => this.toPublicProviderConnection(connection)), runtimeAvailable: response.runtimeAvailable, }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderConnectionActivateOpenCode>( 'controller.provider.connection.activate-opencode', async (requestArg, toolsArg) => { const { providerConnectionId } = validateRequest( () => assertProviderConnectionActivateOpenCodeRequest(requestArg), ); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.connection.activate-opencode', peerId: peer.id }, async () => this.activateOpenCodeProviderConnection( providerConnectionId, this.operationAbortController.signal, ), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderLoginBegin>( 'controller.provider.login.begin', async (requestArg, toolsArg) => { const { providerID } = validateRequest(() => assertProviderLoginBeginRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.login.begin', peerId: peer.id }, async () => { signalArg.throwIfAborted(); const result = await this.requireFlexSupervisor().request( 'provider.login.begin', { providerId: providerID }, ); const login: IControllerProviderLogin = { id: flexRuntimeId(result.loginId), verificationUrl: result.verificationUrl, userCode: result.userCode, status: 'pending', }; this.flexProviderLogins.set(controllerRuntimeIdKey(login.id), login); trimOldestMapEntries(this.flexProviderLogins, maxFlexProviderLogins); return { login }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderLoginGet>( 'controller.provider.login.get', async (requestArg, toolsArg) => { const { loginId } = validateRequest(() => assertProviderLoginGetRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.login.read', peerId: peer.id, requestId: loginId }, async () => { const key = controllerRuntimeIdKey(loginId); const retained = this.flexProviderLogins.get(key); if (!retained) { throw new plugins.typedrequest.TypedResponseError( 'The provider login is no longer available.', { code: 'provider_login_not_found' }, ); } const connection = await this.requireFlexSupervisor().request( 'provider.login.status', { loginId: loginId.nativeId }, signalArg, ); const login = this.toPublicProviderLogin(retained, connection); this.flexProviderLogins.set(key, login); return { login }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderLoginCancel>( 'controller.provider.login.cancel', async (requestArg, toolsArg) => { const { loginId } = validateRequest(() => assertProviderLoginCancelRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.login.cancel', peerId: peer.id, requestId: loginId }, async () => { signalArg.throwIfAborted(); const result = await this.requireFlexSupervisor().request( 'provider.login.cancel', { loginId: loginId.nativeId }, ); if (result.canceled) { const key = controllerRuntimeIdKey(loginId); const retained = this.flexProviderLogins.get(key); if (retained) this.flexProviderLogins.set(key, { ...retained, status: 'cancelled' }); } return { cancelled: result.canceled }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderConnectionLogout>( 'controller.provider.connection.logout', async (requestArg, toolsArg) => { const { providerConnectionId } = validateRequest( () => assertProviderConnectionLogoutRequest(requestArg), ); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.connection.logout', peerId: peer.id }, async () => this.withFlexProviderConnectionMutation(providerConnectionId, async () => { if (this.selectedOpenCodeProviderConnectionId === providerConnectionId) { throw new plugins.typedrequest.TypedResponseError( 'Activate another OpenAI account for OpenCode before logging this account out.', { code: 'concurrent_change' }, ); } await this.logoutFlexProviderConnection(providerConnectionId, signalArg); void this.pushControllerEvent({ type: 'harness.changed', harnessId: 'flex', timestamp: Date.now(), }).catch(() => undefined); void this.pushControllerEvent({ type: 'settings.changed', timestamp: Date.now(), }).catch(() => undefined); return { success: true }; }), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderConnectionRateLimitsGet>( 'controller.provider.connection.ratelimits.get', async (requestArg, toolsArg) => { const { providerConnectionId } = validateRequest( () => assertProviderConnectionRateLimitsGetRequest(requestArg), ); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.connection.ratelimits.read', peerId: peer.id }, async () => { const response = await this.requireFlexSupervisor().request( 'provider.connection.ratelimits.get', { providerConnectionId }, signalArg, ); const { providerId, ...rateLimits } = response.rateLimits; return { rateLimits: { ...rateLimits, providerConnectionId, providerID: providerId, }, }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderModelRefreshBegin>( 'controller.provider.model.refresh.begin', async (requestArg, toolsArg) => { const { providerConnectionId } = validateRequest( () => assertProviderModelRefreshBeginRequest(requestArg), ); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'provider.model.refresh.begin', peerId: peer.id }, async () => { return { job: await this.beginFlexModelRefresh(providerConnectionId, signalArg) }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerProviderModelRefreshGet>( 'controller.provider.model.refresh.get', async (requestArg, toolsArg) => { const { jobId } = validateRequest(() => assertProviderModelRefreshGetRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => this.runAudited( { type: 'provider.model.refresh.read', peerId: peer.id, requestId: jobId }, async () => { const cached = this.flexRefreshJobs.get(controllerRuntimeIdKey(jobId)); if (!cached) { throw new plugins.typedrequest.TypedResponseError( 'The model refresh job is no longer available.', { code: 'model_refresh_not_found' }, ); } return { job: { ...cached.job, id: { ...cached.job.id } } }; }, )); }, ), ); } private registerCodexHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexAccountGet>( 'controller.codex.account.get', async (requestArg, toolsArg) => { const { profileId } = validateRequest(() => assertCodexProfileSelection(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async signal => ({ account: await (profileId ? this.codexConnections!.require(profileId).client : this.requireCodexClient()).account(signal) })); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexRestart>( 'controller.codex.restart', async (requestArg, toolsArg) => { const { profileId } = validateRequest(() => assertCodexProfileSelection(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => { await this.restartCodexRuntime(profileId); return { success: true as const }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexProfilesList>( 'controller.codex.profiles.list', async (requestArg, toolsArg) => { const { projectId } = validateRequest(() => assertCodexProfilesList(requestArg)); return this.runOperation(peerFromLocalData(toolsArg?.localData), async () => { if (projectId) await this.resolveProject(projectId); const store = this.requireAuthStore().requireCodexConnections(); const mapping = projectId ? await store.mapping(projectId) : undefined; const profiles = this.codexConnections && !this.codexStartupDiagnostic ? await this.codexConnections.profiles() : (await store.list()).map(profile => ({ ...store.publicProfile(profile), ...(this.codexStartupDiagnostic ? { status: { harnessId: 'codex' as const, state: 'failed' as const, healthy: false, diagnostic: this.codexStartupDiagnostic, } } : {}) })); return { profiles, ...(mapping ? { mapping: { projectId: mapping.projectId, profileId: mapping.profileId, remoteDirectory: mapping.remoteDirectory, } } : {}) }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexProfileCreate>( 'controller.codex.profile.create', async (requestArg, toolsArg) => { const input = validateRequest(() => assertCodexProfileCreate(requestArg)); return this.runOperation(peerFromLocalData(toolsArg?.localData), async signal => { signal.throwIfAborted(); const store = this.requireAuthStore().requireCodexConnections(); return { profile: store.publicProfile(await store.create(input)) }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexProfileUpdate>( 'controller.codex.profile.update', async (requestArg, toolsArg) => { const { profileId, ...input } = validateRequest(() => assertCodexProfileUpdate(requestArg)); return this.runOperation(peerFromLocalData(toolsArg?.localData), async signal => { signal.throwIfAborted(); const store = this.requireAuthStore().requireCodexConnections(); if (input.token !== undefined) await this.restartCodexRuntime(profileId, async () => { await store.update(profileId, input); }); else await store.update(profileId, input); return { profile: store.publicProfile(await store.get(profileId)) }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexProfileTest>( 'controller.codex.profile.test', async (requestArg, toolsArg) => { const { profileId } = validateRequest(() => assertCodexProfileSelection(requestArg, true)); return this.runOperation(peerFromLocalData(toolsArg?.localData), async signal => { signal.throwIfAborted(); const runtime = await this.codexConnections!.connect(profileId!); await runtime.supervisor.checkHealth(); return { profile: (await this.codexConnections!.profiles()).find(profile => profile.id === profileId)! }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexMappingUpdate>( 'controller.codex.mapping.update', async (requestArg, toolsArg) => { const input = validateRequest(() => assertCodexMappingUpdate(requestArg)); return this.runOperation(peerFromLocalData(toolsArg?.localData), async signal => { const project = await this.resolveProject(input.projectId); const store = this.requireAuthStore().requireCodexConnections(); const profile = await store.get(input.profileId); if (profile.state !== 'active') throw new Error('Select an active Codex connection for new conversations.'); if (profile.mode === 'local' && input.remoteDirectory !== project.directory) throw new Error('The local Codex directory must match the registered project.'); const runtime = await this.codexConnections!.connect(input.profileId); const metadata = await runtime.client.request('fs/getMetadata', { path: input.remoteDirectory }, signal); if (metadata.isDirectory !== true) throw new Error('The path is not a directory on this Codex server.'); signal.throwIfAborted(); await store.setMapping(input.projectId, input.profileId, input.remoteDirectory); void this.pushControllerEvent({ type: 'settings.changed', timestamp: Date.now() }).catch(() => undefined); return { mapping: input }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexThreadsList>( 'controller.codex.threads.list', async (requestArg, toolsArg) => { const input = validateRequest(() => { const value = codexRecord(requestArg); if (Object.keys(value).some(key => !['projectId', 'cursor'].includes(key))) throw new Error('Invalid Codex conversation listing.'); const { projectId } = assertCodexProfilesList({ projectId: value.projectId }); return { projectId: projectId!, ...(value.cursor === undefined ? {} : { cursor: codexString(value.cursor, 8192) }) }; }); return this.runOperation(peerFromLocalData(toolsArg?.localData), async signal => { const { client, profileId, remoteDirectory } = await this.codexConnectionForProject(input.projectId); const result = await client.request('thread/list', { cwd: remoteDirectory, archived: false, useStateDbOnly: true, limit: 50, sortKey: 'updated_at', sortDirection: 'desc', ...(input.cursor ? { cursor: input.cursor } : {}) }, signal); const managed = new Set((await this.requireAuthStore().listCodexMemberships(input.projectId, signal)).map(entry => entry.runtimeId.nativeId)); const sessions = codexArray(result.data, 50).map(value => codexSession(value, remoteDirectory)).filter(session => !managed.has(session.id.nativeId)); const nextCursor = result.nextCursor == null ? undefined : codexString(result.nextCursor, 8192); if (nextCursor && nextCursor === input.cursor) throw new Error('Codex conversation pagination did not advance.'); return { profileId, sessions, ...(nextCursor ? { nextCursor } : {}) }; }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexThreadEnroll>( 'controller.codex.thread.enroll', async (requestArg, toolsArg) => { const { projectId, sessionId } = validateRequest(() => { const input = assertSessionGetRequest(requestArg); if (input.sessionId.harnessId !== 'codex') throw new Error('Select a Codex conversation.'); return input; }); return this.runOperation(peerFromLocalData(toolsArg?.localData), async signal => { if (this.retiringProjectIds.has(projectId) || this.upgradeBarrier || this.codexRestartTask || this.codexCreationAdmissions >= 16) throw new Error('Codex conversation admission is temporarily unavailable.'); const release = this.reserveAdmissionEntry(this.projectHarnessSubmissionAdmissions, projectId); this.codexCreationAdmissions += 1; try { const { client, profileId, remoteDirectory } = await this.codexConnectionForProject(projectId); const rawThreadId = client.rawThreadId(sessionId.nativeId); const session = await client.readSession(remoteDirectory, sessionId.nativeId, signal); if (session.id.nativeId !== sessionId.nativeId) throw new Error('Codex returned a different conversation identity.'); const origin = { version: 2 as const, profileId, rawThreadId, remoteDirectory }; const observation = await this.requireAuthStore().admitManagedSession({ projectIdentityId: projectId, runtimeId: codexRuntimeId(sessionId.nativeId), supervisorGeneration: client.supervisor.generation, providerSessionGeneration: codexSessionProviderGeneration(sessionId.nativeId, session.createdAt), codexOrigin: origin, admissionSource: 'explicit-enrollment', signal }); client.supervisor.signal.throwIfAborted(); this.codexConnections!.bind(projectId, sessionId.nativeId, origin); client.register(projectId, remoteDirectory, session); this.setManagedSessionAuthority(projectId, sessionId, 'managed', observation.identity.sessionIdentityId); this.invalidateHarnessSessionEnumeration(projectId); void this.pushControllerEvent({ type: 'sessions.changed', harnessId: 'codex', projectId, timestamp: Date.now() }).catch(() => undefined); return { session }; } finally { this.codexCreationAdmissions -= 1; release(); } }); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexWriterUpdate>( 'controller.codex.writer.update', async (requestArg, toolsArg) => { const { projectId, sessionId } = validateRequest(() => { const value = codexRecord(requestArg); if (Object.keys(value).length !== 3 || !['release', 'resume', 'resumeQueue'].includes(String(value.action))) throw new Error('Invalid Codex writer operation.'); const result = assertSessionGetRequest({ projectId: value.projectId, sessionId: value.sessionId }); if (result.sessionId.harnessId !== 'codex') throw new Error('Writer handoff requires a Codex conversation.'); return result; }); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, signal => this.withHarnessSubmissionAdmission(projectId, sessionId, signal, async () => { const client = this.requireCodexClient(sessionId.nativeId); if (requestArg.action === 'release') { if (this.harnessOperationManager.listSessionActive(projectId, sessionId).length > 0) throw new Error('Stop active and queued prompts before releasing the conversation.'); await client.releaseWriter(sessionId.nativeId, signal); } else if (requestArg.action === 'resume') await client.resumeWriter(sessionId.nativeId, signal); else { this.pausedCodexQueues.delete(this.sessionStateMutationKey(projectId, sessionId)); this.scheduleQueuedCodexPrompt(projectId, sessionId); } return { success: true as const }; })); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<codexInterfaces.IReq_ControllerCodexModeUpdate>( 'controller.codex.mode.update', async (requestArg, toolsArg) => { const { projectId, sessionId, mode } = validateRequest(() => { const value = codexRecord(requestArg); if (Object.keys(value).length !== 3 || (value.mode !== 'default' && value.mode !== 'plan')) { throw new Error('Invalid Codex mode update.'); } const target = assertSessionGetRequest({ projectId: value.projectId, sessionId: value.sessionId }); if (target.sessionId.harnessId !== 'codex') throw new Error('Mode update requires a Codex conversation.'); return { ...target, mode: value.mode as codexInterfaces.TControllerCodexCollaborationMode, }; }); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, signal => this.runAudited( { type: 'session.mode.update', peerId: peer.id, sessionId }, async () => this.withHarnessSubmissionAdmission(projectId, sessionId, signal, async () => { await this.assertCodexComposerMutationAllowed(projectId, sessionId, signal, true); this.assertSessionMutationAdmission(projectId, sessionId); const client = this.requireCodexClient(sessionId.nativeId); const model = await this.codexModeModelChoice(projectId, sessionId, client, signal); try { const collaborationMode = await client.setCollaborationMode( sessionId.nativeId, mode, model, signal, ); return { collaborationMode }; } catch (errorArg) { if (errorArg instanceof CodexCollaborationModeOutcomeUnknownError) { throw new plugins.typedrequest.TypedResponseError( errorArg.message, { code: 'outcome_unknown' }, ); } throw errorArg; } }), )); }, )); } private registerModelHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerModelList>( 'controller.model.list', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.listModelsForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); } private registerSettingsHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSettingsGet>( 'controller.settings.get', async (requestArg, toolsArg) => { validateRequest(() => assertEmptyRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async () => this.readControllerSettings(peer.id)); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSettingsUpdate>( 'controller.settings.update', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const patch = validateRequest(() => assertSettingsUpdateRequest(requestArg)); return this.runOperation(peer, signal => this.updateControllerSettings(peer.id, patch, signal)); }, )); } private registerConversationHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerConversationList>( 'controller.conversation.list', async (requestArg, toolsArg) => { validateRequest(() => assertConversationListRequest(requestArg)); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => ( this.listTrackedConversationsForPrincipal('browser', peer.id, signalArg) )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerConversationSearch>( 'controller.conversation.search', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => ( this.searchConversationsForPrincipal('browser', peer.id, requestArg, signalArg) )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerConversationOpen>( 'controller.conversation.open', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => ( this.openConversationForPrincipal('browser', peer.id, requestArg, signalArg) )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerConversationArchive>( 'controller.conversation.archive', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => ( this.setConversationArchivedForPrincipal('browser', peer.id, requestArg, true, signalArg) )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerConversationReopen>( 'controller.conversation.reopen', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => ( this.setConversationArchivedForPrincipal('browser', peer.id, requestArg, false, signalArg) )); }, )); } /** Chat sessions and the session-group layout that organises them. */ private registerSessionHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSessionGet>( 'controller.session.get', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId } = validateRequest(() => assertSessionGetRequest(requestArg)); return this.runOperation(peer, async (signalArg) => { const detail = await this.readSessionForPrincipal( 'browser', peer.id, { projectId, sessionId }, signalArg, ); if (sessionId.harnessId !== 'opencode') return detail; const childAttention = await this.readBrowserChildAttention( peer, projectId, sessionId as IControllerRuntimeId & { harnessId: 'opencode' }, signalArg, ); return { ...detail, ...childAttention }; }); }, )); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionChildGet>( 'controller.session.child.get', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const request = validateRequest(() => assertSessionChildGetRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.read', peerId: peer.id, sessionId: request.childSessionId }, async () => this.openChildScope(peer, request, signalArg), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionChildMessagesPage>( 'controller.session.child.messages.page', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const request = validateRequest(() => assertSessionChildMessagesPageRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.messages.page', peerId: peer.id, sessionId: request.childSessionId }, async () => { const scope = this.requireChildScope(peer, request); const first = await this.revalidateChildScope(scope, signalArg); const page = await first.parent.client.listMessagePage( first.parent.project.directory, request.childSessionId.nativeId, { limit: request.limit, ...(request.before === undefined ? {} : { before: request.before }), }, AbortSignal.any([signalArg, first.parent.runtimeAuthority.signal]), ); await this.revalidateChildScope(scope, signalArg); return this.boundOpenCodeChildMessagePage(page); }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionChildMessageGet>( 'controller.session.child.message.get', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const request = validateRequest(() => assertSessionChildMessageGetRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.message.read', peerId: peer.id, sessionId: request.childSessionId, requestId: request.messageId, }, async () => { const scope = this.requireChildScope(peer, request); const first = await this.revalidateChildScope(scope, signalArg); const { sourceRole: _sourceRole, parentMessageId: _parentMessageId, terminal: _terminal, ...bundle } = await first.parent.client.getMessageBundle( first.parent.project.directory, request.childSessionId.nativeId, request.messageId.nativeId, AbortSignal.any([signalArg, first.parent.runtimeAuthority.signal]), ); await this.revalidateChildScope(scope, signalArg); return boundControllerMessageBundle(bundle); }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionChildPermissionReply>( 'controller.session.child.permission.reply', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const request = validateRequest( () => assertSessionChildPermissionReplyRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'permission.reply', peerId: peer.id, sessionId: request.childSessionId, requestId: request.requestId, }, async () => this.withHarnessSubmissionAdmission( request.projectId, request.parentSessionId, signalArg, async () => { const scope = this.requireChildScope(peer, request); const first = await this.revalidateChildScope(scope, signalArg, true); const pending = await first.parent.client.listPermissions( first.parent.project.directory, request.childSessionId.nativeId, AbortSignal.any([signalArg, first.parent.runtimeAuthority.signal]), ); this.assertAttentionRequestOwned(pending, request.requestId, 'permission'); const current = await this.revalidateChildScope(scope, signalArg, true); const success = await current.parent.client.replyPermission( current.parent.project.directory, request.requestId.nativeId, request.reply, request.message, AbortSignal.any([signalArg, current.parent.runtimeAuthority.signal]), ); await this.revalidateChildScope(scope, signalArg); return { success }; }, ), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionChildQuestionReply>( 'controller.session.child.question.reply', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const request = validateRequest( () => assertSessionChildQuestionReplyRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'question.reply', peerId: peer.id, sessionId: request.childSessionId, requestId: request.requestId, }, async () => this.withHarnessSubmissionAdmission( request.projectId, request.parentSessionId, signalArg, async () => { const scope = this.requireChildScope(peer, request); const first = await this.revalidateChildScope(scope, signalArg, true); const pending = await first.parent.client.listQuestions( first.parent.project.directory, request.childSessionId.nativeId, AbortSignal.any([signalArg, first.parent.runtimeAuthority.signal]), ); this.assertAttentionRequestOwned(pending, request.requestId, 'question'); const current = await this.revalidateChildScope(scope, signalArg, true); const success = request.answers === null ? await current.parent.client.rejectQuestion( current.parent.project.directory, request.requestId.nativeId, AbortSignal.any([signalArg, current.parent.runtimeAuthority.signal]), ) : await current.parent.client.replyQuestion( current.parent.project.directory, request.requestId.nativeId, request.answers, AbortSignal.any([signalArg, current.parent.runtimeAuthority.signal]), ); await this.revalidateChildScope(scope, signalArg); return { success }; }, ), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionAuxiliaryGet>( 'controller.session.auxiliary.get', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId } = validateRequest( () => assertSessionAuxiliaryGetRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.auxiliary.read', peerId: peer.id, sessionId }, async () => { const auxiliarySignal = AbortSignal.any([ signalArg, AbortSignal.timeout(30_000), ]); const project = await this.resolveProject(projectId); await this.assertSessionIsNotIntelligenceTemporary( projectId, sessionId, auxiliarySignal, ); const session = await this.readManagedSession(projectId, sessionId, auxiliarySignal); if (sessionId.harnessId === 'codex') return { sessionMetrics: { ...(this.requireCodexClient(sessionId.nativeId).activity(sessionId.nativeId).totalTokens !== undefined ? { lifetimeUsedTokens: this.requireCodexClient(sessionId.nativeId).activity(sessionId.nativeId).totalTokens } : {}), ...(this.requireCodexClient(sessionId.nativeId).activity(sessionId.nativeId).contextWindow !== undefined ? { maxContextTokens: this.requireCodexClient(sessionId.nativeId).activity(sessionId.nativeId).contextWindow } : {}), }, sessionIntelligenceEnabled: false, sessionIntelligenceAvailabilityStatus: 'unavailable' as const, sessionIntelligenceUnavailableReason: 'Session Intelligence is not available for Codex conversations.', }; const [sessionMetrics, intelligenceAvailability] = await Promise.all([ session.status === 'busy' || session.status === 'retry' ? Promise.resolve({} as IControllerSessionMetrics) : (sessionId.harnessId === 'opencode' ? this.requireOpenCodeClient().getSessionMetrics( project.directory, sessionId.nativeId, auxiliarySignal, ) : this.getFlexSessionMetrics(projectId, sessionId.nativeId, auxiliarySignal)) .catch(() => { auxiliarySignal.throwIfAborted(); return {} as IControllerSessionMetrics; }), this.resolveSessionIntelligenceAvailability(auxiliarySignal), ]); auxiliarySignal.throwIfAborted(); return { sessionMetrics, ...intelligenceAvailability, }; }, ), toolsArg?.abortSignal); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionModelUpdate>( 'controller.session.model.update', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.updateSessionModelForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionScratchpadSave>( 'controller.session.scratchpad.save', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, text, expectedRevision } = validateRequest( () => assertSessionScratchpadSaveRequest(requestArg), ); if (sessionId.harnessId === 'controller') { throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions do not have a scratchpad.', { code: 'invalid_request' }, ); } const harnessId = sessionId.harnessId; return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.scratchpad.save', peerId: peer.id, sessionId }, async () => this.withActiveSessionMutation(projectId, sessionId, signalArg, async () => { const state = await this.requireAuthStore().saveSessionScratchpad( projectId, sessionId, text, expectedRevision, ); void this.pushControllerEvent({ type: 'session.changed', projectId, harnessId, sessionId, timestamp: Date.now(), }).catch(() => undefined); return { scratchpad: state.scratchpad }; }), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionIntelligenceAsk>( 'controller.session.intelligence.ask', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, question } = validateRequest( () => assertSessionIntelligenceAskRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => { if (this.activeSessionIntelligenceJobCount >= maxActiveSessionIntelligenceJobs) { throw new plugins.typedrequest.TypedResponseError( 'Session Intelligence is busy. Try again after a running analysis completes.', { code: 'operation_limit' }, ); } this.activeSessionIntelligenceJobCount += 1; let jobStarted = false; let releaseProjectAdmission: (() => void) | undefined; const jobKey = this.sessionStateMutationKey(projectId, sessionId); try { releaseProjectAdmission = this.reserveSessionIntelligenceAdmission( projectId, sessionId, ); const admission = await this.runAudited( { type: 'session.intelligence.ask', peerId: peer.id, sessionId }, async () => this.withSessionStateMutation(projectId, sessionId, async () => { await this.resolveProject(projectId); await this.assertSessionExists(projectId, sessionId, signalArg); if (this.sessionIntelligenceJobs.has(jobKey)) { throw new plugins.typedrequest.TypedResponseError( 'Session Intelligence is already running or cleaning up.', { code: 'concurrent_change' }, ); } const model = await this.resolveSessionIntelligenceModel(signalArg); const admission = await this.requireAuthStore().admitSessionIntelligence( projectId, sessionId, question, ); return { ...admission, model }; }), ); const job: ISessionIntelligenceJob = { abortController: new AbortController(), exchangeId: admission.exchange.id, }; this.sessionIntelligenceJobs.set(jobKey, job); jobStarted = true; const task = this.executeSessionIntelligenceJob( projectId, sessionId, question, admission.exchange.id, admission.scratchpadText, admission.model, job, ); const trackedTask = this.trackOperationPromise(task, () => { if (this.sessionIntelligenceJobs.get(jobKey) === job) { this.sessionIntelligenceJobs.delete(jobKey); } this.activeSessionIntelligenceJobCount -= 1; }); job.task = trackedTask; void trackedTask.catch((errorArg) => { console.error('Session Intelligence background job failed.', errorArg); }); return { exchange: admission.exchange }; } finally { releaseProjectAdmission?.(); if (!jobStarted) this.activeSessionIntelligenceJobCount -= 1; } }); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionMessagesPage>( 'controller.session.messages.page', async (requestArg, toolsArg) => { const { projectId, sessionId, limit, before } = validateRequest( () => assertSessionMessagesPageRequest(requestArg), ); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.messages.page', peerId: peer.id, sessionId }, async () => { const project = await this.resolveProject(projectId); await this.assertSessionIsNotIntelligenceTemporary(projectId, sessionId, signalArg); await this.assertSessionExists(projectId, sessionId, signalArg); if (sessionId.harnessId === 'codex') return boundControllerMessagePage(await this.requireCodexClient(sessionId.nativeId).listMessagePage(sessionId.nativeId, { limit, before }, signalArg)); if (sessionId.harnessId === 'flex') { const page = await this.requireFlexProjectionReader().listMessagePage( projectId, sessionId.nativeId, { limit, ...(before === undefined ? {} : { cursor: before }) }, signalArg, ); return boundControllerMessagePage({ bundles: page.messages.map((message) => this.toFlexMessageBundle(message)), ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }), ...(page.truncated ? { truncated: true } : {}), }); } const page = await this.requireOpenCodeClient().listMessagePage( project.directory, sessionId.nativeId, { limit, ...(before === undefined ? {} : { before }) }, signalArg, ); return boundControllerMessagePage({ ...page, bundles: page.bundles.map(({ sourceRole: _sourceRole, parentMessageId: _parentMessageId, terminal: _terminal, ...bundle }) => bundle), }); }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionMessageGet>( 'controller.session.message.get', async (requestArg, toolsArg) => { const { projectId, sessionId, messageId } = validateRequest( () => assertSessionMessageGetRequest(requestArg), ); const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'session.message.read', peerId: peer.id, sessionId, requestId: messageId }, async () => { const project = await this.resolveProject(projectId); await this.assertSessionExists(projectId, sessionId, signalArg); if (sessionId.harnessId === 'codex') return boundControllerMessageBundle(await this.requireCodexClient(sessionId.nativeId).getMessage(sessionId.nativeId, messageId.nativeId, signalArg)); if (sessionId.harnessId === 'flex') { const message = await this.requireFlexProjectionReader().getMessage( projectId, sessionId.nativeId, messageId.nativeId, signalArg, ); return boundControllerMessageBundle(this.toFlexMessageBundle(message)); } const { sourceRole: _sourceRole, parentMessageId: _parentMessageId, terminal: _terminal, ...bundle } = await this.requireOpenCodeClient().getMessageBundle( project.directory, sessionId.nativeId, messageId.nativeId, signalArg, ); return boundControllerMessageBundle(bundle); }, )); }, ), ); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSessionCreate>( 'controller.session.create', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.createSessionForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSessionDelete>( 'controller.session.delete', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId } = validateRequest(() => assertSessionGetRequest(requestArg)); return this.runOperation(peer, async (signalArg) => { this.claimUnusedSession(projectId, sessionId); return this.runAudited( { type: 'session.delete', peerId: peer.id, sessionId }, async (operationId) => ({ success: await this.deleteSession(projectId, sessionId, signalArg, operationId), }), ); }); }, )); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionDiscardEmpty>( 'controller.session.discard-empty', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId } = validateRequest( () => assertSessionGetRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => ( this.discardUnusedSession( projectId, sessionId, async () => this.runAudited( { type: 'session.discard-empty', peerId: peer.id, sessionId }, async (operationId) => this.deleteSession( projectId, sessionId, signalArg, operationId, ), ), ) )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionDraftGet>( 'controller.session.draft.get', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId } = validateRequest( () => assertSessionGetRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => { await this.assertSessionExists(projectId, sessionId, signalArg); return { draft: this.harnessOperationManager.getDraft(projectId, sessionId) }; }); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionDraftUpdate>( 'controller.session.draft.update', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, expectedRevision, text, attachments } = validateRequest( () => assertSessionDraftUpdateRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => { try { return await this.withActiveSessionMutation( projectId, sessionId, signalArg, async () => { await this.assertCodexComposerMutationAllowed(projectId, sessionId, signalArg); const draft = this.harnessOperationManager.updateDraft( projectId, sessionId, expectedRevision, { ...(text === undefined ? {} : { text }), ...(attachments === undefined ? {} : { attachments }), }, ); return { draft }; }, ); } catch (errorArg) { throw asTypedResponseError(errorArg, 'controller.session.draft.update'); } }); }, ), ); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSessionSend>( 'controller.session.send', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, draftRevision, model, providerConnectionId, delivery, expectedTurnId } = validateRequest( () => assertSessionSendRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.sendSessionForPrincipal( 'browser', peer.id, { source: 'draft', projectId, sessionId, draftRevision, ...(delivery ? { delivery } : {}), ...(expectedTurnId ? { expectedTurnId } : {}), ...(model ? { model } : {}), ...(providerConnectionId ? { providerConnectionId } : {}), }, signalArg, )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerSessionAbort>( 'controller.session.abort', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.abortSessionForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionGroupsGet>( 'controller.sessiongroups.get', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); validateRequest(() => assertSessionGroupsGetRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'sessiongroups.read', peerId: peer.id }, async () => this.runSessionLayoutOperation(async () => { const layout = await this.requireAuthStore().getSessionLayout(); const sessionIdsByProject = this.layoutSessionIdsByProject(layout); let changed = false; // A directory that is unmounted, renamed or not yet attached comes back, so its // rows stay stored and are only left out of this response. const withheldProjectIds = new Set<string>(); try { for (const [projectId, sessionIds] of sessionIdsByProject) { signalArg.throwIfAborted(); const availability = await this.resolveLayoutProjectAvailability(projectId); if (availability.status === 'gone') { // A project that is gone takes its layout items with it: keeping them would // leave rows the sidebar can never resolve. changed = await this.requireAuthStore().removeProjectFromLayout(projectId) || changed; continue; } if (availability.status === 'unavailable') { withheldProjectIds.add(projectId); continue; } const project = availability.project; const unmanaged = await this.requireAuthStore() .pruneDefinitivelyUnmanagedSessionLayoutEntries(projectId, sessionIds, signalArg); changed = unmanaged.changed || changed; const unmanagedSessionKeys = new Set( unmanaged.sessionIds.map(controllerRuntimeIdKey), ); const archivedSessionIds = await this.findArchivedLayoutSessionIds( projectId, project.directory, sessionIds.filter((sessionId) => ( !unmanagedSessionKeys.has(controllerRuntimeIdKey(sessionId)) )), signalArg, ); changed = await this.requireAuthStore() .pruneArchivedSessionLayoutEntries(projectId, archivedSessionIds) || changed; } } finally { // A repair that is already persisted is announced even when a later project's // read fails: other clients must not keep rows this one has removed. if (changed) this.pushSessionLayoutChanged(); } const current = changed ? await this.requireAuthStore().getSessionLayout() : layout; return withheldProjectIds.size === 0 ? current : this.withoutLayoutProjects(current, withheldProjectIds); }), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionGroupsUpdate>( 'controller.sessiongroups.update', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { groups, ungroupedItemIds, expectedRevision } = validateRequest( () => assertSessionGroupsUpdateRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'sessiongroups.update', peerId: peer.id }, async () => this.runSessionLayoutOperation(async () => { const requestedItemIds = [ ...groups.flatMap((group) => group.itemIds), ...(ungroupedItemIds ?? []), ]; const archivedSessionKeys = new Set<string>(); // The rows of a project whose directory is unavailable were withheld from this // client, so a full replacement cannot carry them. They are taken from the stored // layout below instead of being deleted with it. const withheldProjectIds = new Set<string>(); const storedLayout = await this.requireAuthStore().getSessionLayout(); const requestedProjectIds = new Set( requestedItemIds.map((itemRef) => itemRef.projectId), ); const projectIds = [...new Set([ ...requestedItemIds, ...storedLayout.groups.flatMap((group) => group.itemIds), ...storedLayout.ungroupedItemIds, ].map((itemRef) => itemRef.projectId))]; for (const projectId of projectIds) { signalArg.throwIfAborted(); const availability = await this.resolveLayoutProjectAvailability(projectId); if (availability.status === 'unavailable') { withheldProjectIds.add(projectId); continue; } // A project only the stored layout names is not part of this replacement: its // rows are the client's to drop, so there is nothing to validate. if (!requestedProjectIds.has(projectId)) continue; if (availability.status === 'gone') throw availability.error; const project = availability.project; const sessionIds = requestedItemIds.flatMap((itemRef) => ( itemRef.kind === 'session' && itemRef.projectId === projectId ? [itemRef.id] : [] )); for (const sessionId of sessionIds) { await this.assertSessionExists(projectId, sessionId, signalArg); } // A resource member must exist in its own project; a stale id would otherwise be // persisted into the order forever. Only listed when the request names one, so a // conversation-only reorder does not pay for it. const resourceIds = requestedItemIds.flatMap((itemRef) => ( itemRef.kind === 'resource' && itemRef.projectId === projectId ? [itemRef.id] : [] )); if (resourceIds.length > 0) { const projectResourceIds = new Set( (await this.requireResourceCoordinator().listResources(projectId, signalArg)) .map((resource) => resource.id), ); for (const resourceId of resourceIds) { if (!projectResourceIds.has(resourceId)) { throw new ControllerResourceUnavailableError( 'The layout references a resource that does not exist in this project.', ); } } } for (const sessionId of await this.findArchivedLayoutSessionIds( projectId, project.directory, sessionIds, signalArg, )) { archivedSessionKeys.add(`${projectId}:${controllerRuntimeIdKey(sessionId)}`); } } const keepItem = (itemRefArg: TControllerLayoutItemRef): boolean => ( // A withheld row this client could not see is restored from the stored layout, // so a request that still carries one must not persist it twice. !withheldProjectIds.has(itemRefArg.projectId) && ( itemRefArg.kind !== 'session' || !archivedSessionKeys.has( `${itemRefArg.projectId}:${controllerRuntimeIdKey(itemRefArg.id)}`, ) ) ); const retained = this.retainWithheldLayoutItemRefs( storedLayout, groups.map((group) => ({ ...group, itemIds: group.itemIds.filter(keepItem), })), ungroupedItemIds?.filter(keepItem), withheldProjectIds, ); const stored = await this.requireAuthStore().updateSessionLayout( retained.groups, retained.ungroupedItemIds, expectedRevision, ); this.pushSessionLayoutChanged(); return withheldProjectIds.size === 0 ? stored : this.withoutLayoutProjects(stored, withheldProjectIds); }), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionYolo>( 'controller.session.yolo', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, enabled } = validateRequest( () => assertSessionYoloRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => { this.claimUnusedSession(projectId, sessionId); return this.runAudited( { type: 'session.yolo', peerId: peer.id, sessionId }, async () => this.withActiveSessionMutation( projectId, sessionId, signalArg, async () => { await this.assertCodexComposerMutationAllowed(projectId, sessionId, signalArg); const project = await this.resolveProject(projectId); const sessionKey = this.projectRuntimeIdKey(projectId, sessionId); if (enabled) { const session = await this.readControllerSession(projectId, sessionId, signalArg); if ( session.archivedAt !== undefined || (sessionId.harnessId === 'opencode' && session.parentId !== undefined) ) { throw new plugins.typedrequest.TypedResponseError( 'Only an active root session can enable automatic permission acceptance.', { code: 'concurrent_change' }, ); } this.yoloSessionsByProjectKey.set(sessionKey, { projectId, sessionId }); // Bounded: stale switches of long-gone chats drop oldest-first. while (this.yoloSessionsByProjectKey.size > maxYoloSessionIds) { const oldest = this.yoloSessionsByProjectKey.keys().next().value; if (oldest === undefined) break; this.yoloSessionsByProjectKey.delete(oldest); } // Requests already waiting in this chat get accepted right away. if (sessionId.harnessId === 'flex') { this.queueFlexAutoAcceptSweep(projectId, sessionId.nativeId); } else if (sessionId.harnessId === 'codex') { this.queueCodexAutoAccept(projectId, sessionId); } else { void this.autoAcceptPendingPermissions( project.directory, ).catch(() => undefined); } } else { const familyKey = this.openCodeFamilyAdmissionKey(projectId, sessionId); const sealFamily = sessionId.harnessId === 'opencode' && !this.autoAcceptPermissions; if (sealFamily) this.sealedOpenCodeAutoAcceptFamilyKeys.add(familyKey); try { this.yoloSessionsByProjectKey.delete(sessionKey); if (sealFamily) { await this.waitForOpenCodeFamilyAutoAcceptAdmissions(familyKey, signalArg); } } finally { if (sealFamily) this.sealedOpenCodeAutoAcceptFamilyKeys.delete(familyKey); } } void this.pushControllerEvent({ type: 'session.changed', projectId, sessionId, timestamp: Date.now(), }).catch(() => undefined); return { enabled: this.isProjectAutoAcceptedSession(projectId, sessionId) }; }, ), ); }); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionRename>( 'controller.session.rename', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.renameSessionForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerSessionArchive>( 'controller.session.archive', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.archiveSessionForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); } /** * Owner answers to a harness permission or question prompt. The child-session variants * are scoped through their parent session and live with the session handlers. */ private registerPromptReplyHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerPermissionReply>( 'controller.permission.reply', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, requestId, reply, message } = validateRequest( () => assertPermissionReplyRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'permission.reply', peerId: peer.id, sessionId, requestId }, async () => { const project = await this.resolveProject(projectId); await this.assertSessionExists(projectId, sessionId, signalArg); if (sessionId.harnessId === 'codex') { return this.withHarnessSubmissionAdmission(projectId, sessionId, signalArg, async () => { signalArg.throwIfAborted(); this.requireCodexClient(sessionId.nativeId).replyPermission(sessionId.nativeId, requestId.nativeId, reply); return { success: true }; }); } if (sessionId.harnessId === 'flex') { signalArg.throwIfAborted(); const flexSupervisor = this.requireFlexSupervisor(); const pendingPermissions = await flexSupervisor.request('permission.list', { scopeId: projectId, }, signalArg); const permission = pendingPermissions.permissions.find( (candidate) => candidate.permissionId === requestId.nativeId, ); if (!permission) throw new FlexServiceError('NOT_FOUND'); const authority = this.flexPermissionAuthority(projectId, permission); if ( !authority || ( authority.type === 'managed' && authority.run.sessionId !== sessionId.nativeId ) || ( authority.type === 'delegated' && authority.lease.parentAuthority.sessionId !== sessionId.nativeId ) ) throw new FlexServiceError('NOT_FOUND'); await this.respondToFlexPermission( projectId, permission, reply, authority, signalArg, ); return { success: true }; } const pendingPermissions = await this.requireOpenCodeClient().listPermissions( project.directory, sessionId.nativeId, signalArg, ); this.assertAttentionRequestOwned(pendingPermissions, requestId, 'permission'); return { success: await this.requireOpenCodeClient().replyPermission( project.directory, requestId.nativeId, reply, message, signalArg, ), }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerQuestionReply>( 'controller.question.reply', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, sessionId, requestId, answers } = validateRequest( () => assertQuestionReplyRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'question.reply', peerId: peer.id, sessionId, requestId }, async () => { const project = await this.resolveProject(projectId); await this.assertSessionExists(projectId, sessionId, signalArg); if (sessionId.harnessId === 'codex') { return this.withHarnessSubmissionAdmission(projectId, sessionId, signalArg, async () => { signalArg.throwIfAborted(); this.requireCodexClient(sessionId.nativeId).replyQuestion(sessionId.nativeId, requestId.nativeId, answers); return { success: true }; }); } const openCodeClient = this.requireOpenCodeClient(); const pendingQuestions = await openCodeClient.listQuestions( project.directory, sessionId.nativeId, signalArg, ); this.assertAttentionRequestOwned(pendingQuestions, requestId, 'question'); return { success: answers === null ? await openCodeClient.rejectQuestion( project.directory, requestId.nativeId, signalArg, ) : await openCodeClient.replyQuestion( project.directory, requestId.nativeId, answers, signalArg, ), }; }, )); }, ), ); } private registerResourceHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerResourceList>( 'controller.resource.list', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.listResourcesForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerResourceCreate>( 'controller.resource.create', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.createResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerResourceRename>( 'controller.resource.rename', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.renameResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerResourceAttach>( 'controller.resource.attach', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.attachResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerResourceDetach>( 'controller.resource.detach', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.detachResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, ), ); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerResourceStart>( 'controller.resource.start', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.startResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerResourceStop>( 'controller.resource.stop', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.stopResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerResourceRetire>( 'controller.resource.retire', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); return this.runOperation(peer, async (signalArg) => this.retireResourceForPrincipal( 'browser', peer.id, requestArg, signalArg, )); }, )); } private registerBrowserHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerBrowserDevToolsOpen>( 'controller.browser.devtools.open', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const connection = this.requireTypedServer().getServerConnectionForRequest(toolsArg); const parsed = validateRequest(() => assertBrowserDevToolsOpenRequest(requestArg)); return this.runOperation(peer, signal => this.openBrowserDevTools(peer, connection, parsed, signal), toolsArg?.abortSignal); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerBrowserDevToolsClose>( 'controller.browser.devtools.close', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const connection = this.requireTypedServer().getServerConnectionForRequest(toolsArg); const parsed = validateRequest(() => assertBrowserDevToolsCloseRequest(requestArg)); return this.runOperation(peer, async () => this.runBrowserViewSlotOperation(peer.id, async slot => { const view = slot.view; if (!view || view.id !== parsed.viewId || view.streamGeneration !== parsed.streamGeneration || view.transportPeer !== peer || view.transportConnection.peer !== connection.peer || view.credentialId !== peer.context.state[authenticatedPeerStateKey] || view.devTools?.id !== parsed.devToolsId) return { closed: false }; const cause = view.devTools.abortController.signal.reason; await this.closeBrowserDevTools(view); return { closed: true, ...(cause === undefined ? {} : { reason: String(cause instanceof Error ? cause.message : cause).slice(0, 512), }) }; }), toolsArg?.abortSignal); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerBrowserViewOpen>( 'controller.browser.view.open', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const transportConnection = this.requireTypedServer().getServerConnectionForRequest(toolsArg); const parsed = validateRequest(() => assertBrowserViewOpenRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'resource.browser.view.open', peerId: peer.id }, async () => this.openBrowserView(peer, transportConnection, parsed, signalArg), ), toolsArg?.abortSignal); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerBrowserViewActivate>( 'controller.browser.view.activate', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { viewId, streamGeneration } = validateRequest( () => assertBrowserViewActivateRequest(requestArg), ); return this.runOperation(peer, async (signalArg) => { await this.activateBrowserView(viewId, peer, streamGeneration, signalArg); return { activated: true as const }; }, toolsArg?.abortSignal); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerBrowserViewRecover>( 'controller.browser.view.recover', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const transportConnection = this.requireTypedServer().getServerConnectionForRequest(toolsArg); const parsed = validateRequest(() => assertBrowserViewRecoverRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.recoverBrowserView( peer, transportConnection, parsed, signalArg, ), toolsArg?.abortSignal); }, )); protectedRouterArg.addTypedHandler(new plugins.typedrequest.TypedHandler<IReq_ControllerBrowserViewClose>( 'controller.browser.view.close', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { viewId, expectedStreamGeneration } = validateRequest( () => assertBrowserViewCloseRequest(requestArg), ); return this.runOperation(peer, async () => this.runAudited( { type: 'resource.browser.view.close', peerId: peer.id }, async () => { const closed = await this.closeBrowserView( viewId, peer, expectedStreamGeneration, ); return { closed }; }, )); }, )); } private registerTerminalHandlers(protectedRouterArg: plugins.typedrequest.TypedRouter): void { protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalList>( 'controller.terminal.list', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId } = validateRequest(() => assertTerminalListRequest(requestArg)); return this.runOperation(peer, async () => this.runAudited( { type: 'terminal.list', peerId: peer.id }, async () => ({ terminals: await this.requireTerminalManager().listTerminals(projectId), }), )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalCreate>( 'controller.terminal.create', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, title } = validateRequest(() => assertTerminalCreateRequest(requestArg)); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'terminal.create', peerId: peer.id }, async () => { await this.resolveProject(projectId); const resource = await this.requireResourceCoordinator().createResource({ projectId, kind: 'terminal', ...await this.requireTerminalManager().resolveTerminalResourceMetadata(projectId, title), }, signalArg); if (resource.kind !== 'terminal') throw new Error('The terminal resource was not created.'); this.pushResourceChanged(projectId); return { terminal: { id: { harnessId: 'controller' as const, nativeId: resource.id }, title: resource.title, command: resource.command, cwd: resource.cwd, }, }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalRename>( 'controller.terminal.rename', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, terminalId, title } = validateRequest( () => assertTerminalRenameRequest(requestArg), ); return this.runOperation(peer, async () => this.runAudited( { type: 'terminal.rename', peerId: peer.id, requestId: terminalId }, async () => { const exists = await this.requireResourceCoordinator().requireResourceKind( projectId, terminalId.nativeId, 'terminal', ); if (!exists) throw new ControllerResourceUnavailableError( 'The requested terminal resource is unavailable.', ); const resource = await this.requireResourceCoordinator().renameResource( projectId, terminalId.nativeId, title, ); if (resource.kind !== 'terminal') throw new ControllerResourceUnavailableError( 'The requested terminal resource is unavailable.', ); this.pushResourceChanged(projectId); return { terminal: { id: { harnessId: 'controller' as const, nativeId: resource.id }, title: resource.title, command: resource.command, cwd: resource.cwd, }, }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalRemove>( 'controller.terminal.remove', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, terminalId } = validateRequest( () => assertTerminalTargetRequest(requestArg, 'terminal removal request'), ); return this.runOperation(peer, async (signalArg) => this.runAudited( { type: 'terminal.remove', peerId: peer.id, requestId: terminalId }, async () => { const exists = await this.requireResourceCoordinator().requireResourceKind( projectId, terminalId.nativeId, 'terminal', ); if (!exists) return { success: false }; await this.requireResourceCoordinator().retireResource( projectId, terminalId.nativeId, signalArg, ); this.pushResourceChanged(projectId); return { success: true }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalAttach>( 'controller.terminal.attach', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const peerLifecycle = this.terminalPeerLifecycle(peer); const { projectId, terminalId } = validateRequest( () => assertTerminalTargetRequest(requestArg, 'terminal attach request'), ); return this.runOperation(peer, async () => this.runAudited( { type: 'terminal.attach', peerId: peer.id, requestId: terminalId }, async () => { await this.requireTerminalManager().attach( peer.id, projectId, terminalId.nativeId, () => peerLifecycle.connected, ); return { success: true }; }, )); }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalDetach>( 'controller.terminal.detach', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, terminalId } = validateRequest( () => assertTerminalTargetRequest(requestArg, 'terminal detach request'), ); try { await this.requireTerminalManager().detach(peer.id, projectId, terminalId.nativeId); } catch (error) { throw asTypedResponseError(error, 'controller.terminal.detach'); } return { success: true }; }, ), ); // Terminal input and resize skip the audited-operation pipeline on // purpose: they fire per keystroke, so a durable audit record per call // would flood the audit collection and the rate limiter would make // typing impossible. Authentication still gates them like every // protected method, and the terminal manager bounds the payload sizes. protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalInput>( 'controller.terminal.input', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, terminalId, dataBase64 } = validateRequest( () => assertTerminalInputRequest(requestArg), ); try { await this.requireTerminalManager().input( peer.id, projectId, terminalId.nativeId, Buffer.from(dataBase64, 'base64'), ); } catch (error) { throw asTypedResponseError(error, 'controller.terminal.input'); } return { success: true }; }, ), ); protectedRouterArg.addTypedHandler( new plugins.typedrequest.TypedHandler<IReq_ControllerTerminalResize>( 'controller.terminal.resize', async (requestArg, toolsArg) => { const peer = peerFromLocalData(toolsArg?.localData); const { projectId, terminalId, rows, cols } = validateRequest( () => assertTerminalResizeRequest(requestArg), ); try { await this.requireTerminalManager().resize( peer.id, projectId, terminalId.nativeId, rows, cols, ); } catch (error) { throw asTypedResponseError(error, 'controller.terminal.resize'); } return { success: true }; }, ), ); } private async runAudited<T>( auditArg: Omit<IRecordAuditEventInput, 'outcome'>, operationArg: (operationIdArg: string) => Promise<T>, ): Promise<T> { const operationId = plugins.crypto.randomBytes(24).toString('base64url'); const correlatedAudit = { ...auditArg, operationId }; try { // Establish a durable audit record before an authentication transition or // OpenCode side effect can occur. A missing terminal record therefore // means an indeterminate audit completion, never an invisible action. await this.requireAuthStore().recordAuditEvent({ ...correlatedAudit, outcome: 'attempted', }); } catch (error) { throw asTypedResponseError(error, auditArg.type); } let result: T; try { result = await operationArg(operationId); } catch (error) { const outcome = error instanceof AuthError || error instanceof plugins.typedrequest.TypedResponseError ? 'denied' : 'failed'; const suppressUnboundedDenialAudit = error instanceof AuthError && ( error.code === 'rate_limited' || error.code === 'ceremony_limit' || error.code === 'setup_unavailable' || error.code === 'setup_invalid' || error.code === 'setup_expired' || error.code === 'ceremony_invalid' || error.code === 'ceremony_replayed' || error.code === 'ceremony_expired' || error.code === 'origin_mismatch' || error.code === 'peer_mismatch' || error.code === 'credential_not_found' ); if (!suppressUnboundedDenialAudit) { try { await this.requireAuthStore().recordAuditEvent({ ...correlatedAudit, outcome }); } catch { // The original denial/failure remains authoritative. A database outage // must not turn an already-denied request into a different outcome. } } throw asTypedResponseError(error, auditArg.type); } try { await this.requireAuthStore().recordAuditEvent({ ...correlatedAudit, outcome: 'succeeded', }); } catch { // The durable attempted record preserves the audit gap. The completed // operation remains authoritative so clients never retry a side effect // merely because its terminal audit insert failed afterward. } return result; } private requireProjectsRoot(): string { const projectsRoot = this.requireRuntimeConfig().projectsRoot; if (!projectsRoot) { throw new Error('The controller runtime configuration is missing its projects root.'); } return projectsRoot; } private async registerExplicitInitialProject( initialProjectDirectoryArg: string | undefined, ): Promise<IControllerProjectDocument[]> { const authStore = this.requireAuthStore(); const pendingRemovals = await authStore.listPendingProjectRemovals(); if (initialProjectDirectoryArg !== undefined) { const initialProjectDirectory = await resolveCanonicalDirectory(initialProjectDirectoryArg); await authStore.createProject( this.deriveProjectName(initialProjectDirectory.directory), initialProjectDirectory.directory, { ancestry: initialProjectDirectory.ancestry }, ); } return pendingRemovals; } /** `created` is false when the directory was already a registered project. */ private createProjectAndReconcile( requestedDirectoryArg: string, ): Promise<{ project: IControllerProjectDocument; created: boolean }> { return this.runControllerProjectMutation(async () => { const resolvedDirectory = await this.resolveExistingProjectDirectory(requestedDirectoryArg); const { project, created } = await this.requireAuthStore().createProject( this.deriveProjectName(resolvedDirectory.directory), resolvedDirectory.directory, { ancestry: resolvedDirectory.ancestry }, ); const reconciliation = await this.reconcileProjectStateInMutation(true); if (!reconciliation.flexSynchronized) this.scheduleFlexProjectReconciliationRetry(); return { project, created }; }); } /** * The durable deregistration used by `controller.project.remove` and by the rollback of an * implicit registration. A failure schedules the resumable pending removal, exactly as the * operator-initiated path does, so a half-removed project is never left unattended. */ private async removeRegisteredProject( projectArg: IControllerProjectDocument, signalArg: AbortSignal, ): Promise<void> { this.retiringProjectIds.add(projectArg.id); this.fenceFlexDelegatedAuthorityForParentScope(projectArg.id); this.revokeChildScopesForProject(projectArg.id); let removed = false; try { await this.finishProjectRemoval(projectArg, signalArg, true); removed = true; } catch (errorArg) { this.schedulePendingProjectRemoval(projectArg); throw errorArg; } finally { if (removed) this.retiringProjectIds.delete(projectArg.id); } await this.reconcileProjectState(true); void this.pushControllerEvent({ type: 'projects.changed', projectId: projectArg.id, timestamp: Date.now(), }).catch(() => undefined); } private async settleManagedSessionsForProjectRemoval( projectArg: IControllerProjectDocument, signalArg: AbortSignal, flexRuntimeCleanedArg: boolean, ): Promise<void> { const pendingCreations = await this.requireAuthStore() .listPendingSessionCreationObligationsForProjectRemoval({ projectIdentityId: projectArg.id, signal: signalArg, }); for (const obligation of pendingCreations) { signalArg.throwIfAborted(); if (flexRuntimeCleanedArg && obligation.runtimeId.harnessId === 'flex') { throw new Error('A pending Flex creation was not captured before project removal.'); } const admitted = await this.admitProjectRemovalCreationIfPresent( projectArg, obligation, signalArg, ); if (!admitted) { await this.requireAuthStore().retireManagedSessionCreationForProjectRemoval({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, terminalAt: new Date(), }); this.deleteManagedSessionAuthorityCacheEntry(projectArg.id, obligation.runtimeId); } } await this.recoverCodexCreations(projectArg.id, projectArg.directory, signalArg, true); const pendingDeletions = await this.requireAuthStore() .retireProjectManagedSessionsForProjectRemoval({ projectIdentityId: projectArg.id, retiredAt: new Date(), signal: signalArg, }); for (const obligation of pendingDeletions) { signalArg.throwIfAborted(); const providerSessionPresent = await this.assertManagedDeletionProviderGenerationCurrent( projectArg.id, projectArg.directory, obligation, signalArg, ); let dispatchedObligation = obligation; if (obligation.runtimeId.harnessId !== 'flex') { if (obligation.dispatchStartedAt === undefined) { dispatchedObligation = await this.requireAuthStore() .markManagedSessionDeletionDispatchedForProjectRemoval({ projectIdentityId: projectArg.id, runtimeId: obligation.runtimeId, operationId: obligation.operationId, dispatchStartedAt: new Date(), }); } if (providerSessionPresent) { if (obligation.runtimeId.harnessId === 'codex') await this.requireCodexClient(obligation.runtimeId.nativeId).deleteThread(this.codexDirectory(obligation.runtimeId.nativeId, projectArg.directory), obligation.runtimeId.nativeId, signalArg); else await this.requireOpenCodeClient().deleteSessionIfPresent(projectArg.directory, obligation.runtimeId.nativeId, signalArg); } } else { const cleanupCohort = obligation.flexCleanupCohort; const cleanupRoot = cleanupCohort?.find((entry) => entry.cleanupRoot); if (!cleanupCohort || !cleanupRoot) { throw new Error('A project-removal Flex deletion has no exact cleanup cohort.'); } if (flexRuntimeCleanedArg) { const projectRoot = projectArg.flexCleanupCohort?.find((entry) => ( entry.cleanupRoot && entry.sessionId === cleanupRoot.sessionId && entry.sessionGenerationId === cleanupRoot.sessionGenerationId && entry.sessionGenerationSequence === cleanupRoot.sessionGenerationSequence )); if (!projectRoot) { throw new Error('A managed Flex root was absent from project cleanup authority.'); } } else { await this.requireFlexSupervisor().request('session.delete', { scopeId: projectArg.id, root: { sessionId: cleanupRoot.sessionId, sessionGenerationId: cleanupRoot.sessionGenerationId, sessionGenerationSequence: cleanupRoot.sessionGenerationSequence, }, authorizedCohort: cleanupCohort.map((entry) => ({ sessionId: entry.sessionId, sessionGenerationId: entry.sessionGenerationId, sessionGenerationSequence: entry.sessionGenerationSequence, })), }, signalArg); } } await this.completeRecoveredManagedSessionDeletion( projectArg, dispatchedObligation, signalArg, true, ); } const [remainingCreations, remainingDeletions] = await Promise.all([ this.requireAuthStore().listPendingSessionCreationObligationsForProjectRemoval({ projectIdentityId: projectArg.id, signal: signalArg, }), this.requireAuthStore().listManagedSessionDeletionObligationsForProjectRemoval({ projectIdentityId: projectArg.id, signal: signalArg, }), ]); if (remainingCreations.length > 0 || remainingDeletions.length > 0) { throw new Error('Managed session project-removal recovery remains incomplete.'); } this.clearManagedSessionAuthorityProjectScope(projectArg.id); } private async admitProjectRemovalCreationIfPresent( projectArg: IControllerProjectDocument, obligationArg: IControllerSessionCreationObligation, signalArg: AbortSignal, ): Promise<boolean> { if (obligationArg.dispatchStartedAt === undefined) return false; if (obligationArg.runtimeId.harnessId === 'opencode') { const supervisor = this.openCodeSupervisor; const client = this.requireOpenCodeClient(); if (!supervisor) return false; const authority = this.requireOpenCodeRuntimeExitAuthority(supervisor); const current = await client.getEnrollableSession( projectArg.directory, obligationArg.runtimeId.nativeId, AbortSignal.any([signalArg, authority.signal]), ); if (!current || current.session.createdAt < obligationArg.dispatchStartedAt) return false; const observation = await this.requireAuthStore() .completeManagedSessionCreationAdmissionForProjectRemoval({ projectIdentityId: projectArg.id, runtimeId: obligationArg.runtimeId, operationId: obligationArg.operationId, supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration: current.providerSessionGeneration, terminalAt: new Date(), }); this.setManagedSessionAuthority( projectArg.id, obligationArg.runtimeId, 'managed', observation.identity.sessionIdentityId, ); return true; } const supervisor = this.flexSupervisor; if (!supervisor?.getStatus().ready) return false; const authority = this.requireFlexRuntimeExitAuthority(supervisor); let current: TFlexSession; try { current = await supervisor.request('session.get', { scopeId: projectArg.id, sessionId: obligationArg.runtimeId.nativeId, }, AbortSignal.any([signalArg, authority.signal])); } catch (errorArg) { if (errorArg instanceof FlexServiceError && errorArg.code === 'NOT_FOUND') return false; throw errorArg; } if ( obligationArg.expectedFlexSessionGenerationId === undefined || current.sessionGenerationId !== obligationArg.expectedFlexSessionGenerationId ) return false; const observation = await this.requireAuthStore() .completeManagedSessionCreationAdmissionForProjectRemoval({ projectIdentityId: projectArg.id, runtimeId: obligationArg.runtimeId, operationId: obligationArg.operationId, supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration: requireFlexSessionProviderGeneration(current), sessionGenerationId: current.sessionGenerationId, sessionGenerationSequence: current.sessionGenerationSequence, terminalAt: new Date(), }); this.setManagedSessionAuthority( projectArg.id, obligationArg.runtimeId, 'managed', observation.identity.sessionIdentityId, ); return true; } private async reconcileProjectStateInMutation( synchronizeFlexArg: boolean, signalArg?: AbortSignal, ): Promise<IProjectReconciliationResult> { signalArg?.throwIfAborted(); const projects = await this.requireAuthStore().listProjects(); const availableSnapshots = await this.resolveAvailableProjects(projects); const availableProjects: IControllerProjectDocument[] = []; for (const project of availableSnapshots) { const current = await this.resolveCurrentReconciliationProject(project); if (current) availableProjects.push(current); } this.projectIdsByDirectory = new Map( availableProjects.map((project) => [project.directory, project.id]), ); for (const project of availableProjects) { const current = await this.resolveCurrentReconciliationProject(project); if (!current) continue; this.gitReversion?.registerProject(current.id, current.directory, { ancestry: current.directoryIdentity!.ancestry, }); } if (!synchronizeFlexArg) return { projects, availableProjects, flexSynchronized: false }; const supervisor = this.flexSupervisor; if (!supervisor?.getStatus().ready) { return { projects, availableProjects, flexSynchronized: false }; } try { for (const project of availableProjects) { signalArg?.throwIfAborted(); const current = await this.resolveCurrentReconciliationProject(project); if (!current) continue; this.flexProjectIdentityRecoveryPending.add(current.id); this.markManagedSessionAuthorityScopeUncertain(current.id, 'flex'); await this.waitForProjectHarnessSubmissionAdmissions(current.id, signalArg); await this.waitForProjectArchiveAdmissions(current.id); await this.waitForProjectSessionIntelligenceAdmissions(current.id); await this.waitForFlexProjectHostRequestAdmissions(current.id, signalArg); await this.registerFlexProjectForReconciliation( current, supervisor, signalArg ?? this.operationAbortController.signal, ); const authority = this.requireFlexRuntimeExitAuthority(supervisor); const identitySignal = signalArg === undefined ? authority.signal : AbortSignal.any([signalArg, authority.signal]); await this.reconcileFlexProjectSessionIdentities( current, supervisor, authority, identitySignal, true, ); this.flexProjectIdentityRecoveryPending.delete(current.id); } return { projects, availableProjects, flexSynchronized: true }; } catch { console.error( 'Flex project synchronization failed; persisted project state remains authoritative.', ); return { projects, availableProjects, flexSynchronized: false }; } } private reconcileProjectState( synchronizeFlexArg: boolean, signalArg?: AbortSignal, ): Promise<IProjectReconciliationResult> { const reconciliation = this.runControllerProjectMutation( () => this.reconcileProjectStateInMutation(synchronizeFlexArg, signalArg), ); return reconciliation.then((result) => { if (synchronizeFlexArg && !result.flexSynchronized) { this.scheduleFlexProjectReconciliationRetry(); } return result; }); } private scheduleFlexProjectReconciliationRetry(): void { if ( this.lifecycleState !== 'ready' || this.stopRequested || this.operationAbortController.signal.aborted || this.flexProjectReconciliationRetryTask ) return; let retryDelayMs = flexProjectReconciliationInitialRetryMs; let task!: Promise<void>; task = this.trackOperationPromise((async () => { while ( this.lifecycleState === 'ready' && !this.stopRequested && !this.operationAbortController.signal.aborted ) { await this.waitForFlexProjectReconciliationRetry( retryDelayMs, this.operationAbortController.signal, ); if ( this.lifecycleState !== 'ready' || this.stopRequested || this.operationAbortController.signal.aborted ) return; try { const result = await this.reconcileProjectState( true, this.operationAbortController.signal, ); if (result.flexSynchronized) return; } catch (errorArg) { if (this.operationAbortController.signal.aborted || this.stopRequested) return; console.error('Automatic Flex project synchronization retry failed.', errorArg); } retryDelayMs = Math.min( retryDelayMs * 2, flexProjectReconciliationMaximumRetryMs, ); } })(), () => { if (this.flexProjectReconciliationRetryTask === task) { this.flexProjectReconciliationRetryTask = undefined; } }); this.flexProjectReconciliationRetryTask = task; void task.catch(() => undefined); } private waitForFlexProjectReconciliationRetry( delayMsArg: number, signalArg: AbortSignal, ): Promise<void> { return waitForSignalOrDelay(delayMsArg, signalArg); } private finishProjectRemoval( projectArg: IControllerProjectDocument, signalArg: AbortSignal, beginRemovalArg: boolean, ): Promise<void> { return this.runControllerProjectMutation(() => this.requireResourceCoordinator().withProjectRemovalGuard( projectArg.id, async () => { this.revokeChildScopesForProject(projectArg.id); await this.waitForProjectHarnessSubmissionAdmissions(projectArg.id); await this.waitForProjectArchiveAdmissions(projectArg.id); await this.waitForProjectSessionIntelligenceAdmissions(projectArg.id); await this.waitForFlexProjectHostRequestAdmissions(projectArg.id, signalArg); await this.abortProjectSessionBackgroundOperations( projectArg.id, new Error('The source project was removed.'), ); if (projectArg.directoryIdentityState !== 'unbound') { await this.abortProjectHarnessOperations(this.toPublicProject(projectArg), signalArg); } await this.waitForFlexProjectHostRequestAdmissions(projectArg.id, signalArg); let project = projectArg; if (beginRemovalArg) { const operationId = plugins.crypto.randomBytes(24).toString('base64url'); const flexSupervisor = this.requireFlexSupervisor(); const flexAuthority = this.requireFlexRuntimeExitAuthority(flexSupervisor); await this.recoverFlexManagedSessionCreations( projectArg, flexSupervisor, flexAuthority.sessionIdentityGeneration, AbortSignal.any([signalArg, flexAuthority.signal]), ); const flexManagedRoots = await this.requireAuthStore().listFlexManagedCleanupRoots({ projectIdentityId: projectArg.id, signal: signalArg, }); const flexCleanupCohort = await this.captureFlexCleanupCohort( projectArg.id, flexManagedRoots, signalArg, ); const pending = await this.requireAuthStore().beginProjectRemoval( projectArg.id, operationId, flexCleanupCohort, ); if (!pending) { throw new plugins.typedrequest.TypedResponseError( 'The project changed concurrently and could not begin removal.', { code: 'concurrent_change' }, ); } project = pending; } if ( project.removalOperationId === undefined || project.flexCleanupCohort === undefined ) throw new Error('Legacy project removal has no exact Flex cleanup authority.'); const removalOperationId = project.removalOperationId; const removeMetadataOnly = project.directoryIdentityState === 'unbound'; if (removeMetadataOnly) { await this.settleManagedSessionsForProjectRemoval(project, signalArg, false); } if (removeMetadataOnly) { if (project.directoryIdentity !== undefined) { throw new Error( `Cannot remove unbound project ${project.id} with a directory identity.`, ); } if (this.harnessOperationManager.listProjectActive(project.id).length > 0) { throw new Error( `Cannot remove unbound project ${project.id} while harness operations remain active.`, ); } } else { if (project.directoryIdentityState !== 'bound' || !project.directoryIdentity) { throw new Error( `Cannot remove project ${project.id} without a verified directory identity.`, ); } await this.assertProjectDirectory(project); this.requireGitReversion().registerProject(project.id, project.directory, { ancestry: project.directoryIdentity.ancestry, }); this.projectIdsByDirectory.delete(project.directory); } signalArg.throwIfAborted(); const removed = await this.requireTerminalManager().retireProject(project.id, async () => { let finalFlexHostAdmissionSealed = false; try { if (!removeMetadataOnly) { await this.requireFlexSupervisor().request('project.remove', { project: { projectId: project.id, directory: project.directory, directoryIdentity: structuredClone(project.directoryIdentity!), }, removalOperationId, cleanupCohort: project.flexCleanupCohort!.map((entry) => ({ sessionId: entry.sessionId, sessionGenerationId: entry.sessionGenerationId, sessionGenerationSequence: entry.sessionGenerationSequence, cleanupRoot: entry.cleanupRoot, })), }, signalArg); await this.settleManagedSessionsForProjectRemoval(project, signalArg, true); } await this.retireProjectSessionIntelligence(project.id, signalArg); if (removeMetadataOnly) { // Retire controller metadata, but retain artifacts scoped to the untrusted path. await this.requireAuthStore().retireProjectSessionStates( project.id, async () => undefined, signalArg, ); } else { await this.requireAuthStore().retireProjectSessionStates( project.id, async (temporarySessionIdArg) => this.requireOpenCodeClient() .deleteSessionIntelligenceSession( project.directory, temporarySessionIdArg, signalArg, ), signalArg, ); await this.requireOpenCodeClient().cleanupOrphanedSessionIntelligenceSessions( project.directory, this.requireControllerId(), Number.MAX_SAFE_INTEGER, signalArg, ); this.sealedFlexProjectHostRequestAdmissions.add(project.id); finalFlexHostAdmissionSealed = true; await this.waitForFlexProjectHostRequestAdmissions(project.id, signalArg); const gitReversion = this.requireGitReversion(); await gitReversion.releaseProjectCaptures(project.id, signalArg); await gitReversion.cleanupProjectWorktrees(project.id, signalArg); gitReversion.unregisterProject(project.id); } await this.harnessOperationManager.purgeProject(project.id); // Sidebar state of a removed project goes with it. A failure here is reported and // never fails the removal: the listing is scoped to registered projects, so a left // behind entry is unreachable, and project identifiers are never reused. try { await this.requireAuthStore().untrackProjectConversations(project.id); // The layout is controller-wide now, so a removed project's rows must go with it // or the sidebar would keep positions it can never resolve again. if (await this.requireAuthStore().removeProjectFromLayout(project.id)) { this.pushSessionLayoutChanged(); } } catch (errorArg) { console.error('Untracking the removed project conversations failed.', errorArg); } this.purgeOpenCodeStreamStateForProject(project.id); this.purgeFlexToolExecutionsForProject(project.id); for (const [key, entry] of this.yoloSessionsByProjectKey) { if (entry.projectId === project.id) this.yoloSessionsByProjectKey.delete(key); } return this.requireAuthStore().removeProject( project.id, removalOperationId, ); } finally { if (finalFlexHostAdmissionSealed) { this.sealedFlexProjectHostRequestAdmissions.delete(project.id); } } }); if (!removed) throw new Error('The durable project removal did not complete.'); await this.abortProjectResourceReconciliationRetry( project.id, new Error('The project was removed.'), ); this.flexProjectIdentityRecoveryPending.delete(project.id); this.clearManagedSessionAuthorityProjectScope(project.id); }, )); } private runControllerProjectMutation<T>(operationArg: () => Promise<T>): Promise<T> { const operation = this.controllerProjectMutationTail.then(operationArg); this.controllerProjectMutationTail = operation.then(() => undefined, () => undefined); return operation; } private async resolveCurrentReconciliationProject( snapshotArg: IControllerProjectDocument, ): Promise<IControllerProjectDocument | undefined> { const current = await this.requireAuthStore().getProject(snapshotArg.id); if ( !current || current.removalStartedAt !== undefined || current.removedAt !== undefined || current.directory !== snapshotArg.directory || current.directoryIdentityState !== 'bound' || current.directoryIdentity === undefined || snapshotArg.directoryIdentityState !== 'bound' || snapshotArg.directoryIdentity === undefined || !filesystemIdentitiesEqual(current.directoryIdentity, snapshotArg.directoryIdentity) ) return undefined; const [available] = await this.resolveAvailableProjects([current]); return available; } private schedulePendingProjectRemoval( projectArg: IControllerProjectDocument, initialDelayMsArg = projectRemovalInitialRetryMs, ): void { if (this.pendingProjectRemovalTasks.has(projectArg.id)) return; this.retiringProjectIds.add(projectArg.id); let trackedTask!: Promise<void>; /** * The task map entry is this attempt's ownership of the project's removal: exactly one attempt * holds it, and every other scheduler defers to the holder. Ownership is therefore handed back * before the attempt announces that it stopped, never after. */ const releaseOwnership = (): void => { if (this.pendingProjectRemovalTasks.get(projectArg.id) === trackedTask) { this.pendingProjectRemovalTasks.delete(projectArg.id); } }; /** Takes the project back after a released attempt turns out to continue after all. */ const reclaimOwnership = (): boolean => { if (this.pendingProjectRemovalTasks.has(projectArg.id)) return false; this.pendingProjectRemovalTasks.set(projectArg.id, trackedTask); return true; }; const operation = (async () => { let retryDelayMs = initialDelayMsArg; while (!this.operationAbortController.signal.aborted) { await this.waitForPendingProjectRemovalRetry( retryDelayMs, this.operationAbortController.signal, ); try { const pendingProject = await this.requireAuthStore().getPendingProjectRemoval(projectArg.id); if (!pendingProject) { this.retiringProjectIds.delete(projectArg.id); return; } // A scheduled attempt supersedes any earlier block: the cause may be gone, and after an // upgrade the code that stopped on it is gone too. await this.clearPendingProjectRemovalBlock(pendingProject); const attemptSignal = AbortSignal.any([ this.operationAbortController.signal, AbortSignal.timeout(flexIpcLifecycleTimeoutMs), ]); await this.finishProjectRemoval(pendingProject, attemptSignal, false); this.retiringProjectIds.delete(projectArg.id); await this.reconcileProjectState(true); await this.pushProjectsChanged(projectArg.id); return; } catch (errorArg) { if (this.operationAbortController.signal.aborted) return; const block = classifyProjectRemovalBlock(errorArg); if (block) { // The block is recorded and announced only after this attempt has given the project // back, so an operator retry that sees the block always schedules a fresh attempt // instead of joining the one that is already giving up. releaseOwnership(); if (await this.blockPendingProjectRemoval(projectArg.id, block, errorArg)) return; // Nothing was recorded, so this attempt stays responsible for the removal - unless a // fresh attempt claimed it meanwhile, which must never end up running twice. if (!reclaimOwnership()) return; } console.error(`Pending project removal ${projectArg.id} remains incomplete.`, errorArg); retryDelayMs = Math.min( Math.max(projectRemovalInitialRetryMs, retryDelayMs * 2 || projectRemovalInitialRetryMs), projectRemovalMaximumRetryMs, ); } } })().finally(releaseOwnership); trackedTask = this.trackOperationPromise(operation); this.pendingProjectRemovalTasks.set(projectArg.id, trackedTask); void trackedTask.catch(() => undefined); } /** * Stops retrying a pending removal that failed deterministically and records why. The removal * itself is never abandoned: the durable intent, the Git reversion state and the Flex cleanup * authority all stay exactly as they are until the operator retries. */ private async blockPendingProjectRemoval( projectIdArg: string, blockArg: { code: TControllerProjectRemovalBlockedCode; reason: string }, errorArg: unknown, ): Promise<boolean> { try { const authStore = this.requireAuthStore(); const pending = await authStore.getPendingProjectRemoval(projectIdArg); const removalOperationId = pending?.removalOperationId; if (removalOperationId === undefined) return false; if (!await authStore.blockProjectRemoval( projectIdArg, removalOperationId, blockArg.code, blockArg.reason, )) return false; } catch (blockErrorArg) { console.error(`Recording the blocked project removal ${projectIdArg} failed.`, blockErrorArg); return false; } console.error( `Pending project removal ${projectIdArg} is blocked [${blockArg.code}]: ${blockArg.reason}` + ' Resolve the cause and retry the removal from the workspace.', errorArg, ); await this.pushProjectsChanged(projectIdArg); return true; } /** Drops a recorded block so the workspace stops showing it while the attempt runs. */ private async clearPendingProjectRemovalBlock( projectArg: IControllerProjectDocument, ): Promise<void> { if (projectArg.removalBlockedAt === undefined) return; await this.requireAuthStore().clearProjectRemovalBlock(projectArg.id); await this.pushProjectsChanged(projectArg.id); } /** Pending removals the controller stopped retrying, as the workspace shows them. */ private async listBlockedProjectRemovals(): Promise<IControllerBlockedProjectRemoval[]> { return (await this.requireAuthStore().listPendingProjectRemovals()).flatMap((project) => { const { removalBlockedAt: blockedAt, removalBlockedCode: code, removalBlockedReason: reason, } = project; if (blockedAt === undefined || code === undefined || reason === undefined) return []; return [{ id: project.id, name: project.name, directory: project.directory, code, reason, blockedAt: blockedAt.getTime(), }]; }); } /** * Resumes a removal that the controller stopped retrying. It acts on a blocked removal and on * nothing else: a removal that is still being retried owns its own schedule, and clearing its * state from here would take the project away from the attempt that is driving it. */ private async retryBlockedProjectRemoval(projectIdArg: string): Promise<void> { const authStore = this.requireAuthStore(); const blocked = await authStore.getPendingProjectRemoval(projectIdArg); if (!blocked) { throw new plugins.typedrequest.TypedResponseError( 'The project has no pending removal to retry.', { code: 'project_not_found' }, ); } if (blocked.removalBlockedAt === undefined) { throw new plugins.typedrequest.TypedResponseError( 'The pending removal of this project is not blocked; it is still being retried.', { code: 'project_removal_not_blocked' }, ); } const pending = await authStore.clearProjectRemovalBlock(projectIdArg); if (!pending) { throw new plugins.typedrequest.TypedResponseError( 'The project has no pending removal to retry.', { code: 'project_not_found' }, ); } this.schedulePendingProjectRemoval(pending, 0); await this.pushProjectsChanged(projectIdArg); } private pushProjectsChanged(projectIdArg: string): Promise<void> { return this.pushControllerEvent({ type: 'projects.changed', projectId: projectIdArg, timestamp: Date.now(), }).catch(() => undefined); } private async waitForPendingProjectRemovalRetry( delayMsArg: number, signalArg: AbortSignal, ): Promise<void> { signalArg.throwIfAborted(); if (delayMsArg <= 0) return; await new Promise<void>((resolve, reject) => { const timeout = setTimeout(() => { signalArg.removeEventListener('abort', abort); resolve(); }, delayMsArg); const abort = () => { clearTimeout(timeout); reject(signalArg.reason ?? new DOMException('Project removal retry was aborted.', 'AbortError')); }; signalArg.addEventListener('abort', abort, { once: true }); if (signalArg.aborted) abort(); }); } private defaultModelForHarness<THarness extends TControllerSessionHarnessId>( harnessIdArg: THarness, ): Extract<TControllerModelChoice, { harnessId: THarness }> | undefined { return this.defaultModels.find((model) => model.harnessId === harnessIdArg) as | Extract<TControllerModelChoice, { harnessId: THarness }> | undefined; } private async validateControllerModelChoice( modelArg: TControllerModelChoice, signalArg: AbortSignal, codexNativeIdArg?: string, ): Promise<void> { if (modelArg.harnessId === 'codex') return this.requireCodexClient(codexNativeIdArg).validateModel(modelArg, signalArg); if (modelArg.harnessId === 'flex') { if ((await this.validFlexModelProviderConnections(modelArg, signalArg, 1)).length === 0) { throw new plugins.typedrequest.TypedResponseError( 'The selected Flex model is not available.', { code: 'model_not_found' }, ); } return; } const options = await this.requireOpenCodeClient().listModelOptions(signalArg); const option = options.find((candidate) => ( candidate.providerID === modelArg.providerID && candidate.modelID === modelArg.modelID )); if (!option || (modelArg.variant !== undefined && !option.variants.includes(modelArg.variant))) { throw new plugins.typedrequest.TypedResponseError( 'The selected OpenCode model is not available.', { code: 'model_not_found' }, ); } } private isFlexModelValidationMiss(errorArg: unknown): boolean { return errorArg instanceof FlexServiceError && ( errorArg.code === 'INVALID_REQUEST' || errorArg.code === 'NOT_FOUND' || errorArg.code === 'PROVIDER_REAUTH_REQUIRED' || errorArg.code === 'MODEL_CATALOG_REQUIRED' ); } private async validateFlexModelOnProviderConnection( modelArg: Extract<TControllerModelChoice, { harnessId: 'flex' }>, providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.requireFlexSupervisor().request('model.choice.validate', { choice: this.toFlexModelChoice(modelArg, providerConnectionIdArg), }, signalArg); } private async validFlexModelProviderConnections( modelArg: Extract<TControllerModelChoice, { harnessId: 'flex' }>, signalArg: AbortSignal | undefined, maximumMatchesArg: number, ): Promise<string[]> { if (modelArg.providerID !== 'openai') { throw new plugins.typedrequest.TypedResponseError( 'The selected model does not belong to a supported Flex provider.', { code: 'model_harness_mismatch' }, ); } const response = await this.requireFlexSupervisor().request( 'provider.connection.list', {}, signalArg, ); const connectionIds = response.connections .filter((connection) => connection.providerId === 'openai' && connection.status === 'active') .map((connection) => connection.loginId) .sort((left, right) => left.localeCompare(right)); const matches: string[] = []; for (const connectionId of connectionIds) { signalArg?.throwIfAborted(); try { await this.validateFlexModelOnProviderConnection(modelArg, connectionId, signalArg); matches.push(connectionId); if (matches.length >= maximumMatchesArg) return matches; } catch (errorArg) { signalArg?.throwIfAborted(); if (!this.isFlexModelValidationMiss(errorArg)) throw errorArg; } } return matches; } private async resolveFlexModelProviderConnection( modelArg: Extract<TControllerModelChoice, { harnessId: 'flex' }>, providerConnectionIdArg: string | undefined, signalArg: AbortSignal, ): Promise<string> { if (providerConnectionIdArg !== undefined) { try { await this.validateFlexModelOnProviderConnection( modelArg, providerConnectionIdArg, signalArg, ); } catch (errorArg) { signalArg.throwIfAborted(); if (!this.isFlexModelValidationMiss(errorArg)) throw errorArg; throw new plugins.typedrequest.TypedResponseError( 'The selected Flex model is unavailable for the selected provider account.', { code: 'model_not_found' }, ); } return providerConnectionIdArg; } const matches = await this.validFlexModelProviderConnections(modelArg, signalArg, 2); if (matches.length === 1) return matches[0]; throw new plugins.typedrequest.TypedResponseError( matches.length === 0 ? 'The selected Flex model is not available.' : 'Choose an OpenAI account before using this Flex model.', { code: matches.length === 0 ? 'model_not_found' : 'provider_connection_required' }, ); } private async withResolvedFlexModelConnection<T>( modelArg: Extract<TControllerModelChoice, { harnessId: 'flex' }>, providerConnectionIdArg: string | undefined, signalArg: AbortSignal, operationArg: (choiceArg: IFlexModelChoice, providerConnectionIdArg: string) => Promise<T>, ): Promise<T> { const providerConnectionId = await this.resolveFlexModelProviderConnection( modelArg, providerConnectionIdArg, signalArg, ); return this.withFlexProviderConnectionMutation(providerConnectionId, async () => { signalArg.throwIfAborted(); try { await this.validateFlexModelOnProviderConnection( modelArg, providerConnectionId, signalArg, ); } catch (errorArg) { signalArg.throwIfAborted(); if (!this.isFlexModelValidationMiss(errorArg)) throw errorArg; throw new plugins.typedrequest.TypedResponseError( 'The selected Flex model is unavailable for the selected provider account.', { code: 'model_not_found' }, ); } return operationArg( this.toFlexModelChoice(modelArg, providerConnectionId), providerConnectionId, ); }); } private async withEffectiveFlexModelChoice<T>( projectIdArg: string, modelArg: TControllerModelChoice | undefined, providerConnectionIdArg: string | undefined, signalArg: AbortSignal, operationArg: (choiceArg: IFlexModelChoice, providerConnectionIdArg: string) => Promise<T>, ): Promise<T> { if (modelArg !== undefined) { if (modelArg.harnessId !== 'flex') { throw new plugins.typedrequest.TypedResponseError( 'The selected model belongs to another session harness.', { code: 'invalid_request' }, ); } return this.withResolvedFlexModelConnection( modelArg, providerConnectionIdArg, signalArg, operationArg, ); } const current = await this.requireFlexSupervisor().request( 'model.choice.get', { scopeId: projectIdArg }, signalArg, ); if (current.choice === null) { throw new plugins.typedrequest.TypedResponseError( 'Choose a Flex model before sending a prompt.', { code: 'model_not_found' }, ); } const effectiveModel: Extract<TControllerModelChoice, { harnessId: 'flex' }> = { harnessId: 'flex', providerID: 'openai', modelID: current.choice.modelId, ...(current.choice.variant === undefined ? {} : { variant: current.choice.variant }), }; return this.withResolvedFlexModelConnection( effectiveModel, providerConnectionIdArg ?? current.choice.providerConnectionId, signalArg, operationArg, ); } private async resolveSessionIntelligenceModel( signalArg?: AbortSignal, ): Promise<IFlexModelChoice> { const supervisor = this.requireFlexSupervisor(); const response = await supervisor.request('provider.connection.list', {}, signalArg); let reconnectRequired = response.connections.some((connection) => ( connection.providerId === 'openai' && connection.status === 'reauthRequired' )); const activeConnections = response.connections.filter((connection) => ( connection.providerId === 'openai' && connection.status === 'active' )); const validate = async (providerConnectionIdArg: string): Promise<IFlexModelChoice | undefined> => { const choice: IFlexModelChoice = { providerConnectionId: providerConnectionIdArg, modelId: sessionIntelligenceModelId, }; try { await supervisor.request('model.choice.validate', { choice }, signalArg); return choice; } catch (errorArg) { if ( errorArg instanceof FlexServiceError && errorArg.code === 'PROVIDER_REAUTH_REQUIRED' ) { reconnectRequired = true; return undefined; } if ( errorArg instanceof FlexServiceError && ( errorArg.code === 'MODEL_CATALOG_REQUIRED' || errorArg.code === 'NOT_FOUND' ) ) return undefined; throw errorArg; } }; const preferred = this.selectedOpenCodeProviderConnectionId ? activeConnections.find((connection) => ( connection.loginId === this.selectedOpenCodeProviderConnectionId )) : undefined; if (preferred) { const preferredChoice = await validate(preferred.loginId); if (preferredChoice) return preferredChoice; } const capable: IFlexModelChoice[] = []; for (const connection of activeConnections) { if (connection.loginId === preferred?.loginId) continue; const choice = await validate(connection.loginId); if (choice) capable.push(choice); } if (capable.length === 1) return capable[0]; throw new plugins.typedrequest.TypedResponseError( capable.length === 0 ? reconnectRequired ? sessionIntelligenceReconnectReason : activeConnections.length === 0 ? sessionIntelligenceConnectReason : `Session Intelligence requires the connected ${sessionIntelligenceModel} model.` : `Session Intelligence requires one unambiguous ${sessionIntelligenceModel} connection.`, { code: 'session_intelligence_unavailable' }, ); } private async resolveSessionIntelligenceAvailability( signalArg?: AbortSignal, ): Promise<Pick< IControllerSessionAuxiliary, | 'sessionIntelligenceEnabled' | 'sessionIntelligenceAvailabilityStatus' | 'sessionIntelligenceUnavailableReason' >> { try { await this.resolveSessionIntelligenceModel(signalArg); signalArg?.throwIfAborted(); return { sessionIntelligenceEnabled: true, sessionIntelligenceAvailabilityStatus: 'available', sessionIntelligenceUnavailableReason: '', }; } catch (errorArg) { signalArg?.throwIfAborted(); if ( errorArg instanceof plugins.typedrequest.TypedResponseError && errorArg.errorData?.code === 'session_intelligence_unavailable' ) { return { sessionIntelligenceEnabled: true, sessionIntelligenceAvailabilityStatus: 'unavailable', sessionIntelligenceUnavailableReason: errorArg.errorText, }; } console.error('Session Intelligence availability check failed.', errorArg); return { sessionIntelligenceEnabled: true, sessionIntelligenceAvailabilityStatus: 'unavailable', sessionIntelligenceUnavailableReason: sessionIntelligenceTemporarilyUnavailableReason, }; } } private async soleActiveFlexDefaultModelChoice( signalArg?: AbortSignal, ): Promise<IControllerFlexModelSelection | undefined> { const response = await this.requireFlexSupervisor().request( 'provider.connection.list', {}, signalArg, ); const activeConnections = response.connections.filter((connection) => connection.status === 'active'); if (activeConnections.length !== 1) return undefined; const connection = activeConnections[0]; const defaults = (this.flexModelOptionsByConnection.get(connection.loginId) ?? []) .filter((model) => model.isDefault); if (defaults.length !== 1) return undefined; const selected = defaults[0]; return { providerConnectionId: selected.providerConnectionId, model: { harnessId: 'flex', providerID: selected.providerID, modelID: selected.modelID, }, }; } private beginFlexModelRefresh( providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise<IControllerModelRefreshJob> { return this.withFlexProviderConnectionMutation( providerConnectionIdArg, () => this.beginFlexModelRefreshUnqueued(providerConnectionIdArg, signalArg), ); } private beginFlexModelRefreshUnqueued( providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise<IControllerModelRefreshJob> { signalArg?.throwIfAborted(); const running = [...this.flexRefreshJobs.values()].find((entry) => ( entry.providerConnectionId === providerConnectionIdArg && (entry.job.status === 'pending' || entry.job.status === 'running') )); if (running) return Promise.resolve({ ...running.job, id: { ...running.job.id } }); const existingAdmission = this.flexModelRefreshAdmissionTasks.get(providerConnectionIdArg); if (existingAdmission) return existingAdmission; const task = (async (): Promise<IControllerModelRefreshJob> => { const admission = await this.requireFlexSupervisor().request( 'provider.models.refresh', { providerConnectionId: providerConnectionIdArg }, signalArg, ); const job: IControllerModelRefreshJob = { id: flexRuntimeId(admission.jobId), providerConnectionId: providerConnectionIdArg, status: 'running', }; this.flexRefreshJobs.set(controllerRuntimeIdKey(job.id), { job, providerConnectionId: providerConnectionIdArg, }); trimOldestMapEntries(this.flexRefreshJobs, maxFlexRefreshJobs); this.consumePendingFlexFinishedJob(admission.jobId); return { ...job, id: { ...job.id } }; })().finally(() => { if (this.flexModelRefreshAdmissionTasks.get(providerConnectionIdArg) === task) { this.flexModelRefreshAdmissionTasks.delete(providerConnectionIdArg); } }); this.flexModelRefreshAdmissionTasks.set(providerConnectionIdArg, task); return task; } private async hydrateFlexProviderCatalogs( forceArg: boolean, connectionsArg?: IFlexPublicProviderConnection[], ): Promise<void> { const supervisor = this.flexSupervisor; if (!supervisor || !this.flexIsOperational()) return; let connections = connectionsArg; if (connections === undefined) { const [providersResponse, connectionsResponse] = await Promise.all([ supervisor.request('provider.list', {}), supervisor.request('provider.connection.list', {}), ]); for (const provider of providersResponse.providers) { this.flexProviderNames.set(provider.providerId, provider.displayName); } connections = connectionsResponse.connections; } const activeConnectionIds = connections .filter((connection) => connection.status === 'active') .map((connection) => connection.loginId) .filter((connectionId) => forceArg || !this.flexModelOptionsByConnection.has(connectionId)); const results = await Promise.allSettled( activeConnectionIds.map((connectionId) => this.beginFlexModelRefresh(connectionId)), ); const failures = results.filter((result) => result.status === 'rejected'); if (failures.length === activeConnectionIds.length && failures.length > 0) { throw new Error('Every Flex provider catalog refresh admission failed.'); } } private async listFlexProviderConnections(signalArg: AbortSignal): Promise<{ connections: IFlexPublicProviderConnection[]; runtimeAvailable: boolean; }> { const supervisor = this.flexSupervisor; if (supervisor && this.flexIsOperational()) { try { const response = await supervisor.request('provider.connection.list', {}, signalArg); if (this.flexSupervisor === supervisor && this.flexIsOperational()) { void this.trackOperationPromise( this.hydrateFlexProviderCatalogs(false, response.connections), ).catch(() => undefined); return { connections: response.connections, runtimeAvailable: true }; } } catch (errorArg) { if (this.flexSupervisor === supervisor && this.flexIsOperational()) throw errorArg; } } return { connections: await this.requireFlexProjectionReader().listProviderConnections(signalArg), runtimeAvailable: false, }; } private withFlexProviderConnectionMutation<T>( providerConnectionIdArg: string, operationArg: () => Promise<T>, ): Promise<T> { const previous = this.flexProviderConnectionMutationTails.get(providerConnectionIdArg) ?? Promise.resolve(); const operation = previous.catch(() => undefined).then(operationArg); const tail = operation.then(() => undefined, () => undefined); this.flexProviderConnectionMutationTails.set(providerConnectionIdArg, tail); void tail.finally(() => { if (this.flexProviderConnectionMutationTails.get(providerConnectionIdArg) === tail) { this.flexProviderConnectionMutationTails.delete(providerConnectionIdArg); } }); return operation; } private withFlexProviderConnectionMutations<T>( providerConnectionIdsArg: readonly string[], operationArg: () => Promise<T>, ): Promise<T> { const providerConnectionIds = [...new Set(providerConnectionIdsArg)].sort(); const run = (indexArg: number): Promise<T> => indexArg >= providerConnectionIds.length ? operationArg() : this.withFlexProviderConnectionMutation( providerConnectionIds[indexArg], () => run(indexArg + 1), ); return run(0); } private createAuthSwitchOpenCodeHarness(): plugins.authswitch.OpenCodeHarness { return new plugins.authswitch.OpenCodeHarness(); } private getAuthSwitchHarnesses(): readonly plugins.authswitch.IAuthHarness[] { return this.authSwitchHarnesses ??= [ new plugins.authswitch.CodexHarness(), this.createAuthSwitchOpenCodeHarness(), new plugins.authswitch.ClaudeCodeHarness(), ]; } private getAuthSwitchService(): plugins.authswitch.AuthSwitchService { return this.authSwitchService ??= new plugins.authswitch.AuthSwitchService( [...this.getAuthSwitchHarnesses()], request => this.coordinateAuthSwitch(request), ); } /** * What a switch means for each harness, so no view has to know which harness behaves how. The * adapters declare whether their running instances pick a swapped login up by themselves; the * restart is AGL's own coordination. */ private authSwitchHarnessBehaviour(): IControllerAuthSwitchHarness[] { return this.getAuthSwitchHarnesses().map((harnessArg) => ({ id: harnessArg.id, liveSwap: harnessArg.liveSwap === true, restartsManagedRuntime: harnessArg.id === authSwitchCoordinatedHarnessId, ...(harnessArg.renewalUnavailableReason === undefined ? {} : { renewalUnavailableReason: harnessArg.renewalUnavailableReason }), })); } /** A selected Flex connection records account association; native OpenCode owns its refreshed credential. */ private async reconcileSelectedOpenCodeAccount(signal: AbortSignal): Promise<void> { const selected = this.selectedOpenCodeProviderConnectionId; if (!selected) return; const accountId = this.createAuthSwitchOpenCodeHarness().activeOpenAiAccountId(); const connections = await this.listFlexProviderConnections(signal); const connection = connections.connections.find(item => item.loginId === selected); signal.throwIfAborted(); if (!accountId || !connection || connection.account?.accountId !== accountId) { await this.persistSelectedOpenCodeProviderConnection(undefined); } } /** * Runs one controller-owned credential operation at a time. * * authswitch takes an exclusive on-disk lock for the duration of a mutation and reports a locked * store as a failure. Collisions were impossible while every credential operation queued behind the * runtime transaction. A read-only save no longer queues there, so the mutual exclusion is explicit * -- without it a save could fail the switch that has already stopped OpenCode. */ private withAuthSwitchCredentialLock<T>(operationArg: () => T | Promise<T>): Promise<T> { const operation = this.authSwitchCredentialTail.catch(() => undefined).then(operationArg); this.authSwitchCredentialTail = operation.then(() => undefined, () => undefined); return operation; } /** * A save that keeps the login active only reads OpenCode's credential file and writes to * authswitch's own store. Stopping the runtime and waiting for idle work would interrupt live * sessions for an operation that replaces nothing, so it runs directly. */ private async performAuthSwitchOpenCodeKeepSave( mutationArg: plugins.authswitch.TAuthSwitchMutation, ): Promise<plugins.authswitch.TAuthSwitchCoordinationResult> { let outcome: plugins.authswitch.IHarnessOutcome; try { outcome = await this.withAuthSwitchCredentialLock( () => plugins.authswitch.runAuthSwitchMutation(this.createAuthSwitchOpenCodeHarness(), mutationArg), ); } catch { outcome = { lines: [], problems: ['AGL could not complete the credential save. Check the account list before retrying.'] }; } void this.pushControllerEvent({ type: 'settings.changed', timestamp: Date.now() }).catch(() => undefined); return { status: 'complete', outcome }; } private coordinateAuthSwitch( request: plugins.authswitch.IAuthSwitchCoordinationRequest, ): Promise<plugins.authswitch.TAuthSwitchCoordinationResult> { if ( request.mutation.harnessId !== authSwitchCoordinatedHarnessId || request.mutation.action === 'remove' ) { return Promise.resolve({ status: 'unavailable' }); } if (!plugins.authswitch.authSwitchMutationReplacesLogin(request.mutation)) { return this.performAuthSwitchOpenCodeKeepSave(request.mutation); } const previous = this.openCodeAccountSwitchTask; let task: Promise<plugins.authswitch.TAuthSwitchCoordinationResult>; task = (previous?.catch(() => undefined) ?? Promise.resolve()).then(() => this.withOpenCodeRuntimeMutation( () => this.performAuthSwitchOpenCodeMutation(request), )).finally(() => { if (this.openCodeAccountSwitchTask === task) this.openCodeAccountSwitchTask = undefined; if (this.openCodeCrashRecovery) this.scheduleOpenCodeRestart(); }); this.openCodeAccountSwitchTask = task; return task; } private async readAuthSwitchOpenCodeBusyCount(client: OpenCodeClientAdapter, signal: AbortSignal): Promise<number> { const projects = await this.resolveSessionIdentityProjects(signal); let count = 0; for (const project of projects) { const statuses = await client.getStatuses(project.directory, signal); count += Object.values(statuses).filter(status => status === 'busy' || status === 'retry').length; } return count; } private prepareAuthSwitchOpenCodeRecovery(runtime: IControllerOwnedOpenCodeRuntime): void { if (this.openCodeCrashRecovery) return; const exitSignal = this.openCodeSupervisorExitSignals.get(runtime.supervisor)?.signal; if (!exitSignal) throw new Error('The OpenCode recovery identity is unavailable.'); const admissionFenceGeneration = this.sealOpenCodeRuntimeAuthority(true); this.advanceOpenCodeStreams(true); this.openCodeCrashRecovery = { ...runtime, exitSignal, admissionFenceGeneration, temporarySessionIds: runtime.client.getRememberedSessionIntelligenceTemporarySessionIds(), supervisorStopped: runtime.supervisor.getStatus().state === 'stopped', cleanupComplete: false, }; } /** The controller owns the entire transaction, including recovery after a browser/CLI disconnect. */ private async performAuthSwitchOpenCodeMutation( request: plugins.authswitch.IAuthSwitchCoordinationRequest, ): Promise<plugins.authswitch.TAuthSwitchCoordinationResult> { const oldRuntime = this.currentOwnedOpenCodeRuntime(); if (!oldRuntime || !oldRuntime.supervisor.getStatus().healthy || this.openCodeRuntimeAdmissionSealed || this.openCodeAccountSwitchAdmissionSealed || this.stopRequested) { return { status: 'complete', outcome: { lines: [], problems: ['AGL OpenCode is unavailable or already restarting. Check its status before changing accounts.'] } }; } const signal = AbortSignal.any([this.operationAbortController.signal, AbortSignal.timeout(240_000)]); const barrier = this.harnessOperationManager.beginOperationBarrier('opencode'); this.openCodeAccountSwitchBarrier = barrier; this.openCodeAccountSwitchAdmissionSealed = true; this.stopSessionIntelligenceCleanupTimer(); let stopAttempted = false; let stopped = false; let restored = false; let associationReconciled = false; let replacement: IControllerOwnedOpenCodeRuntime | undefined; let outcome: plugins.authswitch.IHarnessOutcome = { lines: [], problems: [] }; try { const busy = barrier.operationIds.length + this.projectSessionIntelligenceAdmissions.size + this.sessionIntelligenceJobs.size + await this.readAuthSwitchOpenCodeBusyCount(oldRuntime.client, signal); if (busy > 0 && !request.waitForIdle) { return { status: 'busy', message: 'OpenCode has active or queued work. Wait for it to finish, then let AGL save or switch the account and restart OpenCode?' }; } if (!await this.harnessOperationManager.waitForOperationBarrier(barrier, 240_000, signal)) { throw new Error('OpenCode work did not settle before the coordination deadline.'); } await this.waitForPromiseWithSignal(this.waitForAllProjectSessionIntelligenceAdmissions(), signal); await this.waitForPromiseWithSignal(this.drainSessionIntelligenceOperations(), signal); // Native status also covers work that has no controller operation reservation. while (await this.readAuthSwitchOpenCodeBusyCount(oldRuntime.client, signal) > 0) { if (!request.waitForIdle) return { status: 'busy', message: 'OpenCode started work during preparation. Wait for it to finish before restarting?' }; await plugins.timersPromises.setTimeout(250, undefined, { signal }); } this.sealOpenCodeRuntimeAdmission(); await this.waitForPromiseWithSignal(this.drainOpenCodeOperationMonitors(), signal); await this.waitForOpenCodeRuntimeLeases(signal); if (await this.readAuthSwitchOpenCodeBusyCount(oldRuntime.client, signal) > 0) { throw new Error('OpenCode activity changed before shutdown.'); } signal.throwIfAborted(); stopAttempted = true; await this.stopOwnedOpenCodeRuntime(oldRuntime); stopped = true; outcome = await this.withAuthSwitchCredentialLock( () => plugins.authswitch.runAuthSwitchMutation(this.createAuthSwitchOpenCodeHarness(), request.mutation), ); await this.reconcileSelectedOpenCodeAccount(signal); associationReconciled = true; } catch { outcome.problems.push(stopAttempted ? 'AGL could not complete the credential operation. Check the account list before retrying.' : 'OpenCode work or runtime preparation did not settle. No credential operation was started.'); } finally { if (stopAttempted) { const recoverySignal = AbortSignal.any([this.operationAbortController.signal, AbortSignal.timeout(60_000)]); try { // A failed stop may retain ownership. Finish that cleanup before starting another process. if (!stopped) await this.stopOwnedOpenCodeRuntime(oldRuntime); await this.retryRetainedOpenCodeRuntimeCleanup(); replacement = await this.startStagedOpenCodeRuntime(undefined, oldRuntime.client.getRememberedSessionIntelligenceTemporarySessionIds(), recoverySignal); if (!associationReconciled) { await this.reconcileSelectedOpenCodeAccount(recoverySignal); associationReconciled = true; } await this.installOpenCodeRuntime(replacement, recoverySignal, { advanceStreams: true, retainPlannedStopUntilCallerCommit: true }); const installed = replacement; await this.withCurrentSessionIdentityProjects(recoverySignal, async projects => { await this.reconcileOpenCodeSessionIdentities(installed.supervisor, installed.client, projects, recoverySignal); const authority = this.requireOpenCodeRuntimeExitAuthority(installed.supervisor); this.assertOpenCodeSessionIdentityGenerationCurrent(installed.supervisor, installed.client, authority); this.releaseOpenCodeAccountSwitchAdmission(barrier); this.plannedOpenCodeSupervisorStops.delete(installed.supervisor); }); restored = true; outcome.lines.push('OpenCode restarted by AGL.'); } catch { const failedRuntime = replacement ?? oldRuntime; this.prepareAuthSwitchOpenCodeRecovery(failedRuntime); outcome.problems.push('OpenCode restart is incomplete. AGL has kept new work blocked and scheduled runtime recovery; the credential operation will not be repeated.'); } } if (!stopAttempted && !this.openCodeCrashRecovery) this.releaseOpenCodeAccountSwitchAdmission(barrier); else if (!restored && this.openCodeCrashRecovery) { this.harnessOperationManager.endOperationBarrier(barrier); if (this.openCodeAccountSwitchBarrier === barrier) this.openCodeAccountSwitchBarrier = undefined; this.openCodeAccountSwitchAdmissionSealed = false; } void this.pushControllerEvent({ type: 'settings.changed', timestamp: Date.now() }).catch(() => undefined); } return { status: 'complete', outcome }; } private activateOpenCodeProviderConnection( providerConnectionIdArg: string, signalArg: AbortSignal, ): Promise<IReq_ControllerProviderConnectionActivateOpenCode['response']> { const previous = this.openCodeAccountSwitchTask; let task: Promise<IReq_ControllerProviderConnectionActivateOpenCode['response']>; task = (previous?.catch(() => undefined) ?? Promise.resolve()).then(async () => { signalArg.throwIfAborted(); const priorSettings = await this.requireAuthStore().getSettings(); const priorConnectionId = priorSettings.selectedOpenCodeProviderConnectionId; this.selectedOpenCodeProviderConnectionId = priorConnectionId; return this.withOpenCodeRuntimeMutation( () => this.withFlexProviderConnectionMutations( [providerConnectionIdArg, ...(priorConnectionId ? [priorConnectionId] : [])], () => this.performOpenCodeProviderConnectionActivation( providerConnectionIdArg, priorConnectionId, signalArg, ), ), ); }).finally(() => { if (this.openCodeAccountSwitchTask === task) this.openCodeAccountSwitchTask = undefined; if (this.openCodeCrashRecovery) this.scheduleOpenCodeRestart(); }); this.openCodeAccountSwitchTask = task; return task; } private async performOpenCodeProviderConnectionActivation( providerConnectionIdArg: string, priorConnectionIdArg: string | undefined, operationSignalArg: AbortSignal, ): Promise<IReq_ControllerProviderConnectionActivateOpenCode['response']> { const timeoutSignal = AbortSignal.timeout(openCodeAccountSwitchTimeoutMs); const signal = AbortSignal.any([ operationSignalArg, this.operationAbortController.signal, timeoutSignal, ]); const recoverySignal = AbortSignal.any([ this.operationAbortController.signal, timeoutSignal, ]); const targetAuth = await this.readActiveOpenCodeProviderAuth(providerConnectionIdArg, signal); const priorAuth = priorConnectionIdArg && priorConnectionIdArg !== providerConnectionIdArg ? await this.readActiveOpenCodeProviderAuth(priorConnectionIdArg, signal).catch(() => undefined) : undefined; signal.throwIfAborted(); const oldRuntime = this.currentOwnedOpenCodeRuntime(); if ( priorConnectionIdArg === providerConnectionIdArg && oldRuntime?.supervisor.getStatus().healthy && !this.openCodeAccountSwitchAdmissionSealed && !this.openCodeRuntimeAdmissionSealed ) { return { activated: true, pausedSessions: 0, continuedSessions: 0 }; } let barrier = this.openCodeAccountSwitchBarrier; if (!barrier) { barrier = this.harnessOperationManager.beginOperationBarrier('opencode'); this.openCodeAccountSwitchBarrier = barrier; } this.openCodeAccountSwitchAdmissionSealed = true; this.stopSessionIntelligenceCleanupTimer(); let cohort: IOpenCodeAccountSwitchSession[] = []; const pausedSessions: IOpenCodeAccountSwitchSession[] = []; let oldRuntimeStopped = oldRuntime === undefined; let targetRuntime: IControllerOwnedOpenCodeRuntime | undefined; let committed = false; let compensationAttempted = false; let compensationComplete = false; let oldRuntimeStopAttempted = false; try { cohort = await this.resolveOpenCodeAccountSwitchCohort(barrier); if (!await this.harnessOperationManager.waitForOperationBarrier( barrier, this.remainingOpenCodeAccountSwitchMs(timeoutSignal), signal, )) throw new Error('Timed out waiting for active OpenCode operations to settle.'); await this.waitForAllProjectSessionIntelligenceAdmissions(); await this.drainSessionIntelligenceOperations(); this.sealOpenCodeRuntimeAdmission(); await this.drainOpenCodeOperationMonitors(); await this.waitForOpenCodeRuntimeLeases(signal); if (oldRuntime) { const pauseResults = await Promise.allSettled(cohort.map(async (session) => { await oldRuntime.client.sendHarnessControlMessageAndWait( session.directory, session.sessionId.nativeId, openCodeAccountSwitchPauseMessage, signal, this.remainingOpenCodeAccountSwitchMs(timeoutSignal), ); pausedSessions.push(session); })); const pauseFailures = pauseResults.filter( (result): result is PromiseRejectedResult => result.status === 'rejected', ); if (pauseFailures.length > 0) { compensationAttempted = true; const compensation = await this.continueOpenCodeAccountSwitchSessions( oldRuntime.client, cohort, recoverySignal, ); compensationComplete = compensation === cohort.length; if (compensation !== cohort.length) { throw new AggregateError( pauseFailures.map((result) => result.reason), 'OpenCode account-switch pause failed and compensation was incomplete.', ); } throw new AggregateError( pauseFailures.map((result) => result.reason), 'One or more active OpenCode sessions could not pause for restart.', ); } oldRuntimeStopAttempted = true; await this.stopOwnedOpenCodeRuntime(oldRuntime); oldRuntimeStopped = true; } targetRuntime = await this.startStagedOpenCodeRuntime( targetAuth, oldRuntime?.client.getRememberedSessionIntelligenceTemporarySessionIds() ?? [], signal, ); await this.installOpenCodeRuntime(targetRuntime, signal, { advanceStreams: this.openCodeCrashRecovery === undefined, }); const installedTarget = targetRuntime; let continuedSessions = 0; await this.withCurrentSessionIdentityProjects(signal, async (identityProjects) => { await this.reconcileOpenCodeSessionIdentities( installedTarget.supervisor, installedTarget.client, identityProjects, signal, ); if ( this.openCodeAccountSwitchBarrier !== barrier || !this.openCodeAccountSwitchAdmissionSealed ) throw new Error('OpenCode account-switch identity authority changed before commit.'); await this.persistSelectedOpenCodeProviderConnection(providerConnectionIdArg); targetRuntime = undefined; committed = true; const installedClient = this.requireOpenCodeClient(); continuedSessions = await this.continueOpenCodeAccountSwitchSessions( installedClient, cohort, recoverySignal, ); if (continuedSessions !== cohort.length) { throw new Error('OpenCode account-switch continuation was incomplete.'); } const authority = this.requireOpenCodeRuntimeExitAuthority(installedTarget.supervisor); this.assertOpenCodeSessionIdentityGenerationCurrent( installedTarget.supervisor, installedTarget.client, authority, ); this.releaseOpenCodeAccountSwitchAdmission(barrier); }); void this.pushControllerEvent({ type: 'settings.changed', timestamp: Date.now(), }).catch(() => undefined); return { activated: true, pausedSessions: pausedSessions.length, continuedSessions, }; } catch (errorArg) { if (committed) { const installedRuntime = this.currentOwnedOpenCodeRuntime(); if (installedRuntime) { try { await this.stopOwnedOpenCodeRuntime(installedRuntime); } catch (cleanupErrorArg) { throw new AggregateError( [errorArg, cleanupErrorArg], 'OpenCode account switching committed but fail-closed runtime shutdown failed.', ); } } throw errorArg; } if (targetRuntime) { try { await this.stopOwnedOpenCodeRuntime(targetRuntime); } catch (cleanupErrorArg) { throw new AggregateError( [errorArg, cleanupErrorArg], 'OpenCode account switching failed and the staged runtime could not be stopped.', ); } } if ( oldRuntime && !oldRuntimeStopped && this.openCodeSupervisor !== oldRuntime.supervisor && oldRuntime.supervisor.getStatus().state === 'stopped' ) { oldRuntimeStopped = true; } if (oldRuntime && !oldRuntimeStopped && oldRuntimeStopAttempted) { try { await this.stopOwnedOpenCodeRuntime(oldRuntime); oldRuntimeStopped = true; } catch (cleanupErrorArg) { throw new AggregateError( [errorArg, cleanupErrorArg], 'OpenCode account switching failed with uncertain prior-runtime stop ownership.', ); } } if (oldRuntime && !oldRuntimeStopped) { const compensationSessions = compensationAttempted ? cohort : pausedSessions; const continued = compensationAttempted ? (compensationComplete ? compensationSessions.length : 0) : await this.continueOpenCodeAccountSwitchSessions( oldRuntime.client, compensationSessions, recoverySignal, ); let oldRuntimeHealthy = false; if (continued === compensationSessions.length) { oldRuntimeHealthy = await oldRuntime.supervisor.checkHealth().then( () => true, () => false, ); } if (oldRuntimeHealthy) { this.releaseOpenCodeAccountSwitchAdmission(barrier); throw errorArg; } try { await this.stopOwnedOpenCodeRuntime(oldRuntime); oldRuntimeStopped = true; } catch (cleanupErrorArg) { throw new AggregateError( [errorArg, cleanupErrorArg], 'OpenCode account switching failed and the prior runtime could not be stopped safely.', ); } } if ( priorConnectionIdArg && priorAuth && (!compensationAttempted || compensationComplete) ) { let rollbackRuntime: IControllerOwnedOpenCodeRuntime | undefined; try { rollbackRuntime = await this.startStagedOpenCodeRuntime( priorAuth, oldRuntime?.client.getRememberedSessionIntelligenceTemporarySessionIds() ?? [], recoverySignal, ); await this.installOpenCodeRuntime(rollbackRuntime, recoverySignal, { advanceStreams: this.openCodeCrashRecovery === undefined, }); const installedRollback = rollbackRuntime; await this.withCurrentSessionIdentityProjects(recoverySignal, async (identityProjects) => { await this.reconcileOpenCodeSessionIdentities( installedRollback.supervisor, installedRollback.client, identityProjects, recoverySignal, ); await this.persistSelectedOpenCodeProviderConnection(priorConnectionIdArg); rollbackRuntime = undefined; const continued = await this.continueOpenCodeAccountSwitchSessions( this.requireOpenCodeClient(), cohort, recoverySignal, ); if (continued !== cohort.length) { throw new Error('OpenCode account-switch rollback continuation was incomplete.'); } const authority = this.requireOpenCodeRuntimeExitAuthority( installedRollback.supervisor, ); this.assertOpenCodeSessionIdentityGenerationCurrent( installedRollback.supervisor, installedRollback.client, authority, ); this.releaseOpenCodeAccountSwitchAdmission(barrier); }); } catch (rollbackErrorArg) { let cleanupError: unknown; if (rollbackRuntime) { await this.stopOwnedOpenCodeRuntime(rollbackRuntime).catch((error) => { cleanupError = error; }); } else if (this.openCodeClient || this.openCodeSupervisor) { const installedRollback = this.currentOwnedOpenCodeRuntime(); if (installedRollback) { await this.stopOwnedOpenCodeRuntime(installedRollback).catch((error) => { cleanupError = error; }); } } throw new AggregateError( [errorArg, rollbackErrorArg, ...(cleanupError ? [cleanupError] : [])], 'OpenCode account switching and managed-account rollback both failed.', ); } } throw errorArg; } finally { if ( this.openCodeCrashRecovery && this.openCodeAccountSwitchBarrier === barrier ) { this.harnessOperationManager.endOperationBarrier(barrier); this.openCodeAccountSwitchBarrier = undefined; this.openCodeAccountSwitchAdmissionSealed = false; } } } private async readActiveOpenCodeProviderAuth( providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise<IFlexOpenCodeOAuthAuth> { const connections = await this.requireFlexSupervisor().request( 'provider.connection.list', {}, signalArg, ); const connection = connections.connections.find( (candidate) => candidate.loginId === providerConnectionIdArg, ); if (!connection || connection.providerId !== 'openai' || connection.status !== 'active') { throw new plugins.typedrequest.TypedResponseError( 'The selected OpenAI account is not active.', { code: 'provider_connection_inactive' }, ); } return (await this.requireFlexSupervisor().request( 'provider.connection.opencode-auth.get', { providerConnectionId: providerConnectionIdArg }, signalArg, )).auth; } private async persistSelectedOpenCodeProviderConnection( providerConnectionIdArg: string | undefined, ): Promise<void> { try { const settings = await this.requireAuthStore().updateSettings({ selectedOpenCodeProviderConnectionId: providerConnectionIdArg ?? null, }); if (settings.selectedOpenCodeProviderConnectionId !== providerConnectionIdArg) { throw new Error('The selected OpenCode provider account was not persisted.'); } } catch (errorArg) { const reconciled = await this.requireAuthStore().getSettings().catch(() => undefined); if (!reconciled || reconciled.selectedOpenCodeProviderConnectionId !== providerConnectionIdArg) { throw errorArg; } } this.selectedOpenCodeProviderConnectionId = providerConnectionIdArg; } private currentOwnedOpenCodeRuntime(): IControllerOwnedOpenCodeRuntime | undefined { if (!this.openCodeSupervisor || !this.openCodeClient) return undefined; return { supervisor: this.openCodeSupervisor, client: this.openCodeClient, ...(this.eventStreamTask ? { eventStreamTask: this.eventStreamTask } : {}), }; } private async startStagedOpenCodeRuntime( authArg: IFlexOpenCodeOAuthAuth | undefined, temporarySessionIdsArg: readonly string[], signalArg: AbortSignal, ): Promise<IControllerOwnedOpenCodeRuntime> { if (authArg) { signalArg.throwIfAborted(); const outcome = await this.withAuthSwitchCredentialLock( () => this.createAuthSwitchOpenCodeHarness().activateCredential('openai', { ...authArg }), ); if (outcome.problems.length) throw new Error('The OpenCode credential could not be activated safely: ' + outcome.problems.join('; ')); } const supervisor = this.createOpenCodeSupervisor(); this.plannedOpenCodeSupervisorStops.add(supervisor); let client: OpenCodeClientAdapter | undefined; try { await supervisor.start(signalArg); client = this.createOpenCodeClient(supervisor); client.rememberSessionIntelligenceTemporarySessionIds([...temporarySessionIdsArg]); await supervisor.checkHealth(); return { supervisor, client }; } catch (errorArg) { try { await this.stopUninstalledOpenCodeSupervisor(supervisor); } catch (cleanupErrorArg) { throw new AggregateError( [errorArg, cleanupErrorArg], 'OpenCode staged-runtime startup and cleanup both failed.', ); } throw errorArg; } } private async installOpenCodeRuntime( runtimeArg: IControllerOwnedOpenCodeRuntime, signalArg: AbortSignal, optionsArg: IOpenCodeRuntimeInstallationOptions = { advanceStreams: true }, ): Promise<void> { const crashRecovery = this.openCodeCrashRecovery; if (crashRecovery && !crashRecovery.cleanupComplete) { await this.cleanupOpenCodeCrashGeneration(crashRecovery); } signalArg.throwIfAborted(); if (this.openCodeCrashRecovery && !this.openCodeCrashRecovery.cleanupComplete) { throw new Error('Prior OpenCode crash cleanup is incomplete.'); } if (this.openCodeSupervisor || this.openCodeClient) { throw new Error('An OpenCode runtime is already installed.'); } const status = runtimeArg.supervisor.getStatus(); if (status.state !== 'ready' || !status.healthy) { throw new Error('The staged OpenCode runtime exited before installation.'); } const exitSignal = this.openCodeSupervisorExitSignals.get(runtimeArg.supervisor)?.signal; if (!exitSignal || exitSignal.aborted) { throw new Error('The staged OpenCode runtime lost its exit authority before installation.'); } this.openCodeSupervisor = runtimeArg.supervisor; this.openCodeResourceGeneration += 1; this.openCodeClient = runtimeArg.client; if (optionsArg.advanceStreams) this.advanceOpenCodeStreams(true); const initialConnectionTask = this.startOpenCodeEventStream(runtimeArg.client, true); const installationSignal = AbortSignal.any([signalArg, exitSignal]); let abortListener!: () => void; const aborted = new Promise<never>((_resolve, reject) => { abortListener = () => reject( installationSignal.reason ?? new DOMException('The operation was aborted.', 'AbortError'), ); installationSignal.addEventListener('abort', abortListener, { once: true }); if (installationSignal.aborted) abortListener(); }); try { await Promise.race([initialConnectionTask, aborted]); } finally { installationSignal.removeEventListener('abort', abortListener); } const finalStatus = runtimeArg.supervisor.getStatus(); if ( exitSignal.aborted || this.openCodeSupervisor !== runtimeArg.supervisor || this.openCodeClient !== runtimeArg.client || finalStatus.state !== 'ready' || !finalStatus.healthy ) throw new Error('The staged OpenCode runtime exited during installation.'); if (!optionsArg.retainPlannedStopUntilCallerCommit) { this.plannedOpenCodeSupervisorStops.delete(runtimeArg.supervisor); } } private async stopOwnedOpenCodeRuntime(runtimeArg: IControllerOwnedOpenCodeRuntime): Promise<void> { const errors: unknown[] = []; await runtimeArg.client.stopEventStream().catch((errorArg) => errors.push(errorArg)); this.plannedOpenCodeSupervisorStops.add(runtimeArg.supervisor); let supervisorStopped = false; await runtimeArg.supervisor.stop().then(() => { supervisorStopped = true; }).catch((errorArg) => errors.push(errorArg)); if (supervisorStopped) { this.plannedOpenCodeSupervisorStops.delete(runtimeArg.supervisor); this.forgetOpenCodeRuntimeCleanup(runtimeArg.supervisor); if ( this.eventStreamTask === runtimeArg.eventStreamTask || this.openCodeClient === runtimeArg.client ) this.eventStreamTask = undefined; if (this.openCodeSupervisor === runtimeArg.supervisor) this.openCodeSupervisor = undefined; if (this.openCodeClient === runtimeArg.client) this.openCodeClient = undefined; const crashRecovery = this.openCodeCrashRecovery; if (crashRecovery?.supervisor === runtimeArg.supervisor) { crashRecovery.supervisorStopped = true; await this.completeOpenCodeCrashCleanup(crashRecovery).catch((errorArg) => { errors.push(errorArg); }); } } else { this.retainOpenCodeRuntimeCleanup(runtimeArg.supervisor, runtimeArg); } if (errors.length === 1) throw errors[0]; if (errors.length > 1) { throw new AggregateError(errors, 'The owned OpenCode runtime did not stop cleanly.'); } } private async stopUninstalledOpenCodeSupervisor(supervisorArg: OpenCodeSupervisor): Promise<void> { this.plannedOpenCodeSupervisorStops.add(supervisorArg); try { await supervisorArg.stop(); this.plannedOpenCodeSupervisorStops.delete(supervisorArg); this.forgetOpenCodeRuntimeCleanup(supervisorArg); } catch (errorArg) { this.retainOpenCodeRuntimeCleanup(supervisorArg, undefined); throw errorArg; } } private async resolveOpenCodeAccountSwitchCohort( barrierArg: IControllerHarnessOperationBarrier, ): Promise<IOpenCodeAccountSwitchSession[]> { const sessions = new Map<string, IOpenCodeAccountSwitchSession>(); for (const entry of barrierArg.sessions) { if (entry.sessionId.harnessId !== 'opencode') continue; const project = await this.resolveProject(entry.projectId); const session: IOpenCodeAccountSwitchSession = { projectId: entry.projectId, sessionId: { ...entry.sessionId, harnessId: 'opencode' }, directory: project.directory, }; sessions.set(this.sessionStateMutationKey(entry.projectId, entry.sessionId), session); } return [...sessions.values()]; } private async continueOpenCodeAccountSwitchSessions( clientArg: OpenCodeClientAdapter, sessionsArg: readonly IOpenCodeAccountSwitchSession[], signalArg: AbortSignal, ): Promise<number> { const results = await Promise.allSettled(sessionsArg.map((session) => ( clientArg.sendHarnessControlMessage( session.directory, session.sessionId.nativeId, openCodeAccountSwitchContinueMessage, signalArg, ) ))); return results.filter((result) => result.status === 'fulfilled').length; } private remainingOpenCodeAccountSwitchMs(timeoutSignalArg: AbortSignal): number { if (timeoutSignalArg.aborted) throw timeoutSignalArg.reason; return openCodeAccountSwitchTimeoutMs; } private releaseOpenCodeAccountSwitchAdmission( barrierArg: IControllerHarnessOperationBarrier, ): void { const crashRecovery = this.openCodeCrashRecovery; if (crashRecovery) { const supervisor = this.openCodeSupervisor; const status = supervisor?.getStatus(); if ( !crashRecovery.cleanupComplete || !supervisor || supervisor === crashRecovery.supervisor || status?.state !== 'ready' || !status.healthy ) throw new Error('OpenCode crash authority cannot reopen from account switching.'); if (!this.reopenOpenCodeRuntimeAuthority(crashRecovery.admissionFenceGeneration)) { throw new Error('A newer OpenCode crash fence superseded account switching.'); } } if (this.openCodeAccountSwitchBarrier === barrierArg) { this.harnessOperationManager.endOperationBarrier(barrierArg); this.openCodeAccountSwitchBarrier = undefined; } this.openCodeAccountSwitchAdmissionSealed = false; if (!crashRecovery) this.openCodeRuntimeAdmissionSealed = false; if (this.openCodeCrashRecovery === crashRecovery) this.openCodeCrashRecovery = undefined; if (this.retainedOpenCodeRuntimeCleanup.size > 0) { this.scheduleRetainedOpenCodeRuntimeCleanup(); } this.openCodeRestartAttempt = 0; if (this.openCodeRestartTimer) { clearTimeout(this.openCodeRestartTimer); this.openCodeRestartTimer = undefined; } this.startSessionIntelligenceCleanupTimer(); } private async clearFlexProviderConnectionChoices( providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.requireAuthStore().clearSessionProviderConnectionsForConnection( providerConnectionIdArg, signalArg, ); } private async logoutFlexProviderConnection( providerConnectionIdArg: string, signalArg?: AbortSignal, ): Promise<void> { signalArg?.throwIfAborted(); await this.requireFlexSupervisor().request('provider.connection.logout.prepare', { providerConnectionId: providerConnectionIdArg, }); // Once Flex fences the account as deleting, controller-owned recovery must // finish cleanup even if the requesting browser disconnects. await this.clearFlexProviderConnectionChoices( providerConnectionIdArg, this.operationAbortController.signal, ); await this.requireFlexSupervisor().request('provider.connection.logout', { providerConnectionId: providerConnectionIdArg, }); for (const [jobKey, entry] of this.flexRefreshJobs) { if (entry.providerConnectionId !== providerConnectionIdArg) continue; this.flexRefreshJobs.delete(jobKey); this.pendingFlexFinishedJobs.delete(jobKey); } this.flexModelOptionsByConnection.delete(providerConnectionIdArg); } private async recoverDeletingFlexProviderConnections(signalArg: AbortSignal): Promise<void> { const response = await this.requireFlexSupervisor().request( 'provider.connection.list', {}, signalArg, ); const failures: unknown[] = []; for (const connection of response.connections) { signalArg.throwIfAborted(); if (connection.status !== 'deleting') continue; try { await this.withFlexProviderConnectionMutation(connection.loginId, async () => { await this.logoutFlexProviderConnection(connection.loginId, signalArg); }); } catch (errorArg) { failures.push(errorArg); } } if (failures.length > 0) { throw new AggregateError(failures, 'One or more pending Flex provider logouts failed.'); } } private async recoverDeletingFlexProviderConnection( providerConnectionIdArg: string, signalArg: AbortSignal, ): Promise<void> { await this.withFlexProviderConnectionMutation(providerConnectionIdArg, async () => { signalArg.throwIfAborted(); const response = await this.requireFlexSupervisor().request( 'provider.connection.list', {}, signalArg, ); const connection = response.connections.find( (candidate) => candidate.loginId === providerConnectionIdArg, ); if (connection?.status !== 'deleting') return; await this.logoutFlexProviderConnection(providerConnectionIdArg, signalArg); }); } private toFlexModelChoice( modelArg: TControllerModelChoice, providerConnectionIdArg: string, ): IFlexModelChoice { if (modelArg.harnessId !== 'flex' || modelArg.providerID !== 'openai') { throw new plugins.typedrequest.TypedResponseError( 'The selected model does not belong to a supported Flex provider.', { code: 'model_harness_mismatch' }, ); } return { providerConnectionId: providerConnectionIdArg, modelId: modelArg.modelID, ...(modelArg.variant === undefined ? {} : { variant: modelArg.variant }), }; } private async listFlexSessions( projectIdArg: string, signalArg?: AbortSignal, ): Promise<IControllerSession[]> { const reader = this.flexProjectionReader; if (!reader) return []; try { const snapshot = await reader.listAllSessions(projectIdArg, signalArg); if (snapshot.truncated) { throw new Error('The Flex session projection is truncated.'); } const sessions = snapshot.sessions; if (sessions.length > 2_048) { throw new Error('The Flex session list exceeded its exhaustive read bound.'); } const rootsWithWaitingChildren = new Set( sessions.flatMap((session) => ( session.status === 'waiting_permission' && session.parentSessionId !== undefined ? [session.parentSessionId] : [] )), ); const publicSessions = sessions .filter((session) => session.parentSessionId === undefined) .map((session) => this.toFlexSession( session, rootsWithWaitingChildren.has(session.sessionId), )); const providerSessionGenerations = new Map<string, string>(); const flexGenerationFacts = new Map<string, { sessionGenerationId: string; sessionGenerationSequence: number }>(); for (const session of sessions) { if ( session.sessionGenerationId === undefined || session.sessionGenerationSequence === undefined ) continue; flexGenerationFacts.set(session.sessionId, { sessionGenerationId: session.sessionGenerationId, sessionGenerationSequence: session.sessionGenerationSequence }); providerSessionGenerations.set( session.sessionId, requireFlexSessionProviderGeneration(session), ); } this.completeHarnessSessionRuntimeIdsByList.set( publicSessions, { liveRuntimeIds: sessions.map((session) => flexRuntimeId(session.sessionId)), providerSessionGenerations, flexGenerationFacts, }, ); return publicSessions; } catch (errorArg) { if (errorArg instanceof FlexProjectionNotFoundError) return []; throw errorArg; } } private markManagedSessionAuthorityScopeUncertain( projectIdArg: string, harnessIdArg: TControllerSessionHarnessId, ): void { const prefix = `${projectIdArg}\0[${JSON.stringify(harnessIdArg)},`; for (const key of this.managedSessionAuthorities.keys()) { if (key.startsWith(prefix)) this.managedSessionAuthorities.set(key, { state: 'uncertain' }); } } private deleteManagedSessionAuthorityCacheEntry( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { const key = this.managedSessionAuthorityKey(projectIdArg, sessionIdArg); this.managedSessionAuthorities.delete(key); if (sessionIdArg.harnessId === 'flex') this.currentFlexEventGenerations.delete(key); } private markRetiredSessionAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): IRetiredSessionAuthorityToken { const key = this.managedSessionAuthorityKey(projectIdArg, sessionIdArg); const authority = { state: 'definitively_unmanaged' as const }; const flexEventGeneration = sessionIdArg.harnessId === 'flex' ? this.currentFlexEventGenerations.get(key) : undefined; this.managedSessionAuthorities.set(key, authority); return { key, authority, ...(flexEventGeneration === undefined ? {} : { flexEventGeneration }), }; } private clearRetiredSessionAuthorityIfCurrent( tokenArg: IRetiredSessionAuthorityToken, ): void { if (this.managedSessionAuthorities.get(tokenArg.key) === tokenArg.authority) { this.managedSessionAuthorities.delete(tokenArg.key); } if ( tokenArg.flexEventGeneration !== undefined && this.currentFlexEventGenerations.get(tokenArg.key) === tokenArg.flexEventGeneration ) { this.currentFlexEventGenerations.delete(tokenArg.key); } } private scheduleProjectResourceReconciliationRetry( projectIdArg: string, tokenArg: IRetiredSessionAuthorityToken, ): void { if ( this.operationAbortController.signal.aborted ) return; const current = this.projectResourceReconciliationRetries.get(projectIdArg); if (current) { current.requestGeneration += 1; current.tokens.set(tokenArg.key, tokenArg); current.retryWakeController.abort(new Error('New resource reconciliation work arrived.')); return; } let entry!: IProjectResourceReconciliationRetry; let task!: Promise<void>; task = this.trackOperationPromise( Promise.resolve().then(() => this.runProjectResourceReconciliationRetry( projectIdArg, entry, )), () => { if (this.projectResourceReconciliationRetries.get(projectIdArg) === entry) { this.projectResourceReconciliationRetries.delete(projectIdArg); } }, ); entry = { requestGeneration: 1, tokens: new Map([[tokenArg.key, tokenArg]]), task, abortController: new AbortController(), retryWakeController: new AbortController(), }; this.projectResourceReconciliationRetries.set(projectIdArg, entry); void task.catch(() => undefined); } private async runProjectResourceReconciliationRetry( projectIdArg: string, entryArg: IProjectResourceReconciliationRetry, ): Promise<void> { const lifetimeSignal = AbortSignal.any([ this.operationAbortController.signal, entryArg.abortController.signal, ]); let retryDelayMs = projectResourceReconciliationInitialRetryMs; while (!lifetimeSignal.aborted) { const attemptGeneration = entryArg.requestGeneration; const attemptAbortController = new AbortController(); const attemptTimeout = setTimeout(() => { attemptAbortController.abort(new DOMException( 'Project resource reconciliation timed out.', 'TimeoutError', )); }, projectResourceReconciliationAttemptTimeoutMs); attemptTimeout.unref?.(); try { await this.requireResourceCoordinator().reconcileProjectResources( projectIdArg, AbortSignal.any([lifetimeSignal, attemptAbortController.signal]), ); if (lifetimeSignal.aborted) return; if (entryArg.requestGeneration !== attemptGeneration) { retryDelayMs = projectResourceReconciliationInitialRetryMs; continue; } for (const token of entryArg.tokens.values()) { this.clearRetiredSessionAuthorityIfCurrent(token); } if (this.projectResourceReconciliationRetries.get(projectIdArg) === entryArg) { this.projectResourceReconciliationRetries.delete(projectIdArg); } return; } catch (errorArg) { if (lifetimeSignal.aborted) return; console.error( `Project resource reconciliation ${projectIdArg} remains incomplete.`, errorArg, ); } finally { clearTimeout(attemptTimeout); } if (entryArg.requestGeneration !== attemptGeneration) { retryDelayMs = projectResourceReconciliationInitialRetryMs; continue; } const retryWakeController = new AbortController(); entryArg.retryWakeController = retryWakeController; if (entryArg.requestGeneration !== attemptGeneration) continue; await waitForSignalOrDelay( retryDelayMs, AbortSignal.any([lifetimeSignal, retryWakeController.signal]), ); if (lifetimeSignal.aborted) return; if (entryArg.requestGeneration !== attemptGeneration) { retryDelayMs = projectResourceReconciliationInitialRetryMs; continue; } retryDelayMs = Math.min( retryDelayMs * 2, projectResourceReconciliationMaximumRetryMs, ); } } private async abortProjectResourceReconciliationRetry( projectIdArg: string, reasonArg: unknown, ): Promise<void> { const entry = this.projectResourceReconciliationRetries.get(projectIdArg); if (!entry) return; if (this.projectResourceReconciliationRetries.get(projectIdArg) === entry) { this.projectResourceReconciliationRetries.delete(projectIdArg); } entry.abortController.abort(reasonArg); entry.retryWakeController.abort(reasonArg); await entry.task; } private pruneManagedSessionAuthorityCacheScope( projectIdArg: string, harnessIdArg: TControllerSessionHarnessId, liveRuntimeKeysArg: ReadonlySet<string>, ): void { const projectPrefix = `${projectIdArg}\0`; const scopePrefix = `${projectPrefix}[${JSON.stringify(harnessIdArg)},`; const isStale = (keyArg: string): boolean => keyArg.startsWith(scopePrefix) && !liveRuntimeKeysArg.has(keyArg.slice(projectPrefix.length)); for (const key of this.managedSessionAuthorities.keys()) { if (isStale(key)) this.managedSessionAuthorities.delete(key); } if (harnessIdArg !== 'flex') return; for (const key of this.currentFlexEventGenerations.keys()) { if (isStale(key)) this.currentFlexEventGenerations.delete(key); } } private clearManagedSessionAuthorityProjectScope(projectIdArg: string): void { const prefix = `${projectIdArg}\0`; for (const key of this.managedSessionAuthorities.keys()) { if (key.startsWith(prefix)) this.managedSessionAuthorities.delete(key); } for (const key of this.currentFlexEventGenerations.keys()) { if (key.startsWith(prefix)) this.currentFlexEventGenerations.delete(key); } } private async filterManagedSessions( projectIdArg: string, sessionsArg: readonly IControllerSession[], completeScopesArg: readonly ICompleteManagedSessionHarnessScope[], signalArg: AbortSignal, discoverFolderSessionsArg = false, ): Promise<IControllerSession[]> { const openCodeScopeRequested = sessionsArg.some( (session) => session.id.harnessId === 'opencode', ) || completeScopesArg.some((scope) => scope.harnessId === 'opencode'); const flexScopeRequested = sessionsArg.some( (session) => session.id.harnessId === 'flex', ) || completeScopesArg.some((scope) => scope.harnessId === 'flex'); const openCodeSupervisor = this.openCodeSupervisor; const openCodeClient = this.openCodeClient; const openCodeAuthority = openCodeScopeRequested && openCodeSupervisor && openCodeClient ? this.requireOpenCodeRuntimeExitAuthority(openCodeSupervisor) : undefined; const flexSupervisor = this.flexSupervisor; const flexAuthority = flexScopeRequested && flexSupervisor?.getStatus().ready ? this.requireFlexRuntimeExitAuthority(flexSupervisor) : undefined; const flexScope = completeScopesArg.find((scope) => scope.harnessId === 'flex'); const flexGenerations = flexScope?.providerSessionGenerations; const visible: IControllerSession[] = []; for (let index = 0; index < sessionsArg.length; index += 32) { signalArg.throwIfAborted(); const batch = sessionsArg.slice(index, index + 32); const results = await Promise.all(batch.map(async (session) => { const authority = session.id.harnessId === 'opencode' ? openCodeAuthority : session.id.harnessId === 'flex' ? flexAuthority : undefined; const providerSessionGeneration = session.id.harnessId === 'opencode' ? openCodeSessionProviderGeneration(session.id.nativeId, session.createdAt) : flexGenerations?.get(session.id.nativeId); if (!authority || providerSessionGeneration === undefined) { this.setManagedSessionAuthority(projectIdArg, session.id, 'uncertain'); return undefined; } try { const input = { projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(session.id), supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration, }; let managed = await this.requireAuthStore().resolveManagedSession(input); // Only an exhaustive provider-scoped sidebar discovery may admit native // roots. Cleanup and other callers retain lookup-only semantics. if (!managed && discoverFolderSessionsArg && session.archivedAt === undefined && session.parentId === undefined) { const flexFacts = flexScope?.flexGenerationFacts?.get(session.id.nativeId); if (session.id.harnessId === 'flex' && !flexFacts) return undefined; signalArg.throwIfAborted(); managed = await this.withSessionCreationAdmission(projectIdArg, session.id, () => ( this.requireAuthStore().admitManagedSession({ ...input, ...(session.id.harnessId === 'flex' ? flexFacts : {}), admissionSource: 'project-folder-discovery', signal: signalArg }) )); } if (managed) { this.setManagedSessionAuthority( projectIdArg, session.id, 'managed', managed.identity.sessionIdentityId, ); } else { this.setManagedSessionAuthority( projectIdArg, session.id, 'definitively_unmanaged', ); } return managed ? session : undefined; } catch (errorArg) { if (!(errorArg instanceof ControllerSessionIdentityError)) throw errorArg; this.setManagedSessionAuthority(projectIdArg, session.id, 'uncertain'); return undefined; } })); for (const session of results) { if (session) visible.push(session); } } if (openCodeSupervisor && openCodeClient && openCodeAuthority) { this.assertOpenCodeSessionIdentityGenerationCurrent( openCodeSupervisor, openCodeClient, openCodeAuthority, ); } if (flexSupervisor && flexAuthority) { this.assertFlexSessionIdentityGenerationCurrent(flexSupervisor, flexAuthority); } for (const scope of completeScopesArg) { if ( (scope.harnessId === 'opencode' && !openCodeAuthority) || (scope.harnessId === 'flex' && !flexAuthority) ) continue; this.pruneManagedSessionAuthorityCacheScope( projectIdArg, scope.harnessId, new Set(scope.liveRuntimeIds.map((runtimeId) => controllerRuntimeIdKey(runtimeId))), ); } return visible; } private async enumerateHarnessSessions( projectIdArg: string, directoryArg: string, signalArg: AbortSignal, ): Promise<IControllerHarnessSessionEnumeration> { while (true) { signalArg.throwIfAborted(); let shared = this.harnessSessionEnumerationTasks.get(projectIdArg); if (!shared || shared.invalidated) { const lifecycleSignal = this.operationAbortController.signal; const operation = Promise.allSettled([ Promise.resolve().then(() => ( this.requireOpenCodeClient().listSessions(directoryArg, lifecycleSignal) )), Promise.resolve().then(() => this.listFlexSessions(projectIdArg, lifecycleSignal)), ]).then(([openCode, flex]) => ({ openCode, flex })); let taskRecord!: IControllerHarnessSessionEnumerationTask; let trackedTask!: Promise<IControllerHarnessSessionEnumeration>; trackedTask = this.trackOperationPromise(operation, () => { taskRecord.settled = true; const current = this.harnessSessionEnumerationTasks.get(projectIdArg); if (current === taskRecord && taskRecord.waiterCount === 0) { this.harnessSessionEnumerationTasks.delete(projectIdArg); } }); taskRecord = { task: trackedTask, invalidated: false, settled: false, waiterCount: 0, }; shared = taskRecord; this.harnessSessionEnumerationTasks.set(projectIdArg, shared); } shared.waiterCount += 1; try { const result = await this.waitForPromiseWithSignal(shared.task, signalArg); if (!shared.invalidated) return result; } finally { shared.waiterCount -= 1; if ( shared.settled && shared.waiterCount === 0 && this.harnessSessionEnumerationTasks.get(projectIdArg) === shared ) { this.harnessSessionEnumerationTasks.delete(projectIdArg); } } } } private toFlexSession( sessionArg: TFlexSession, hasWaitingChildArg = false, ): IControllerSession { const status = sessionArg.status === 'running' || sessionArg.status === 'waiting_permission' || sessionArg.status === 'scheduled' ? 'busy' : sessionArg.status === 'failed' ? 'error' : 'idle'; return { id: flexRuntimeId(sessionArg.sessionId), title: sessionArg.title || 'Untitled session', ...(sessionArg.parentSessionId === undefined ? {} : { parentId: flexRuntimeId(sessionArg.parentSessionId) }), createdAt: Date.parse(sessionArg.createdAt), updatedAt: Date.parse(sessionArg.updatedAt), ...(sessionArg.archivedAt === undefined ? {} : { archivedAt: Date.parse(sessionArg.archivedAt) }), status, ...(sessionArg.status === 'waiting_permission' || hasWaitingChildArg ? { attention: true } : {}), }; } private async findArchivedLayoutSessionIds( projectIdArg: string, directoryArg: string, layoutSessionIdsArg: readonly IControllerRuntimeId[], signalArg: AbortSignal, ): Promise<IControllerRuntimeId[]> { const candidatesByKey = new Map<string, IControllerRuntimeId>(); for (const sessionId of layoutSessionIdsArg) { if (sessionId.harnessId === 'controller') continue; candidatesByKey.set(controllerRuntimeIdKey(sessionId), sessionId); } if (candidatesByKey.size === 0) return []; signalArg.throwIfAborted(); const enumeration = await this.enumerateHarnessSessions( projectIdArg, directoryArg, signalArg, ); signalArg.throwIfAborted(); const knownSessions = [ ...(enumeration.openCode.status === 'fulfilled' ? enumeration.openCode.value : []), ...(enumeration.flex.status === 'fulfilled' ? enumeration.flex.value : []), ]; const knownSessionKeys = new Set<string>(); const archivedByKey = new Map<string, IControllerRuntimeId>(); for (const session of knownSessions) { const key = controllerRuntimeIdKey(session.id); knownSessionKeys.add(key); if (session.archivedAt !== undefined && candidatesByKey.has(key)) { archivedByKey.set(key, session.id); } } const unresolved = [...candidatesByKey.entries()] .filter(([key]) => !knownSessionKeys.has(key)) .map(([, sessionId]) => sessionId); if (unresolved.length > maxArchivedLayoutExactReads) { throw new Error('The archived layout exact lookup limit was exceeded.'); } for (let index = 0; index < unresolved.length; index += maxConcurrentArchivedLayoutReads) { signalArg.throwIfAborted(); await Promise.all( unresolved .slice(index, index + maxConcurrentArchivedLayoutReads) .map(async (sessionId) => { let session: IControllerSession | undefined; if (sessionId.harnessId === 'opencode') { session = await this.requireOpenCodeClient().getSessionIfPresent( directoryArg, sessionId.nativeId, signalArg, ); } else if (sessionId.harnessId === 'codex') { session = await this.requireCodexClient(sessionId.nativeId).readIfPresent(this.codexDirectory(sessionId.nativeId, directoryArg), sessionId.nativeId, signalArg); } else { try { session = this.toFlexSession( await this.requireFlexProjectionReader().getSession( projectIdArg, sessionId.nativeId, signalArg, ), ); } catch (errorArg) { if (!(errorArg instanceof FlexProjectionNotFoundError)) throw errorArg; } } if (session?.archivedAt !== undefined) { archivedByKey.set(controllerRuntimeIdKey(sessionId), sessionId); } }), ); signalArg.throwIfAborted(); } return [...archivedByKey.values()].map((sessionId) => ({ ...sessionId })); } private assertAttentionRequestOwned( pendingRequestsArg: ReadonlyArray<{ id: IControllerRuntimeId }>, requestIdArg: IControllerRuntimeId, kindArg: 'permission' | 'question', ): void { if (pendingRequestsArg.some((request) => ( controllerRuntimeIdKey(request.id) === controllerRuntimeIdKey(requestIdArg) ))) return; throw new plugins.typedrequest.TypedResponseError( `The ${kindArg} request does not belong to the selected conversation.`, { code: 'invalid_request' }, ); } private toFlexMessageBundle(projectedArg: IFlexProjectedMessage): IControllerMessageBundle { const messageArg = projectedArg.message; const sourceMessageId = flexRuntimeId(messageArg.messageId); const model = messageArg.model ? `${messageArg.model.provider}/${messageArg.model.model}` : undefined; const createdAt = Date.parse(messageArg.createdAt); const updatedAt = messageArg.completedAt === undefined ? undefined : Date.parse(messageArg.completedAt); const messages: IControllerMessage[] = []; let sourceMessageIdClaimed = false; for (const [partIndex, part] of messageArg.parts.entries()) { const order = { messageIndex: projectedArg.messageIndex, partIndex }; if (part.type === 'text') { messages.push({ id: sourceMessageIdClaimed ? flexRuntimeId(part.partId) : sourceMessageId, role: messageArg.role, text: part.text, createdAt, order, }); sourceMessageIdClaimed = true; } else if (part.type === 'reasoning') { messages.push({ id: sourceMessageIdClaimed ? flexRuntimeId(part.partId) : sourceMessageId, role: messageArg.role, text: '', createdAt, order, reasoning: [{ id: flexRuntimeId(part.partId), text: part.text, startedAt: createdAt, ...(part.status === 'running' || updatedAt === undefined ? {} : { endedAt: updatedAt }), }], }); sourceMessageIdClaimed = true; } else if (part.type === 'tool') { const status = part.status === 'running' ? 'running' : part.status === 'completed' ? 'completed' : part.status === 'cancelled' ? 'stopped' : 'error'; messages.push({ id: flexRuntimeId(part.partId), role: 'tool', text: '', createdAt, order, ...(part.status === 'running' ? { streaming: true } : {}), toolCall: { id: flexRuntimeId(part.toolCallId), name: part.toolName, status, input: part.input, ...(part.output === undefined ? {} : { output: part.output }), ...(part.error === undefined ? {} : { errorText: part.error }), ...(part.childSessionId === undefined ? {} : { childSessionId: flexRuntimeId(part.childSessionId) }), ...(part.model === undefined ? {} : { model: `${part.model.provider}/${part.model.model}` }), ...(updatedAt === undefined ? {} : { finishedAt: updatedAt }), }, }); } } if (messages.length === 0) { messages.push({ id: sourceMessageId, role: messageArg.role, text: '', createdAt, order: { messageIndex: projectedArg.messageIndex, partIndex: 0 }, }); } const finalMessage = messages.at(-1)!; if (updatedAt !== undefined) { for (const message of messages) message.updatedAt = updatedAt; } if (messageArg.usage !== undefined) finalMessage.usage = { ...messageArg.usage }; if (messageArg.error !== undefined) finalMessage.error = messageArg.error; if (model !== undefined) finalMessage.model = model; if (messageArg.model?.variant !== undefined) finalMessage.effort = messageArg.model.variant; if (messageArg.role === 'assistant' && messageArg.status === 'streaming') { const finalContentMessage = [...messages].reverse().find((message) => message.role !== 'tool'); if (finalContentMessage) finalContentMessage.streaming = true; } const structuralDigest = plugins.crypto .createHash('sha256') .update(JSON.stringify({ sourceMessageId: controllerRuntimeIdKey(sourceMessageId), entries: messages.map((message) => ({ id: controllerRuntimeIdKey(message.id), type: message.role, order: message.order, })), })) .digest('hex'); return { sourceMessageId, structuralDigest, messages }; } private toFlexPermission(permissionArg: TFlexPermissionRequest): IControllerPermission { const metadata = permissionArg.metadata && typeof permissionArg.metadata === 'object' && !Array.isArray(permissionArg.metadata) ? permissionArg.metadata as Record<string, unknown> : {}; return { id: flexRuntimeId(permissionArg.permissionId), sessionId: flexRuntimeId(permissionArg.sessionId), title: permissionArg.description, type: permissionArg.kind, patterns: permissionArg.rememberKey ? [permissionArg.rememberKey] : [], metadata, createdAt: Date.parse(permissionArg.createdAt), }; } private sessionStateMutationKey( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): string { return `${projectIdArg}\0${controllerRuntimeIdKey(sessionIdArg)}`; } private withSessionStateMutation<T>( projectIdArg: string, sessionIdArg: IControllerRuntimeId, operationArg: () => Promise<T>, ): Promise<T> { const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); const previous = this.sessionStateMutationTails.get(key) ?? Promise.resolve(); const operation = previous.then(operationArg); const tail = operation.then(() => undefined, () => undefined); this.sessionStateMutationTails.set(key, tail); void tail.finally(() => { if (this.sessionStateMutationTails.get(key) === tail) { this.sessionStateMutationTails.delete(key); } }); return operation; } private async withSessionCreationAdmission<T>( projectIdArg: string, sessionIdArg: IControllerRuntimeId, operationArg: () => Promise<T>, ): Promise<T> { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); const release = this.reserveAdmissionEntry( this.projectHarnessSubmissionAdmissions, projectIdArg, ); try { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); return await operationArg(); } finally { release(); } } private withActiveSessionMutation<T>( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal | undefined, operationArg: () => Promise<T>, ): Promise<T> { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); return this.withSessionStateMutation(projectIdArg, sessionIdArg, async () => { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); await this.assertSessionExists(projectIdArg, sessionIdArg, signalArg); this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); return operationArg(); }); } private reserveHarnessSubmissionAdmission( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): () => void { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); const projectRelease = this.reserveAdmissionEntry( this.projectHarnessSubmissionAdmissions, projectIdArg, ); const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); try { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); } catch (errorArg) { projectRelease(); throw errorArg; } const sessionRelease = this.reserveAdmissionEntry( this.sessionHarnessSubmissionAdmissions, sessionKey, ); try { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); } catch (errorArg) { sessionRelease(); projectRelease(); throw errorArg; } let released = false; return () => { if (released) return; released = true; sessionRelease(); projectRelease(); }; } private reserveAdmissionEntry( mapArg: Map<string, IHarnessSubmissionAdmissions>, keyArg: string, ): () => void { let admissions = mapArg.get(keyArg); if (!admissions) { let resolveDrained!: () => void; const drained = new Promise<void>((resolve) => { resolveDrained = resolve; }); admissions = { count: 0, drained, resolveDrained }; mapArg.set(keyArg, admissions); } admissions.count += 1; let released = false; return () => { if (released) return; released = true; admissions.count -= 1; if (admissions.count !== 0) return; if (mapArg.get(keyArg) === admissions) mapArg.delete(keyArg); admissions.resolveDrained(); }; } private async withHarnessSubmissionAdmission<T>( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal | undefined, operationArg: () => Promise<T>, ): Promise<T> { const release = this.reserveHarnessSubmissionAdmission(projectIdArg, sessionIdArg); try { await this.assertSessionExists(projectIdArg, sessionIdArg, signalArg); this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); if (sessionIdArg.harnessId === 'codex') await this.finalizeCodexCreation(projectIdArg, sessionIdArg.nativeId, signalArg); return await operationArg(); } finally { release(); } } private async waitForAdmissionDrain( drainedArg: Promise<void> | undefined, signalArg?: AbortSignal, ): Promise<void> { if (!drainedArg) return; if (!signalArg) { await drainedArg; return; } await this.waitForPromiseWithSignal(drainedArg, signalArg); } private async waitForPromiseWithSignal<T>( promiseArg: Promise<T>, signalArg: AbortSignal, ): Promise<T> { signalArg.throwIfAborted(); let abortListener!: () => void; const aborted = new Promise<never>((_resolve, reject) => { abortListener = () => reject(signalArg.reason); signalArg.addEventListener('abort', abortListener, { once: true }); if (signalArg.aborted) abortListener(); }); try { return await Promise.race([promiseArg, aborted]); } finally { signalArg.removeEventListener('abort', abortListener); } } private runWithSignal<T>( operationArg: () => Promise<T>, signalArg: AbortSignal, ): Promise<T> { signalArg.throwIfAborted(); // A bounded caller may stop waiting before its dependency settles. Keep // that underlying work owned so shutdown cannot close resources beneath it. const operation = this.trackOperationPromise(Promise.resolve().then(() => { signalArg.throwIfAborted(); return operationArg(); })); return this.waitForPromiseWithSignal(operation, signalArg); } private async waitForProjectHarnessSubmissionAdmissions( projectIdArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.projectHarnessSubmissionAdmissions.get(projectIdArg)?.drained, signalArg, ); } private async waitForFlexProjectHostRequestAdmissions( projectIdArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.flexProjectHostRequestAdmissions.get(projectIdArg)?.drained, signalArg, ); } private async waitForFlexSessionHostRequestAdmissions( sessionKeyArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.flexSessionHostRequestAdmissions.get(sessionKeyArg)?.drained, signalArg, ); } private async waitForSessionHarnessSubmissionAdmissions( sessionKeyArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.sessionHarnessSubmissionAdmissions.get(sessionKeyArg)?.drained, signalArg, ); } private async waitForProjectArchiveAdmissions(projectIdArg: string): Promise<void> { await this.waitForAdmissionDrain(this.projectArchiveAdmissions.get(projectIdArg)?.drained); } private assertSessionMutationAdmission( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { if (sessionIdArg.harnessId === 'codex') this.requireCodexClient(sessionIdArg.nativeId); if (this.retiringProjectIds.has(projectIdArg)) { throw new plugins.typedrequest.TypedResponseError( 'The requested project is being removed.', { code: 'project_not_found' }, ); } if (this.flexProjectIdentityRecoveryPending.has(projectIdArg)) { throw new plugins.typedrequest.TypedResponseError( 'The requested Flex project is reconciling session authority.', { code: 'concurrent_change' }, ); } if (this.retiringSessionKeys.has( this.sessionStateMutationKey(projectIdArg, sessionIdArg), )) { throw new plugins.typedrequest.TypedResponseError( 'The requested session is being deleted.', { code: 'concurrent_change' }, ); } if (this.archivingSessionKeys.has( this.sessionStateMutationKey(projectIdArg, sessionIdArg), )) { throw new plugins.typedrequest.TypedResponseError( 'The requested session is being archived.', { code: 'concurrent_change' }, ); } if (this.abortingSessionKeys.has( this.sessionStateMutationKey(projectIdArg, sessionIdArg), )) { throw new plugins.typedrequest.TypedResponseError( 'The requested session is being aborted.', { code: 'concurrent_change' }, ); } if ( sessionIdArg.harnessId === 'opencode' && this.openCodePromptQueueFenceCounts.has( this.sessionStateMutationKey(projectIdArg, sessionIdArg), ) ) { throw new plugins.typedrequest.TypedResponseError( 'The OpenCode session is recovering from a provider error.', { code: 'concurrent_change' }, ); } } private startSessionBackgroundOperation( projectIdArg: string, sessionIdArg: IControllerRuntimeId, operationArg: (signal: AbortSignal) => Promise<void>, ): Promise<void> { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.activeSessionBackgroundOperations.has(key)) { throw new plugins.typedrequest.TypedResponseError( 'A model-backed command is already running for this conversation.', { code: 'concurrent_change' }, ); } const abortController = new AbortController(); const signal = AbortSignal.any([ this.operationAbortController.signal, abortController.signal, ]); const task = this.trackOperationPromise(Promise.resolve().then(() => operationArg(signal))); const operation: IActiveSessionBackgroundOperation = { abortController, task }; this.activeSessionBackgroundOperations.set(key, operation); void task.then( () => { if (this.activeSessionBackgroundOperations.get(key) === operation) { this.activeSessionBackgroundOperations.delete(key); } }, () => { if (this.activeSessionBackgroundOperations.get(key) === operation) { this.activeSessionBackgroundOperations.delete(key); } }, ); return task; } private async abortSessionBackgroundOperation( sessionKeyArg: string, reasonArg: Error, ): Promise<void> { const operation = this.activeSessionBackgroundOperations.get(sessionKeyArg); if (!operation) return; operation.abortController.abort(reasonArg); await Promise.allSettled([operation.task]); } private async abortProjectSessionBackgroundOperations( projectIdArg: string, reasonArg: Error, ): Promise<void> { const projectPrefix = `${projectIdArg}\0`; const operations = [...this.activeSessionBackgroundOperations.entries()] .filter(([key]) => key.startsWith(projectPrefix)) .map(([, operation]) => operation); for (const operation of operations) operation.abortController.abort(reasonArg); await Promise.allSettled(operations.map((operation) => operation.task)); } private async abortActiveHarnessOperation( projectArg: IControllerProject, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal, ): Promise<void> { const active = this.harnessOperationManager.listSessionActive(projectArg.id, sessionIdArg); if (active.length === 0) return; if (sessionIdArg.harnessId === 'codex') { const client = this.requireCodexClient(sessionIdArg.nativeId); await this.abortObservedCodexTurn(client, sessionIdArg.nativeId, signalArg); await client.waitForIdle(sessionIdArg.nativeId, signalArg); return; } if (sessionIdArg.harnessId === 'opencode') { await this.requireOpenCodeClient().abortSession( projectArg.directory, sessionIdArg.nativeId, signalArg, ); return; } if (sessionIdArg.harnessId === 'flex') { signalArg.throwIfAborted(); for (const operation of active) { if (!operation.flexQueueId) continue; await this.requireFlexSupervisor().request('prompt.cancel', { scopeId: projectArg.id, sessionId: sessionIdArg.nativeId, queueId: operation.flexQueueId, }); } } } private async abortProjectHarnessOperations( projectArg: IControllerProject, signalArg: AbortSignal, ): Promise<void> { const sessions = new Map<string, IControllerRuntimeId>(); for (const active of this.harnessOperationManager.listProjectActive(projectArg.id)) { sessions.set(controllerRuntimeIdKey(active.reservation.sessionId), active.reservation.sessionId); } for (const sessionId of sessions.values()) { signalArg.throwIfAborted(); await this.abortActiveHarnessOperation(projectArg, sessionId, signalArg); } } private reserveProjectSessionIntelligenceAdmission(projectIdArg: string): () => void { this.operationAbortController.signal.throwIfAborted(); if (this.sessionIntelligenceFlexAdmissionSealed || !this.flexIsOperational()) { throw new plugins.typedrequest.TypedResponseError( 'Session Intelligence is temporarily unavailable while Flex restarts.', { code: 'concurrent_change' }, ); } if (this.openCodeAccountSwitchAdmissionSealed || this.openCodeRuntimeAdmissionSealed) { throw new plugins.typedrequest.TypedResponseError( 'Session Intelligence is temporarily unavailable while OpenCode restarts.', { code: 'concurrent_change' }, ); } if (this.retiringProjectIds.has(projectIdArg)) { throw new plugins.typedrequest.TypedResponseError( 'The requested project is being removed.', { code: 'project_not_found' }, ); } return this.reserveSessionIntelligenceAdmissionEntry( this.projectSessionIntelligenceAdmissions, projectIdArg, ); } private reserveSessionIntelligenceAdmission( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): () => void { const releaseProjectAdmission = this.reserveProjectSessionIntelligenceAdmission(projectIdArg); const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.retiringSessionKeys.has(key) || this.archivingSessionKeys.has(key)) { releaseProjectAdmission(); throw new plugins.typedrequest.TypedResponseError( this.retiringSessionKeys.has(key) ? 'The requested session is being deleted.' : 'The requested session is being archived.', { code: this.retiringSessionKeys.has(key) ? 'session_not_found' : 'concurrent_change' }, ); } const releaseSessionAdmission = this.reserveSessionIntelligenceAdmissionEntry( this.sessionIntelligenceAdmissions, key, ); let released = false; return () => { if (released) return; released = true; releaseSessionAdmission(); releaseProjectAdmission(); }; } private reserveSessionIntelligenceAdmissionEntry( admissionMapArg: Map<string, IProjectSessionIntelligenceAdmissions>, keyArg: string, ): () => void { let admissions = admissionMapArg.get(keyArg); if (!admissions) { let resolveDrained!: () => void; const drained = new Promise<void>((resolve) => { resolveDrained = resolve; }); admissions = { count: 0, drained, resolveDrained }; admissionMapArg.set(keyArg, admissions); } admissions.count += 1; let released = false; return () => { if (released) return; released = true; admissions.count -= 1; if (admissions.count !== 0) return; if (admissionMapArg.get(keyArg) === admissions) { admissionMapArg.delete(keyArg); } admissions.resolveDrained(); }; } private async waitForProjectSessionIntelligenceAdmissions( projectIdArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.projectSessionIntelligenceAdmissions.get(projectIdArg)?.drained, signalArg, ); } private async waitForSessionIntelligenceAdmissions( sessionKeyArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.sessionIntelligenceAdmissions.get(sessionKeyArg)?.drained, signalArg, ); } private async waitForAllProjectSessionIntelligenceAdmissions(): Promise<void> { while (this.projectSessionIntelligenceAdmissions.size > 0) { await Promise.all( [...this.projectSessionIntelligenceAdmissions.values()].map((entry) => entry.drained), ); } } private async assertSessionExists( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg?: AbortSignal, ): Promise<void> { await this.readManagedSession(projectIdArg, sessionIdArg, signalArg); } private managedSessionAuthorityKey( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): string { return this.sessionStateMutationKey(projectIdArg, sessionIdArg); } private setManagedSessionAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId, authorityArg: 'managed', sessionIdentityIdArg: string, ): void; private setManagedSessionAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId, authorityArg: Exclude<TControllerManagedSessionAuthority, 'managed'>, ): void; private setManagedSessionAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId, authorityArg: TControllerManagedSessionAuthority, sessionIdentityIdArg?: string, ): void { if (authorityArg === 'managed' && sessionIdentityIdArg === undefined) { throw new Error('Managed session authority requires an exact session identity.'); } // A task that stops being managed at an exact identity stops being an MCP subject. if (authorityArg !== 'managed') this.mcpCallerRegistry.revokeSession(projectIdArg, sessionIdArg); this.managedSessionAuthorities.set( this.managedSessionAuthorityKey(projectIdArg, sessionIdArg), authorityArg === 'managed' ? { state: 'managed', sessionIdentityId: sessionIdentityIdArg!, } : { state: authorityArg }, ); } private managedSessionAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): TControllerResourceSessionAuthoritySnapshot { const authority = this.managedSessionAuthorities.get( this.managedSessionAuthorityKey(projectIdArg, sessionIdArg), ); return authority === undefined ? { state: 'uncertain' } : { ...authority }; } private async readManagedSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg?: AbortSignal, ): Promise<IControllerSession> { const project = await this.resolveProject(projectIdArg); if (sessionIdArg.harnessId === 'opencode') { return (await this.readManagedOpenCodeScopeAuthority( projectIdArg, sessionIdArg as IControllerRuntimeId & { harnessId: 'opencode' }, signalArg, project, )).session; } if (sessionIdArg.harnessId === 'codex') return this.readManagedCodexSession(projectIdArg, project.directory, sessionIdArg.nativeId, signalArg); if (sessionIdArg.harnessId === 'flex') { const supervisor = this.requireFlexSupervisor(); const authority = this.requireFlexRuntimeExitAuthority(supervisor); const signal = signalArg === undefined ? authority.signal : AbortSignal.any([signalArg, authority.signal]); const raw = await supervisor.request('session.get', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId, }, signal); const providerSessionGeneration = requireFlexSessionProviderGeneration(raw); this.assertFlexSessionIdentityGenerationCurrent(supervisor, authority); let managedObservation: IControllerSessionManagedObservation; try { managedObservation = await this.requireAuthStore().requireManagedSession({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration, }); } catch (errorArg) { this.setManagedSessionAuthority( projectIdArg, sessionIdArg, errorArg instanceof ControllerSessionIdentityError && (errorArg.code === 'not_found' || errorArg.code === 'tombstoned') ? 'definitively_unmanaged' : 'uncertain', ); throw errorArg; } this.assertFlexSessionIdentityGenerationCurrent(supervisor, authority); this.setManagedSessionAuthority( projectIdArg, sessionIdArg, 'managed', managedObservation.identity.sessionIdentityId, ); return this.toFlexSession(raw); } throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions cannot be selected.', { code: 'invalid_request' }, ); } private async readManagedOpenCodeScopeAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId & { harnessId: 'opencode' }, signalArg?: AbortSignal, projectArg?: IControllerProjectDocument, ): Promise<IOpenCodeManagedSessionScopeAuthority> { const project = projectArg ?? await this.resolveProject(projectIdArg); await this.assertSessionIsNotIntelligenceTemporary(projectIdArg, sessionIdArg, signalArg); const supervisor = this.openCodeSupervisor; const client = this.requireOpenCodeClient(); if (!supervisor) throw new Error('The OpenCode runtime is unavailable.'); const runtimeAuthority = this.requireOpenCodeRuntimeExitAuthority(supervisor); const signal = signalArg === undefined ? runtimeAuthority.signal : AbortSignal.any([signalArg, runtimeAuthority.signal]); const observation = await client.getSessionAuthorityObservation( project.directory, sessionIdArg.nativeId, signal, ); this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, runtimeAuthority); let managedObservation: IControllerSessionManagedObservation; try { managedObservation = await this.requireAuthStore().requireManagedSession({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), supervisorGeneration: runtimeAuthority.sessionIdentityGeneration, providerSessionGeneration: observation.providerSessionGeneration, }); } catch (errorArg) { this.setManagedSessionAuthority( projectIdArg, sessionIdArg, errorArg instanceof ControllerSessionIdentityError && (errorArg.code === 'not_found' || errorArg.code === 'tombstoned') ? 'definitively_unmanaged' : 'uncertain', ); throw errorArg; } this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, runtimeAuthority); this.setManagedSessionAuthority( projectIdArg, sessionIdArg, 'managed', managedObservation.identity.sessionIdentityId, ); return { project, session: observation.session, sessionIdentityId: managedObservation.identity.sessionIdentityId, providerSessionGeneration: observation.providerSessionGeneration, supervisor, client, runtimeAuthority, }; } private childScopeBindingKey( peerIdArg: string, credentialIdArg: string, projectIdArg: string, parentSessionIdArg: IControllerRuntimeId, childSessionIdArg: IControllerRuntimeId, ): string { return JSON.stringify([ peerIdArg, credentialIdArg, projectIdArg, controllerRuntimeIdKey(parentSessionIdArg), controllerRuntimeIdKey(childSessionIdArg), ]); } private childScopeParentKey( peerIdArg: string, projectIdArg: string, parentSessionIdArg: IControllerRuntimeId, ): string { return JSON.stringify([ peerIdArg, projectIdArg, controllerRuntimeIdKey(parentSessionIdArg), ]); } private childScopeCredentialId(peerArg: IControllerPeer): string { const credentialId = peerArg.context.state[authenticatedPeerStateKey]; if (typeof credentialId !== 'string') { throw new plugins.typedrequest.TypedResponseError( 'Passkey authentication is required on this connection.', { code: 'authentication_required' }, ); } return credentialId; } private setAuthenticatedPeerCredential( peerArg: IControllerPeer, credentialIdArg: string, ): void { const previousCredentialId = peerArg.context.state[authenticatedPeerStateKey]; if (previousCredentialId !== credentialIdArg || !this.authenticatedPeerGenerations.has(peerArg)) { this.authenticatedPeerGenerations.set(peerArg, Symbol('peer authentication')); void this.closeBrowserViewsForPeer(peerArg.id).catch((errorArg) => { console.error('Browser authentication change cleanup remains pending.', errorArg); }); } if (typeof previousCredentialId === 'string' && previousCredentialId !== credentialIdArg) { this.revokeChildScopesForPeer(peerArg.id); } peerArg.context.state[authenticatedPeerStateKey] = credentialIdArg; this.authenticatedPeerIds.add(peerArg.id); } private childScopeInvalid(messageArg = 'The direct-child session scope is no longer valid'): never { throw new plugins.typedrequest.TypedResponseError(messageArg, { code: 'child_scope_invalid' }); } private assertChildScopeParentAdmission( projectIdArg: string, parentSessionIdArg: IControllerRuntimeId, ): void { const parentKey = this.sessionStateMutationKey(projectIdArg, parentSessionIdArg); if ( this.lifecycleState !== 'ready' || this.stopRequested || this.openCodeRuntimeAdmissionSealed || this.openCodeAccountSwitchAdmissionSealed || this.retiringProjectIds.has(projectIdArg) || this.retiringSessionKeys.has(parentKey) || this.archivingSessionKeys.has(parentKey) ) this.childScopeInvalid('The direct-child parent authority is not active'); } private async observeOpenCodeDirectChild( projectIdArg: string, parentSessionIdArg: IControllerRuntimeId & { harnessId: 'opencode' }, childSessionIdArg: IControllerRuntimeId & { harnessId: 'opencode' }, signalArg: AbortSignal, ): Promise<{ parent: IOpenCodeManagedSessionScopeAuthority; child: IOpenCodeDirectChildObservation; }> { this.assertChildScopeParentAdmission(projectIdArg, parentSessionIdArg); const parent = await this.readManagedOpenCodeScopeAuthority( projectIdArg, parentSessionIdArg, signalArg, ); if (parent.session.parentId !== undefined || parent.session.archivedAt !== undefined) { this.childScopeInvalid('Only an active managed root may open direct-child scope'); } const signal = AbortSignal.any([signalArg, parent.runtimeAuthority.signal]); const children = await parent.client.listDirectChildSessionObservations( parent.project.directory, parentSessionIdArg.nativeId, signal, ); this.assertOpenCodeSessionIdentityGenerationCurrent( parent.supervisor, parent.client, parent.runtimeAuthority, ); this.assertChildScopeParentAdmission(projectIdArg, parentSessionIdArg); const child = children.find((candidateArg) => ( candidateArg.session.id.nativeId === childSessionIdArg.nativeId )); if (!child) this.childScopeInvalid('The requested session is not a direct child of the managed parent'); return { parent, child }; } private async resolveOpenCodeFamilyAuthority( projectIdArg: string, sessionIdArg: IControllerRuntimeId & { harnessId: 'opencode' }, signalArg: AbortSignal, ): Promise<IOpenCodeFamilyAuthority> { const project = await this.resolveProject(projectIdArg); const supervisor = this.openCodeSupervisor; const client = this.requireOpenCodeClient(); if (!supervisor) throw new Error('The OpenCode runtime is unavailable.'); const runtimeAuthority = this.requireOpenCodeRuntimeExitAuthority(supervisor); const signal = AbortSignal.any([signalArg, runtimeAuthority.signal]); const observation = await client.getSessionAuthorityObservation( project.directory, sessionIdArg.nativeId, signal, ); this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, runtimeAuthority); if (observation.session.parentId === undefined) { const parent = await this.readManagedOpenCodeScopeAuthority( projectIdArg, sessionIdArg, signal, project, ); if ( parent.session.parentId !== undefined || parent.session.archivedAt !== undefined || parent.providerSessionGeneration !== observation.providerSessionGeneration ) throw new Error('The OpenCode root authority changed during family resolution.'); return { parent }; } if (observation.session.parentId.harnessId !== 'opencode') { throw new Error('The OpenCode child has an invalid parent harness.'); } const family = await this.observeOpenCodeDirectChild( projectIdArg, observation.session.parentId as IControllerRuntimeId & { harnessId: 'opencode' }, sessionIdArg, signalArg, ); if (family.child.providerSessionGeneration !== observation.providerSessionGeneration) { throw new Error('The OpenCode child authority changed during family resolution.'); } return family; } private openCodeFamilyAdmissionKey( projectIdArg: string, parentSessionIdArg: IControllerRuntimeId, ): string { return this.sessionStateMutationKey(projectIdArg, parentSessionIdArg); } private openCodeExactFamilyAdmissionKey( projectIdArg: string, authorityArg: IOpenCodeFamilyAuthority, ): string { return JSON.stringify([ projectIdArg, controllerRuntimeIdKey(authorityArg.parent.session.id), authorityArg.parent.sessionIdentityId, authorityArg.parent.providerSessionGeneration, authorityArg.parent.runtimeAuthority.sessionIdentityGeneration, authorityArg.child === undefined ? null : controllerRuntimeIdKey(authorityArg.child.session.id), authorityArg.child?.providerSessionGeneration ?? null, ]); } private openCodeFamilyAuthoritiesEqual( leftArg: IOpenCodeFamilyAuthority, rightArg: IOpenCodeFamilyAuthority, ): boolean { return controllerRuntimeIdKey(leftArg.parent.session.id) === controllerRuntimeIdKey(rightArg.parent.session.id) && leftArg.parent.sessionIdentityId === rightArg.parent.sessionIdentityId && leftArg.parent.providerSessionGeneration === rightArg.parent.providerSessionGeneration && leftArg.parent.runtimeAuthority.sessionIdentityGeneration === rightArg.parent.runtimeAuthority.sessionIdentityGeneration && (leftArg.child === undefined ? rightArg.child === undefined : rightArg.child !== undefined && controllerRuntimeIdKey(leftArg.child.session.id) === controllerRuntimeIdKey(rightArg.child.session.id)) && leftArg.child?.providerSessionGeneration === rightArg.child?.providerSessionGeneration; } private acquireOpenCodeAutoAcceptAdmissionSeal(): () => void { this.openCodeAutoAcceptAdmissionSealCount += 1; let released = false; return () => { if (released) return; released = true; this.openCodeAutoAcceptAdmissionSealCount = Math.max( 0, this.openCodeAutoAcceptAdmissionSealCount - 1, ); }; } private reserveOpenCodeAutoAcceptAdmission( projectIdArg: string, authorityArg: IOpenCodeFamilyAuthority, ): () => void { const familyKey = this.openCodeFamilyAdmissionKey(projectIdArg, authorityArg.parent.session.id); if ( this.openCodeAutoAcceptAdmissionSealCount > 0 || this.sealedOpenCodeAutoAcceptFamilyKeys.has(familyKey) || !this.isProjectAutoAcceptedSession(projectIdArg, authorityArg.parent.session.id) || (authorityArg.child !== undefined && this.childScopeObservedMode(authorityArg.child.session) !== 'active') ) throw new Error('The OpenCode family is not admitted for automatic permission replies.'); const exactRelease = this.reserveAdmissionEntry( this.openCodeAutoAcceptExactAdmissions, this.openCodeExactFamilyAdmissionKey(projectIdArg, authorityArg), ); const familyRelease = this.reserveAdmissionEntry( this.openCodeAutoAcceptFamilyAdmissions, familyKey, ); let released = false; return () => { if (released) return; released = true; familyRelease(); exactRelease(); }; } private async waitForAllOpenCodeAutoAcceptAdmissions(signalArg?: AbortSignal): Promise<void> { while (this.openCodeAutoAcceptExactAdmissions.size > 0) { await this.waitForPromiseWithSignal( Promise.all( [...this.openCodeAutoAcceptExactAdmissions.values()].map((entryArg) => entryArg.drained), ).then(() => undefined), signalArg ?? this.operationAbortController.signal, ); } } private async waitForOpenCodeFamilyAutoAcceptAdmissions( familyKeyArg: string, signalArg?: AbortSignal, ): Promise<void> { await this.waitForAdmissionDrain( this.openCodeAutoAcceptFamilyAdmissions.get(familyKeyArg)?.drained, signalArg, ); } private childScopeObservedMode(sessionArg: IControllerSession): 'active' | 'terminal' { return sessionArg.status === 'busy' || sessionArg.status === 'retry' ? 'active' : 'terminal'; } private refreshChildScopeExpiry( scopeArg: IControllerChildScopeRecord, nowArg: number, touchArg: boolean, ): void { if (scopeArg.mode === 'terminal') return; if (touchArg) scopeArg.lastUsedAt = nowArg; scopeArg.expiresAt = Math.min( scopeArg.issuedAt + childScopeActiveLifetimeMs, scopeArg.lastUsedAt + childScopeIdleTtlMs, ); } private childScopeDescriptor(scopeArg: IControllerChildScopeRecord) { return { projectId: scopeArg.projectId, parentSessionId: scopeArg.parentSessionId, childSessionId: scopeArg.childSessionId, scopeGeneration: scopeArg.scopeGeneration, mode: scopeArg.mode, expiresAt: scopeArg.expiresAt, }; } private startChildScopeCleanupTimer(): void { if (this.childScopeCleanupTimer || this.childScopesByGeneration.size === 0) return; this.childScopeCleanupTimer = setInterval(() => this.cleanupExpiredChildScopes(), childScopeCleanupIntervalMs); this.childScopeCleanupTimer.unref?.(); } private stopChildScopeCleanupTimerIfIdle(): void { if (!this.childScopeCleanupTimer || this.childScopesByGeneration.size > 0) return; clearInterval(this.childScopeCleanupTimer); this.childScopeCleanupTimer = undefined; } private cleanupExpiredChildScopes(): void { const now = Date.now(); for (const scope of this.childScopesByGeneration.values()) { if (!scope.revoked && scope.expiresAt <= now) this.revokeChildScope(scope); } this.stopChildScopeCleanupTimerIfIdle(); } private deleteChildScope(scopeArg: IControllerChildScopeRecord): void { if (this.childScopesByGeneration.get(scopeArg.scopeGeneration) !== scopeArg) return; this.childScopesByGeneration.delete(scopeArg.scopeGeneration); const bindingKey = this.childScopeBindingKey( scopeArg.peerId, scopeArg.credentialId, scopeArg.projectId, scopeArg.parentSessionId, scopeArg.childSessionId, ); if (this.childScopeGenerationByBinding.get(bindingKey) === scopeArg.scopeGeneration) { this.childScopeGenerationByBinding.delete(bindingKey); } this.pendingChildScopeEventCount -= scopeArg.pendingEvents.length; this.pendingChildScopeEventBytes -= scopeArg.pendingEventBytes; scopeArg.pendingEvents.length = 0; scopeArg.pendingEventBytes = 0; this.stopChildScopeCleanupTimerIfIdle(); } private revokeChildScope(scopeArg: IControllerChildScopeRecord, notifyArg = true): void { if (scopeArg.revoked) return; scopeArg.revoked = true; const bindingKey = this.childScopeBindingKey( scopeArg.peerId, scopeArg.credentialId, scopeArg.projectId, scopeArg.parentSessionId, scopeArg.childSessionId, ); if (this.childScopeGenerationByBinding.get(bindingKey) === scopeArg.scopeGeneration) { this.childScopeGenerationByBinding.delete(bindingKey); } this.pendingChildScopeEventCount -= scopeArg.pendingEvents.length; this.pendingChildScopeEventBytes -= scopeArg.pendingEventBytes; scopeArg.pendingEvents.length = 0; scopeArg.pendingEventBytes = 0; if (!notifyArg) { this.deleteChildScope(scopeArg); return; } scopeArg.sequence += 1; const event: IControllerChildEvent = { ...this.childScopeDescriptor(scopeArg), sequence: scopeArg.sequence, timestamp: Date.now(), kind: 'scope.revoked', }; const eventBytes = Buffer.byteLength(JSON.stringify(event), 'utf8'); scopeArg.pendingEvents.push(event); scopeArg.pendingEventBytes = eventBytes; this.pendingChildScopeEventCount += 1; this.pendingChildScopeEventBytes += eventBytes; this.drainChildScopeEvents(scopeArg); } private revokeChildScopesForPeer(peerIdArg: string): void { for (const scope of this.childScopesByGeneration.values()) { if (scope.peerId === peerIdArg) this.revokeChildScope(scope, false); } } private revokeChildScopesForProject(projectIdArg: string): void { for (const scope of this.childScopesByGeneration.values()) { if (scope.projectId === projectIdArg) this.revokeChildScope(scope); } } private revokeChildScopesForParent( projectIdArg: string, parentSessionIdArg: IControllerRuntimeId, ): void { const parentKey = controllerRuntimeIdKey(parentSessionIdArg); for (const scope of this.childScopesByGeneration.values()) { if ( scope.projectId === projectIdArg && controllerRuntimeIdKey(scope.parentSessionId) === parentKey ) this.revokeChildScope(scope); } } private revokeAllChildScopes(notifyArg: boolean): void { for (const scope of [...this.childScopesByGeneration.values()]) { this.revokeChildScope(scope, notifyArg); } } private queueChildScopeEventForSession( projectIdArg: string, childSessionIdArg: IControllerRuntimeId, kindArg: Exclude<IControllerChildEvent['kind'], 'scope.revoked'>, ): void { const childKey = controllerRuntimeIdKey(childSessionIdArg); for (const scope of this.childScopesByGeneration.values()) { if ( scope.revoked || scope.projectId !== projectIdArg || controllerRuntimeIdKey(scope.childSessionId) !== childKey ) continue; this.queueChildScopeEvent(scope, kindArg); } } private queueChildScopeEvent( scopeArg: IControllerChildScopeRecord, kindArg: Exclude<IControllerChildEvent['kind'], 'scope.revoked'>, ): void { if (scopeArg.revoked) return; scopeArg.sequence += 1; const event: IControllerChildEvent = { ...this.childScopeDescriptor(scopeArg), sequence: scopeArg.sequence, timestamp: Date.now(), kind: kindArg, }; const eventBytes = Buffer.byteLength(JSON.stringify(event), 'utf8'); if ( scopeArg.pendingEvents.length >= maxPendingEventsPerChildScope || scopeArg.pendingEventBytes + eventBytes > maxPendingEventBytesPerChildScope || this.pendingChildScopeEventCount >= maxPendingChildScopeEvents || this.pendingChildScopeEventBytes + eventBytes > maxPendingChildScopeEventBytes ) { this.revokeChildScope(scopeArg); return; } scopeArg.pendingEvents.push(event); scopeArg.pendingEventBytes += eventBytes; this.pendingChildScopeEventCount += 1; this.pendingChildScopeEventBytes += eventBytes; this.drainChildScopeEvents(scopeArg); } private drainChildScopeEvents(scopeArg: IControllerChildScopeRecord): void { if (scopeArg.eventDrainTask) return; const task = (async () => { while (scopeArg.pendingEvents.length > 0) { const event = scopeArg.pendingEvents.shift()!; const eventBytes = Buffer.byteLength(JSON.stringify(event), 'utf8'); scopeArg.pendingEventBytes -= eventBytes; this.pendingChildScopeEventCount -= 1; this.pendingChildScopeEventBytes -= eventBytes; await this.sendChildScopeEvent(scopeArg.peerId, scopeArg.credentialId, event); if (event.kind === 'scope.revoked') { this.deleteChildScope(scopeArg); return; } } })(); scopeArg.eventDrainTask = task; void task.then( () => { if (scopeArg.eventDrainTask === task) scopeArg.eventDrainTask = undefined; if (scopeArg.pendingEvents.length > 0) this.drainChildScopeEvents(scopeArg); }, () => { if (scopeArg.eventDrainTask === task) scopeArg.eventDrainTask = undefined; scopeArg.revoked = true; this.deleteChildScope(scopeArg); }, ); } private async sendChildScopeEvent( peerIdArg: string, credentialIdArg: string, eventArg: IControllerChildEvent, ): Promise<void> { const typedSocket = this.typedServer?.typedsocket; if (!typedSocket) throw new Error('The controller transport is unavailable.'); const target = await typedSocket.findTargetConnection(async (connectionArg) => ( connectionArg.peer.id === peerIdArg && connectionArg.peer.context.state[authenticatedPeerStateKey] === credentialIdArg && connectionArg.peer.context.headers.get('origin') === this.requireRuntimeConfig().publicOrigin )); if (!target) throw new Error('The child-scope peer is no longer connected.'); const request = typedSocket.createTypedRequest<IReq_ControllerSessionChildEvent>( 'controller.session.child.event', target, { timeoutMs: 2_000 }, ); await request.fire(eventArg, { timeoutMs: 2_000, maxRetries: 0 }); } private withChildScopeOpen<T>(bindingKeyArg: string, operationArg: () => Promise<T>): Promise<T> { const previous = this.childScopeOpenTails.get(bindingKeyArg) ?? Promise.resolve(); const operation = previous.then(operationArg); const tail = operation.then(() => undefined, () => undefined); this.childScopeOpenTails.set(bindingKeyArg, tail); void tail.finally(() => { if (this.childScopeOpenTails.get(bindingKeyArg) === tail) { this.childScopeOpenTails.delete(bindingKeyArg); } }); return operation; } private assertChildScopeCapacity( peerIdArg: string, projectIdArg: string, parentSessionIdArg: IControllerRuntimeId, ): void { this.cleanupExpiredChildScopes(); const parentKey = this.childScopeParentKey(peerIdArg, projectIdArg, parentSessionIdArg); let peerCount = 0; let parentCount = 0; for (const scope of this.childScopesByGeneration.values()) { if (scope.revoked || scope.peerId !== peerIdArg) continue; peerCount += 1; if (this.childScopeParentKey(scope.peerId, scope.projectId, scope.parentSessionId) === parentKey) { parentCount += 1; } } if ( parentCount >= maxChildScopesPerPeerParent || peerCount >= maxChildScopesPerPeer || this.childScopesByGeneration.size >= maxControllerChildScopes ) throw new plugins.typedrequest.TypedResponseError( 'The direct-child scope limit was reached.', { code: 'operation_limit' }, ); } private async revalidateChildScope( scopeArg: IControllerChildScopeRecord, signalArg: AbortSignal, activeRequiredArg = false, ): Promise<{ parent: IOpenCodeManagedSessionScopeAuthority; child: IOpenCodeDirectChildObservation; }> { this.assertChildScopeRecordCurrent(scopeArg); let observation: Awaited<ReturnType<OpenCodeController['observeOpenCodeDirectChild']>>; try { observation = await this.observeOpenCodeDirectChild( scopeArg.projectId, scopeArg.parentSessionId, scopeArg.childSessionId, signalArg, ); } catch (errorArg) { this.revokeChildScope(scopeArg); throw errorArg; } this.assertChildScopeRecordCurrent(scopeArg); if ( observation.parent.sessionIdentityId !== scopeArg.parentSessionIdentityId || observation.parent.providerSessionGeneration !== scopeArg.parentProviderSessionGeneration || observation.parent.runtimeAuthority.sessionIdentityGeneration !== scopeArg.supervisorSessionIdentityGeneration || observation.child.providerSessionGeneration !== scopeArg.childProviderSessionGeneration ) { this.revokeChildScope(scopeArg); this.childScopeInvalid(); } const observedMode = this.childScopeObservedMode(observation.child.session); const now = Date.now(); if (scopeArg.mode === 'terminal' && observedMode === 'active') { this.revokeChildScope(scopeArg); this.childScopeInvalid('The direct-child session resumed under a new scope generation'); } if (scopeArg.mode === 'active' && observedMode === 'terminal') { scopeArg.mode = 'terminal'; scopeArg.expiresAt = now + childScopeTerminalTtlMs; } if (activeRequiredArg && scopeArg.mode !== 'active') { this.childScopeInvalid('Terminal direct-child scopes are read-only'); } this.refreshChildScopeExpiry(scopeArg, now, true); return observation; } private assertChildScopeRecordCurrent(scopeArg: IControllerChildScopeRecord): void { const registered = this.childScopesByGeneration.get(scopeArg.scopeGeneration) === scopeArg; if (!scopeArg.revoked && registered && scopeArg.expiresAt > Date.now()) return; if (!scopeArg.revoked) { if (registered) this.revokeChildScope(scopeArg); else scopeArg.revoked = true; } this.childScopeInvalid(); } private requireChildScope( peerArg: IControllerPeer, requestArg: { projectId: string; parentSessionId: IControllerRuntimeId & { harnessId: 'opencode' }; childSessionId: IControllerRuntimeId & { harnessId: 'opencode' }; scopeGeneration: string; }, ): IControllerChildScopeRecord { const credentialId = this.childScopeCredentialId(peerArg); const scope = this.childScopesByGeneration.get(requestArg.scopeGeneration); if ( !scope || scope.revoked || scope.peerId !== peerArg.id || scope.credentialId !== credentialId || scope.projectId !== requestArg.projectId || controllerRuntimeIdKey(scope.parentSessionId) !== controllerRuntimeIdKey(requestArg.parentSessionId) || controllerRuntimeIdKey(scope.childSessionId) !== controllerRuntimeIdKey(requestArg.childSessionId) ) this.childScopeInvalid(); return scope; } private boundOpenCodeChildMessagePage( pageArg: Awaited<ReturnType<OpenCodeClientAdapter['listMessagePage']>>, ): IControllerMessagePage { return boundControllerMessagePage({ ...pageArg, bundles: pageArg.bundles.map(({ sourceRole: _sourceRole, parentMessageId: _parentMessageId, terminal: _terminal, ...bundle }) => bundle), }); } private async openChildScope( peerArg: IControllerPeer, requestArg: IReq_ControllerSessionChildGet['request'], signalArg: AbortSignal, ): Promise<IControllerChildSessionDetail> { const credentialId = this.childScopeCredentialId(peerArg); const bindingKey = this.childScopeBindingKey( peerArg.id, credentialId, requestArg.projectId, requestArg.parentSessionId, requestArg.childSessionId, ); return this.withChildScopeOpen(bindingKey, async () => { let createdScope: IControllerChildScopeRecord | undefined; try { const first = await this.observeOpenCodeDirectChild( requestArg.projectId, requestArg.parentSessionId, requestArg.childSessionId, signalArg, ); const existingGeneration = this.childScopeGenerationByBinding.get(bindingKey); let scope = existingGeneration === undefined ? undefined : this.childScopesByGeneration.get(existingGeneration); const firstObservedMode = this.childScopeObservedMode(first.child.session); if (scope && ( scope.revoked || scope.expiresAt <= Date.now() || scope.parentSessionIdentityId !== first.parent.sessionIdentityId || scope.parentProviderSessionGeneration !== first.parent.providerSessionGeneration || scope.supervisorSessionIdentityGeneration !== first.parent.runtimeAuthority.sessionIdentityGeneration || scope.childProviderSessionGeneration !== first.child.providerSessionGeneration || (scope.mode === 'terminal' && firstObservedMode === 'active') )) { this.revokeChildScope(scope); scope = undefined; } const scopeOpenedAt = Date.now(); if (!scope) { this.assertChildScopeCapacity(peerArg.id, requestArg.projectId, requestArg.parentSessionId); scope = { ...requestArg, scopeGeneration: plugins.crypto.randomBytes(32).toString('base64url'), mode: firstObservedMode, peerId: peerArg.id, credentialId, parentSessionIdentityId: first.parent.sessionIdentityId, parentProviderSessionGeneration: first.parent.providerSessionGeneration, supervisorSessionIdentityGeneration: first.parent.runtimeAuthority.sessionIdentityGeneration, childProviderSessionGeneration: first.child.providerSessionGeneration, issuedAt: scopeOpenedAt, lastUsedAt: scopeOpenedAt, expiresAt: firstObservedMode === 'active' ? scopeOpenedAt + childScopeIdleTtlMs : scopeOpenedAt + childScopeTerminalTtlMs, sequence: 0, pendingEvents: [], pendingEventBytes: 0, revoked: false, }; createdScope = scope; this.childScopesByGeneration.set(scope.scopeGeneration, scope); this.childScopeGenerationByBinding.set(bindingKey, scope.scopeGeneration); this.startChildScopeCleanupTimer(); } else { if (scope.mode === 'active' && firstObservedMode === 'terminal') { scope.mode = 'terminal'; scope.expiresAt = scopeOpenedAt + childScopeTerminalTtlMs; } this.refreshChildScopeExpiry(scope, scopeOpenedAt, true); } this.assertChildScopeRecordCurrent(scope); const snapshotSequence = scope.sequence; const detail = await first.parent.client.getSessionDetail( first.parent.project.directory, requestArg.childSessionId.nativeId, AbortSignal.any([signalArg, first.parent.runtimeAuthority.signal]), ); this.assertChildScopeRecordCurrent(scope); const second = await this.observeOpenCodeDirectChild( requestArg.projectId, requestArg.parentSessionId, requestArg.childSessionId, signalArg, ); this.assertChildScopeRecordCurrent(scope); if ( first.parent.sessionIdentityId !== second.parent.sessionIdentityId || first.parent.providerSessionGeneration !== second.parent.providerSessionGeneration || first.parent.runtimeAuthority.sessionIdentityGeneration !== second.parent.runtimeAuthority.sessionIdentityGeneration || first.child.providerSessionGeneration !== second.child.providerSessionGeneration || controllerRuntimeIdKey(detail.session.id) !== controllerRuntimeIdKey(requestArg.childSessionId) ) { this.revokeChildScope(scope); this.childScopeInvalid('The direct-child authority changed during hydration'); } const secondObservedMode = this.childScopeObservedMode(second.child.session); const hydratedAt = Date.now(); if (scope.mode === 'terminal' && secondObservedMode === 'active') { this.revokeChildScope(scope); this.childScopeInvalid('The direct-child session resumed during hydration'); } if (scope.mode === 'active' && secondObservedMode === 'terminal') { scope.mode = 'terminal'; scope.expiresAt = hydratedAt + childScopeTerminalTtlMs; } this.refreshChildScopeExpiry(scope, hydratedAt, true); this.assertChildScopeRecordCurrent(scope); const messagePage = this.boundOpenCodeChildMessagePage(detail.messagePage); return { ...this.childScopeDescriptor(scope), session: detail.session, messagePage, permissions: detail.permissions, questions: detail.questions, sequence: snapshotSequence, toolStreamCursor: { streamEpoch: 1, revision: snapshotSequence }, messageStreamCursor: { streamEpoch: 1, revision: snapshotSequence }, ...(this.isProjectAutoAcceptedSession(requestArg.projectId, requestArg.parentSessionId) ? { autoAcceptPermissions: true } : {}), ...(detail.model === undefined ? {} : { model: detail.model }), ...(detail.effort === undefined ? {} : { effort: detail.effort }), }; } catch (errorArg) { if ( createdScope && !createdScope.revoked && this.childScopesByGeneration.get(createdScope.scopeGeneration) === createdScope ) this.revokeChildScope(createdScope, false); throw errorArg; } }); } private async readBrowserChildAttention( peerArg: IControllerPeer, projectIdArg: string, parentSessionIdArg: IControllerRuntimeId & { harnessId: 'opencode' }, signalArg: AbortSignal, ): Promise<Pick<IReq_ControllerSessionGet['response'], 'childAttention' | 'childAttentionLimited'>> { try { const parent = await this.readManagedOpenCodeScopeAuthority( projectIdArg, parentSessionIdArg, signalArg, ); if (parent.session.parentId !== undefined || parent.session.archivedAt !== undefined) return {}; const signal = AbortSignal.any([signalArg, parent.runtimeAuthority.signal]); const [children, pending] = await Promise.all([ parent.client.listDirectChildSessionObservations( parent.project.directory, parentSessionIdArg.nativeId, signal, ), settleControllerAttentionLists( parent.client.listPermissions(parent.project.directory, undefined, signal), parent.client.listQuestions(parent.project.directory, undefined, signal), ), ]); this.assertOpenCodeSessionIdentityGenerationCurrent( parent.supervisor, parent.client, parent.runtimeAuthority, ); this.assertChildScopeParentAdmission(projectIdArg, parentSessionIdArg); const permissionsBySession = new Map<string, IControllerPermission[]>(); for (const permission of pending.permissions) { const key = controllerRuntimeIdKey(permission.sessionId); const entries = permissionsBySession.get(key) ?? []; entries.push(permission); permissionsBySession.set(key, entries); } const questionsBySession = new Map<string, IControllerQuestion[]>(); for (const question of pending.questions) { const key = controllerRuntimeIdKey(question.sessionId); const entries = questionsBySession.get(key) ?? []; entries.push(question); questionsBySession.set(key, entries); } const candidates = children .filter((childArg) => { const key = controllerRuntimeIdKey(childArg.session.id); return (permissionsBySession.get(key)?.length ?? 0) > 0 || (questionsBySession.get(key)?.length ?? 0) > 0; }) .sort((leftArg, rightArg) => ( rightArg.session.updatedAt - leftArg.session.updatedAt || leftArg.session.id.nativeId.localeCompare(rightArg.session.id.nativeId) )); let limited = candidates.length > maxChildAttentionChildren || candidates.reduce((countArg, childArg) => { const key = controllerRuntimeIdKey(childArg.session.id); return countArg + (permissionsBySession.get(key)?.length ?? 0) + (questionsBySession.get(key)?.length ?? 0); }, 0) > maxChildAttentionItems; const childAttention: IControllerChildAttention[] = []; let itemCount = 0; for (const candidate of candidates.slice(0, maxChildAttentionChildren)) { signalArg.throwIfAborted(); let detail: IControllerChildSessionDetail; try { detail = await this.openChildScope(peerArg, { projectId: projectIdArg, parentSessionId: parentSessionIdArg, childSessionId: candidate.session.id as IControllerRuntimeId & { harnessId: 'opencode' }, }, signalArg); } catch { signalArg.throwIfAborted(); limited = true; continue; } const entry: IControllerChildAttention = { projectId: detail.projectId, parentSessionId: detail.parentSessionId, childSessionId: detail.childSessionId, scopeGeneration: detail.scopeGeneration, mode: detail.mode, expiresAt: detail.expiresAt, session: detail.session, permissions: [], questions: [], sequence: detail.sequence, }; const items = [ ...detail.permissions.map((value) => ({ kind: 'permission' as const, value })), ...detail.questions.map((value) => ({ kind: 'question' as const, value })), ].sort((leftArg, rightArg) => ( controllerRuntimeIdKey(leftArg.value.id).localeCompare(controllerRuntimeIdKey(rightArg.value.id)) || leftArg.kind.localeCompare(rightArg.kind) )); for (const item of items) { if (itemCount >= maxChildAttentionItems) { limited = true; break; } if (item.kind === 'permission') entry.permissions.push(item.value); else entry.questions.push(item.value); if (Buffer.byteLength(JSON.stringify([...childAttention, entry]), 'utf8') > maxChildAttentionBytes) { if (item.kind === 'permission') entry.permissions.pop(); else entry.questions.pop(); limited = true; break; } itemCount += 1; } if (entry.permissions.length > 0 || entry.questions.length > 0) childAttention.push(entry); if (itemCount >= maxChildAttentionItems) break; } return { ...(childAttention.length === 0 ? {} : { childAttention }), ...(limited ? { childAttentionLimited: true as const } : {}), }; } catch (errorArg) { signalArg.throwIfAborted(); console.error('Reading direct-child attention failed.', errorArg); return { childAttentionLimited: true }; } } private async sessionEventIsManaged( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise<boolean> { try { await this.readManagedSession( projectIdArg, sessionIdArg, this.operationAbortController.signal, ); return true; } catch { return false; } } private async readControllerSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg?: AbortSignal, ): Promise<IControllerSession> { return this.readManagedSession(projectIdArg, sessionIdArg, signalArg); } private async assertSessionIsNotIntelligenceTemporary( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg?: AbortSignal, ): Promise<void> { if (sessionIdArg.harnessId !== 'opencode') return; const hiddenIds = await this.requireAuthStore() .findProjectSessionIntelligenceTemporarySessionIds( projectIdArg, [sessionIdArg.nativeId], signalArg, ); if (!hiddenIds.has(sessionIdArg.nativeId)) return; throw new plugins.typedrequest.TypedResponseError( 'The requested session was not found.', { code: 'session_not_found' }, ); } private async getFlexSessionMetrics( projectIdArg: string, sessionIdArg: string, signalArg?: AbortSignal, ): Promise<IControllerSessionMetrics> { const reader = this.requireFlexProjectionReader(); const snapshot = await reader.listAllSessionMessages( projectIdArg, sessionIdArg, signalArg, ); if (snapshot.truncated) return {}; let lifetimeUsedTokens = 0; let lifetimeProvable = true; for (const entry of snapshot.messages) { const message = entry.message; if (message.role !== 'assistant') continue; if ( message.usage === undefined || !Number.isSafeInteger(message.usage.totalTokens) || message.usage.totalTokens < 0 ) { lifetimeProvable = false; continue; } lifetimeUsedTokens += message.usage.totalTokens; if (!Number.isSafeInteger(lifetimeUsedTokens)) lifetimeProvable = false; } return lifetimeProvable ? { lifetimeUsedTokens } : {}; } private async executeSessionIntelligenceJob( projectIdArg: string, sessionIdArg: IControllerRuntimeId, questionArg: string, exchangeIdArg: string, scratchpadTextArg: string, modelArg: IFlexModelChoice, jobArg: ISessionIntelligenceJob, ): Promise<void> { const signal = AbortSignal.any([ this.operationAbortController.signal, jobArg.abortController.signal, ]); let sessionState: IStoredControllerSessionState | undefined; let capabilityToken: string | undefined; let flexJobId: string | undefined; let supervisor: FlexSupervisor | undefined; let capabilityCanClose = true; let terminalError: unknown; let capabilityCloseError: unknown; try { const project = await this.resolveProject(projectIdArg); signal.throwIfAborted(); if (sessionIdArg.harnessId !== 'opencode' && sessionIdArg.harnessId !== 'flex') { throw new Error('Controller-internal sessions cannot be analyzed.'); } const sourceSessionId: IFlexIntelligenceSessionId = { harnessId: sessionIdArg.harnessId, nativeId: sessionIdArg.nativeId, }; supervisor = this.requireFlexSupervisor(); capabilityToken = await this.createSessionIntelligenceCapability( project, sourceSessionId, supervisor, signal, ); jobArg.capabilityToken = capabilityToken; const admission = await supervisor.request('intelligence.start', { scopeId: projectIdArg, sourceSessionId, question: questionArg, scratchpad: scratchpadTextArg, model: modelArg, capabilityToken, }, signal); flexJobId = admission.jobId; jobArg.flexJobId = flexJobId; const result = await this.waitForSessionIntelligenceResult( supervisor, projectIdArg, flexJobId, capabilityToken, signal, ); const capability = this.sessionIntelligenceCapabilities.get(capabilityToken); if (!capability?.peerId || capability.sourceState !== 'read') { throw new Error('The Session Intelligence read capability did not complete its source read.'); } this.assertSessionIntelligenceCapabilityActive( capabilityToken, capability, capability.peerId, ); sessionState = await this.withSessionStateMutation( projectIdArg, sessionIdArg, async () => this.requireAuthStore().completeSessionIntelligence( projectIdArg, sessionIdArg, exchangeIdArg, result.answer, result.scratchpad, result.model, ), ); } catch (jobErrorArg) { let cleanupError: unknown; if (supervisor && capabilityToken) { try { await this.cancelAndDrainSessionIntelligenceJob( supervisor, projectIdArg, capabilityToken, flexJobId, ); } catch (errorArg) { capabilityCanClose = false; cleanupError = errorArg; this.failClosedFlexAfterSessionIntelligenceCleanup(supervisor, errorArg); } } try { sessionState = await this.withSessionStateMutation( projectIdArg, sessionIdArg, async () => this.requireAuthStore().failSessionIntelligence( projectIdArg, sessionIdArg, exchangeIdArg, 'Session Intelligence failed.', sessionIntelligenceModel, ), ); } catch (stateErrorArg) { if (cleanupError) { throw new AggregateError( [jobErrorArg, cleanupError, stateErrorArg], 'Session Intelligence failed without confirmed worker disposal or state persistence.', ); } throw stateErrorArg; } if (cleanupError) { terminalError = new AggregateError( [jobErrorArg, cleanupError], 'Session Intelligence failed and worker disposal could not be confirmed.', ); } } finally { if (capabilityToken && capabilityCanClose) { try { await this.closeSessionIntelligenceCapability(capabilityToken); } catch (errorArg) { capabilityCloseError = errorArg; if (supervisor) { this.failClosedFlexAfterSessionIntelligenceCleanup(supervisor, errorArg); } } } } if (sessionState) { void this.pushControllerEvent({ type: 'session.changed', projectId: projectIdArg, harnessId: requireManagedRuntimeId(sessionIdArg).harnessId, sessionId: sessionIdArg, timestamp: Date.now(), }).catch(() => undefined); } if (terminalError && capabilityCloseError) { throw new AggregateError( [terminalError, capabilityCloseError], 'Session Intelligence worker and capability cleanup both failed.', ); } if (terminalError) throw terminalError; if (capabilityCloseError) throw capabilityCloseError; } private async createSessionIntelligenceCapability( projectArg: IControllerProjectDocument, sourceSessionIdArg: IFlexIntelligenceSessionId, flexSupervisorArg: FlexSupervisor, signalArg: AbortSignal, ): Promise<string> { const openCodeSupervisor = this.openCodeSupervisor; const openCodeClient = this.openCodeClient; if (!openCodeSupervisor || !openCodeClient) { throw new Error('The OpenCode runtime is unavailable.'); } const reader = new SessionIntelligenceReader({ ...openCodeSupervisor.getConnectionConfig(), directory: projectArg.directory, }); await reader.init(signalArg); try { signalArg.throwIfAborted(); if ( this.flexSupervisor !== flexSupervisorArg || this.openCodeSupervisor !== openCodeSupervisor || this.openCodeClient !== openCodeClient || this.sessionIntelligenceFlexAdmissionSealed || !this.flexIsOperational() || !openCodeSupervisor.getStatus().healthy ) throw new Error('A harness generation changed while creating the read capability.'); let capabilityToken: string; do { capabilityToken = plugins.crypto.randomBytes(32).toString('base64url'); } while (this.sessionIntelligenceCapabilities.has(capabilityToken)); this.sessionIntelligenceCapabilities.set(capabilityToken, { projectId: projectArg.id, directory: projectArg.directory, sourceSessionId: { ...sourceSessionIdArg }, flexSupervisor: flexSupervisorArg, openCodeSupervisor, openCodeClient, reader, abortController: new AbortController(), sourceState: 'unread', activeRequests: 0, closed: false, }); return capabilityToken; } catch (errorArg) { await reader.close(); throw errorArg; } } private closeSessionIntelligenceCapability(capabilityTokenArg: string): Promise<void> { const capability = this.sessionIntelligenceCapabilities.get(capabilityTokenArg); if (!capability) return Promise.resolve(); if (capability.closeTask) return capability.closeTask; let task: Promise<void>; task = (async () => { capability.closed = true; capability.abortController.abort(new Error('The Session Intelligence read capability closed.')); const results = await Promise.allSettled([ capability.reader.close(), capability.drained ?? Promise.resolve(), ]); const errors = results.flatMap((result) => ( result.status === 'rejected' ? [result.reason] : [] )); if (errors.length === 1) throw errors[0]; if (errors.length > 1) { throw new AggregateError(errors, 'Closing the Session Intelligence read capability failed.'); } if (this.sessionIntelligenceCapabilities.get(capabilityTokenArg) === capability) { this.sessionIntelligenceCapabilities.delete(capabilityTokenArg); } })().finally(() => { if (capability.closeTask === task) capability.closeTask = undefined; }); capability.closeTask = task; return task; } private beginSessionIntelligenceCapabilityRequest( capabilityTokenArg: string, peerIdArg: string, ): { capability: ISessionIntelligenceCapability; release: () => void } { const capability = this.sessionIntelligenceCapabilities.get(capabilityTokenArg); if (!capability || capability.closed) throw new Error('The read capability is unavailable.'); if ( this.flexSupervisor !== capability.flexSupervisor || this.openCodeSupervisor !== capability.openCodeSupervisor || this.openCodeClient !== capability.openCodeClient || !capability.flexSupervisor.ownsBrowserPeer(peerIdArg) ) throw new Error('The read capability generation is stale.'); if (capability.peerId === undefined) capability.peerId = peerIdArg; if (capability.peerId !== peerIdArg) throw new Error('The read capability peer is stale.'); if (capability.activeRequests === 0) { let resolveDrained!: () => void; capability.drained = new Promise<void>((resolve) => { resolveDrained = resolve; }); capability.resolveDrained = resolveDrained; } capability.activeRequests += 1; let released = false; return { capability, release: () => { if (released) return; released = true; capability.activeRequests -= 1; if (capability.activeRequests === 0) { capability.resolveDrained?.(); capability.resolveDrained = undefined; } }, }; } private assertSessionIntelligenceCapabilityActive( capabilityTokenArg: string, capabilityArg: ISessionIntelligenceCapability, peerIdArg: string, ): void { if ( capabilityArg.closed || this.sessionIntelligenceCapabilities.get(capabilityTokenArg) !== capabilityArg || capabilityArg.peerId !== peerIdArg || this.flexSupervisor !== capabilityArg.flexSupervisor || this.openCodeSupervisor !== capabilityArg.openCodeSupervisor || this.openCodeClient !== capabilityArg.openCodeClient || !capabilityArg.flexSupervisor.ownsBrowserPeer(peerIdArg) ) throw new Error('The read capability is no longer active.'); } private async listSessionIntelligenceChats( capabilityTokenArg: string, limitArg: number, peerIdArg: string, ): Promise<IFlexIntelligenceChatSummary[]> { const { capability, release } = this.beginSessionIntelligenceCapabilityRequest( capabilityTokenArg, peerIdArg, ); try { if (capability.sourceState !== 'read') { throw new Error('The exact source session must be read first.'); } if (!await this.sessionEventIsManaged(capability.projectId, capability.sourceSessionId)) { throw new Error('The Session Intelligence source session is no longer managed.'); } const [openCodeChats, flexSnapshot] = await Promise.all([ capability.reader.listChats(sessionIntelligenceSourceOverfetchLimit), this.requireFlexProjectionReader().listAllSessions( capability.projectId, capability.abortController.signal, ).catch((errorArg) => { if (errorArg instanceof FlexProjectionNotFoundError) { return { sessions: [] as TFlexSession[], truncated: false }; } throw errorArg; }), ]); if (openCodeChats.length >= sessionIntelligenceSourceSessionLimit) { throw new Error('The OpenCode Session Intelligence source list is not exhaustive.'); } if (flexSnapshot.truncated) { throw new Error('The Flex Session Intelligence source list is truncated.'); } if (flexSnapshot.sessions.length > sessionIntelligenceSourceSessionLimit) { throw new Error('The Flex Session Intelligence source list exceeded its entry limit.'); } const hiddenIds = await this.findHiddenSessionIntelligenceChatIds( capability, openCodeChats.map((chat) => chat.chatId), ); this.assertSessionIntelligenceCapabilityActive( capabilityTokenArg, capability, peerIdArg, ); const candidates = [ ...openCodeChats .filter((chat) => !hiddenIds.has(chat.chatId)) .map((chat): IFlexIntelligenceChatSummary => ({ sessionId: { harnessId: 'opencode', nativeId: chat.chatId }, title: truncateToolUtf8(chat.title, 4096), updatedAt: chat.updatedAt, ...(chat.isActive === undefined ? {} : { isActive: chat.isActive }), })), ...flexSnapshot.sessions.map((session): IFlexIntelligenceChatSummary => ({ sessionId: { harnessId: 'flex', nativeId: session.sessionId }, title: truncateToolUtf8(session.title ?? '', 4096), updatedAt: session.updatedAt, ...(session.status === 'running' || session.status === 'scheduled' ? { isActive: true } : {}), })), ]; const managed: IFlexIntelligenceChatSummary[] = []; for (const candidate of candidates) { if (await this.sessionEventIsManaged(capability.projectId, candidate.sessionId)) { managed.push(candidate); } } managed.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt) || left.sessionId.harnessId.localeCompare(right.sessionId.harnessId) || left.sessionId.nativeId.localeCompare(right.sessionId.nativeId)); this.assertSessionIntelligenceCapabilityActive( capabilityTokenArg, capability, peerIdArg, ); return managed.slice(0, limitArg); } finally { release(); } } private async readSessionIntelligenceChat( capabilityTokenArg: string, sessionIdArg: IFlexIntelligenceSessionId, limitArg: number, maxCharsArg: number, peerIdArg: string, ): Promise<IFlexIntelligenceChatTranscript> { const { capability, release } = this.beginSessionIntelligenceCapabilityRequest( capabilityTokenArg, peerIdArg, ); const isSource = sessionIdArg.harnessId === capability.sourceSessionId.harnessId && sessionIdArg.nativeId === capability.sourceSessionId.nativeId; const claimsSourceRead = isSource && capability.sourceState === 'unread'; try { if (!await this.sessionEventIsManaged(capability.projectId, capability.sourceSessionId)) { throw new Error('The Session Intelligence source session is no longer managed.'); } if (!await this.sessionEventIsManaged(capability.projectId, sessionIdArg)) { throw new Error('The requested Session Intelligence chat is not managed.'); } if (!isSource && capability.sourceState !== 'read') { throw new Error('The exact source session must be read first.'); } if (isSource && capability.sourceState === 'reading') { throw new Error('The exact source session read is already in progress.'); } if (claimsSourceRead) capability.sourceState = 'reading'; let transcript: IFlexIntelligenceChatTranscript; if (sessionIdArg.harnessId === 'opencode') { const hiddenIds = await this.findHiddenSessionIntelligenceChatIds( capability, [sessionIdArg.nativeId], ); if (hiddenIds.has(sessionIdArg.nativeId)) { throw new Error('The requested session was not found.'); } const source = await capability.reader.readChat( sessionIdArg.nativeId, limitArg, maxCharsArg, ); transcript = this.boundSessionIntelligenceTranscript({ sessionId: { ...sessionIdArg }, title: truncateToolUtf8(source.title, 4096), messages: source.messages.slice(-limitArg).map((message) => ({ role: truncateToolUtf8(message.role || 'unknown', 128), text: this.boundSessionIntelligenceMessageText(message.text, maxCharsArg), ...(this.isCanonicalIsoDate(message.timestamp) ? { timestamp: message.timestamp } : {}), })), ...(source.totalMessages === undefined ? {} : { totalMessages: source.totalMessages }), }); } else { const [session, page] = await Promise.all([ this.requireFlexProjectionReader().getSession( capability.projectId, sessionIdArg.nativeId, capability.abortController.signal, ), this.requireFlexProjectionReader().listMessagePage( capability.projectId, sessionIdArg.nativeId, { limit: limitArg }, capability.abortController.signal, ), ]); transcript = this.boundSessionIntelligenceTranscript({ sessionId: { ...sessionIdArg }, title: truncateToolUtf8(session.title ?? '', 4096), messages: page.messages.map(({ message }) => ({ role: message.role, text: this.boundSessionIntelligenceMessageText(JSON.stringify({ status: message.status, parts: message.parts, ...(message.model === undefined ? {} : { model: message.model }), ...(message.usage === undefined ? {} : { usage: message.usage }), ...(message.error === undefined ? {} : { error: message.error }), }), maxCharsArg), timestamp: message.createdAt, })), }); } this.assertSessionIntelligenceCapabilityActive( capabilityTokenArg, capability, peerIdArg, ); if (isSource) capability.sourceState = 'read'; return transcript; } catch (errorArg) { if (claimsSourceRead && capability.sourceState === 'reading') { capability.sourceState = 'unread'; } throw errorArg; } finally { release(); } } private boundSessionIntelligenceMessageText(textArg: string, maxCharsArg: number): string { return truncateToolUtf8(textArg.slice(0, maxCharsArg), 16 * 1024); } private async findHiddenSessionIntelligenceChatIds( capabilityArg: ISessionIntelligenceCapability, candidateIdsArg: string[], ): Promise<Set<string>> { const hiddenIds = new Set<string>(); for ( let index = 0; index < candidateIdsArg.length; index += sessionIntelligenceHiddenLookupBatchSize ) { const batchHiddenIds = await this.requireAuthStore() .findProjectSessionIntelligenceTemporarySessionIds( capabilityArg.projectId, candidateIdsArg.slice(index, index + sessionIntelligenceHiddenLookupBatchSize), capabilityArg.abortController.signal, ); for (const sessionId of batchHiddenIds) hiddenIds.add(sessionId); } const candidateSet = new Set(candidateIdsArg); for (const sessionId of capabilityArg.openCodeClient .getRememberedSessionIntelligenceTemporarySessionIds()) { if (candidateSet.has(sessionId)) hiddenIds.add(sessionId); } return hiddenIds; } private boundSessionIntelligenceTranscript( transcriptArg: IFlexIntelligenceChatTranscript, ): IFlexIntelligenceChatTranscript { const transcript: IFlexIntelligenceChatTranscript = { ...transcriptArg, sessionId: { ...transcriptArg.sessionId }, messages: transcriptArg.messages.map((message) => ({ ...message })), }; while ( transcript.messages.length > 0 && Buffer.byteLength(JSON.stringify(transcript), 'utf8') > sessionIntelligenceTranscriptBytes ) transcript.messages.shift(); return transcript; } private isCanonicalIsoDate(valueArg: string | undefined): valueArg is string { if (!valueArg || !Number.isFinite(Date.parse(valueArg))) return false; return new Date(valueArg).toISOString() === valueArg; } private async waitForSessionIntelligenceResult( supervisorArg: FlexSupervisor, projectIdArg: string, flexJobIdArg: string, capabilityTokenArg: string, signalArg: AbortSignal, ): Promise<{ answer: string; scratchpad: string; model: string }> { while (true) { signalArg.throwIfAborted(); const status = await supervisorArg.request('intelligence.get', { scopeId: projectIdArg, jobId: flexJobIdArg, capabilityToken: capabilityTokenArg, }, signalArg); if (status.status === 'completed') return status.result!; if (status.status !== 'running') throw new Error('Session Intelligence failed.'); await this.waitForSessionIntelligencePoll(signalArg); } } private async cancelAndDrainSessionIntelligenceJob( supervisorArg: FlexSupervisor, projectIdArg: string, capabilityTokenArg: string, flexJobIdArg?: string, ): Promise<void> { const signal = AbortSignal.timeout(sessionIntelligenceCancellationDrainMs); let cancellation: IFlexRequestMap['intelligence.cancel']['response'] | undefined; try { cancellation = await supervisorArg.request('intelligence.cancel', { scopeId: projectIdArg, capabilityToken: capabilityTokenArg, }, signal); } catch { await supervisorArg.stop(); return; } if (cancellation?.cancelled) return; if (flexJobIdArg === undefined) return; while (!signal.aborted) { try { const status = await supervisorArg.request('intelligence.get', { scopeId: projectIdArg, jobId: flexJobIdArg, capabilityToken: capabilityTokenArg, }, signal); if (status.status !== 'running') return; } catch { if (!supervisorArg.getStatus().ready) { await supervisorArg.stop(); return; } } await this.waitForSessionIntelligencePoll(signal).catch(() => undefined); } await supervisorArg.stop(); } private waitForSessionIntelligencePoll(signalArg: AbortSignal): Promise<void> { return new Promise((resolve, reject) => { if (signalArg.aborted) { reject(signalArg.reason ?? new DOMException('The operation was aborted.', 'AbortError')); return; } const timer = setTimeout(() => { signalArg.removeEventListener('abort', onAbort); resolve(); }, sessionIntelligencePollMs); timer.unref?.(); const onAbort = () => { clearTimeout(timer); reject(signalArg.reason ?? new DOMException('The operation was aborted.', 'AbortError')); }; signalArg.addEventListener('abort', onAbort, { once: true }); }); } private requireControllerId(): string { if (!this.controllerId) throw new Error('Controller identity is unavailable.'); return this.controllerId; } private async recoverSessionIntelligence( controllerIdArg: string, projectsArg: IControllerProjectDocument[], signalArg: AbortSignal, cleanLegacyOpenCodeSessionsArg = true, ): Promise<void> { const projectById = new Map(projectsArg.map((project) => [project.id, project])); let exhaustive = false; for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) { signalArg.throwIfAborted(); const recovery = await this.requireAuthStore().recoverInterruptedSessionIntelligence(); if (cleanLegacyOpenCodeSessionsArg) { for (const temporary of recovery.temporarySessionIds) { const project = projectById.get(temporary.projectId); if (!project) continue; await this.requireOpenCodeClient().deleteSessionIntelligenceSession( project.directory, temporary.nativeId, signalArg, ).then(async () => { await this.requireAuthStore().clearSessionIntelligenceTemporarySession( temporary.projectId, temporary.sessionId, temporary.exchangeId, temporary.nativeId, ); }).catch((errorArg) => { console.error('Cleaning an interrupted Session Intelligence session failed.', errorArg); }); } } if (recovery.exhaustive) { exhaustive = true; break; } } if (!exhaustive) { throw new Error('Interrupted Session Intelligence recovery exceeded its bounded scan.'); } if (cleanLegacyOpenCodeSessionsArg) { await this.cleanupSessionIntelligenceOrphans( controllerIdArg, Number.MAX_SAFE_INTEGER, signalArg, projectsArg, ); } } private scheduleSessionIntelligenceCleanup(controllerIdArg: string): void { if ( this.sessionIntelligenceCleanupTask || this.openCodeAccountSwitchAdmissionSealed || this.openCodeRuntimeAdmissionSealed ) return; const cleanup = this.cleanupSessionIntelligenceOrphans( controllerIdArg, Date.now() - sessionIntelligenceOrphanGraceMs, this.operationAbortController.signal, ); let trackedCleanup: Promise<void>; trackedCleanup = this.trackOperationPromise(cleanup, () => { if (this.sessionIntelligenceCleanupTask === trackedCleanup) { this.sessionIntelligenceCleanupTask = undefined; } }); this.sessionIntelligenceCleanupTask = trackedCleanup; void trackedCleanup.catch(() => undefined); } private startSessionIntelligenceCleanupTimer(): void { if ( this.sessionIntelligenceCleanupTimer || !this.controllerId || !this.openCodeClient || this.openCodeAccountSwitchAdmissionSealed || this.openCodeRuntimeAdmissionSealed ) return; const controllerId = this.controllerId; this.sessionIntelligenceCleanupTimer = setInterval(() => { this.scheduleSessionIntelligenceCleanup(controllerId); }, sessionIntelligenceCleanupIntervalMs); this.sessionIntelligenceCleanupTimer.unref?.(); } private stopSessionIntelligenceCleanupTimer(): void { if (!this.sessionIntelligenceCleanupTimer) return; clearInterval(this.sessionIntelligenceCleanupTimer); this.sessionIntelligenceCleanupTimer = undefined; } private async drainOpenCodeOperationMonitors(): Promise<void> { const monitors = [...this.openCodeOperationMonitors.values()]; for (const monitor of monitors) monitor.abortController.abort(); await Promise.allSettled(monitors.map((monitor) => monitor.task)); } private async retireProjectSessionIntelligence( projectIdArg: string, signalArg: AbortSignal, ): Promise<void> { const projectPrefix = `${projectIdArg}\0`; const jobs = [...this.sessionIntelligenceJobs.entries()] .filter(([key]) => key.startsWith(projectPrefix)) .map(([, job]) => job); for (const job of jobs) { job.abortController.abort(new Error('The source project is being removed.')); } await this.drainSessionIntelligenceOperations(projectIdArg); while (true) { const mutations = [...this.sessionStateMutationTails.entries()] .filter(([key]) => key.startsWith(projectPrefix)) .map(([, mutation]) => mutation); if (mutations.length === 0) break; await Promise.allSettled(mutations); } } private async drainSessionIntelligenceOperations(projectIdArg?: string): Promise<void> { await this.waitForSessionIntelligenceOperations(projectIdArg); const retainedCapability = [...this.sessionIntelligenceCapabilities.values()].some( (capability) => projectIdArg === undefined || capability.projectId === projectIdArg, ); if (retainedCapability) { throw new Error('Session Intelligence worker cleanup is incomplete.'); } } private async waitForSessionIntelligenceOperations(projectIdArg?: string): Promise<void> { const projectPrefix = projectIdArg === undefined ? undefined : `${projectIdArg}\0`; const tasks = [...this.sessionIntelligenceJobs.entries()].flatMap(([key, job]) => (projectPrefix === undefined || key.startsWith(projectPrefix)) && job.task ? [job.task] : []); if (projectIdArg === undefined && this.sessionIntelligenceCleanupTask) { tasks.push(this.sessionIntelligenceCleanupTask); } await Promise.allSettled(tasks); } private async cleanupSessionIntelligenceOrphans( controllerIdArg: string, olderThanArg: number, signalArg: AbortSignal, projectsArg?: IControllerProjectDocument[], ): Promise<void> { const timeoutSignal = AbortSignal.timeout(sessionIntelligenceCleanupTimeoutMs); const signal = AbortSignal.any([signalArg, timeoutSignal]); const projects = projectsArg ?? await this.requireAuthStore().listProjects(); for (let index = 0; index < projects.length; index += 8) { await Promise.all(projects.slice(index, index + 8).map(async (project) => { try { let obligationsExhaustive = false; for (let pageIndex = 0; pageIndex < 16; pageIndex += 1) { const cleanupObligations = await this.requireAuthStore() .listProjectSessionIntelligenceCleanupObligations(project.id); this.requireOpenCodeClient().rememberSessionIntelligenceTemporarySessionIds( cleanupObligations.map((obligation) => obligation.nativeId), ); const cleanupErrors: unknown[] = []; for (const obligation of cleanupObligations) { const activeJob = this.sessionIntelligenceJobs.get( this.sessionStateMutationKey(obligation.projectId, obligation.sessionId), ); if (activeJob?.temporarySessionId === obligation.nativeId) continue; signal.throwIfAborted(); try { await this.requireOpenCodeClient().deleteSessionIntelligenceSession( project.directory, obligation.nativeId, signal, ); await this.requireAuthStore().clearSessionIntelligenceTemporarySession( obligation.projectId, obligation.sessionId, obligation.exchangeId, obligation.nativeId, ); } catch (errorArg) { cleanupErrors.push(errorArg); } } if (cleanupErrors.length > 0) { throw new AggregateError( cleanupErrors, `Cleaning ${cleanupErrors.length} Session Intelligence session(s) failed.`, ); } if (cleanupObligations.length < 500) { obligationsExhaustive = true; break; } } if (!obligationsExhaustive) { throw new Error('Session Intelligence cleanup obligations exceeded their bounded scan.'); } await this.requireOpenCodeClient().cleanupOrphanedSessionIntelligenceSessions( project.directory, controllerIdArg, olderThanArg, signal, ); } catch (errorArg) { if (!signal.aborted) { console.error(`Session Intelligence orphan cleanup failed for ${project.id}.`, errorArg); } } })); if (signal.aborted) return; } } private async getFlexSessionDetail( projectIdArg: string, sessionIdArg: string, signalArg?: AbortSignal, ): Promise<{ session: IControllerSession; messagePage: IControllerMessagePage; permissions: IControllerPermission[]; questions: IControllerQuestion[]; todos: IControllerTodo[]; model?: string; effort?: string; }> { const reader = this.requireFlexProjectionReader(); const supervisor = this.flexSupervisor; const sessionPromise = reader.getSession(projectIdArg, sessionIdArg, signalArg); const todosPromise: Promise<IControllerTodo[]> = sessionPromise.then(async (session) => { if ( session.sessionGenerationId === undefined || session.sessionGenerationSequence === undefined ) return []; signalArg?.throwIfAborted(); const tasks = await this.requireAuthStore().readFlexProjectTasks( projectIdArg, sessionIdArg, { sessionGenerationId: session.sessionGenerationId, sessionGenerationSequence: session.sessionGenerationSequence, }, ); signalArg?.throwIfAborted(); return tasks.map((task) => ({ id: flexRuntimeId(task.id), content: task.content, status: task.status, })); }); const [session, messagePage, permissionResult, todos] = await Promise.all([ sessionPromise, reader.listMessagePage(projectIdArg, sessionIdArg, { limit: controllerInitialMessageBundleLimit, }, signalArg), supervisor && this.flexIsOperational() ? supervisor.request('permission.list', { scopeId: projectIdArg, }, signalArg).catch(() => ({ permissions: [] as TFlexPermissionRequest[] })) : Promise.resolve({ permissions: [] as TFlexPermissionRequest[] }), todosPromise, ]); const bundles = messagePage.messages.map((message) => this.toFlexMessageBundle(message)); const messages = bundles.flatMap((bundle) => bundle.messages); let model: string | undefined; let effort: string | undefined; for (let index = messages.length - 1; index >= 0; index -= 1) { const message = messages[index]; if (!message.model) continue; model = message.model; effort = message.effort; break; } const permissions: IControllerPermission[] = []; for (const permission of permissionResult.permissions) { const authority = this.flexPermissionAuthority(projectIdArg, permission); if ( !authority || ( authority.type === 'managed' && authority.run.sessionId !== sessionIdArg ) || ( authority.type === 'delegated' && authority.lease.parentAuthority.sessionId !== sessionIdArg ) ) continue; permissions.push(this.toFlexPermission(permission)); } return { session: this.toFlexSession(session), messagePage: boundControllerMessagePage({ bundles, ...(messagePage.nextCursor === undefined ? {} : { nextCursor: messagePage.nextCursor }), ...(messagePage.truncated ? { truncated: true } : {}), }), permissions, questions: [], todos, ...(model === undefined ? {} : { model }), ...(effort === undefined ? {} : { effort }), }; } private toPublicProviderConnection( connectionArg: IFlexPublicProviderConnection, ): IControllerProviderConnection { if (connectionArg.status !== 'active' && connectionArg.status !== 'reauthRequired') { throw new Error('The Flex provider connection is not publicly listable.'); } return { id: connectionArg.loginId, providerID: connectionArg.providerId, status: connectionArg.status, account: this.toPublicProviderAccount(connectionArg), ...(connectionArg.loginId === this.selectedOpenCodeProviderConnectionId ? { selectedForOpenCode: true } : {}), }; } private toPublicProviderAccount( connectionArg: IFlexPublicProviderConnection, ): IControllerProviderConnection['account'] { return { ...(connectionArg.account?.accountId === undefined ? {} : { accountId: connectionArg.account.accountId }), ...(connectionArg.account?.email === undefined ? {} : { email: connectionArg.account.email }), ...(connectionArg.account?.plan === undefined ? {} : { plan: connectionArg.account.plan }), }; } private toPublicProviderLogin( retainedArg: IControllerProviderLogin, connectionArg: IFlexPublicProviderConnection, ): IControllerProviderLogin { const status = connectionArg.status === 'pending' ? 'pending' : connectionArg.status === 'active' ? 'succeeded' : connectionArg.status === 'deleting' ? 'cancelled' : 'failed'; const account = this.toPublicProviderAccount(connectionArg); return { ...retainedArg, status, ...(Object.keys(account).length === 0 ? {} : { account }), }; } private toFlexModelOptions( providerConnectionIdArg: string, modelsArg: IFlexPublicProviderModel[], ): IFlexConnectionModelOption[] { return modelsArg.filter((model) => !model.hidden).map((model) => ({ harnessId: 'flex', providerConnectionId: providerConnectionIdArg, providerID: model.providerId, providerName: this.flexProviderNames.get(model.providerId) ?? model.providerId, modelID: model.modelId, modelName: model.displayName, variants: [...new Set([ ...model.reasoningEfforts.map((effort) => effort.effort), ...model.serviceTiers.map((tier) => tier.id), ])], isDefault: model.isDefault, })); } private aggregateFlexModelOptions(): IControllerFlexModelOption[] { const aggregated = new Map<string, IControllerFlexModelOption>(); const catalogs = [...this.flexModelOptionsByConnection.entries()] .sort(([leftConnectionId], [rightConnectionId]) => ( leftConnectionId.localeCompare(rightConnectionId) )); for (const [providerConnectionId, models] of catalogs) { for (const model of models) { const key = JSON.stringify([model.providerID, model.modelID]); const existing = aggregated.get(key); if (!existing) { aggregated.set(key, { harnessId: 'flex', providerID: model.providerID, providerName: model.providerName, modelID: model.modelID, modelName: model.modelName, variants: [...model.variants], availability: [{ providerConnectionId, variants: [...model.variants], isDefault: model.isDefault, }], }); continue; } for (const variant of model.variants) { if (!existing.variants.includes(variant)) existing.variants.push(variant); } existing.availability.push({ providerConnectionId, variants: [...model.variants], isDefault: model.isDefault, }); } } return [...aggregated.values()].sort((left, right) => ( left.providerName.localeCompare(right.providerName) || left.modelName.localeCompare(right.modelName) || left.providerID.localeCompare(right.providerID) || left.modelID.localeCompare(right.modelID) )); } private consumePendingFlexFinishedPrompt( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, queueIdArg: string, ): void { const key = flexPromptQueueKey(projectIdArg, sessionIdArg, generationArg, queueIdArg); const event = this.pendingFlexFinishedPrompts.get(key); if (!event || !this.retireFlexRun( projectIdArg, sessionIdArg, generationArg, queueIdArg, event.runId, )) return; this.pendingFlexFinishedPrompts.delete(key); if (!this.harnessOperationManager.ownsFlexRun( projectIdArg, flexRuntimeId(sessionIdArg), queueIdArg, event.runId, )) return; void this.harnessOperationManager.completeFlex( projectIdArg, flexRuntimeId(sessionIdArg), queueIdArg, event.runId, ).catch((errorArg) => { console.error('Flex prompt operation cleanup failed.', errorArg); }); } private registerFlexRun(authorityArg: IActiveFlexRunAuthority): void { if (this.flexRunAuthorityFenced) { throw new Error('Flex run authority is fenced until the child restarts.'); } const key = flexRunKey( authorityArg.projectId, authorityArg.sessionId, authorityArg, authorityArg.runId, ); const existing = this.activeFlexRuns.get(key); if (existing !== undefined) { if ( existing.queueId !== authorityArg.queueId || existing.supervisor !== authorityArg.supervisor || existing.runtimeAuthority !== authorityArg.runtimeAuthority || existing.peerId !== authorityArg.peerId || existing.managedSessionIdentityId !== authorityArg.managedSessionIdentityId || existing.providerSessionGeneration !== authorityArg.providerSessionGeneration || existing.sessionDepth !== authorityArg.sessionDepth || !plugins.util.isDeepStrictEqual(existing.model, authorityArg.model) ) throw new Error('The Flex run is already bound to conflicting authority.'); return; } if ( this.activeFlexRuns.size + this.pendingFlexRunCleanups.size >= maxActiveFlexRuns ) throw new Error('The active Flex run limit was reached.'); this.activeFlexRuns.set(key, Object.freeze({ ...authorityArg, model: Object.freeze({ ...authorityArg.model }), })); } private unregisterFlexRun( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, queueIdArg: string, runIdArg: string, ): boolean { const key = flexRunKey(projectIdArg, sessionIdArg, generationArg, runIdArg); const authority = this.activeFlexRuns.get(key); if (authority?.queueId !== queueIdArg) return false; this.activeFlexRuns.delete(key); this.fenceFlexDelegatedAuthorityForParent(authority); return true; } private activeFlexRunAuthority( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, runIdArg: string, ): IActiveFlexRunAuthority | undefined { const authority = this.activeFlexRuns.get(flexRunKey( projectIdArg, sessionIdArg, generationArg, runIdArg, )); if ( authority === undefined || this.flexRunAuthorityFenced || this.flexSupervisor !== authority.supervisor || this.flexSupervisorExitSignals.get(authority.supervisor) !== authority.runtimeAuthority || authority.runtimeAuthority.signal.aborted || !authority.supervisor.ownsHostPeer(authority.peerId) || this.retiringProjectIds.has(authority.projectId) || this.flexProjectIdentityRecoveryPending.has(authority.projectId) || this.retiringSessionKeys.has(this.sessionStateMutationKey( authority.projectId, flexRuntimeId(authority.sessionId), )) ) return undefined; return authority; } private activeFlexRunAuthorityForSessionGeneration( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, ): IActiveFlexRunAuthority | undefined { let match: IActiveFlexRunAuthority | undefined; for (const authority of this.activeFlexRuns.values()) { if ( authority.projectId !== projectIdArg || authority.sessionId !== sessionIdArg || authority.sessionGenerationId !== generationArg.sessionGenerationId || authority.sessionGenerationSequence !== generationArg.sessionGenerationSequence || this.activeFlexRunAuthority( authority.projectId, authority.sessionId, authority, authority.runId, ) !== authority ) continue; if (match) return undefined; match = authority; } return match; } private assertActiveFlexRunAuthority(authorityArg: IActiveFlexRunAuthority): void { if (this.activeFlexRunAuthority( authorityArg.projectId, authorityArg.sessionId, authorityArg, authorityArg.runId, ) !== authorityArg) throw new FlexServiceError('STALE_RUN'); } private flexDelegationPermissionKey( projectIdArg: string, permissionArg: TFlexPermissionRequest, ): string { return JSON.stringify([ projectIdArg, permissionArg.permissionId, permissionArg.sessionId, permissionArg.sessionGenerationId, permissionArg.sessionGenerationSequence, permissionArg.runId, ]); } private flexDelegationContextKey( contextArg: IFlexDelegationTicketContext, ): string { return JSON.stringify([ contextArg.scopeId, contextArg.storageKey, contextArg.sessionId, contextArg.parentSessionId, contextArg.parentSessionGenerationId, contextArg.parentSessionGenerationSequence, contextArg.parentQueueId, contextArg.parentRunId, contextArg.parentToolCallId, contextArg.originParentRunId, contextArg.originParentToolCallId, contextArg.agent, contextArg.depth, ]); } private flexDelegatedRunKey( contextArg: TFlexHostRequest<'delegated-run-admission.acquire'>, ): string { return flexRunKey(contextArg.scopeId, contextArg.sessionId, contextArg, contextArg.runId); } private flexDelegatedSessionKey( contextArg: Pick< TFlexHostRequest<'delegated-run-admission.acquire'>, 'scopeId' | 'sessionId' | 'sessionGenerationId' | 'sessionGenerationSequence' >, ): string { return JSON.stringify([ contextArg.scopeId, contextArg.sessionId, contextArg.sessionGenerationId, contextArg.sessionGenerationSequence, ]); } private requireFlexDelegatedRunLeaseIssuer( supervisorArg: FlexSupervisor, runtimeAuthorityArg: IControllerRuntimeExitSignal, ): IFlexDelegatedRunLeaseIssuer { const current = this.flexDelegatedRunLeaseIssuer; if (current) { if ( current.supervisor !== supervisorArg || current.runtimeAuthority !== runtimeAuthorityArg ) throw new FlexServiceError('STALE_RUN'); return current; } const created: IFlexDelegatedRunLeaseIssuer = { supervisor: supervisorArg, runtimeAuthority: runtimeAuthorityArg, secret: plugins.crypto.randomBytes(32), }; this.flexDelegatedRunLeaseIssuer = created; return created; } private flexDelegatedRunLeaseTag( issuerArg: IFlexDelegatedRunLeaseIssuer, contextArg: TFlexHostRequest<'delegated-run-admission.acquire'>, peerIdArg: string, nonceArg: Buffer, ): Buffer { const macInput = JSON.stringify([ 'agl:flex-delegated-run-lease:v1', peerIdArg, ...flexDelegatedRunAdmissionContextKeys.map((key) => contextArg[key]), nonceArg.toString('base64url'), ]); return plugins.crypto.createHmac('sha256', issuerArg.secret) .update(macInput, 'utf8') .digest() .subarray(0, 16); } private issueFlexDelegatedRunLeaseId( issuerArg: IFlexDelegatedRunLeaseIssuer, contextArg: TFlexHostRequest<'delegated-run-admission.acquire'>, peerIdArg: string, ): string { for (;;) { const nonce = plugins.crypto.randomBytes(16); const leaseId = Buffer.concat([ nonce, this.flexDelegatedRunLeaseTag(issuerArg, contextArg, peerIdArg, nonce), ]).toString('base64url'); if (!this.flexDelegatedRunLeases.has(leaseId)) return leaseId; } } private ownsFlexDelegatedRunLeaseReceipt( payloadArg: TFlexHostRequest<'delegated-run-admission.close'>, peerIdArg: string, ): boolean { if (!isFlexDelegatedRunAdmissionLeaseId(payloadArg.leaseId)) return false; const supervisor = this.flexSupervisor; const runtimeAuthority = supervisor ? this.flexSupervisorExitSignals.get(supervisor) : undefined; const issuer = this.flexDelegatedRunLeaseIssuer; if ( !supervisor || !runtimeAuthority || runtimeAuthority.signal.aborted || !issuer || issuer.supervisor !== supervisor || issuer.runtimeAuthority !== runtimeAuthority || !supervisor.ownsDelegatedRunClosePeer(peerIdArg) ) return false; const token = Buffer.from(payloadArg.leaseId, 'base64url'); const nonce = token.subarray(0, 16); const suppliedTag = token.subarray(16); const { leaseId: _leaseId, ...context } = payloadArg; const expectedTag = this.flexDelegatedRunLeaseTag(issuer, context, peerIdArg, nonce); return plugins.crypto.timingSafeEqual(suppliedTag, expectedTag); } private createFlexDelegationTicket( permissionArg: TFlexPermissionRequest, contextArg: IFlexDelegationTicketContext, parentAuthorityArg: IActiveFlexRunAuthority, ): IFlexDelegationTicket { this.assertActiveFlexRunAuthority(parentAuthorityArg); if ( contextArg.scopeId !== parentAuthorityArg.projectId || contextArg.storageKey !== parentAuthorityArg.projectId || contextArg.parentSessionId !== parentAuthorityArg.sessionId || contextArg.parentSessionGenerationId !== parentAuthorityArg.sessionGenerationId || contextArg.parentSessionGenerationSequence !== parentAuthorityArg.sessionGenerationSequence || contextArg.parentQueueId !== parentAuthorityArg.queueId || contextArg.parentRunId !== parentAuthorityArg.runId || permissionArg.sessionId !== parentAuthorityArg.sessionId || permissionArg.sessionGenerationId !== parentAuthorityArg.sessionGenerationId || permissionArg.sessionGenerationSequence !== parentAuthorityArg.sessionGenerationSequence || permissionArg.runId !== parentAuthorityArg.runId || permissionArg.toolCallId !== contextArg.parentToolCallId ) throw new FlexServiceError('STALE_RUN'); if ( this.flexDelegationTicketsByPermission.size + this.flexDelegatedRunLeases.size >= maxFlexDelegatedAuthorities ) throw new FlexServiceError('LIMIT_EXCEEDED'); const permissionKey = this.flexDelegationPermissionKey( parentAuthorityArg.projectId, permissionArg, ); const contextKey = this.flexDelegationContextKey(contextArg); if ( this.flexDelegationTicketsByPermission.has(permissionKey) || this.flexDelegationTicketsByContext.has(contextKey) ) throw new FlexServiceError('CONFLICT'); let resolveActivation!: (activeArg: boolean) => void; const activation = new Promise<boolean>((resolve) => { resolveActivation = resolve; }); let ticket!: IFlexDelegationTicket; const timeout = setTimeout(() => this.revokeFlexDelegationTicket(ticket), flexDelegationTicketTtlMs); timeout.unref?.(); ticket = { permissionId: permissionArg.permissionId, permissionKey, contextKey, context: Object.freeze({ ...contextArg }), parentAuthority: parentAuthorityArg, activation, resolveActivation, abortController: new AbortController(), timeout, activationState: 'provisional', consuming: false, }; this.flexDelegationTicketsByPermission.set(permissionKey, ticket); this.flexDelegationTicketsByContext.set(contextKey, ticket); return ticket; } private activateFlexDelegationTicket(ticketArg: IFlexDelegationTicket): void { if ( ticketArg.activationState !== 'provisional' || this.flexDelegationTicketsByPermission.get(ticketArg.permissionKey) !== ticketArg || this.flexDelegationTicketsByContext.get(ticketArg.contextKey) !== ticketArg ) throw new FlexServiceError('STALE_RUN'); this.assertActiveFlexRunAuthority(ticketArg.parentAuthority); ticketArg.activationState = 'active'; ticketArg.resolveActivation(true); } private revokeFlexDelegationTicket(ticketArg: IFlexDelegationTicket): void { if (this.flexDelegationTicketsByPermission.get(ticketArg.permissionKey) === ticketArg) { this.flexDelegationTicketsByPermission.delete(ticketArg.permissionKey); } if (this.flexDelegationTicketsByContext.get(ticketArg.contextKey) === ticketArg) { this.flexDelegationTicketsByContext.delete(ticketArg.contextKey); } clearTimeout(ticketArg.timeout); if (ticketArg.activationState !== 'revoked') { ticketArg.activationState = 'revoked'; ticketArg.abortController.abort(new Error('The Flex delegation ticket closed.')); ticketArg.resolveActivation(false); } } private consumeFlexDelegationTicket(ticketArg: IFlexDelegationTicket): void { if (this.flexDelegationTicketsByPermission.get(ticketArg.permissionKey) === ticketArg) { this.flexDelegationTicketsByPermission.delete(ticketArg.permissionKey); } if (this.flexDelegationTicketsByContext.get(ticketArg.contextKey) === ticketArg) { this.flexDelegationTicketsByContext.delete(ticketArg.contextKey); } clearTimeout(ticketArg.timeout); ticketArg.abortController.abort(new Error('The Flex delegation ticket was consumed.')); } private markFlexDelegatedRunLeaseClosed(leaseArg: IFlexDelegatedRunLease): void { if (this.flexDelegatedRunLeases.get(leaseArg.leaseId) !== leaseArg) return; const runKey = this.flexDelegatedRunKey(leaseArg.context); if (this.flexDelegatedRunLeasesByRun.get(runKey) === leaseArg) { this.flexDelegatedRunLeasesByRun.delete(runKey); } const sessionKey = this.flexDelegatedSessionKey(leaseArg.context); if (this.flexDelegatedRunLeasesBySession.get(sessionKey) === leaseArg) { this.flexDelegatedRunLeasesBySession.delete(sessionKey); } leaseArg.state = 'closed'; this.flexDelegatedRunLeases.delete(leaseArg.leaseId); } private settleFlexDelegatedRunLease(leaseArg: IFlexDelegatedRunLease): void { if ( leaseArg.state === 'closed' || !leaseArg.closeObserved || !leaseArg.terminalObserved || !leaseArg.cleanupCompleted ) return; if (leaseArg.cleanupPromise) { void leaseArg.cleanupPromise.then( () => this.markFlexDelegatedRunLeaseClosed(leaseArg), () => undefined, ); return; } this.markFlexDelegatedRunLeaseClosed(leaseArg); } private cleanupFlexDelegatedRunLease(leaseArg: IFlexDelegatedRunLease): Promise<void> { if (leaseArg.cleanupCompleted) return Promise.resolve(); leaseArg.cleanupPromise ??= (this.browserResourceHost ? this.browserResourceHost.closeFlexChannelsForRun( leaseArg.context.scopeId, leaseArg.context.sessionId, leaseArg.context.runId, ) : Promise.resolve()).then(() => { leaseArg.cleanupCompleted = true; }).finally(() => { leaseArg.cleanupPromise = undefined; }); return leaseArg.cleanupPromise; } private clearFlexDelegatedAuthority(): void { const issuer = this.flexDelegatedRunLeaseIssuer; this.flexDelegatedRunLeaseIssuer = undefined; issuer?.secret.fill(0); for (const ticket of [...this.flexDelegationTicketsByPermission.values()]) { this.revokeFlexDelegationTicket(ticket); } let requiresGlobalCleanup = false; for (const lease of this.flexDelegatedRunLeases.values()) { if (lease.state === 'closed') continue; requiresGlobalCleanup = true; lease.state = 'terminal-pending'; lease.abortController.abort(new Error('The Flex runtime authority closed.')); } if (requiresGlobalCleanup) { this.queueFlexGlobalCleanup('Flex delegated browser cleanup after runtime loss failed.'); } this.flexDelegatedRunLeases.clear(); this.flexDelegatedRunLeasesByRun.clear(); this.flexDelegatedRunLeasesBySession.clear(); } private fenceFlexDelegatedAuthority(messageArg: string): void { for (const ticket of [...this.flexDelegationTicketsByPermission.values()]) { this.revokeFlexDelegationTicket(ticket); } for (const lease of this.flexDelegatedRunLeases.values()) { if (lease.state === 'closed') continue; lease.state = 'terminal-pending'; lease.abortController.abort(new Error(messageArg)); void this.cleanupFlexDelegatedRunLease(lease).catch(() => undefined); } } private fenceFlexDelegatedAuthorityForParent(authorityArg: IActiveFlexRunAuthority): void { for (const ticket of [...this.flexDelegationTicketsByPermission.values()]) { if (ticket.parentAuthority === authorityArg) this.revokeFlexDelegationTicket(ticket); } for (const lease of [...this.flexDelegatedRunLeases.values()]) { if (lease.parentAuthority !== authorityArg || lease.state === 'closed') continue; lease.state = 'terminal-pending'; lease.abortController.abort(new Error('The parent Flex run authority closed.')); void this.cleanupFlexDelegatedRunLease(lease).catch(() => undefined); } } private fenceFlexDelegatedAuthorityForParentScope( projectIdArg: string, sessionIdArg?: string, ): void { const authorities = new Set<IActiveFlexRunAuthority>(); for (const ticket of this.flexDelegationTicketsByPermission.values()) { if ( ticket.parentAuthority.projectId === projectIdArg && ( sessionIdArg === undefined || ticket.parentAuthority.sessionId === sessionIdArg ) ) authorities.add(ticket.parentAuthority); } for (const lease of this.flexDelegatedRunLeases.values()) { if ( lease.parentAuthority.projectId === projectIdArg && ( sessionIdArg === undefined || lease.parentAuthority.sessionId === sessionIdArg ) ) authorities.add(lease.parentAuthority); } for (const authority of authorities) this.fenceFlexDelegatedAuthorityForParent(authority); } private async acquireFlexDelegatedRunAdmission( payloadArg: TFlexHostRequest<'delegated-run-admission.acquire'>, peerIdArg: string, signalArg: AbortSignal, ): Promise<TFlexHostResponse<'delegated-run-admission.acquire'>> { const contextKey = this.flexDelegationContextKey(payloadArg); const ticket = this.flexDelegationTicketsByContext.get(contextKey); if (!ticket || ticket.consuming) throw new FlexServiceError('STALE_RUN'); ticket.consuming = true; try { const activated = await awaitWithAbortSignal(ticket.activation, signalArg); if (!activated || ticket.activationState !== 'active') throw new FlexServiceError('STALE_RUN'); signalArg.throwIfAborted(); this.assertActiveFlexRunAuthority(ticket.parentAuthority); if ( ticket.parentAuthority.supervisor !== this.flexSupervisor || ticket.parentAuthority.peerId !== peerIdArg || !ticket.parentAuthority.supervisor.ownsHostPeer(peerIdArg) ) throw new FlexServiceError('STALE_RUN'); if (this.flexDelegatedRunLeases.size >= maxFlexDelegatedAuthorities) { throw new FlexServiceError('LIMIT_EXCEEDED'); } const runKey = this.flexDelegatedRunKey(payloadArg); const sessionKey = this.flexDelegatedSessionKey(payloadArg); if ( this.flexDelegatedRunLeasesByRun.has(runKey) || this.flexDelegatedRunLeasesBySession.has(sessionKey) ) throw new FlexServiceError('CONFLICT'); const issuer = this.requireFlexDelegatedRunLeaseIssuer( ticket.parentAuthority.supervisor, ticket.parentAuthority.runtimeAuthority, ); const leaseId = this.issueFlexDelegatedRunLeaseId(issuer, payloadArg, peerIdArg); const lease: IFlexDelegatedRunLease = { leaseId, context: Object.freeze({ ...payloadArg }), parentAuthority: ticket.parentAuthority, permissionId: ticket.permissionId, model: Object.freeze({ ...ticket.parentAuthority.model }), supervisor: ticket.parentAuthority.supervisor, runtimeAuthority: ticket.parentAuthority.runtimeAuthority, peerId: peerIdArg, abortController: new AbortController(), state: 'active', closeObserved: false, terminalObserved: false, cleanupCompleted: false, }; this.flexDelegatedRunLeases.set(leaseId, lease); this.flexDelegatedRunLeasesByRun.set(runKey, lease); this.flexDelegatedRunLeasesBySession.set(sessionKey, lease); this.consumeFlexDelegationTicket(ticket); return { leaseId, model: { ...lease.model } }; } catch (errorArg) { this.revokeFlexDelegationTicket(ticket); throw errorArg; } } private async closeFlexDelegatedRunAdmission( payloadArg: TFlexHostRequest<'delegated-run-admission.close'>, peerIdArg: string, retireUnacknowledgedAcquireArg = false, ): Promise<TFlexHostResponse<'delegated-run-admission.close'>> { const lease = this.flexDelegatedRunLeases.get(payloadArg.leaseId); const { leaseId: _leaseId, ...context } = payloadArg; if (!lease) { if (this.ownsFlexDelegatedRunLeaseReceipt(payloadArg, peerIdArg)) { return { closed: true }; } throw new FlexServiceError('STALE_RUN'); } if ( lease.peerId !== peerIdArg || lease.supervisor !== this.flexSupervisor || this.flexSupervisorExitSignals.get(lease.supervisor) !== lease.runtimeAuthority || !lease.supervisor.ownsDelegatedRunClosePeer(peerIdArg) || !plugins.util.isDeepStrictEqual(lease.context, context) ) throw new FlexServiceError('STALE_RUN'); if (lease.state === 'closed') return { closed: true }; lease.closeObserved = true; lease.state = 'terminal-pending'; lease.abortController.abort(new Error('The delegated Flex run admission closed.')); await this.cleanupFlexDelegatedRunLease(lease); if (retireUnacknowledgedAcquireArg) this.markFlexDelegatedRunLeaseClosed(lease); else this.settleFlexDelegatedRunLease(lease); return { closed: true }; } private activeFlexDelegatedRunLease( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, runIdArg: string, ): IFlexDelegatedRunLease | undefined { const lease = this.flexDelegatedRunLeasesByRun.get(flexRunKey( projectIdArg, sessionIdArg, generationArg, runIdArg, )); if ( !lease || lease.state !== 'active' || lease.closeObserved || lease.terminalObserved || lease.supervisor !== this.flexSupervisor || this.flexSupervisorExitSignals.get(lease.supervisor) !== lease.runtimeAuthority || lease.runtimeAuthority.signal.aborted || !lease.supervisor.ownsHostPeer(lease.peerId) || this.activeFlexRunAuthority( lease.parentAuthority.projectId, lease.parentAuthority.sessionId, lease.parentAuthority, lease.parentAuthority.runId, ) !== lease.parentAuthority ) return undefined; return lease; } private flexPermissionAuthority( projectIdArg: string, permissionArg: TFlexPermissionRequest, ): TFlexPermissionAuthority | undefined { const run = this.activeFlexRunAuthority( projectIdArg, permissionArg.sessionId, permissionArg, permissionArg.runId, ); if (run) return { type: 'managed', run }; const lease = this.activeFlexDelegatedRunLease( projectIdArg, permissionArg.sessionId, permissionArg, permissionArg.runId, ); return lease ? { type: 'delegated', lease } : undefined; } private flexPermissionAuthoritiesEqual( leftArg: TFlexPermissionAuthority, rightArg: TFlexPermissionAuthority, ): boolean { return leftArg.type === 'managed' ? rightArg.type === 'managed' && leftArg.run === rightArg.run : rightArg.type === 'delegated' && leftArg.lease === rightArg.lease; } private flexPermissionAuthorityIsCurrent(authorityArg: TFlexPermissionAuthority): boolean { if (authorityArg.type === 'managed') { return this.activeFlexRunAuthority( authorityArg.run.projectId, authorityArg.run.sessionId, authorityArg.run, authorityArg.run.runId, ) === authorityArg.run; } return this.activeFlexDelegatedRunLease( authorityArg.lease.context.scopeId, authorityArg.lease.context.sessionId, authorityArg.lease.context, authorityArg.lease.context.runId, ) === authorityArg.lease; } private assertFlexPermissionAuthority( projectIdArg: string, permissionArg: TFlexPermissionRequest, authorityArg: TFlexPermissionAuthority, ): void { const current = this.flexPermissionAuthority(projectIdArg, permissionArg); if (!current || !this.flexPermissionAuthoritiesEqual(current, authorityArg)) { throw new FlexServiceError('STALE_RUN'); } } private flexSubagentPermissionMetadata(permissionArg: TFlexPermissionRequest): { agent: string; childSessionId: string; taskId?: string; } { const metadata = permissionArg.metadata; if ( permissionArg.kind !== 'subagent.start' || typeof permissionArg.toolCallId !== 'string' || permissionArg.toolCallId.length === 0 || !metadata || typeof metadata !== 'object' || Array.isArray(metadata) ) throw new FlexServiceError('INVALID_REQUEST'); const value = metadata as Record<string, unknown>; if ( typeof value.agent !== 'string' || value.agent.length === 0 || typeof value.childSessionId !== 'string' || value.childSessionId.length === 0 || (value.taskId !== undefined && typeof value.taskId !== 'string') || (value.taskId !== undefined && value.taskId !== value.childSessionId) ) throw new FlexServiceError('INVALID_REQUEST'); return { agent: value.agent, childSessionId: value.childSessionId, ...(value.taskId === undefined ? {} : { taskId: value.taskId }), }; } private async prepareFlexDelegationTicket( projectIdArg: string, permissionArg: TFlexPermissionRequest, parentAuthorityArg: IActiveFlexRunAuthority, signalArg: AbortSignal, ): Promise<IFlexDelegationTicket> { const metadata = this.flexSubagentPermissionMetadata(permissionArg); const expectedDepth = parentAuthorityArg.sessionDepth + 1; let originParentRunId = parentAuthorityArg.runId; let originParentToolCallId = permissionArg.toolCallId!; if (metadata.taskId !== undefined) { try { const child = await parentAuthorityArg.supervisor.request('session.get', { scopeId: projectIdArg, sessionId: metadata.childSessionId, }, signalArg); if ( child.parentSessionId === parentAuthorityArg.sessionId && child.agent === metadata.agent && child.depth === expectedDepth && typeof child.parentRunId === 'string' && typeof child.parentToolCallId === 'string' ) { originParentRunId = child.parentRunId; originParentToolCallId = child.parentToolCallId; } } catch (errorArg) { signalArg.throwIfAborted(); if (!(errorArg instanceof FlexServiceError && errorArg.code === 'NOT_FOUND')) throw errorArg; } } signalArg.throwIfAborted(); return this.createFlexDelegationTicket( permissionArg, { scopeId: projectIdArg, storageKey: projectIdArg, sessionId: metadata.childSessionId, parentSessionId: parentAuthorityArg.sessionId, parentSessionGenerationId: parentAuthorityArg.sessionGenerationId, parentSessionGenerationSequence: parentAuthorityArg.sessionGenerationSequence, parentQueueId: parentAuthorityArg.queueId, parentRunId: parentAuthorityArg.runId, parentToolCallId: permissionArg.toolCallId!, originParentRunId, originParentToolCallId, agent: metadata.agent, depth: expectedDepth, }, parentAuthorityArg, ); } private async respondToFlexPermission( projectIdArg: string, permissionArg: TFlexPermissionRequest, decisionArg: plugins.flexharness.TFlexPermissionDecision, authorityArg: TFlexPermissionAuthority, signalArg: AbortSignal, ): Promise<void> { if ( authorityArg.type === 'delegated' && decisionArg !== 'once' && decisionArg !== 'reject' ) throw new FlexServiceError('INVALID_REQUEST'); if (permissionArg.kind === 'subagent.start' && decisionArg === 'always') { throw new FlexServiceError('INVALID_REQUEST'); } const ticket = permissionArg.kind === 'subagent.start' && decisionArg === 'once' ? authorityArg.type === 'managed' ? await this.prepareFlexDelegationTicket( projectIdArg, permissionArg, authorityArg.run, signalArg, ) : undefined : undefined; if (permissionArg.kind === 'subagent.start' && decisionArg === 'once' && !ticket) { throw new FlexServiceError('INVALID_REQUEST'); } signalArg.throwIfAborted(); try { this.assertFlexPermissionAuthority(projectIdArg, permissionArg, authorityArg); const response = authorityArg.type === 'managed' ? authorityArg.run.supervisor.request('permission.respond', { scopeId: projectIdArg, sessionId: permissionArg.sessionId, sessionGenerationId: permissionArg.sessionGenerationId, sessionGenerationSequence: permissionArg.sessionGenerationSequence, permissionId: permissionArg.permissionId, decision: decisionArg, }, signalArg) : authorityArg.lease.supervisor.request('permission.respond', { scopeId: projectIdArg, sessionId: permissionArg.sessionId, sessionGenerationId: permissionArg.sessionGenerationId, sessionGenerationSequence: permissionArg.sessionGenerationSequence, permissionId: permissionArg.permissionId, decision: decisionArg, }, signalArg); await response; if (ticket) this.activateFlexDelegationTicket(ticket); } catch (errorArg) { if (ticket) this.revokeFlexDelegationTicket(ticket); throw errorArg; } } private ownsFlexRun( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, runIdArg: string, ): boolean { return this.activeFlexRunAuthority( projectIdArg, sessionIdArg, generationArg, runIdArg, ) !== undefined; } private ownsManagedOrDelegatedFlexRun( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, runIdArg: string, ): boolean { return this.ownsFlexRun(projectIdArg, sessionIdArg, generationArg, runIdArg) || this.activeFlexDelegatedRunLease( projectIdArg, sessionIdArg, generationArg, runIdArg, ) !== undefined; } private retireFlexRun( projectIdArg: string, sessionIdArg: string, generationArg: TFlexSessionGeneration, queueIdArg: string, runIdArg: string, ): boolean { if (!this.unregisterFlexRun( projectIdArg, sessionIdArg, generationArg, queueIdArg, runIdArg, )) return false; const key = flexRunKey(projectIdArg, sessionIdArg, generationArg, runIdArg); if (!this.pendingFlexRunCleanups.has(key)) { if (this.pendingFlexRunCleanups.size >= maxActiveFlexRuns) { this.fenceFlexRunAuthority('The Flex browser cleanup limit was reached.'); return true; } this.pendingFlexRunCleanups.set(key, { projectId: projectIdArg, sessionId: sessionIdArg, sessionGenerationId: generationArg.sessionGenerationId, sessionGenerationSequence: generationArg.sessionGenerationSequence, runId: runIdArg, }); } void this.retryFlexRunCleanup(key).catch((errorArg) => { console.error('Flex browser run cleanup failed; it remains pending.', errorArg); this.scheduleFlexRunCleanupRetry(); }); return true; } private async retryFlexRunCleanup(keyArg: string): Promise<void> { const pending = this.pendingFlexRunCleanups.get(keyArg); if (!pending) return; pending.cleanupPromise ??= (async () => { const browserResourceHost = this.browserResourceHost; if (browserResourceHost) { await browserResourceHost.closeFlexChannelsForRun( pending.projectId, pending.sessionId, pending.runId, ); } if (this.pendingFlexRunCleanups.get(keyArg) === pending) { this.pendingFlexRunCleanups.delete(keyArg); } })().finally(() => { pending.cleanupPromise = undefined; }); await pending.cleanupPromise; } private retryPendingFlexRunCleanups(): void { if (this.flexGlobalCleanupPending) { void this.retryFlexGlobalCleanup().catch(() => this.scheduleFlexRunCleanupRetry()); return; } for (const key of this.pendingFlexRunCleanups.keys()) { void this.retryFlexRunCleanup(key).catch(() => this.scheduleFlexRunCleanupRetry()); } } private retryFlexGlobalCleanup(): Promise<void> { if (!this.flexGlobalCleanupPending) return Promise.resolve(); if (this.flexGlobalCleanupPromise) return this.flexGlobalCleanupPromise; const cleanupGeneration = this.flexGlobalCleanupGeneration; let cleanup: Promise<void>; cleanup = Promise.resolve().then(async () => { await this.browserResourceHost?.closeAllFlexChannels(); if (this.flexGlobalCleanupGeneration === cleanupGeneration) { this.flexGlobalCleanupPending = false; this.pendingFlexRunCleanups.clear(); } }).finally(() => { if (this.flexGlobalCleanupPromise === cleanup) this.flexGlobalCleanupPromise = undefined; if (this.flexGlobalCleanupPending) this.scheduleFlexRunCleanupRetry(); }); this.flexGlobalCleanupPromise = cleanup; return cleanup; } private async drainFlexGlobalCleanup(): Promise<void> { while (this.flexGlobalCleanupPending) { await this.retryFlexGlobalCleanup(); } } private queueFlexGlobalCleanup(failureMessageArg: string): void { this.flexGlobalCleanupGeneration += 1; this.flexGlobalCleanupPending = true; void this.retryFlexGlobalCleanup().catch((errorArg) => { console.error(failureMessageArg, errorArg); this.scheduleFlexRunCleanupRetry(); }); } private scheduleFlexRunCleanupRetry(): void { if ( this.flexRunCleanupRetryTimer || (!this.flexGlobalCleanupPending && this.pendingFlexRunCleanups.size === 0) || this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped' ) return; this.flexRunCleanupRetryTimer = setTimeout(() => { this.flexRunCleanupRetryTimer = undefined; this.retryPendingFlexRunCleanups(); }, flexRunCleanupRetryMs); this.flexRunCleanupRetryTimer.unref?.(); } private fenceFlexRunAuthority(messageArg: string): void { if (this.flexRunAuthorityFenced) return; this.flexRunAuthorityFenced = true; this.fenceFlexDelegatedAuthority(messageArg); this.activeFlexRuns.clear(); console.error(messageArg); this.queueFlexGlobalCleanup('Flex browser channel cleanup after authority fencing failed.'); } private async acknowledgeFlexRun( projectIdArg: string, sessionIdArg: string, queueIdArg: string, runIdArg: string, modelArg: Readonly<IFlexModelChoice>, signalArg?: AbortSignal, ): Promise<void> { this.retryPendingFlexRunCleanups(); const supervisor = this.requireFlexSupervisor(); const runtimeAuthority = this.requireFlexRuntimeExitAuthority(supervisor); const signal = signalArg === undefined ? runtimeAuthority.signal : AbortSignal.any([signalArg, runtimeAuthority.signal]); const rawSession = await supervisor.request('session.get', { scopeId: projectIdArg, sessionId: sessionIdArg, }, signal); const generation = requireFlexSessionGeneration(rawSession); const providerSessionGeneration = flexSessionProviderGeneration( generation.sessionGenerationId, generation.sessionGenerationSequence, ); const managedObservation = await this.requireAuthStore().requireManagedSession({ projectIdentityId: projectIdArg, runtimeId: flexRuntimeId(sessionIdArg), supervisorGeneration: runtimeAuthority.sessionIdentityGeneration, providerSessionGeneration, }); this.assertFlexSessionIdentityGenerationCurrent(supervisor, runtimeAuthority); const peerId = supervisor.getActiveHostPeerId(); if (peerId === undefined) throw new Error('The Flex child peer is unavailable.'); const operationModel = this.harnessOperationManager.getFlexRunModel( projectIdArg, flexRuntimeId(sessionIdArg), queueIdArg, runIdArg, ); if (operationModel !== undefined && !plugins.util.isDeepStrictEqual(operationModel, modelArg)) { throw new Error('The accepted Flex run model does not match its harness operation.'); } this.registerFlexRun({ projectId: projectIdArg, sessionId: sessionIdArg, ...generation, queueId: queueIdArg, runId: runIdArg, model: modelArg, sessionDepth: rawSession.depth ?? 0, managedSessionIdentityId: managedObservation.identity.sessionIdentityId, providerSessionGeneration, supervisor, runtimeAuthority, peerId, }); try { await supervisor.request('prompt.acknowledge', { scopeId: projectIdArg, sessionId: sessionIdArg, queueId: queueIdArg, runId: runIdArg, }, signal); } catch (errorArg) { this.unregisterFlexRun(projectIdArg, sessionIdArg, generation, queueIdArg, runIdArg); await supervisor.stop().catch((stopErrorArg) => { throw new AggregateError( [errorArg, stopErrorArg], 'Flex run acknowledgement failed and the child could not be stopped.', ); }); throw errorArg; } this.consumePendingFlexFinishedPrompt(projectIdArg, sessionIdArg, generation, queueIdArg); } private handleHarnessOperationReleased( reservationArg: TControllerHarnessOperationReservation, ): void { this.queuedOpenCodePromptSubmissions.delete(reservationArg.operationId); this.queuedCodexPrompts.delete(reservationArg.operationId); if (this.lifecycleState !== 'ready') return; this.queueHarnessPromptChanged(reservationArg.projectId, reservationArg.sessionId); if (reservationArg.sessionId.harnessId === 'codex') this.scheduleQueuedCodexPrompt(reservationArg.projectId, reservationArg.sessionId); if (reservationArg.sessionId.harnessId === 'opencode') { this.scheduleQueuedOpenCodePrompt(reservationArg.projectId, reservationArg.sessionId); } } private queueOpenCodePromptIfBlocked( submissionArg: IQueuedOpenCodePromptSubmission, ): boolean { this.harnessOperationManager.assertCurrentHarnessAdmission(submissionArg.operation); if (!this.harnessOperationManager.queueOpenCodeIfBlocked(submissionArg.operation)) return false; const retained: IQueuedOpenCodePromptSubmission = { ...submissionArg, operation: submissionArg.operation, ...(submissionArg.model === undefined ? {} : { model: { ...submissionArg.model } }), }; this.queuedOpenCodePromptSubmissions.set(submissionArg.operation.operationId, retained); try { this.harnessOperationManager.acceptQueued(submissionArg.operation); } catch (errorArg) { this.queuedOpenCodePromptSubmissions.delete(submissionArg.operation.operationId); throw errorArg; } this.queueHarnessPromptChanged( submissionArg.operation.projectId, submissionArg.operation.sessionId, ); this.scheduleQueuedOpenCodePrompt( submissionArg.operation.projectId, submissionArg.operation.sessionId, ); return true; } private openCodePromptQueueCanDispatch( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): boolean { const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); return sessionIdArg.harnessId === 'opencode' && this.lifecycleState === 'ready' && !this.stopRequested && !this.operationAbortController.signal.aborted && !this.openCodeRuntimeAdmissionSealed && !this.retiringProjectIds.has(projectIdArg) && !this.retiringSessionKeys.has(sessionKey) && !this.archivingSessionKeys.has(sessionKey) && !this.abortingSessionKeys.has(sessionKey) && !this.openCodePromptQueueFenceCounts.has(sessionKey); } private scheduleQueuedOpenCodePrompt( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { if (!this.openCodePromptQueueCanDispatch(projectIdArg, sessionIdArg)) return; const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.openCodePromptQueueDrainTasks.has(sessionKey)) return; const head = this.harnessOperationManager.getActive(projectIdArg, sessionIdArg); if ( head?.state !== 'queued' || !this.queuedOpenCodePromptSubmissions.has(head.reservation.operationId) ) return; let task: Promise<void>; task = this.trackOperationPromise( this.dispatchQueuedOpenCodePrompt(projectIdArg, sessionIdArg), ).finally(() => { if (this.openCodePromptQueueDrainTasks.get(sessionKey) !== task) return; this.openCodePromptQueueDrainTasks.delete(sessionKey); const next = this.harnessOperationManager.getActive(projectIdArg, sessionIdArg); if ( this.openCodePromptQueueCanDispatch(projectIdArg, sessionIdArg) && next?.state === 'queued' && this.queuedOpenCodePromptSubmissions.has(next.reservation.operationId) ) { queueMicrotask(() => this.scheduleQueuedOpenCodePrompt(projectIdArg, sessionIdArg)); } }); this.openCodePromptQueueDrainTasks.set(sessionKey, task); void task.catch(() => undefined); } private async waitForOpenCodePromptQueueDrains(timeoutMsArg: number): Promise<boolean> { const tasks = [...this.openCodePromptQueueDrainTasks.values()]; if (tasks.length === 0) return true; let timeout: NodeJS.Timeout | undefined; try { return await Promise.race([ Promise.allSettled(tasks).then(() => true), new Promise<false>((resolve) => { timeout = setTimeout(() => resolve(false), timeoutMsArg); }), ]); } finally { if (timeout) clearTimeout(timeout); } } private async dispatchQueuedOpenCodePrompt( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise<void> { if (!this.openCodePromptQueueCanDispatch(projectIdArg, sessionIdArg)) return; const head = this.harnessOperationManager.getActive(projectIdArg, sessionIdArg); if (head?.state !== 'queued') return; const queued = this.queuedOpenCodePromptSubmissions.get(head.reservation.operationId); if (!queued) return; const operation = queued.operation; let submissionStarted = false; let submissionAccepted = false; try { await this.withHarnessSubmissionAdmission( projectIdArg, sessionIdArg, this.operationAbortController.signal, async () => { if (!this.openCodePromptQueueCanDispatch(projectIdArg, sessionIdArg)) return; const runtime = this.currentOwnedOpenCodeRuntime(); if (!runtime) throw new Error('The OpenCode runtime is unavailable.'); const authority = this.requireOpenCodeRuntimeExitAuthority(runtime.supervisor); const signal = AbortSignal.any([ this.operationAbortController.signal, authority.signal, ]); this.assertOpenCodeStartupRuntimeReady( runtime.supervisor, runtime.client, authority.signal, ); this.harnessOperationManager.assertCurrentHarnessAdmission(operation); this.harnessOperationManager.beginQueuedSubmission(operation); const messageId: IControllerRuntimeId = { harnessId: 'opencode', nativeId: `msg_hcon_${operation.operationId}`, }; this.harnessOperationManager.bindOpenCodeMessage(operation, messageId); submissionStarted = true; await runtime.client.sendMessage( queued.projectDirectory, sessionIdArg.nativeId, queued.prompt, queued.model, queued.agent, signal, messageId.nativeId, ); submissionAccepted = true; this.assertOpenCodeStartupRuntimeReady( runtime.supervisor, runtime.client, authority.signal, ); this.harnessOperationManager.assertCurrentHarnessAdmission(operation); this.harnessOperationManager.accept(operation); this.queuedOpenCodePromptSubmissions.delete(operation.operationId); this.queueHarnessPromptChanged(projectIdArg, sessionIdArg); this.startOpenCodeOperationMonitor(projectIdArg, sessionIdArg, queued.projectDirectory); }, ); } catch (errorArg) { const active = this.harnessOperationManager.getOperation(operation); if (!active || active.state === 'cancelled') return; if ( submissionAccepted || submissionStarted ) { const terminalObserved = this.harnessOperationManager.markOutcomeUnknown(operation); if (!terminalObserved) this.failClosedOpenCodeHarness(); } else { await this.harnessOperationManager.fail(operation).catch(() => undefined); } this.queueHarnessSessionError(projectIdArg, sessionIdArg); } } private acquireOpenCodePromptQueueFence( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): () => void { const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); this.openCodePromptQueueFenceCounts.set( sessionKey, (this.openCodePromptQueueFenceCounts.get(sessionKey) ?? 0) + 1, ); let released = false; return () => { if (released) return; released = true; const remaining = (this.openCodePromptQueueFenceCounts.get(sessionKey) ?? 1) - 1; if (remaining > 0) { this.openCodePromptQueueFenceCounts.set(sessionKey, remaining); return; } this.openCodePromptQueueFenceCounts.delete(sessionKey); this.scheduleQueuedOpenCodePrompt(projectIdArg, sessionIdArg); }; } private pendingPromptsForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, messagesArg: readonly IControllerMessage[], ): IControllerPendingPrompt[] { const visibleIds = new Set(messagesArg.map((message) => controllerRuntimeIdKey(message.id))); return this.harnessOperationManager.listSessionActive(projectIdArg, sessionIdArg) .filter((operation) => ( operation.reservation.kind !== 'slash' || operation.inputAccepted || operation.reservation.messageId !== undefined || operation.flexQueueId !== undefined )) .filter((operation) => ( operation.state !== 'cancelled' && (operation.state !== 'submitting' || operation.inputAccepted) )) .filter((operation) => ( operation.reservation.delivery !== 'steer' || !operation.inputAccepted )) .filter((operation) => ( operation.reservation.messageId === undefined || !visibleIds.has(controllerRuntimeIdKey(operation.reservation.messageId)) )) .map((operation) => ({ id: operation.reservation.messageId ?? { harnessId: sessionIdArg.harnessId, nativeId: sessionIdArg.harnessId === 'flex' ? operation.flexQueueId ?? operation.reservation.operationId : operation.reservation.operationId, }, text: operation.reservation.source === 'direct' ? operation.reservation.direct.text.trim() : operation.reservation.draft.text.trim(), attachments: operation.reservation.source === 'direct' ? [] : operation.reservation.draft.attachments.map(({ dataBase64: _dataBase64, ...entry }) => entry), createdAt: operation.reservation.createdAt, })); } private queueHarnessPromptChanged( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { const event: IControllerEvent = { type: 'session.changed', harnessId: requireManagedRuntimeId(sessionIdArg).harnessId, projectId: projectIdArg, sessionId: { ...sessionIdArg }, timestamp: Date.now(), }; if (sessionIdArg.harnessId === 'codex') void this.pushControllerEvent(event).catch(() => undefined); else if (sessionIdArg.harnessId === 'flex') this.queueFlexControllerEvent(event); else this.queueOpenCodeControllerEvent(event); } private releaseOpenCodeOperationsAfterSessionError( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise<void> { if (sessionIdArg.harnessId !== 'opencode') return Promise.resolve(); const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); const existing = this.openCodeSessionErrorCleanupTasks.get(sessionKey); if (existing) return existing; const releaseFence = this.acquireOpenCodePromptQueueFence(projectIdArg, sessionIdArg); let task: Promise<void>; task = this.trackOperationPromise((async () => { await this.waitForSessionHarnessSubmissionAdmissions( sessionKey, this.operationAbortController.signal, ); let cleanupFailureLogged = false; while (true) { this.operationAbortController.signal.throwIfAborted(); try { await this.harnessOperationManager.releaseSession(projectIdArg, sessionIdArg); break; } catch (errorArg) { if (!cleanupFailureLogged) { cleanupFailureLogged = true; console.error( `OpenCode session ${sessionIdArg.nativeId} operation cleanup failed; retrying.`, errorArg, ); } await waitForSignalOrDelay( openCodeSessionErrorCleanupRetryMs, this.operationAbortController.signal, ); } } })().finally(() => { releaseFence(); if (this.openCodeSessionErrorCleanupTasks.get(sessionKey) === task) { this.openCodeSessionErrorCleanupTasks.delete(sessionKey); } })); this.openCodeSessionErrorCleanupTasks.set(sessionKey, task); return task; } private startOpenCodeOperationMonitor( projectIdArg: string, sessionIdArg: IControllerRuntimeId, projectDirectoryArg: string, ): void { const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.openCodeOperationMonitors.has(key)) return; const abortController = new AbortController(); let task: Promise<void>; task = this.reconcileOpenCodeOperations( projectIdArg, sessionIdArg, projectDirectoryArg, AbortSignal.any([this.operationAbortController.signal, abortController.signal]), ).finally(() => { if (this.openCodeOperationMonitors.get(key)?.task === task) { this.openCodeOperationMonitors.delete(key); if ( !abortController.signal.aborted && this.lifecycleState === 'ready' && !this.openCodeFailClosedTask && !this.retiringProjectIds.has(projectIdArg) && !this.retiringSessionKeys.has(key) && this.harnessOperationManager.listSessionActive(projectIdArg, sessionIdArg) .some((operation) => operation.reservation.messageId?.harnessId === 'opencode') ) { queueMicrotask(() => this.startOpenCodeOperationMonitor( projectIdArg, sessionIdArg, projectDirectoryArg, )); } } }); this.openCodeOperationMonitors.set(key, { abortController, task }); void task.catch(() => undefined); } private async reconcileOpenCodeOperations( projectIdArg: string, sessionIdArg: IControllerRuntimeId, projectDirectoryArg: string, signalArg: AbortSignal, ): Promise<void> { let failures = 0; while (!signalArg.aborted) { const operations = this.harnessOperationManager.listSessionActive(projectIdArg, sessionIdArg) .filter((operation) => operation.reservation.messageId?.harnessId === 'opencode'); if (operations.length === 0) return; try { const activeMessageKeys = new Set(operations.map((operation) => ( controllerRuntimeIdKey(operation.reservation.messageId!) ))); const terminalParents = new Map<string, IControllerRuntimeId>(); const observedActiveMessages = new Set<string>(); let before: string | undefined; let reachedTranscriptEnd = false; for (let pageIndex = 0; pageIndex < maxOpenCodeOperationRecoveryPages; pageIndex += 1) { const page = await this.requireOpenCodeClient().listMessagePage( projectDirectoryArg, sessionIdArg.nativeId, { limit: 50, ...(before === undefined ? {} : { before }) }, signalArg, ); for (const bundle of page.bundles) { const sourceKey = controllerRuntimeIdKey(bundle.sourceMessageId); if (bundle.sourceRole === 'user' && activeMessageKeys.has(sourceKey)) { observedActiveMessages.add(sourceKey); } if ( bundle.sourceRole === 'assistant' && bundle.terminal && bundle.parentMessageId?.harnessId === 'opencode' && activeMessageKeys.has(controllerRuntimeIdKey(bundle.parentMessageId)) ) terminalParents.set(controllerRuntimeIdKey(bundle.parentMessageId), bundle.parentMessageId); } if ( new Set([...observedActiveMessages, ...terminalParents.keys()]).size === activeMessageKeys.size ) break; if (page.nextCursor === undefined) { reachedTranscriptEnd = true; break; } before = page.nextCursor; } for (const messageId of terminalParents.values()) { await this.harnessOperationManager.completeOpenCodeMessage( projectIdArg, sessionIdArg, messageId, ); } if ( !reachedTranscriptEnd && new Set([...observedActiveMessages, ...terminalParents.keys()]).size < activeMessageKeys.size ) { throw new Error('OpenCode operation recovery exceeded its bounded transcript scan.'); } failures = 0; } catch { if (signalArg.aborted) return; failures += 1; if (failures >= maxOpenCodeOperationRecoveryFailures) { this.failClosedOpenCodeHarness(); return; } } await new Promise<void>((resolve) => { const finish = () => { clearTimeout(timer); signalArg.removeEventListener('abort', finish); resolve(); }; const timer = setTimeout(finish, openCodeOperationRecoveryPollMs); timer.unref?.(); signalArg.addEventListener('abort', finish, { once: true }); if (signalArg.aborted) finish(); }); } } private handleOpenCodeMessageLifecycle( lifecycleArg: IOpenCodeMessageLifecycle, directoryArg: string, ): void { if (lifecycleArg.role !== 'assistant' || !lifecycleArg.terminal) return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; const sessionKey = controllerRuntimeIdKey(lifecycleArg.sessionId); const messageKey = controllerRuntimeIdKey(lifecycleArg.messageId); for (const [key, entry] of this.latestOpenCodeReasoningUpdates) { if ( entry.projectId === projectId && controllerRuntimeIdKey(entry.update.sessionId) === sessionKey && controllerRuntimeIdKey(entry.update.messageId) === messageKey ) this.latestOpenCodeReasoningUpdates.delete(key); } for (const [key, entry] of this.latestOpenCodeTextUpdates) { if ( entry.projectId === projectId && controllerRuntimeIdKey(entry.update.sessionId) === sessionKey && controllerRuntimeIdKey(entry.update.messageId) === messageKey ) this.latestOpenCodeTextUpdates.delete(key); } if (lifecycleArg.parentMessageId?.harnessId !== 'opencode') return; void this.harnessOperationManager.completeOpenCodeMessage( projectId, lifecycleArg.sessionId, lifecycleArg.parentMessageId, ).catch(() => undefined); } private failClosedOpenCodeHarness(): void { this.revokeAllChildScopes(true); if (this.openCodeFailClosedTask || !this.openCodeSupervisor) return; if (this.openCodeFailClosedRetryTimer) { clearTimeout(this.openCodeFailClosedRetryTimer); this.openCodeFailClosedRetryTimer = undefined; } const supervisor = this.openCodeSupervisor; const client = this.openCodeClient; let supervisorStopped = false; let operationCleanupComplete = false; this.sealOpenCodeRuntimeAdmission(); if (client) { this.abortOpenCodeRuntimeLeases( client, new Error('The OpenCode runtime is shutting down fail-closed.'), ); } this.plannedOpenCodeSupervisorStops.add(supervisor); const task = (async () => { await client?.stopEventStream().catch(() => undefined); if (this.openCodeClient === client) this.eventStreamTask = undefined; const errors: unknown[] = []; await supervisor.stop().then(() => { supervisorStopped = true; }).catch((errorArg) => errors.push(errorArg)); await this.harnessOperationManager.releaseHarness('opencode').then(() => { operationCleanupComplete = true; }).catch((errorArg) => errors.push(errorArg)); if ( supervisorStopped && operationCleanupComplete && this.openCodeSupervisor === supervisor ) { this.openCodeSupervisor = undefined; } if (supervisorStopped) this.plannedOpenCodeSupervisorStops.delete(supervisor); if (this.openCodeClient === client) this.openCodeClient = undefined; if (errors.length === 1) throw errors[0]; if (errors.length > 1) { throw new AggregateError(errors, 'OpenCode fail-closed shutdown and cleanup failed.'); } })().catch((errorArg) => { console.error('OpenCode fail-closed shutdown failed.', errorArg); }).finally(() => { if (this.openCodeFailClosedTask === task) this.openCodeFailClosedTask = undefined; if ( (!supervisorStopped || !operationCleanupComplete) && this.lifecycleState === 'ready' && this.openCodeSupervisor === supervisor && !this.openCodeFailClosedRetryTimer ) { this.openCodeFailClosedRetryTimer = setTimeout(() => { this.openCodeFailClosedRetryTimer = undefined; this.failClosedOpenCodeHarness(); }, openCodeFailClosedRetryMs); this.openCodeFailClosedRetryTimer.unref?.(); } if (this.openCodeCrashRecovery?.supervisor === supervisor) { this.scheduleOpenCodeRestart(); } }); this.openCodeFailClosedTask = task; } private queueHarnessSessionErrors(harnessIdArg: TControllerSessionHarnessId): void { if (this.lifecycleState !== 'ready') return; const affected = this.harnessOperationManager.listHarnessActive(harnessIdArg); const affectedSessions = new Map<string, { projectId: string; sessionId: IControllerRuntimeId; }>(); for (const active of affected) { const reservation = active.reservation; affectedSessions.set( this.sessionStateMutationKey(reservation.projectId, reservation.sessionId), { projectId: reservation.projectId, sessionId: reservation.sessionId }, ); } for (const { projectId, sessionId } of affectedSessions.values()) { this.queueHarnessSessionError(projectId, sessionId); } } private handleHarnessChildExit( harnessIdArg: TControllerSessionHarnessId, notifyAffectedSessionsArg = true, ): void { if (notifyAffectedSessionsArg) this.queueHarnessSessionErrors(harnessIdArg); if (harnessIdArg === 'opencode' && this.lifecycleState === 'ready') { this.failClosedOpenCodeHarness(); } if (harnessIdArg === 'flex') { const admissionWasAlreadyFenced = this.flexHarnessAdmissionFenceGeneration !== undefined; this.sealFlexRuntimeAuthority(false); if (!admissionWasAlreadyFenced && this.lifecycleState === 'ready') { this.advanceFlexStreams(true); } this.flexChildExitCleanupPending = true; void this.cleanupFlexAfterChildExit().catch((errorArg) => { console.error('Flex child-exit cleanup failed; recovery will retry it.', errorArg); }); } else { void this.harnessOperationManager.releaseHarness(harnessIdArg).catch((errorArg) => { console.error(`${harnessIdArg} operation cleanup after child exit failed.`, errorArg); }); } } private handleOpenCodeChildExitObserved(supervisorArg: OpenCodeSupervisor): void { if ( this.openCodeSupervisor !== supervisorArg || this.plannedOpenCodeSupervisorStops.has(supervisorArg) || this.lifecycleState !== 'ready' || this.stopRequested ) return; const client = this.openCodeClient; const exitSignal = this.openCodeSupervisorExitSignals.get(supervisorArg)?.signal; if (!client || !exitSignal) return; const existing = this.openCodeCrashRecovery; if (existing?.supervisor === supervisorArg) return; const admissionFenceGeneration = this.sealOpenCodeRuntimeAuthority(true); this.advanceOpenCodeStreams(true); const exitReason = new Error('The OpenCode runtime generation exited.'); this.abortOpenCodeRuntimeLeases(client, exitReason); try { this.queueHarnessSessionErrors('opencode'); } catch (errorArg) { console.error('OpenCode child-exit session notification failed.', errorArg); } if (existing && !existing.cleanupComplete) { console.error('A newer OpenCode runtime exited before prior crash cleanup completed.'); return; } this.openCodeCrashRecovery = { supervisor: supervisorArg, client, ...(this.eventStreamTask ? { eventStreamTask: this.eventStreamTask } : {}), exitSignal, admissionFenceGeneration, temporarySessionIds: client.getRememberedSessionIntelligenceTemporarySessionIds(), supervisorStopped: false, cleanupComplete: false, }; this.scheduleOpenCodeRestart(); } private handleFlexChildExitObserved(supervisorArg: FlexSupervisor): void { if ( this.flexSupervisor !== supervisorArg || this.stopRequested || this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped' ) return; this.sealFlexRuntimeAuthority(true); this.clearFlexDelegatedAuthority(); this.scheduleFlexRestart(); if (this.lifecycleState === 'ready') this.advanceFlexStreams(true); if (!this.plannedFlexSupervisorStops.has(supervisorArg)) { try { this.queueHarnessSessionErrors('flex'); } catch (errorArg) { console.error('Flex child-exit session notification failed.', errorArg); } } } private handleFlexChildExit( supervisorArg: FlexSupervisor, exitArg: IFlexSupervisorExit, ): void { if ( this.flexSupervisor !== supervisorArg || this.plannedFlexSupervisorStops.has(supervisorArg) || this.lifecycleState !== 'ready' || this.stopRequested ) return; const detail = exitArg.type === 'error' ? exitArg.errorMessage ?? 'child process error' : `code ${String(exitArg.code ?? 'unknown')}, signal ${String(exitArg.signal ?? 'none')}`; console.error(`Flex child terminated (${detail}); automatic recovery is scheduled.`); this.handleHarnessChildExit('flex', false); this.scheduleFlexRestart(); } private async handleUndeliveredFlexHostSuccess<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, resultArg: TFlexHostResponse<TMethod>, peerIdArg: string, ): Promise<void> { if (methodArg === 'delegated-run-admission.acquire') { const payload = payloadArg as TFlexHostRequest<'delegated-run-admission.acquire'>; const result = resultArg as TFlexHostResponse<'delegated-run-admission.acquire'>; await this.closeFlexDelegatedRunAdmission( { ...payload, leaseId: result.leaseId }, peerIdArg, true, ); return; } if (methodArg === 'browser.channel.open') { const payload = payloadArg as TFlexHostRequest<'browser.channel.open'>; const result = resultArg as TFlexHostResponse<'browser.channel.open'>; if ( result.binding.channelId !== payload.channelId || result.binding.sessionGenerationId !== payload.sessionGenerationId || result.binding.sessionGenerationSequence !== payload.sessionGenerationSequence ) throw new Error('The undelivered Flex browser channel binding is invalid.'); await this.requireBrowserResourceHost().closeFlexChannel( payload.channelId, false, peerIdArg, ); this.flexBrowserChannelAuthorities.delete(payload.channelId); return; } if (methodArg === 'browser.channel.close') { throw new Error('An undelivered Flex browser channel close cannot be compensated.'); } if (methodArg !== 'git.worktree.create') return; const payload = payloadArg as TFlexHostRequest<'git.worktree.create'>; const result = resultArg as TFlexHostResponse<'git.worktree.create'>; await this.requireGitReversion().compensateDetachedSessionWorktreeCreation({ projectId: payload.scopeId, sessionId: payload.sessionId, worktreeId: result.worktree.worktreeId, }); } private resolveFlexHostAuthorityTarget<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, ): { projectId: string; sessionId: string; providerSessionGeneration: string; } | undefined { if (methodArg.startsWith('reversion.')) { const payload = payloadArg as TFlexHostRequest<'reversion.prepare'>; return { projectId: payload.scopeId, sessionId: payload.sessionId, providerSessionGeneration: flexSessionProviderGeneration( payload.sessionGenerationId, payload.sessionGenerationSequence, ), }; } if (methodArg.startsWith('git.worktree.')) { const payload = payloadArg as TFlexHostRequest<'git.worktree.create'>; return { projectId: payload.scopeId, sessionId: payload.sessionId, providerSessionGeneration: flexSessionProviderGeneration( payload.sessionGenerationId, payload.sessionGenerationSequence, ), }; } if (methodArg === 'project-management.load') { const payload = payloadArg as TFlexHostRequest<'project-management.load'>; return { projectId: payload.storageKey, sessionId: payload.sessionId, providerSessionGeneration: flexSessionProviderGeneration( payload.sessionContext.sessionGenerationId, payload.sessionContext.sessionGenerationSequence, ), }; } if (methodArg === 'project-management.save') { const payload = payloadArg as TFlexHostRequest<'project-management.save'>; return { projectId: payload.storageKey, sessionId: payload.sessionId, providerSessionGeneration: flexSessionProviderGeneration( payload.snapshot.sessionGenerationId, payload.snapshot.sessionGenerationSequence, ), }; } if (methodArg === 'project-management.tombstone') { const payload = payloadArg as TFlexHostRequest<'project-management.tombstone'>; return { projectId: payload.storageKey, sessionId: payload.sessionId, providerSessionGeneration: flexSessionProviderGeneration( payload.tombstone.sessionGenerationId, payload.tombstone.sessionGenerationSequence, ), }; } if (methodArg === 'browser.resources.resolve' || methodArg === 'browser.channel.open') { const payload = payloadArg as TFlexHostRequest<'browser.resources.resolve'>; return { projectId: payload.scopeId, sessionId: payload.sessionId, providerSessionGeneration: flexSessionProviderGeneration( payload.sessionGenerationId, payload.sessionGenerationSequence, ), }; } return undefined; } private flexHostRequestGeneration<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, ): TFlexSessionGeneration | undefined { if (methodArg.startsWith('reversion.') || methodArg.startsWith('git.worktree.')) { const payload = payloadArg as TFlexHostRequest<'reversion.prepare'>; return { sessionGenerationId: payload.sessionGenerationId, sessionGenerationSequence: payload.sessionGenerationSequence, }; } if (methodArg === 'project-management.load') { return { ...(payloadArg as TFlexHostRequest<'project-management.load'>).sessionContext }; } if (methodArg === 'project-management.save') { const snapshot = (payloadArg as TFlexHostRequest<'project-management.save'>).snapshot; return { sessionGenerationId: snapshot.sessionGenerationId, sessionGenerationSequence: snapshot.sessionGenerationSequence, }; } if (methodArg === 'project-management.tombstone') { const tombstone = (payloadArg as TFlexHostRequest<'project-management.tombstone'>).tombstone; return { sessionGenerationId: tombstone.sessionGenerationId, sessionGenerationSequence: tombstone.sessionGenerationSequence, }; } if (methodArg === 'browser.resources.resolve' || methodArg === 'browser.channel.open') { const payload = payloadArg as TFlexHostRequest<'browser.resources.resolve'>; return { sessionGenerationId: payload.sessionGenerationId, sessionGenerationSequence: payload.sessionGenerationSequence, }; } return undefined; } private flexDelegatedHostCleanupOnly<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, ): boolean { return methodArg === 'reversion.release' || ( methodArg === 'git.worktree.remove' && (payloadArg as TFlexHostRequest<'git.worktree.remove'>).compensate === true ) || methodArg === 'project-management.load' || methodArg === 'project-management.tombstone'; } private flexDelegationTicketForProjectManagement<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, ): IFlexDelegationTicket | undefined { if (!methodArg.startsWith('project-management.')) return undefined; const payload = payloadArg as TFlexHostRequest<'project-management.load'>; const candidates = [...this.flexDelegationTicketsByContext.values()].filter((ticket) => ( ticket.activationState === 'active' && ticket.context.storageKey === payload.storageKey && ticket.context.sessionId === payload.sessionId )); if (candidates.length !== 1) return undefined; const ticket = candidates[0]; if (methodArg === 'project-management.save') { const save = payloadArg as TFlexHostRequest<'project-management.save'>; return save.writeContext.actor === 'application' ? ticket : undefined; } const sessionContext = methodArg === 'project-management.load' ? (payloadArg as TFlexHostRequest<'project-management.load'>).sessionContext : (payloadArg as TFlexHostRequest<'project-management.tombstone'>).sessionContext; const subagent = sessionContext.subagent; if ( !subagent || subagent.parentSessionId !== ticket.context.parentSessionId || subagent.parentSessionGenerationId !== ticket.context.parentSessionGenerationId || subagent.parentSessionGenerationSequence !== ticket.context.parentSessionGenerationSequence || subagent.originParentRunId !== ticket.context.originParentRunId || subagent.originParentToolCallId !== ticket.context.originParentToolCallId || subagent.agent !== ticket.context.agent || subagent.depth !== ticket.context.depth ) return undefined; return ticket; } private async resolveFlexDelegatedHostAuthority<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, peerIdArg: string, signalArg: AbortSignal, ): Promise<TFlexDelegatedHostAuthority | undefined> { const target = this.resolveFlexHostAuthorityTarget(methodArg, payloadArg); const generation = this.flexHostRequestGeneration(methodArg, payloadArg); const supervisor = this.flexSupervisor; if (!target || !generation || !supervisor?.ownsHostPeer(peerIdArg)) return undefined; const lease = this.flexDelegatedRunLeasesBySession.get(this.flexDelegatedSessionKey({ scopeId: target.projectId, sessionId: target.sessionId, ...generation, })); if (lease) { const requestRunId = methodArg.startsWith('reversion.') ? (payloadArg as TFlexHostRequest<'reversion.prepare'>).runId : methodArg === 'browser.resources.resolve' || methodArg === 'browser.channel.open' ? (payloadArg as TFlexHostRequest<'browser.resources.resolve'>).runId : methodArg === 'project-management.save' ? (payloadArg as TFlexHostRequest<'project-management.save'>).writeContext.runId : undefined; if ( lease.supervisor !== supervisor || lease.peerId !== peerIdArg || this.flexSupervisorExitSignals.get(supervisor) !== lease.runtimeAuthority || lease.runtimeAuthority.signal.aborted || (requestRunId !== undefined && requestRunId !== lease.context.runId) ) throw new FlexServiceError('STALE_RUN'); const cleanupOnly = this.flexDelegatedHostCleanupOnly(methodArg, payloadArg); if (lease.state === 'active' && !lease.closeObserved && !lease.terminalObserved) { this.assertActiveFlexRunAuthority(lease.parentAuthority); return { type: 'lease', lease, cleanupOnly: false }; } if (cleanupOnly && lease.state === 'terminal-pending') { return { type: 'lease', lease, cleanupOnly: true }; } throw new FlexServiceError('STALE_RUN'); } const ticket = this.flexDelegationTicketForProjectManagement(methodArg, payloadArg); if (ticket) { this.assertActiveFlexRunAuthority(ticket.parentAuthority); if (ticket.parentAuthority.peerId !== peerIdArg) throw new FlexServiceError('STALE_RUN'); return { type: 'ticket', ticket }; } if (methodArg !== 'project-management.load' && methodArg !== 'project-management.tombstone') { return undefined; } const sessionContext = methodArg === 'project-management.load' ? (payloadArg as TFlexHostRequest<'project-management.load'>).sessionContext : (payloadArg as TFlexHostRequest<'project-management.tombstone'>).sessionContext; const subagent = sessionContext.subagent; if (!subagent) return undefined; const runtimeAuthority = this.requireFlexRuntimeExitAuthority(supervisor); const parentAuthority = await this.requireAuthStore().resolveFlexHostSessionAuthority({ projectIdentityId: target.projectId, runtimeId: flexRuntimeId(subagent.parentSessionId), providerSessionGeneration: flexSessionProviderGeneration( subagent.parentSessionGenerationId, subagent.parentSessionGenerationSequence, ), signal: signalArg, }); this.assertFlexSessionIdentityGenerationCurrent(supervisor, runtimeAuthority); if ( parentAuthority !== 'active' && !( this.flexHostMethodAllowsCleanup(methodArg) && ( parentAuthority === 'deleting_cleanup' || parentAuthority === 'project_removal_cleanup' ) ) ) throw new FlexServiceError('STALE_RUN'); if ( target.sessionId === subagent.parentSessionId || this.retiringProjectIds.has(target.projectId) && parentAuthority === 'active' ) throw new FlexServiceError('STALE_RUN'); return { type: 'relationship', supervisor, runtimeAuthority, peerId: peerIdArg, projectId: target.projectId, parentSessionId: subagent.parentSessionId, }; } private assertFlexDelegatedHostAuthority(authorityArg: TFlexDelegatedHostAuthority): void { if (authorityArg.type === 'lease') { const lease = authorityArg.lease; if ( this.flexDelegatedRunLeases.get(lease.leaseId) !== lease || lease.supervisor !== this.flexSupervisor || this.flexSupervisorExitSignals.get(lease.supervisor) !== lease.runtimeAuthority || lease.runtimeAuthority.signal.aborted || !lease.supervisor.ownsHostPeer(lease.peerId) ) throw new FlexServiceError('STALE_RUN'); if (authorityArg.cleanupOnly) { if (lease.state !== 'terminal-pending') throw new FlexServiceError('STALE_RUN'); } else if ( lease.state !== 'active' || lease.closeObserved || lease.terminalObserved ) throw new FlexServiceError('STALE_RUN'); return; } if (authorityArg.type === 'ticket') { const ticket = authorityArg.ticket; if ( ticket.activationState !== 'active' || this.flexDelegationTicketsByContext.get(ticket.contextKey) !== ticket || ticket.parentAuthority.peerId !== ticket.parentAuthority.supervisor.getActiveHostPeerId() ) throw new FlexServiceError('STALE_RUN'); this.assertActiveFlexRunAuthority(ticket.parentAuthority); return; } if ( this.flexSupervisor !== authorityArg.supervisor || this.flexSupervisorExitSignals.get(authorityArg.supervisor) !== authorityArg.runtimeAuthority || authorityArg.runtimeAuthority.signal.aborted || !authorityArg.supervisor.ownsHostPeer(authorityArg.peerId) ) throw new FlexServiceError('STALE_RUN'); } private flexHostMethodAllowsCleanup(methodArg: TFlexHostRequestMethod): boolean { return methodArg === 'reversion.release' || methodArg === 'git.worktree.remove' || methodArg === 'project-management.load' || methodArg === 'project-management.tombstone'; } private reserveFlexProjectHostRequestAdmission( projectIdArg: string, cleanupMethodArg: boolean, sessionIdArg?: string, ): () => void { const sessionKey = sessionIdArg === undefined ? undefined : this.sessionStateMutationKey(projectIdArg, flexRuntimeId(sessionIdArg)); const assertAdmissionOpen = () => { if ( this.sealedFlexProjectHostRequestAdmissions.has(projectIdArg) || ( sessionKey !== undefined && this.sealedFlexSessionHostRequestAdmissions.has(sessionKey) ) ) throw new Error('The Flex host request admission is sealed.'); if ( !cleanupMethodArg && ( this.retiringProjectIds.has(projectIdArg) || this.flexProjectIdentityRecoveryPending.has(projectIdArg) ) ) throw new Error('The Flex project does not admit ordinary host requests.'); }; assertAdmissionOpen(); const release = this.reserveAdmissionEntry( this.flexProjectHostRequestAdmissions, projectIdArg, ); const releaseSession = sessionKey === undefined ? undefined : this.reserveAdmissionEntry( this.flexSessionHostRequestAdmissions, sessionKey, ); try { assertAdmissionOpen(); } catch (errorArg) { releaseSession?.(); release(); throw errorArg; } return () => { releaseSession?.(); release(); }; } private assertFlexHostAuthority( methodArg: TFlexHostRequestMethod, authorityArg: TControllerFlexHostSessionAuthority, projectIdArg: string, sessionIdArg: string, ): void { if (authorityArg === 'active') { if ( this.retiringProjectIds.has(projectIdArg) || this.flexProjectIdentityRecoveryPending.has(projectIdArg) || this.retiringSessionKeys.has(this.sessionStateMutationKey( projectIdArg, flexRuntimeId(sessionIdArg), )) ) throw new Error('The Flex session authority is being reconciled.'); return; } if ( this.flexHostMethodAllowsCleanup(methodArg) && ( authorityArg === 'deleting_cleanup' || authorityArg === 'project_removal_cleanup' ) ) return; throw new Error('The Flex session is not authorized for this host operation.'); } private async handleFlexHostRequest<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, peerIdArg: string, signalArg: AbortSignal, ): Promise<TFlexHostResponse<TMethod>> { const supervisor = this.flexSupervisor; if (methodArg === 'delegated-run-admission.close') { if (!supervisor?.ownsDelegatedRunClosePeer(peerIdArg)) { throw new Error('The Flex child generation is unavailable.'); } return await this.closeFlexDelegatedRunAdmission( payloadArg as TFlexHostRequest<'delegated-run-admission.close'>, peerIdArg, ) as TFlexHostResponse<TMethod>; } if (!supervisor?.ownsHostPeer(peerIdArg)) { throw new Error('The Flex child generation is unavailable.'); } signalArg.throwIfAborted(); if (methodArg === 'delegated-run-admission.acquire') { return await this.acquireFlexDelegatedRunAdmission( payloadArg as TFlexHostRequest<'delegated-run-admission.acquire'>, peerIdArg, signalArg, ) as TFlexHostResponse<TMethod>; } const delegatedAuthority = await this.resolveFlexDelegatedHostAuthority( methodArg, payloadArg, peerIdArg, signalArg, ); if (delegatedAuthority) { const target = this.resolveFlexHostAuthorityTarget(methodArg, payloadArg); if (!target) throw new FlexServiceError('STALE_RUN'); const cleanupMethod = delegatedAuthority.type === 'lease' ? delegatedAuthority.cleanupOnly : this.flexHostMethodAllowsCleanup(methodArg); const release = this.reserveFlexProjectHostRequestAdmission( target.projectId, cleanupMethod, target.sessionId, ); const parentSessionId = delegatedAuthority.type === 'lease' ? delegatedAuthority.lease.parentAuthority.sessionId : delegatedAuthority.type === 'ticket' ? delegatedAuthority.ticket.parentAuthority.sessionId : delegatedAuthority.parentSessionId; let releaseParent: (() => void) | undefined; try { releaseParent = this.reserveFlexProjectHostRequestAdmission( target.projectId, cleanupMethod, parentSessionId, ); this.assertFlexDelegatedHostAuthority(delegatedAuthority); const authoritySignal = delegatedAuthority.type === 'lease' ? delegatedAuthority.cleanupOnly ? signalArg : AbortSignal.any([signalArg, delegatedAuthority.lease.abortController.signal]) : delegatedAuthority.type === 'ticket' ? AbortSignal.any([signalArg, delegatedAuthority.ticket.abortController.signal]) : AbortSignal.any([signalArg, delegatedAuthority.runtimeAuthority.signal]); return await this.handleAuthorizedFlexHostRequest( methodArg, payloadArg, peerIdArg, authoritySignal, ); } finally { releaseParent?.(); release(); } } if (methodArg === 'project-management.load') { const payload = payloadArg as TFlexHostRequest<'project-management.load'>; const registration = payload.registrationOperationId === undefined ? undefined : this.pendingFlexProjectRegistrationOperations.get(payload.registrationOperationId); const release = this.reserveFlexProjectHostRequestAdmission( payload.storageKey, registration !== undefined || this.flexHostMethodAllowsCleanup(methodArg), payload.sessionId, ); try { if ( payload.registrationOperationId !== undefined && ( !registration || registration.projectId !== payload.storageKey || registration.supervisor !== supervisor ) ) throw new Error('The Flex registration migration operation is not authorized.'); if (registration) { const registrationAuthority = await this.requireAuthStore() .resolveFlexProjectManagementRegistrationLoadAuthority({ projectIdentityId: payload.storageKey, runtimeId: flexRuntimeId(payload.sessionId), providerSessionGeneration: flexSessionProviderGeneration( payload.sessionContext.sessionGenerationId, payload.sessionContext.sessionGenerationSequence, ), expectedFlexSessionGenerationId: payload.sessionContext.sessionGenerationId, signal: signalArg, }); if (this.flexSupervisor !== supervisor || !supervisor.ownsHostPeer(peerIdArg)) { throw new Error('The Flex child generation is unavailable.'); } if (registrationAuthority !== 'active_load') { return { record: null } as TFlexHostResponse<TMethod>; } if (this.retiringProjectIds.has(payload.storageKey)) { throw new Error('The Flex project is being removed.'); } if (this.retiringSessionKeys.has(this.sessionStateMutationKey( payload.storageKey, flexRuntimeId(payload.sessionId), ))) throw new Error('The Flex session is being deleted.'); } else { const authority = await this.requireAuthStore().resolveFlexHostSessionAuthority({ projectIdentityId: payload.storageKey, runtimeId: flexRuntimeId(payload.sessionId), providerSessionGeneration: flexSessionProviderGeneration( payload.sessionContext.sessionGenerationId, payload.sessionContext.sessionGenerationSequence, ), signal: signalArg, }); this.assertFlexHostAuthority( methodArg, authority, payload.storageKey, payload.sessionId, ); } if (this.flexSupervisor !== supervisor || !supervisor.ownsHostPeer(peerIdArg)) { throw new Error('The Flex child generation is unavailable.'); } return await this.handleAuthorizedFlexHostRequest( methodArg, payloadArg, peerIdArg, signalArg, ); } finally { release(); } } const authorityTarget = this.resolveFlexHostAuthorityTarget(methodArg, payloadArg); if (authorityTarget) { const release = this.reserveFlexProjectHostRequestAdmission( authorityTarget.projectId, this.flexHostMethodAllowsCleanup(methodArg), authorityTarget.sessionId, ); try { const authority = await this.requireAuthStore().resolveFlexHostSessionAuthority({ projectIdentityId: authorityTarget.projectId, runtimeId: flexRuntimeId(authorityTarget.sessionId), providerSessionGeneration: authorityTarget.providerSessionGeneration, signal: signalArg, }); this.assertFlexHostAuthority( methodArg, authority, authorityTarget.projectId, authorityTarget.sessionId, ); if (this.flexSupervisor !== supervisor || !supervisor.ownsHostPeer(peerIdArg)) { throw new Error('The Flex child generation is unavailable.'); } return await this.handleAuthorizedFlexHostRequest( methodArg, payloadArg, peerIdArg, signalArg, ); } finally { release(); } } return this.handleAuthorizedFlexHostRequest(methodArg, payloadArg, peerIdArg, signalArg); } private async handleAuthorizedFlexHostRequest<TMethod extends TFlexHostRequestMethod>( methodArg: TMethod, payloadArg: TFlexHostRequest<TMethod>, peerIdArg: string, signalArg: AbortSignal, ): Promise<TFlexHostResponse<TMethod>> { const supervisor = this.flexSupervisor; if (!supervisor?.ownsHostPeer(peerIdArg)) { throw new Error('The Flex child generation is unavailable.'); } signalArg.throwIfAborted(); if (methodArg.startsWith('reversion.')) { const payload = payloadArg as TFlexHostRequest<'reversion.prepare'>; const project = await this.resolveFlexHostGitProject(payload.scopeId); if (payload.storageKey !== project.projectId) { throw new Error('The Flex Git storage scope is invalid.'); } const identity = { projectId: project.projectId, sessionId: payload.sessionId, runId: payload.runId, captureId: payload.captureId, }; const gitReversion = this.requireGitReversion(); switch (methodArg) { case 'reversion.prepare': await gitReversion.prepare({ ...identity, signal: signalArg }); return { prepared: true } as TFlexHostResponse<TMethod>; case 'reversion.inspect-capture': return await gitReversion.inspectCapture({ ...identity, signal: signalArg, }) as TFlexHostResponse<TMethod>; case 'reversion.finalize': return await gitReversion.finalize({ ...identity, signal: signalArg, }) as TFlexHostResponse<TMethod>; case 'reversion.inspect-apply': { const apply = payloadArg as TFlexHostRequest<'reversion.inspect-apply'>; return await gitReversion.inspectApply({ identity, reference: apply.reference as unknown as TGitReversionReference, operationId: apply.operationId, direction: apply.direction, signal: signalArg, }) as TFlexHostResponse<TMethod>; } case 'reversion.apply': { const apply = payloadArg as TFlexHostRequest<'reversion.apply'>; await gitReversion.apply({ identity, reference: apply.reference as unknown as TGitReversionReference, operationId: apply.operationId, direction: apply.direction, signal: signalArg, }); return { applied: true } as TFlexHostResponse<TMethod>; } case 'reversion.release': { const release = payloadArg as TFlexHostRequest<'reversion.release'>; await gitReversion.release({ identity, reference: release.reference as unknown as TGitReversionReference, signal: signalArg, }); return { released: true } as TFlexHostResponse<TMethod>; } } } if (methodArg.startsWith('git.worktree.')) { const payload = payloadArg as TFlexHostRequest<'git.worktree.create'>; const storedProject = await this.resolveFlexHostGitProject(payload.scopeId); const gitReversion = this.requireGitReversion(); if (methodArg === 'git.worktree.create') { return { worktree: toFlexGitWorktreeDescriptor( await gitReversion.createDetachedSessionWorktree({ projectId: storedProject.projectId, sessionId: payload.sessionId, signal: signalArg, }), ), } as TFlexHostResponse<TMethod>; } if (methodArg === 'git.worktree.list') { return { worktrees: (await gitReversion.listDetachedSessionWorktrees({ projectId: storedProject.projectId, sessionId: payload.sessionId, signal: signalArg, })).map(toFlexGitWorktreeDescriptor), } as TFlexHostResponse<TMethod>; } const remove = payloadArg as TFlexHostRequest<'git.worktree.remove'>; if (remove.compensate) { await gitReversion.compensateDetachedSessionWorktreeCreation({ projectId: storedProject.projectId, sessionId: remove.sessionId, worktreeId: remove.worktreeId, }); } else { await gitReversion.removeDetachedSessionWorktree({ projectId: storedProject.projectId, sessionId: remove.sessionId, worktreeId: remove.worktreeId, signal: signalArg, }); } return { removed: true } as TFlexHostResponse<TMethod>; } if ( (methodArg === 'crossharness.chats.list' || methodArg === 'crossharness.chat.read') && !supervisor.ownsBrowserPeer(peerIdArg) ) throw new Error('The Flex child generation is unavailable.'); if (methodArg === 'crossharness.chats.list') { const payload = payloadArg as TFlexHostRequest<'crossharness.chats.list'>; return { chats: await this.listSessionIntelligenceChats( payload.capabilityToken, payload.limit, peerIdArg, ), } as TFlexHostResponse<TMethod>; } if (methodArg === 'crossharness.chat.read') { const payload = payloadArg as TFlexHostRequest<'crossharness.chat.read'>; return { transcript: await this.readSessionIntelligenceChat( payload.capabilityToken, payload.sessionId, payload.limit, payload.maxChars, peerIdArg, ), } as TFlexHostResponse<TMethod>; } if (methodArg === 'project-management.load') { const payload = payloadArg as TFlexHostRequest<'project-management.load'>; const record = await this.requireAuthStore().loadFlexProjectManagement( payload.storageKey, payload.sessionId, payload.sessionContext, ); return { record: record ?? null } as TFlexHostResponse<TMethod>; } if ( methodArg === 'project-management.save' || methodArg === 'project-management.tombstone' ) { try { if (methodArg === 'project-management.save') { const payload = payloadArg as TFlexHostRequest<'project-management.save'>; await this.requireAuthStore().saveFlexProjectManagement( payload.storageKey, payload.sessionId, payload.snapshot, payload.expectedRevision, payload.writeContext, ); } else { const payload = payloadArg as TFlexHostRequest<'project-management.tombstone'>; await this.requireAuthStore().tombstoneFlexProjectManagement( payload.storageKey, payload.sessionId, payload.tombstone, payload.expectedRevision, payload.sessionContext, ); } return { committed: true } as TFlexHostResponse<TMethod>; } catch (errorArg) { if (!(errorArg instanceof plugins.flexharness.FlexHarnessStoreConflictError)) throw errorArg; return { committed: false, actualRevision: errorArg.actualRevision, } as TFlexHostResponse<TMethod>; } } if (!supervisor.ownsBrowserPeer(peerIdArg)) { throw new Error('The Flex browser child generation is unavailable.'); } if (methodArg === 'browser.channel.close') { const payload = payloadArg as TFlexHostRequest<'browser.channel.close'>; const channelAuthority = this.flexBrowserChannelAuthorities.get(payload.channelId); if ( !channelAuthority || channelAuthority.supervisor !== supervisor || channelAuthority.peerId !== peerIdArg || channelAuthority.sessionGenerationId !== payload.sessionGenerationId || channelAuthority.sessionGenerationSequence !== payload.sessionGenerationSequence ) throw new Error('The Flex browser channel close generation is stale.'); await this.requireBrowserResourceHost().closeFlexChannelFromPeer({ channelId: payload.channelId, peerId: peerIdArg, sessionGenerationId: payload.sessionGenerationId, sessionGenerationSequence: payload.sessionGenerationSequence, }); if (this.flexBrowserChannelAuthorities.get(payload.channelId) === channelAuthority) { this.flexBrowserChannelAuthorities.delete(payload.channelId); } return { closed: true } as TFlexHostResponse<TMethod>; } const coordinator = this.requireResourceCoordinator(); if (methodArg === 'browser.resources.resolve') { const payload = payloadArg as TFlexHostRequest<'browser.resources.resolve'>; if (!this.ownsManagedOrDelegatedFlexRun( payload.scopeId, payload.sessionId, payload, payload.runId, )) { throw new Error('The Flex browser run is no longer active.'); } const resources = await coordinator.resolveFlexBrowserResources( payload.scopeId, payload.sessionId, ); if (!this.ownsManagedOrDelegatedFlexRun( payload.scopeId, payload.sessionId, payload, payload.runId, )) { throw new Error('The Flex browser run is no longer active.'); } return { resources, } as TFlexHostResponse<TMethod>; } if (methodArg !== 'browser.channel.open') { throw new Error('The Flex host request method is unsupported.'); } const payload = payloadArg as TFlexHostRequest<'browser.channel.open'>; if (!this.ownsManagedOrDelegatedFlexRun( payload.scopeId, payload.sessionId, payload, payload.runId, )) { throw new Error('The Flex browser run is no longer active.'); } const resource = await coordinator.requireFlexBrowserResource({ projectId: payload.scopeId, sessionId: payload.sessionId, resourceId: payload.resourceId, attachmentRevision: payload.attachmentRevision, }); if (!this.ownsManagedOrDelegatedFlexRun( payload.scopeId, payload.sessionId, payload, payload.runId, )) { throw new Error('The Flex browser run is no longer active.'); } const browserResourceHost = this.requireBrowserResourceHost(); const channelAuthority = { projectId: payload.scopeId, sessionId: payload.sessionId, sessionGenerationId: payload.sessionGenerationId, sessionGenerationSequence: payload.sessionGenerationSequence, peerId: peerIdArg, supervisor, }; this.flexBrowserChannelAuthorities.set(payload.channelId, channelAuthority); let channel: Awaited<ReturnType<ControllerBrowserResourceHost['openFlexChannel']>>; try { channel = await browserResourceHost.openFlexChannel({ resource, scopeId: payload.scopeId, sessionId: payload.sessionId, sessionGenerationId: payload.sessionGenerationId, sessionGenerationSequence: payload.sessionGenerationSequence, runId: payload.runId, channelId: payload.channelId, peerId: peerIdArg, signal: signalArg, sendFrame: (channelIdArg, bytesArg) => supervisor.sendBrowserFrame( channelIdArg, bytesArg, peerIdArg, ), notifyClosed: async (channelIdArg) => { this.flexBrowserChannelAuthorities.delete(channelIdArg); await supervisor.notifyBrowserChannelClosed(channelIdArg, peerIdArg); }, }); } catch (errorArg) { if (this.flexBrowserChannelAuthorities.get(payload.channelId) === channelAuthority) { this.flexBrowserChannelAuthorities.delete(payload.channelId); } throw errorArg; } if ( !supervisor.ownsBrowserPeer(peerIdArg) || !this.ownsManagedOrDelegatedFlexRun( payload.scopeId, payload.sessionId, payload, payload.runId, ) ) { await browserResourceHost.closeFlexChannel(payload.channelId, false, peerIdArg); this.flexBrowserChannelAuthorities.delete(payload.channelId); throw new Error('The Flex browser run closed while opening the browser channel.'); } return channel as TFlexHostResponse<TMethod>; } private queueHarnessSessionError( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { if (sessionIdArg.harnessId === 'controller') return; const event: IControllerEvent = { type: 'session.changed', harnessId: sessionIdArg.harnessId, projectId: projectIdArg, sessionId: sessionIdArg, sessionError: true, timestamp: Date.now(), }; if (sessionIdArg.harnessId === 'codex') void this.pushControllerEvent(event).catch(() => undefined); else if (sessionIdArg.harnessId === 'flex') this.queueFlexControllerEvent(event); else this.queueOpenCodeControllerEvent(event); } private consumePendingFlexFinishedJob(jobIdArg: string): void { const key = controllerRuntimeIdKey(flexRuntimeId(jobIdArg)); const event = this.pendingFlexFinishedJobs.get(key); if (!event) return; this.pendingFlexFinishedJobs.delete(key); this.handleFlexEvent(event); } private handleFlexEvent( eventArg: TFlexChildEvent, sourceSupervisorArg: FlexSupervisor | undefined = this.flexSupervisor, ): void { if (!sourceSupervisorArg || this.flexSupervisor !== sourceSupervisorArg) return; const scopeId = 'scopeId' in eventArg && typeof eventArg.scopeId === 'string' ? eventArg.scopeId : undefined; const nativeSessionId = 'sessionId' in eventArg && typeof eventArg.sessionId === 'string' ? eventArg.sessionId : undefined; if ( scopeId !== undefined && nativeSessionId !== undefined && 'sessionGenerationId' in eventArg ) { const delegatedLease = this.flexDelegatedRunLeasesBySession.get( this.flexDelegatedSessionKey({ scopeId, sessionId: nativeSessionId, sessionGenerationId: eventArg.sessionGenerationId, sessionGenerationSequence: eventArg.sessionGenerationSequence, }), ); if (delegatedLease) { const hasMatchingRun = !('runId' in eventArg) || eventArg.runId === undefined || eventArg.runId === delegatedLease.context.runId; const hasMatchingQueue = !('queueId' in eventArg) || eventArg.queueId === delegatedLease.context.queueId; const peerIsCurrent = delegatedLease.supervisor === sourceSupervisorArg && this.flexSupervisorExitSignals.get(sourceSupervisorArg) === delegatedLease.runtimeAuthority && !delegatedLease.runtimeAuthority.signal.aborted && sourceSupervisorArg.ownsChildPeer(delegatedLease.peerId); if ( eventArg.type === 'prompt.finished' && hasMatchingRun && hasMatchingQueue && peerIsCurrent && !delegatedLease.terminalObserved ) { delegatedLease.terminalObserved = true; delegatedLease.state = 'terminal-pending'; delegatedLease.abortController.abort(new Error('The delegated Flex run finished.')); this.handleAuthorizedFlexDelegatedEvent(eventArg, delegatedLease); this.settleFlexDelegatedRunLease(delegatedLease); } else if ( delegatedLease.state === 'active' && !delegatedLease.closeObserved && !delegatedLease.terminalObserved && hasMatchingRun && hasMatchingQueue && peerIsCurrent ) { this.handleAuthorizedFlexDelegatedEvent(eventArg, delegatedLease); } return; } const operation = this.flexEventGenerationIsManaged( scopeId, nativeSessionId, eventArg.sessionGenerationId, eventArg.sessionGenerationSequence, ).then((managed) => { if (managed) this.handleAuthorizedFlexEvent(eventArg); }); void this.trackOperationPromise(operation).catch(() => undefined); return; } this.handleAuthorizedFlexEvent(eventArg); } private handleAuthorizedFlexDelegatedEvent( eventArg: TFlexChildEvent & TFlexSessionGeneration & { scopeId: string; sessionId: string }, leaseArg: IFlexDelegatedRunLease, ): void { const parentSessionId = flexRuntimeId(leaseArg.parentAuthority.sessionId); if (eventArg.type === 'permissions.changed') { if (this.autoAcceptPermissions || this.hasFlexYoloSessions()) { this.queueFlexAutoAcceptSweep( leaseArg.parentAuthority.projectId, leaseArg.parentAuthority.sessionId, { type: 'delegated', lease: leaseArg }, ); } this.queueFlexControllerEvent({ type: 'permissions.changed', harnessId: 'flex', projectId: leaseArg.parentAuthority.projectId, sessionId: parentSessionId, timestamp: Date.now(), }, eventArg); return; } if ( eventArg.type === 'sessions.changed' || eventArg.type === 'prompt.queued' || eventArg.type === 'prompt.started' || eventArg.type === 'prompt.running' || eventArg.type === 'prompt.finished' || eventArg.type === 'prompt.message-bound' ) { this.queueFlexControllerEvent({ type: 'session.changed', harnessId: 'flex', projectId: leaseArg.parentAuthority.projectId, sessionId: parentSessionId, timestamp: Date.now(), }, eventArg); } } private async flexEventGenerationIsManaged( projectIdArg: string, sessionIdArg: string, sessionGenerationIdArg: string, sessionGenerationSequenceArg: number, ): Promise<boolean> { const supervisor = this.flexSupervisor; if (!supervisor) return false; try { const runtimeAuthority = this.requireFlexRuntimeExitAuthority(supervisor); const authority = await this.requireAuthStore().resolveFlexHostSessionAuthority({ projectIdentityId: projectIdArg, runtimeId: flexRuntimeId(sessionIdArg), providerSessionGeneration: flexSessionProviderGeneration( sessionGenerationIdArg, sessionGenerationSequenceArg, ), signal: runtimeAuthority.signal, }); this.assertFlexSessionIdentityGenerationCurrent(supervisor, runtimeAuthority); const managed = authority === 'active' && !this.retiringProjectIds.has(projectIdArg) && !this.flexProjectIdentityRecoveryPending.has(projectIdArg) && !this.retiringSessionKeys.has(this.sessionStateMutationKey( projectIdArg, flexRuntimeId(sessionIdArg), )); if (managed) { const sessionId = flexRuntimeId(sessionIdArg); const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionId); const generationKey = JSON.stringify([ sessionGenerationIdArg, sessionGenerationSequenceArg, ]); const previousGeneration = this.currentFlexEventGenerations.get(sessionKey); if (previousGeneration !== undefined && previousGeneration !== generationKey) { this.purgeFlexToolExecutionsForSession(projectIdArg, sessionId); } this.currentFlexEventGenerations.set(sessionKey, generationKey); } return managed; } catch { return false; } } private handleAuthorizedFlexEvent(eventArg: TFlexChildEvent): void { if (eventArg.type === 'sessions.changed') { const isGlobalGap = !('scopeId' in eventArg); if ( isGlobalGap && (this.autoAcceptPermissions || this.hasFlexYoloSessions()) ) { for (const projectId of this.projectIdsByDirectory.values()) { this.queueFlexAutoAcceptSweep(projectId); } } if (isGlobalGap) { this.advanceFlexStreams(true); return; } this.queueFlexControllerEvent({ type: 'session.changed', harnessId: 'flex', projectId: eventArg.scopeId, sessionId: flexRuntimeId(eventArg.sessionId), timestamp: Date.now(), }, eventArg); return; } if (eventArg.type === 'tool.updated') { this.handleFlexToolExecution(eventArg); return; } if (eventArg.type === 'reasoning.updated') { this.handleFlexReasoningUpdate(eventArg); return; } if (eventArg.type === 'text.updated') { this.handleFlexTextUpdate(eventArg); return; } if (eventArg.type === 'part.delta') { this.handleFlexPartDelta(eventArg); return; } if (eventArg.type === 'permissions.changed') { if (this.autoAcceptPermissions || this.hasFlexYoloSessions()) { const run = this.activeFlexRunAuthorityForSessionGeneration( eventArg.scopeId, eventArg.sessionId, eventArg, ); if (run) { this.queueFlexAutoAcceptSweep( eventArg.scopeId, eventArg.sessionId, { type: 'managed', run }, ); } } this.queueFlexControllerEvent({ type: 'permissions.changed', harnessId: 'flex', projectId: eventArg.scopeId, sessionId: flexRuntimeId(eventArg.sessionId), timestamp: Date.now(), }, eventArg); return; } if ('queueId' in eventArg) { if (eventArg.type === 'prompt.message-bound') { const bound = this.harnessOperationManager.bindFlexMessage( eventArg.scopeId, flexRuntimeId(eventArg.sessionId), eventArg.queueId, eventArg.runId, flexRuntimeId(eventArg.messageId), ); if (bound) this.queueHarnessPromptChanged(eventArg.scopeId, flexRuntimeId(eventArg.sessionId)); return; } if (eventArg.type === 'prompt.finished') { const key = flexPromptQueueKey( eventArg.scopeId, eventArg.sessionId, eventArg, eventArg.queueId, ); const finishedEvent: TFlexFinishedPromptEvent = { ...eventArg, type: 'prompt.finished', }; this.pendingFlexFinishedPrompts.set(key, finishedEvent); trimOldestMapEntries( this.pendingFlexFinishedPrompts, maxPendingFlexPromptFinishes, ); this.consumePendingFlexFinishedPrompt( eventArg.scopeId, eventArg.sessionId, eventArg, eventArg.queueId, ); } this.queueFlexControllerEvent({ type: 'session.changed', harnessId: 'flex', projectId: eventArg.scopeId, sessionId: flexRuntimeId(eventArg.sessionId), timestamp: Date.now(), }, eventArg); return; } if (eventArg.type === 'provider.connection.changed') { const loginKey = controllerRuntimeIdKey(flexRuntimeId(eventArg.connection.loginId)); const retained = this.flexProviderLogins.get(loginKey); if (retained) { this.flexProviderLogins.set( loginKey, this.toPublicProviderLogin(retained, eventArg.connection), ); } const modelsChanged = eventArg.connection.status !== 'active' && this.flexModelOptionsByConnection.delete(eventArg.connection.loginId); this.queueFlexControllerEvent({ type: 'harness.changed', harnessId: 'flex', timestamp: Date.now(), }); if (modelsChanged) { this.queueFlexControllerEvent({ type: 'settings.changed', timestamp: Date.now(), }); } if (eventArg.connection.status === 'active') { void this.trackOperationPromise( this.hydrateFlexProviderCatalogs(false, [eventArg.connection]), ).catch(() => undefined); } if (eventArg.connection.status === 'deleting') { void this.trackOperationPromise(this.recoverDeletingFlexProviderConnection( eventArg.connection.loginId, this.operationAbortController.signal, )).catch(() => { console.error('Flex provider logout recovery failed; it will retry on restart.'); }); } return; } if (eventArg.type === 'session.history.changed') { this.queueFlexControllerEvent({ type: 'session.history.changed', harnessId: 'flex', projectId: eventArg.scopeId, sessionId: flexRuntimeId(eventArg.sessionId), timestamp: Date.now(), }, eventArg); return; } const jobKey = controllerRuntimeIdKey(flexRuntimeId(eventArg.jobId)); const cached = this.flexRefreshJobs.get(jobKey); if (!cached) { this.pendingFlexFinishedJobs.set(jobKey, eventArg); trimOldestMapEntries(this.pendingFlexFinishedJobs, maxFlexRefreshJobs); return; } const options = eventArg.status === 'completed' && eventArg.models ? this.toFlexModelOptions(cached.providerConnectionId, eventArg.models) : undefined; if (eventArg.status === 'failed' || eventArg.status === 'cancelled') { this.flexModelOptionsByConnection.delete(cached.providerConnectionId); } if (options) { this.flexModelOptionsByConnection.set(cached.providerConnectionId, options); trimOldestMapEntries(this.flexModelOptionsByConnection, maxFlexModelConnections); } cached.job = { ...cached.job, status: eventArg.status, ...(options === undefined ? {} : { modelCount: options.length }), }; this.queueFlexControllerEvent({ type: 'settings.changed', timestamp: Date.now(), }); } private getFlexToolStreamCursor(): IControllerToolStreamCursor { return { streamEpoch: this.flexToolStreamEpoch, revision: this.flexToolStreamRevision, }; } private async prepareCoreMessagePageForLiveSnapshots( pageArg: IControllerMessagePage, snapshotsArg: Array<{ messageId: IControllerRuntimeId }>, reloadPageArg: (limitArg: number) => Promise<IControllerMessagePage>, ): Promise<IControllerMessagePage> { const durableKeys = new Set( pageArg.bundles.map((bundle) => controllerRuntimeIdKey(bundle.sourceMessageId)), ); const liveOnlyKeys = new Set( snapshotsArg .map((snapshot) => controllerRuntimeIdKey(snapshot.messageId)) .filter((key) => !durableKeys.has(key)), ); if (liveOnlyKeys.size === 0) return pageArg; const durableLimit = controllerInitialMessageBundleLimit - Math.min( liveOnlyKeys.size, controllerInitialMessageBundleLimit - 1, ); if (pageArg.bundles.length <= durableLimit) return pageArg; // A continuation cursor belongs immediately after the durable page. Keep // capacity for live-only sources so bounding cannot drop a durable bundle // and create an unrecoverable cursor gap. return reloadPageArg(durableLimit); } private boundLiveSnapshotsForCorePage( pageArg: IControllerMessagePage, executionsArg: IControllerToolExecution[], reasoningUpdatesArg: IControllerReasoningUpdate[], textUpdatesArg: IControllerTextUpdate[], ): { executions: IControllerToolExecution[]; reasoningUpdates: IControllerReasoningUpdate[]; textUpdates: IControllerTextUpdate[]; } { const durableKeys = new Set( pageArg.bundles.map((bundle) => controllerRuntimeIdKey(bundle.sourceMessageId)), ); const latestLiveObservedAtBySource = new Map<string, number>(); for (const snapshot of [...executionsArg, ...reasoningUpdatesArg, ...textUpdatesArg]) { const key = controllerRuntimeIdKey(snapshot.messageId); if (durableKeys.has(key)) continue; latestLiveObservedAtBySource.set( key, Math.max(latestLiveObservedAtBySource.get(key) ?? -1, snapshot.sourceUpdatedAt), ); } const retainedLiveKeys = new Set( [...latestLiveObservedAtBySource.entries()] .sort((left, right) => left[1] - right[1]) .slice(-(controllerInitialMessageBundleLimit - pageArg.bundles.length)) .map(([key]) => key), ); const keepSnapshot = (snapshotArg: { messageId: IControllerRuntimeId }): boolean => { const key = controllerRuntimeIdKey(snapshotArg.messageId); return durableKeys.has(key) || retainedLiveKeys.has(key); }; const executions = executionsArg.filter(keepSnapshot); const reasoningUpdates = reasoningUpdatesArg.filter(keepSnapshot); const textUpdates = textUpdatesArg.filter(keepSnapshot); return { executions, reasoningUpdates, textUpdates }; } private getFlexMessageStreamCursor(): IControllerMessageStreamCursor { return { streamEpoch: this.flexMessageStreamEpoch, revision: this.flexMessageStreamRevision, }; } private flexCorePageRepresentsToolExecution( pageArg: IControllerMessagePage, executionArg: IControllerToolExecution, ): boolean { const bundle = pageArg.bundles.find((candidateArg) => ( controllerRuntimeIdKey(candidateArg.sourceMessageId) === controllerRuntimeIdKey(executionArg.messageId) )); const message = bundle?.messages.find((candidateArg) => ( controllerRuntimeIdKey(candidateArg.id) === controllerRuntimeIdKey(executionArg.partId) && ( executionArg.order === undefined || ( candidateArg.order?.messageIndex === executionArg.order.messageIndex && candidateArg.order.partIndex === executionArg.order.partIndex ) ) )); return controllerLiveToolExecutionIsCovered(executionArg, message?.toolCall); } private flexCorePageRepresentsReasoningUpdate( pageArg: IControllerMessagePage, updateArg: IControllerReasoningUpdate, ): boolean { const bundle = pageArg.bundles.find((candidateArg) => ( controllerRuntimeIdKey(candidateArg.sourceMessageId) === controllerRuntimeIdKey(updateArg.messageId) )); const message = bundle?.messages.find((candidateArg) => ( updateArg.order === undefined || ( candidateArg.order?.messageIndex === updateArg.order.messageIndex && candidateArg.order.partIndex === updateArg.order.partIndex ) )); const part = message?.reasoning?.find((candidateArg) => ( controllerRuntimeIdKey(candidateArg.id) === controllerRuntimeIdKey(updateArg.partId) )); return part !== undefined && (part.text === updateArg.text || part.text.startsWith(updateArg.text)) && (updateArg.status === 'running' || part.endedAt !== undefined || message?.streaming !== true); } private flexCorePageRepresentsTextUpdate( pageArg: IControllerMessagePage, updateArg: IControllerTextUpdate, ): boolean { if (updateArg.order === undefined) return false; const bundle = pageArg.bundles.find((candidateArg) => ( controllerRuntimeIdKey(candidateArg.sourceMessageId) === controllerRuntimeIdKey(updateArg.messageId) )); const message = bundle?.messages.find((candidateArg) => ( candidateArg.role === 'assistant' && candidateArg.order?.messageIndex === updateArg.order!.messageIndex && candidateArg.order.partIndex === updateArg.order!.partIndex )); return message !== undefined && (message.text === updateArg.text || message.text.startsWith(updateArg.text)) && (updateArg.status === 'running' || message.streaming !== true); } private retireCoveredFlexSnapshots( projectIdArg: string, corePageArg: IControllerMessagePage, executionsArg: IControllerToolExecution[], reasoningUpdatesArg: IControllerReasoningUpdate[], textUpdatesArg: IControllerTextUpdate[], ): void { for (const execution of executionsArg) { if ( (execution.status !== 'completed' && execution.status !== 'error' && execution.status !== 'stopped') || !this.flexCorePageRepresentsToolExecution(corePageArg, execution) ) continue; for (const [key, current] of this.latestFlexToolExecutions) { if ( current.projectId === projectIdArg && current.execution.streamEpoch === execution.streamEpoch && current.execution.revision === execution.revision && controllerRuntimeIdKey(current.execution.sessionId) === controllerRuntimeIdKey(execution.sessionId) && controllerRuntimeIdKey(current.execution.partId) === controllerRuntimeIdKey(execution.partId) ) { this.deleteLatestFlexToolExecution(key); break; } } } for (const update of reasoningUpdatesArg) { if ( update.status === 'running' || !this.flexCorePageRepresentsReasoningUpdate(corePageArg, update) ) continue; for (const [key, current] of this.latestFlexReasoningUpdates) { if ( current.projectId === projectIdArg && current.update.streamEpoch === update.streamEpoch && current.update.revision === update.revision && controllerRuntimeIdKey(current.update.sessionId) === controllerRuntimeIdKey(update.sessionId) && controllerRuntimeIdKey(current.update.partId) === controllerRuntimeIdKey(update.partId) ) { this.deleteLatestFlexReasoningUpdate(key); break; } } } for (const update of textUpdatesArg) { if ( update.status !== 'completed' || !this.flexCorePageRepresentsTextUpdate(corePageArg, update) ) continue; for (const [key, current] of this.latestFlexTextUpdates) { if ( current.projectId === projectIdArg && current.update.streamEpoch === update.streamEpoch && current.update.revision === update.revision && controllerRuntimeIdKey(current.update.sessionId) === controllerRuntimeIdKey(update.sessionId) && controllerRuntimeIdKey(current.update.partId) === controllerRuntimeIdKey(update.partId) ) { this.deleteLatestFlexTextUpdate(key); break; } } } } private deleteLatestFlexToolExecution(keyArg: string): boolean { const entry = this.latestFlexToolExecutions.get(keyArg); if (!entry) return false; this.latestFlexToolExecutions.delete(keyArg); this.latestFlexToolExecutionBytes = Math.max( 0, this.latestFlexToolExecutionBytes - (entry.bytes ?? 0), ); return true; } private deleteLatestFlexReasoningUpdate(keyArg: string): boolean { const entry = this.latestFlexReasoningUpdates.get(keyArg); if (!entry) return false; this.latestFlexReasoningUpdates.delete(keyArg); this.latestFlexMessageUpdateBytes = Math.max( 0, this.latestFlexMessageUpdateBytes - (entry.bytes ?? 0), ); return true; } private deleteLatestFlexTextUpdate(keyArg: string): boolean { const entry = this.latestFlexTextUpdates.get(keyArg); if (!entry) return false; this.latestFlexTextUpdates.delete(keyArg); this.latestFlexMessageUpdateBytes = Math.max( 0, this.latestFlexMessageUpdateBytes - (entry.bytes ?? 0), ); return true; } private setLatestFlexToolExecution( keyArg: string, entryArg: ILatestToolExecution, bytesArg: number, ): void { this.deleteLatestFlexToolExecution(keyArg); this.latestFlexToolExecutions.set(keyArg, { ...entryArg, bytes: bytesArg }); this.latestFlexToolExecutionBytes += bytesArg; } private setLatestFlexReasoningUpdate( keyArg: string, entryArg: ILatestReasoningUpdate, bytesArg: number, ): void { this.deleteLatestFlexReasoningUpdate(keyArg); this.latestFlexReasoningUpdates.set(keyArg, { ...entryArg, bytes: bytesArg }); this.latestFlexMessageUpdateBytes += bytesArg; } private setLatestFlexTextUpdate( keyArg: string, entryArg: ILatestTextUpdate, bytesArg: number, ): void { this.deleteLatestFlexTextUpdate(keyArg); this.latestFlexTextUpdates.set(keyArg, { ...entryArg, bytes: bytesArg }); this.latestFlexMessageUpdateBytes += bytesArg; } private flexControllerEventLiveBytes(eventArg: IControllerEvent): { tool: number; message: number; } { const bytes = Buffer.byteLength(JSON.stringify(eventArg), 'utf8'); return { tool: eventArg.toolExecution ? bytes : 0, message: eventArg.reasoningUpdate || eventArg.reasoningDelta || eventArg.textUpdate || eventArg.textDelta ? bytes : 0, }; } private deletePendingFlexControllerEvent(keyArg: string): boolean { const event = this.pendingFlexControllerEvents.get(keyArg); if (!event) return false; this.pendingFlexControllerEvents.delete(keyArg); const bytes = this.flexControllerEventLiveBytes(event); this.pendingFlexToolEventBytes = Math.max(0, this.pendingFlexToolEventBytes - bytes.tool); this.pendingFlexMessageEventBytes = Math.max( 0, this.pendingFlexMessageEventBytes - bytes.message, ); return true; } private setPendingFlexControllerEvent(keyArg: string, eventArg: IControllerEvent): void { this.deletePendingFlexControllerEvent(keyArg); this.pendingFlexControllerEvents.set(keyArg, eventArg); const bytes = this.flexControllerEventLiveBytes(eventArg); this.pendingFlexToolEventBytes += bytes.tool; this.pendingFlexMessageEventBytes += bytes.message; } private clearPendingFlexControllerEvents(): void { this.pendingFlexControllerEvents.clear(); this.pendingFlexToolEventBytes = 0; this.pendingFlexMessageEventBytes = 0; } private flexSnapshotsForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, toolCursorArg: IControllerToolStreamCursor, messageCursorArg: IControllerMessageStreamCursor, ): { executions: IControllerToolExecution[]; reasoningUpdates: IControllerReasoningUpdate[]; textUpdates: IControllerTextUpdate[]; } { return { executions: this.flexToolExecutionsForSession(projectIdArg, sessionIdArg, toolCursorArg), reasoningUpdates: this.flexReasoningUpdatesForSession( projectIdArg, sessionIdArg, messageCursorArg, ), textUpdates: this.flexTextUpdatesForSession( projectIdArg, sessionIdArg, messageCursorArg, ), }; } private flexToolExecutionsForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, cursorArg: IControllerToolStreamCursor, ): IControllerToolExecution[] { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return [...this.latestFlexToolExecutions.values()] .filter((entryArg) => ( entryArg.projectId === projectIdArg && controllerRuntimeIdKey(entryArg.execution.sessionId) === sessionKey && entryArg.execution.streamEpoch === cursorArg.streamEpoch && entryArg.execution.revision <= cursorArg.revision )) .map((entryArg) => entryArg.execution); } private flexReasoningUpdatesForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, cursorArg: IControllerMessageStreamCursor, ): IControllerReasoningUpdate[] { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return [...this.latestFlexReasoningUpdates.values()] .filter((entryArg) => ( entryArg.projectId === projectIdArg && controllerRuntimeIdKey(entryArg.update.sessionId) === sessionKey && entryArg.update.streamEpoch === cursorArg.streamEpoch && entryArg.update.revision <= cursorArg.revision )) .map((entryArg) => entryArg.update); } private flexTextUpdatesForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, cursorArg: IControllerMessageStreamCursor, ): IControllerTextUpdate[] { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return [...this.latestFlexTextUpdates.values()] .filter((entryArg) => ( entryArg.projectId === projectIdArg && controllerRuntimeIdKey(entryArg.update.sessionId) === sessionKey && entryArg.update.streamEpoch === cursorArg.streamEpoch && entryArg.update.revision <= cursorArg.revision )) .map((entryArg) => entryArg.update); } private advanceFlexToolStreamEpoch(includeSessionsChangedArg: boolean): boolean { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return false; const pendingEntries = includeSessionsChangedArg ? 2 : 1; if ( this.pendingFlexControllerEvents.size > maxPendingFlexControllerEvents - pendingEntries ) { this.advanceFlexStreams(includeSessionsChangedArg); return true; } this.flexToolStreamEpoch += 1; this.flexToolStreamRevision = 0; this.latestFlexToolExecutions.clear(); this.latestFlexToolExecutionBytes = 0; for (const [key, event] of this.pendingFlexControllerEvents) { if (event.toolExecution) this.deletePendingFlexControllerEvent(key); } const timestamp = Date.now(); const barrier: IControllerEvent = { type: 'harness.changed', harnessId: 'flex', toolStreamEpoch: this.flexToolStreamEpoch, timestamp, }; this.setPendingFlexControllerEvent(this.controllerEventKey(barrier), barrier); if (includeSessionsChangedArg) { const sessionsChanged: IControllerEvent = { type: 'sessions.changed', harnessId: 'flex', timestamp, }; this.setPendingFlexControllerEvent( this.controllerEventKey(sessionsChanged), sessionsChanged, ); } this.startFlexControllerEventDrain(); return false; } private advanceFlexMessageStreamEpoch(includeSessionsChangedArg: boolean): boolean { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return false; const pendingEntries = includeSessionsChangedArg ? 2 : 1; if ( this.pendingFlexControllerEvents.size > maxPendingFlexControllerEvents - pendingEntries ) { this.advanceFlexStreams(includeSessionsChangedArg); return true; } this.flexMessageStreamEpoch += 1; this.flexMessageStreamRevision = 0; this.latestFlexReasoningUpdates.clear(); this.latestFlexTextUpdates.clear(); this.latestFlexMessageUpdateBytes = 0; for (const [key, event] of this.pendingFlexControllerEvents) { if ( event.reasoningUpdate || event.reasoningDelta || event.textUpdate || event.textDelta ) this.deletePendingFlexControllerEvent(key); } const timestamp = Date.now(); const barrier: IControllerEvent = { type: 'harness.changed', harnessId: 'flex', messageStreamEpoch: this.flexMessageStreamEpoch, timestamp, }; this.setPendingFlexControllerEvent(this.controllerEventKey(barrier), barrier); if (includeSessionsChangedArg) { const sessionsChanged: IControllerEvent = { type: 'sessions.changed', harnessId: 'flex', timestamp, }; this.setPendingFlexControllerEvent( this.controllerEventKey(sessionsChanged), sessionsChanged, ); } this.startFlexControllerEventDrain(); return false; } private advanceFlexStreams(includeSessionsChangedArg: boolean): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; this.clearPendingFlexControllerEvents(); this.flexToolStreamEpoch += 1; this.flexToolStreamRevision = 0; this.flexMessageStreamEpoch += 1; this.flexMessageStreamRevision = 0; this.latestFlexToolExecutions.clear(); this.latestFlexReasoningUpdates.clear(); this.latestFlexTextUpdates.clear(); this.latestFlexToolExecutionBytes = 0; this.latestFlexMessageUpdateBytes = 0; const timestamp = Date.now(); const barrier: IControllerEvent = { type: 'harness.changed', harnessId: 'flex', toolStreamEpoch: this.flexToolStreamEpoch, messageStreamEpoch: this.flexMessageStreamEpoch, timestamp, }; this.setPendingFlexControllerEvent(this.controllerEventKey(barrier), barrier); if (includeSessionsChangedArg) { const sessionsChanged: IControllerEvent = { type: 'sessions.changed', harnessId: 'flex', timestamp, }; this.setPendingFlexControllerEvent( this.controllerEventKey(sessionsChanged), sessionsChanged, ); } this.startFlexControllerEventDrain(); } private purgeFlexToolExecutionsForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { const sessionKey = controllerRuntimeIdKey(sessionIdArg); for (const [key, entry] of this.latestFlexToolExecutions) { if ( entry.projectId === projectIdArg && controllerRuntimeIdKey(entry.execution.sessionId) === sessionKey ) this.deleteLatestFlexToolExecution(key); } for (const [key, entry] of this.latestFlexReasoningUpdates) { if ( entry.projectId === projectIdArg && controllerRuntimeIdKey(entry.update.sessionId) === sessionKey ) this.deleteLatestFlexReasoningUpdate(key); } for (const [key, entry] of this.latestFlexTextUpdates) { if ( entry.projectId === projectIdArg && controllerRuntimeIdKey(entry.update.sessionId) === sessionKey ) this.deleteLatestFlexTextUpdate(key); } for (const [key, event] of this.pendingFlexControllerEvents) { if ( event.projectId === projectIdArg && ( event.toolExecution || event.reasoningUpdate || event.reasoningDelta || event.textUpdate || event.textDelta ) && controllerRuntimeIdKey( event.toolExecution?.sessionId ?? event.reasoningUpdate?.sessionId ?? event.reasoningDelta?.sessionId ?? event.textUpdate?.sessionId ?? event.textDelta!.sessionId, ) === sessionKey ) this.deletePendingFlexControllerEvent(key); } } private purgeOpenCodeStreamStateForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { const sessionKey = controllerRuntimeIdKey(sessionIdArg); for (const [key, entry] of this.latestOpenCodeToolExecutions) { if ( entry.projectId === projectIdArg && controllerRuntimeIdKey(entry.execution.sessionId) === sessionKey ) this.latestOpenCodeToolExecutions.delete(key); } for (const [key, entry] of this.latestOpenCodeReasoningUpdates) { if ( entry.projectId === projectIdArg && controllerRuntimeIdKey(entry.update.sessionId) === sessionKey ) this.latestOpenCodeReasoningUpdates.delete(key); } for (const [key, entry] of this.latestOpenCodeTextUpdates) { if ( entry.projectId === projectIdArg && controllerRuntimeIdKey(entry.update.sessionId) === sessionKey ) this.latestOpenCodeTextUpdates.delete(key); } for (const [key, event] of this.pendingOpenCodeControllerEvents) { if ( event.projectId === projectIdArg && event.sessionId && controllerRuntimeIdKey(event.sessionId) === sessionKey ) this.pendingOpenCodeControllerEvents.delete(key); } } private purgeOpenCodeStreamStateForProject(projectIdArg: string): void { for (const [key, entry] of this.latestOpenCodeToolExecutions) { if (entry.projectId === projectIdArg) this.latestOpenCodeToolExecutions.delete(key); } for (const [key, entry] of this.latestOpenCodeReasoningUpdates) { if (entry.projectId === projectIdArg) this.latestOpenCodeReasoningUpdates.delete(key); } for (const [key, entry] of this.latestOpenCodeTextUpdates) { if (entry.projectId === projectIdArg) this.latestOpenCodeTextUpdates.delete(key); } for (const [key, event] of this.pendingOpenCodeControllerEvents) { if (event.projectId === projectIdArg) this.pendingOpenCodeControllerEvents.delete(key); } } private purgeFlexToolExecutionsForProject(projectIdArg: string): void { for (const [key, entry] of this.latestFlexToolExecutions) { if (entry.projectId === projectIdArg) this.deleteLatestFlexToolExecution(key); } for (const [key, entry] of this.latestFlexReasoningUpdates) { if (entry.projectId === projectIdArg) this.deleteLatestFlexReasoningUpdate(key); } for (const [key, entry] of this.latestFlexTextUpdates) { if (entry.projectId === projectIdArg) this.deleteLatestFlexTextUpdate(key); } for (const [key, event] of this.pendingFlexControllerEvents) { if ( event.projectId === projectIdArg && ( event.toolExecution || event.reasoningUpdate || event.reasoningDelta || event.textUpdate || event.textDelta ) ) { this.deletePendingFlexControllerEvent(key); } } } private handleFlexToolExecution( eventArg: Extract<TFlexChildEvent, { type: 'tool.updated' }>, ): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; if (this.flexToolStreamEpoch === 0) this.advanceFlexToolStreamEpoch(false); const sessionId = flexRuntimeId(eventArg.sessionId); const partId = flexRuntimeId(eventArg.part.partId); const cacheKey = this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.scopeId, { sessionId, partId }), eventArg, ); if ( !this.latestFlexToolExecutions.has(cacheKey) && this.latestFlexToolExecutions.size >= maxLatestFlexToolExecutions ) this.advanceFlexToolStreamEpoch(true); const sourceUpdatedAt = Date.parse(eventArg.timestamp); const previous = this.latestFlexToolExecutions.get(cacheKey)?.execution; const terminal = eventArg.part.status !== 'running'; let snapshot: TControllerToolExecutionSnapshot; try { snapshot = boundLiveToolExecution({ sessionId, messageId: flexRuntimeId(eventArg.messageId), partId, callId: flexRuntimeId(eventArg.part.toolCallId), toolName: eventArg.part.toolName, status: eventArg.part.status === 'running' ? 'running' : eventArg.part.status === 'completed' ? 'completed' : eventArg.part.status === 'cancelled' ? 'stopped' : 'error', input: eventArg.part.input, ...(eventArg.part.output === undefined ? {} : { output: eventArg.part.output }), ...(eventArg.part.error === undefined ? {} : { errorText: eventArg.part.error }), ...(eventArg.part.childSessionId === undefined ? {} : { childSessionId: flexRuntimeId(eventArg.part.childSessionId) }), ...(eventArg.part.model === undefined ? {} : { model: `${eventArg.part.model.provider}/${eventArg.part.model.model}` }), ...(terminal ? { ...(previous?.startedAt === undefined ? {} : { startedAt: previous.startedAt }), finishedAt: sourceUpdatedAt, } : { startedAt: previous?.startedAt ?? sourceUpdatedAt }), order: { messageIndex: eventArg.messageIndex, partIndex: eventArg.partIndex, }, sourceUpdatedAt, }, 'Flex'); } catch { this.advanceFlexToolStreamEpoch(true); return; } this.flexToolStreamRevision += 1; const execution: IControllerToolExecution = { ...snapshot, revision: this.flexToolStreamRevision, streamEpoch: this.flexToolStreamEpoch, }; const controllerEvent: IControllerEvent = { type: 'session.tool.updated', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, toolExecution: execution, timestamp: sourceUpdatedAt, }; let eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); if (eventBytes > controllerMaxToolEventBytes) { this.advanceFlexToolStreamEpoch(true); return; } const replacedBytes = this.latestFlexToolExecutions.get(cacheKey)?.bytes ?? 0; if ( this.latestFlexToolExecutionBytes - replacedBytes + eventBytes > maxLatestFlexToolExecutionBytes ) { this.advanceFlexToolStreamEpoch(true); this.flexToolStreamRevision += 1; execution.revision = this.flexToolStreamRevision; execution.streamEpoch = this.flexToolStreamEpoch; eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); } this.setLatestFlexToolExecution( cacheKey, { projectId: eventArg.scopeId, execution }, eventBytes, ); this.queueFlexControllerEvent(controllerEvent, eventArg); } private handleFlexReasoningUpdate( eventArg: Extract<TFlexChildEvent, { type: 'reasoning.updated' }>, ): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; if (this.flexMessageStreamEpoch === 0) this.advanceFlexMessageStreamEpoch(false); const sourceUpdatedAt = Date.parse(eventArg.timestamp); if (!Number.isFinite(sourceUpdatedAt)) { this.advanceFlexMessageStreamEpoch(true); return; } const sessionId = flexRuntimeId(eventArg.sessionId); const partId = flexRuntimeId(eventArg.part.partId); const key = this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.scopeId, { sessionId, partId }), eventArg, ); if ( !this.latestFlexReasoningUpdates.has(key) && this.latestFlexReasoningUpdates.size >= maxLatestFlexToolExecutions ) { this.advanceFlexMessageStreamEpoch(true); } this.flexMessageStreamRevision += 1; const update: IControllerReasoningUpdate = { sessionId, messageId: flexRuntimeId(eventArg.messageId), partId, text: eventArg.part.text, status: eventArg.part.status, order: { messageIndex: eventArg.messageIndex, partIndex: eventArg.partIndex }, sourceUpdatedAt, revision: this.flexMessageStreamRevision, streamEpoch: this.flexMessageStreamEpoch, }; const controllerEvent: IControllerEvent = { type: 'session.reasoning.updated', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, reasoningUpdate: update, timestamp: sourceUpdatedAt, }; let eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); if (eventBytes > controllerMaxLiveMessageEventBytes) { this.advanceFlexMessageStreamEpoch(true); return; } const replacedBytes = this.latestFlexReasoningUpdates.get(key)?.bytes ?? 0; if ( this.latestFlexMessageUpdateBytes - replacedBytes + eventBytes > maxLatestFlexMessageUpdateBytes ) { this.advanceFlexMessageStreamEpoch(true); this.flexMessageStreamRevision += 1; update.revision = this.flexMessageStreamRevision; update.streamEpoch = this.flexMessageStreamEpoch; eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); } this.setLatestFlexReasoningUpdate( key, { projectId: eventArg.scopeId, update, textUtf8Bytes: Buffer.byteLength(update.text, 'utf8'), }, eventBytes, ); this.queueFlexControllerEvent(controllerEvent, eventArg); } private handleFlexTextUpdate( eventArg: Extract<TFlexChildEvent, { type: 'text.updated' }>, ): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; if (this.flexMessageStreamEpoch === 0) this.advanceFlexMessageStreamEpoch(false); const sourceUpdatedAt = Date.parse(eventArg.timestamp); if (!Number.isFinite(sourceUpdatedAt)) { this.advanceFlexMessageStreamEpoch(true); return; } const sessionId = flexRuntimeId(eventArg.sessionId); const partId = flexRuntimeId(eventArg.part.partId); const key = this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.scopeId, { sessionId, partId }), eventArg, ); if (!this.latestFlexTextUpdates.has(key) && this.latestFlexTextUpdates.size >= maxLatestFlexToolExecutions) { this.advanceFlexMessageStreamEpoch(true); } this.flexMessageStreamRevision += 1; const update: IControllerTextUpdate = { sessionId, messageId: flexRuntimeId(eventArg.messageId), partId, text: eventArg.part.text, status: eventArg.status, order: { messageIndex: eventArg.messageIndex, partIndex: eventArg.partIndex }, sourceUpdatedAt, revision: this.flexMessageStreamRevision, streamEpoch: this.flexMessageStreamEpoch, }; const controllerEvent: IControllerEvent = { type: 'session.text.updated', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, textUpdate: update, timestamp: sourceUpdatedAt, }; let eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); if (eventBytes > controllerMaxLiveMessageEventBytes) { this.advanceFlexMessageStreamEpoch(true); return; } const replacedBytes = this.latestFlexTextUpdates.get(key)?.bytes ?? 0; if ( this.latestFlexMessageUpdateBytes - replacedBytes + eventBytes > maxLatestFlexMessageUpdateBytes ) { this.advanceFlexMessageStreamEpoch(true); this.flexMessageStreamRevision += 1; update.revision = this.flexMessageStreamRevision; update.streamEpoch = this.flexMessageStreamEpoch; eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); } this.setLatestFlexTextUpdate( key, { projectId: eventArg.scopeId, update, textUtf8Bytes: Buffer.byteLength(update.text, 'utf8'), }, eventBytes, ); this.queueFlexControllerEvent(controllerEvent, eventArg); } private handleFlexPartDelta( eventArg: Extract<TFlexChildEvent, { type: 'part.delta' }>, ): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; const sourceUpdatedAt = Date.parse(eventArg.timestamp); if (!Number.isFinite(sourceUpdatedAt)) { this.advanceFlexMessageStreamEpoch(true); return; } const sessionId = flexRuntimeId(eventArg.sessionId); const messageId = flexRuntimeId(eventArg.messageId); const partId = flexRuntimeId(eventArg.partId); const order = { messageIndex: eventArg.messageIndex, partIndex: eventArg.partIndex }; const key = this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.scopeId, { sessionId, partId }), eventArg, ); const cache = eventArg.partType === 'reasoning' ? this.latestFlexReasoningUpdates : this.latestFlexTextUpdates; const entry = cache.get(key); const previous = entry?.update; if ( !entry || !previous || this.flexMessageStreamEpoch === 0 || entry.projectId !== eventArg.scopeId || previous.status !== 'running' || previous.streamEpoch !== this.flexMessageStreamEpoch || previous.revision > this.flexMessageStreamRevision || controllerRuntimeIdKey(previous.sessionId) !== controllerRuntimeIdKey(sessionId) || controllerRuntimeIdKey(previous.messageId) !== controllerRuntimeIdKey(messageId) || controllerRuntimeIdKey(previous.partId) !== controllerRuntimeIdKey(partId) || previous.order?.messageIndex !== order.messageIndex || previous.order.partIndex !== order.partIndex || sourceUpdatedAt < previous.sourceUpdatedAt || entry.textUtf8Bytes !== eventArg.baseTextUtf8Bytes ) { this.advanceFlexMessageStreamEpoch(true); return; } const textUtf8Bytes = controllerTextAfterAppendUtf8Bytes( previous.text, entry.textUtf8Bytes, eventArg.delta, ); if ( textUtf8Bytes === undefined || textUtf8Bytes <= entry.textUtf8Bytes || textUtf8Bytes !== eventArg.textUtf8Bytes ) { this.advanceFlexMessageStreamEpoch(true); return; } const revision = this.flexMessageStreamRevision + 1; const update = { ...previous, text: `${previous.text}${eventArg.delta}`, sourceUpdatedAt, revision, streamEpoch: this.flexMessageStreamEpoch, } as IControllerReasoningUpdate | IControllerTextUpdate; const controllerEvent: IControllerEvent = eventArg.partType === 'reasoning' ? { type: 'session.reasoning.delta', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, reasoningDelta: { sessionId, messageId, partId, delta: eventArg.delta, baseTextUtf8Bytes: eventArg.baseTextUtf8Bytes, textUtf8Bytes, order, sourceUpdatedAt, revision, streamEpoch: this.flexMessageStreamEpoch, }, timestamp: sourceUpdatedAt, } : { type: 'session.text.delta', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, textDelta: { sessionId, messageId, partId, delta: eventArg.delta, baseTextUtf8Bytes: eventArg.baseTextUtf8Bytes, textUtf8Bytes, order, sourceUpdatedAt, revision, streamEpoch: this.flexMessageStreamEpoch, }, timestamp: sourceUpdatedAt, }; const snapshotEvent: IControllerEvent = eventArg.partType === 'reasoning' ? { type: 'session.reasoning.updated', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, reasoningUpdate: update as IControllerReasoningUpdate, timestamp: sourceUpdatedAt, } : { type: 'session.text.updated', harnessId: 'flex', projectId: eventArg.scopeId, sessionId, textUpdate: update as IControllerTextUpdate, timestamp: sourceUpdatedAt, }; const eventBytes = Buffer.byteLength(JSON.stringify(controllerEvent), 'utf8'); const snapshotBytes = Buffer.byteLength(JSON.stringify(snapshotEvent), 'utf8'); if ( eventBytes > controllerMaxLiveMessageEventBytes || snapshotBytes > controllerMaxLiveMessageEventBytes || this.latestFlexMessageUpdateBytes - (entry.bytes ?? 0) + snapshotBytes > maxLatestFlexMessageUpdateBytes ) { this.advanceFlexMessageStreamEpoch(true); return; } this.flexMessageStreamRevision = revision; if (eventArg.partType === 'reasoning') { this.setLatestFlexReasoningUpdate( key, { projectId: eventArg.scopeId, update: update as IControllerReasoningUpdate, textUtf8Bytes, }, snapshotBytes, ); } else { this.setLatestFlexTextUpdate( key, { projectId: eventArg.scopeId, update: update as IControllerTextUpdate, textUtf8Bytes, }, snapshotBytes, ); } this.queueFlexControllerEvent(controllerEvent, eventArg); } private getOpenCodeToolStreamCursor(): IControllerToolStreamCursor { return { streamEpoch: this.openCodeToolStreamEpoch, revision: this.openCodeToolStreamRevision, }; } private getOpenCodeMessageStreamCursor(): IControllerMessageStreamCursor { return { streamEpoch: this.openCodeMessageStreamEpoch, revision: this.openCodeMessageStreamRevision, }; } private openCodeToolExecutionKey( projectIdArg: string, executionArg: Pick<IControllerToolExecution, 'sessionId' | 'partId'>, ): string { return JSON.stringify([ projectIdArg, controllerRuntimeIdKey(executionArg.sessionId), controllerRuntimeIdKey(executionArg.partId), ]); } private flexEventCacheKey(keyArg: string, generationArg: TFlexSessionGeneration): string { return `${keyArg}\0${JSON.stringify([ generationArg.sessionGenerationId, generationArg.sessionGenerationSequence, ])}`; } private openCodeToolExecutionsForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, throughCursorArg?: IControllerToolStreamCursor, ): IControllerToolExecution[] { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return [...this.latestOpenCodeToolExecutions.values()] .filter((entryArg) => ( entryArg.projectId === projectIdArg && controllerRuntimeIdKey(entryArg.execution.sessionId) === sessionKey && entryArg.execution.streamEpoch === this.openCodeToolStreamEpoch && ( throughCursorArg === undefined || ( entryArg.execution.streamEpoch === throughCursorArg.streamEpoch && entryArg.execution.revision <= throughCursorArg.revision ) ) )) .map((entryArg) => entryArg.execution); } private openCodeReasoningUpdatesForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, throughCursorArg?: IControllerMessageStreamCursor, ): IControllerReasoningUpdate[] { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return [...this.latestOpenCodeReasoningUpdates.values()] .filter((entryArg) => ( entryArg.projectId === projectIdArg && controllerRuntimeIdKey(entryArg.update.sessionId) === sessionKey && entryArg.update.streamEpoch === this.openCodeMessageStreamEpoch && ( throughCursorArg === undefined || ( entryArg.update.streamEpoch === throughCursorArg.streamEpoch && entryArg.update.revision <= throughCursorArg.revision ) ) )) .map((entryArg) => entryArg.update); } private openCodeTextUpdatesForSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, throughCursorArg?: IControllerMessageStreamCursor, ): IControllerTextUpdate[] { const sessionKey = controllerRuntimeIdKey(sessionIdArg); return [...this.latestOpenCodeTextUpdates.values()] .filter((entryArg) => ( entryArg.projectId === projectIdArg && controllerRuntimeIdKey(entryArg.update.sessionId) === sessionKey && entryArg.update.streamEpoch === this.openCodeMessageStreamEpoch && ( throughCursorArg === undefined || ( entryArg.update.streamEpoch === throughCursorArg.streamEpoch && entryArg.update.revision <= throughCursorArg.revision ) ) )) .map((entryArg) => entryArg.update); } private advanceOpenCodeToolStreamEpoch(includeSessionsChangedArg: boolean): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; this.openCodeToolStreamEpoch += 1; this.openCodeToolStreamRevision = 0; this.latestOpenCodeToolExecutions.clear(); for (const [key, event] of this.pendingOpenCodeControllerEvents) { if (event.toolExecution) this.pendingOpenCodeControllerEvents.delete(key); } const timestamp = Date.now(); const barrier: IControllerEvent = { type: 'harness.changed', harnessId: 'opencode', toolStreamEpoch: this.openCodeToolStreamEpoch, timestamp, }; this.pendingOpenCodeControllerEvents.set(this.controllerEventKey(barrier), barrier); if (includeSessionsChangedArg) { const sessionsChanged: IControllerEvent = { type: 'sessions.changed', harnessId: 'opencode', timestamp, }; this.pendingOpenCodeControllerEvents.set( this.controllerEventKey(sessionsChanged), sessionsChanged, ); } this.startOpenCodeControllerEventDrain(); } private advanceOpenCodeMessageStreamEpoch(includeSessionsChangedArg: boolean): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; const pendingEntries = includeSessionsChangedArg ? 2 : 1; if ( this.pendingOpenCodeControllerEvents.size > maxPendingOpenCodeControllerEvents - pendingEntries ) { this.advanceOpenCodeStreams(includeSessionsChangedArg); return; } this.openCodeMessageStreamEpoch += 1; this.openCodeMessageStreamRevision = 0; this.latestOpenCodeReasoningUpdates.clear(); this.latestOpenCodeTextUpdates.clear(); for (const [key, event] of this.pendingOpenCodeControllerEvents) { if ( event.reasoningUpdate || event.reasoningDelta || event.textUpdate || event.textDelta ) this.pendingOpenCodeControllerEvents.delete(key); } const timestamp = Date.now(); const barrier: IControllerEvent = { type: 'harness.changed', harnessId: 'opencode', messageStreamEpoch: this.openCodeMessageStreamEpoch, timestamp, }; this.pendingOpenCodeControllerEvents.set(this.controllerEventKey(barrier), barrier); if (includeSessionsChangedArg) { const sessionsChanged: IControllerEvent = { type: 'sessions.changed', harnessId: 'opencode', timestamp, }; this.pendingOpenCodeControllerEvents.set( this.controllerEventKey(sessionsChanged), sessionsChanged, ); } this.startOpenCodeControllerEventDrain(); } private advanceOpenCodeStreams(includeSessionsChangedArg: boolean): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; this.pendingOpenCodeControllerEvents.clear(); this.openCodeToolStreamEpoch += 1; this.openCodeToolStreamRevision = 0; this.openCodeMessageStreamEpoch += 1; this.openCodeMessageStreamRevision = 0; this.latestOpenCodeToolExecutions.clear(); this.latestOpenCodeReasoningUpdates.clear(); this.latestOpenCodeTextUpdates.clear(); const timestamp = Date.now(); const barrier: IControllerEvent = { type: 'harness.changed', harnessId: 'opencode', toolStreamEpoch: this.openCodeToolStreamEpoch, messageStreamEpoch: this.openCodeMessageStreamEpoch, timestamp, }; this.pendingOpenCodeControllerEvents.set(this.controllerEventKey(barrier), barrier); if (includeSessionsChangedArg) { const sessionsChanged: IControllerEvent = { type: 'sessions.changed', harnessId: 'opencode', timestamp, }; this.pendingOpenCodeControllerEvents.set( this.controllerEventKey(sessionsChanged), sessionsChanged, ); } this.startOpenCodeControllerEventDrain(); } private handleOpenCodeReasoningUpdate( updateArg: TOpenCodeReasoningUpdate, directoryArg: string, ): void { this.handleOpenCodeMessageUpdate('reasoning', updateArg, directoryArg); } private handleOpenCodeTextUpdate( updateArg: TOpenCodeTextUpdate, directoryArg: string, ): void { this.handleOpenCodeMessageUpdate('text', updateArg, directoryArg); } private handleOpenCodeMessageUpdate( typeArg: 'reasoning' | 'text', updateArg: TOpenCodeReasoningUpdate | TOpenCodeTextUpdate, directoryArg: string, ): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; if (this.openCodeMessageStreamEpoch === 0) this.advanceOpenCodeMessageStreamEpoch(false); const cache = typeArg === 'reasoning' ? this.latestOpenCodeReasoningUpdates : this.latestOpenCodeTextUpdates; const key = this.openCodeToolExecutionKey(projectId, updateArg); if (!cache.has(key) && cache.size >= maxLatestOpenCodeMessageUpdates) { this.advanceOpenCodeMessageStreamEpoch(true); } const previousEntry = cache.get(key); const previous = previousEntry?.update; if ( previousEntry !== undefined && previous !== undefined && previousEntry.projectId === projectId && previous.streamEpoch === this.openCodeMessageStreamEpoch && previous.revision <= this.openCodeMessageStreamRevision && controllerRuntimeIdKey(previous.sessionId) === controllerRuntimeIdKey(updateArg.sessionId) && controllerRuntimeIdKey(previous.messageId) === controllerRuntimeIdKey(updateArg.messageId) && controllerRuntimeIdKey(previous.partId) === controllerRuntimeIdKey(updateArg.partId) && previous.status === 'running' && updateArg.status === 'running' && updateArg.text.length > previous.text.length && updateArg.text.startsWith(previous.text) && Number.isSafeInteger(previousEntry.textUtf8Bytes) ) { const deltaText = updateArg.text.slice(previous.text.length); const fragments = fragmentControllerTextDelta(deltaText); const textUtf8Bytes = controllerTextAfterAppendUtf8Bytes( previous.text, previousEntry.textUtf8Bytes, deltaText, ); const finalRevision = this.openCodeMessageStreamRevision + fragments.length; if ( fragments.length === 0 || textUtf8Bytes === undefined || textUtf8Bytes <= previousEntry.textUtf8Bytes || !Number.isSafeInteger(finalRevision) ) { this.advanceOpenCodeMessageStreamEpoch(true); return; } const update = { ...updateArg, revision: finalRevision, streamEpoch: this.openCodeMessageStreamEpoch, } as IControllerReasoningUpdate | IControllerTextUpdate; const snapshotEvent: IControllerEvent = typeArg === 'reasoning' ? { type: 'session.reasoning.updated', harnessId: 'opencode', projectId, sessionId: update.sessionId, reasoningUpdate: update as IControllerReasoningUpdate, timestamp: update.sourceUpdatedAt, } : { type: 'session.text.updated', harnessId: 'opencode', projectId, sessionId: update.sessionId, textUpdate: update as IControllerTextUpdate, timestamp: update.sourceUpdatedAt, }; const snapshotBytes = Buffer.byteLength(JSON.stringify(snapshotEvent), 'utf8'); if (snapshotBytes > controllerMaxLiveMessageEventBytes) { this.advanceOpenCodeMessageStreamEpoch(true); return; } const events: IControllerEvent[] = []; let baseTextUtf8Bytes = previousEntry.textUtf8Bytes; for (const [index, fragment] of fragments.entries()) { const fragmentTextUtf8Bytes = index === 0 ? controllerTextAfterAppendUtf8Bytes( previous.text, baseTextUtf8Bytes, fragment.delta, ) : baseTextUtf8Bytes + fragment.utf8Bytes; if ( fragmentTextUtf8Bytes === undefined || !Number.isSafeInteger(fragmentTextUtf8Bytes) || fragmentTextUtf8Bytes <= baseTextUtf8Bytes ) { this.advanceOpenCodeMessageStreamEpoch(true); return; } const revision = this.openCodeMessageStreamRevision + index + 1; const commonDelta = { sessionId: update.sessionId, messageId: update.messageId, partId: update.partId, delta: fragment.delta, baseTextUtf8Bytes, textUtf8Bytes: fragmentTextUtf8Bytes, sourceUpdatedAt: update.sourceUpdatedAt, revision, streamEpoch: this.openCodeMessageStreamEpoch, }; const event: IControllerEvent = typeArg === 'reasoning' ? { type: 'session.reasoning.delta', harnessId: 'opencode', projectId, sessionId: update.sessionId, reasoningDelta: commonDelta, timestamp: update.sourceUpdatedAt, } : { type: 'session.text.delta', harnessId: 'opencode', projectId, sessionId: update.sessionId, textDelta: commonDelta, timestamp: update.sourceUpdatedAt, }; if (Buffer.byteLength(JSON.stringify(event), 'utf8') > controllerMaxLiveMessageEventBytes) { this.advanceOpenCodeMessageStreamEpoch(true); return; } events.push(event); baseTextUtf8Bytes = fragmentTextUtf8Bytes; } if (baseTextUtf8Bytes !== textUtf8Bytes) { this.advanceOpenCodeMessageStreamEpoch(true); return; } this.openCodeMessageStreamRevision = finalRevision; cache.delete(key); cache.set(key, { projectId, update, textUtf8Bytes, bytes: snapshotBytes, } as ILatestReasoningUpdate & ILatestTextUpdate); const streamEpoch = this.openCodeMessageStreamEpoch; for (const event of events) { this.queueOpenCodeControllerEvent(event); if (this.openCodeMessageStreamEpoch !== streamEpoch) return; } return; } this.openCodeMessageStreamRevision += 1; const update = { ...updateArg, revision: this.openCodeMessageStreamRevision, streamEpoch: this.openCodeMessageStreamEpoch, } as IControllerReasoningUpdate | IControllerTextUpdate; const event: IControllerEvent = typeArg === 'reasoning' ? { type: 'session.reasoning.updated', harnessId: 'opencode', projectId, sessionId: update.sessionId, reasoningUpdate: update as IControllerReasoningUpdate, timestamp: update.sourceUpdatedAt, } : { type: 'session.text.updated', harnessId: 'opencode', projectId, sessionId: update.sessionId, textUpdate: update as IControllerTextUpdate, timestamp: update.sourceUpdatedAt, }; const eventBytes = Buffer.byteLength(JSON.stringify(event), 'utf8'); if (eventBytes > controllerMaxLiveMessageEventBytes) { this.advanceOpenCodeMessageStreamEpoch(true); return; } cache.delete(key); cache.set(key, { projectId, update, textUtf8Bytes: Buffer.byteLength(update.text, 'utf8'), bytes: eventBytes, } as ILatestReasoningUpdate & ILatestTextUpdate); this.queueOpenCodeControllerEvent(event); } private handleOpenCodeToolExecution( executionArg: TOpenCodeToolExecution, directoryArg: string, timestampArg: number, ): void { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return; const projectId = this.projectIdsByDirectory.get(directoryArg); if (projectId === undefined) return; if (this.openCodeToolStreamEpoch === 0) { this.advanceOpenCodeToolStreamEpoch(false); } const provisionalKey = this.openCodeToolExecutionKey(projectId, executionArg); if ( !this.latestOpenCodeToolExecutions.has(provisionalKey) && this.latestOpenCodeToolExecutions.size >= maxLatestOpenCodeToolExecutions ) { this.advanceOpenCodeToolStreamEpoch(true); } this.openCodeToolStreamRevision += 1; const execution: IControllerToolExecution = { ...executionArg, revision: this.openCodeToolStreamRevision, streamEpoch: this.openCodeToolStreamEpoch, }; const event: IControllerEvent = { type: 'session.tool.updated', harnessId: 'opencode', projectId, sessionId: execution.sessionId, toolExecution: execution, timestamp: timestampArg, }; if (Buffer.byteLength(JSON.stringify(event), 'utf8') > controllerMaxToolEventBytes) { this.advanceOpenCodeToolStreamEpoch(true); return; } const key = this.openCodeToolExecutionKey(projectId, execution); this.latestOpenCodeToolExecutions.delete(key); this.latestOpenCodeToolExecutions.set(key, { projectId, execution }); this.queueOpenCodeControllerEvent(event); } private controllerEventKey(eventArg: IControllerEvent): string { const delta = controllerEventMessageDelta(eventArg); return JSON.stringify([ eventArg.type, eventArg.harnessId ?? null, eventArg.projectId ?? null, eventArg.sessionId ? controllerRuntimeIdKey(eventArg.sessionId) : null, eventArg.toolExecution ? controllerRuntimeIdKey(eventArg.toolExecution.partId) : null, eventArg.reasoningUpdate ? controllerRuntimeIdKey(eventArg.reasoningUpdate.partId) : null, eventArg.textUpdate ? controllerRuntimeIdKey(eventArg.textUpdate.partId) : null, delta ? [ controllerRuntimeIdKey(delta.messageId), controllerRuntimeIdKey(delta.partId), delta.streamEpoch, delta.order?.messageIndex ?? null, delta.order?.partIndex ?? null, delta.baseTextUtf8Bytes, delta.textUtf8Bytes, delta.revision, ] : null, eventArg.toolExecution ? eventArg.toolExecution.status === 'completed' || eventArg.toolExecution.status === 'error' || eventArg.toolExecution.status === 'stopped' ? 'terminal' : 'active' : null, eventArg.reasoningUpdate ? eventArg.reasoningUpdate.status === 'running' ? 'active' : 'terminal' : null, eventArg.textUpdate ? eventArg.textUpdate.status === 'running' ? 'active' : 'terminal' : null, eventArg.type === 'harness.changed' && eventArg.toolStreamEpoch !== undefined, eventArg.type === 'harness.changed' && eventArg.messageStreamEpoch !== undefined, ]); } private latestPendingControllerDelta( queueArg: Map<string, IControllerEvent>, eventArg: IControllerEvent, keySuffixArg?: string, ): { key: string; event: IControllerEvent } | undefined { const pendingKey = controllerMessageDeltaPendingKey(eventArg); if (!pendingKey) return undefined; let tail: [string, IControllerEvent] | undefined; for (const entry of queueArg) tail = entry; if ( !tail || (keySuffixArg !== undefined && !tail[0].endsWith(keySuffixArg)) || controllerMessageDeltaPendingKey(tail[1]) !== pendingKey ) return undefined; return { key: tail[0], event: tail[1] }; } private queueOpenCodeControllerEvent(eventArg: IControllerEvent): boolean { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return false; const delta = controllerEventMessageDelta(eventArg); if (delta) { if (Buffer.byteLength(JSON.stringify(eventArg), 'utf8') > controllerMaxLiveMessageEventBytes) { this.advanceOpenCodeMessageStreamEpoch(true); return false; } const pending = this.latestPendingControllerDelta( this.pendingOpenCodeControllerEvents, eventArg, ); if (pending) { const merged = mergeControllerMessageDeltaEvents(pending.event, eventArg); if (!merged) { this.advanceOpenCodeMessageStreamEpoch(true); return false; } if ( Buffer.byteLength(JSON.stringify(merged), 'utf8') <= controllerMaxLiveMessageEventBytes ) { this.pendingOpenCodeControllerEvents.set(pending.key, merged); this.startOpenCodeControllerEventDrain(); return true; } } } const key = this.controllerEventKey(eventArg); if (delta && this.pendingOpenCodeControllerEvents.has(key)) { this.advanceOpenCodeMessageStreamEpoch(true); return false; } if (!delta && this.pendingOpenCodeControllerEvents.has(key)) { this.pendingOpenCodeControllerEvents.delete(key); } else if ( this.pendingOpenCodeControllerEvents.size >= maxPendingOpenCodeControllerEvents ) { if (delta) { this.advanceOpenCodeStreams(true); } else if (eventArg.reasoningUpdate || eventArg.textUpdate) { this.advanceOpenCodeMessageStreamEpoch(true); } else if (eventArg.toolExecution) { this.advanceOpenCodeToolStreamEpoch(true); } else { this.advanceOpenCodeStreams(true); } if (eventArg.toolExecution) { this.openCodeToolStreamRevision += 1; const execution: IControllerToolExecution = { ...eventArg.toolExecution, revision: this.openCodeToolStreamRevision, streamEpoch: this.openCodeToolStreamEpoch, }; const rebasedEvent: IControllerEvent = { ...eventArg, sessionId: execution.sessionId, toolExecution: execution, }; const executionKey = this.openCodeToolExecutionKey( rebasedEvent.projectId!, execution, ); this.latestOpenCodeToolExecutions.set(executionKey, { projectId: rebasedEvent.projectId!, execution, }); this.pendingOpenCodeControllerEvents.set( this.controllerEventKey(rebasedEvent), rebasedEvent, ); } return false; } this.pendingOpenCodeControllerEvents.set(key, eventArg); this.startOpenCodeControllerEventDrain(); return true; } private startOpenCodeControllerEventDrain(): void { if ( this.openCodeControllerEventDrainTask || (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') ) return; let trackedTask: Promise<void>; trackedTask = this.drainOpenCodeControllerEvents() .catch(() => undefined) .finally(() => { if (this.openCodeControllerEventDrainTask === trackedTask) { this.openCodeControllerEventDrainTask = undefined; } if (this.pendingOpenCodeControllerEvents.size > 0) { this.startOpenCodeControllerEventDrain(); } }); this.openCodeControllerEventDrainTask = trackedTask; } private async drainOpenCodeControllerEvents(): Promise<void> { while (this.lifecycleState === 'starting' || this.lifecycleState === 'ready') { const next = this.pendingOpenCodeControllerEvents.entries().next().value; if (!next) return; const [key, event] = next; this.pendingOpenCodeControllerEvents.delete(key); await this.pushControllerEvent(event).catch(() => undefined); } } private rebaseFlexLiveControllerEvent( eventArg: IControllerEvent, generationArg?: TFlexSessionGeneration, ): IControllerEvent { if (!eventArg.projectId) return eventArg; if (eventArg.toolExecution) { this.flexToolStreamRevision += 1; const execution: IControllerToolExecution = { ...eventArg.toolExecution, revision: this.flexToolStreamRevision, streamEpoch: this.flexToolStreamEpoch, }; const event: IControllerEvent = { ...eventArg, sessionId: execution.sessionId, toolExecution: execution, }; this.setLatestFlexToolExecution( generationArg === undefined ? this.openCodeToolExecutionKey(eventArg.projectId, execution) : this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.projectId, execution), generationArg, ), { projectId: eventArg.projectId, execution }, Buffer.byteLength(JSON.stringify(event), 'utf8'), ); return event; } if (eventArg.reasoningUpdate) { this.flexMessageStreamRevision += 1; const update: IControllerReasoningUpdate = { ...eventArg.reasoningUpdate, revision: this.flexMessageStreamRevision, streamEpoch: this.flexMessageStreamEpoch, }; const event: IControllerEvent = { ...eventArg, sessionId: update.sessionId, reasoningUpdate: update, }; this.setLatestFlexReasoningUpdate( generationArg === undefined ? this.openCodeToolExecutionKey(eventArg.projectId, update) : this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.projectId, update), generationArg, ), { projectId: eventArg.projectId, update, textUtf8Bytes: Buffer.byteLength(update.text, 'utf8'), }, Buffer.byteLength(JSON.stringify(event), 'utf8'), ); return event; } if (eventArg.textUpdate) { this.flexMessageStreamRevision += 1; const update: IControllerTextUpdate = { ...eventArg.textUpdate, revision: this.flexMessageStreamRevision, streamEpoch: this.flexMessageStreamEpoch, }; const event: IControllerEvent = { ...eventArg, sessionId: update.sessionId, textUpdate: update, }; this.setLatestFlexTextUpdate( generationArg === undefined ? this.openCodeToolExecutionKey(eventArg.projectId, update) : this.flexEventCacheKey( this.openCodeToolExecutionKey(eventArg.projectId, update), generationArg, ), { projectId: eventArg.projectId, update, textUtf8Bytes: Buffer.byteLength(update.text, 'utf8'), }, Buffer.byteLength(JSON.stringify(event), 'utf8'), ); return event; } return eventArg; } private queueFlexControllerEvent( eventArg: IControllerEvent, generationArg?: TFlexSessionGeneration, ): boolean { if (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') return false; const generationKeySuffix = generationArg === undefined ? undefined : `\0${JSON.stringify([ generationArg.sessionGenerationId, generationArg.sessionGenerationSequence, ])}`; const delta = controllerEventMessageDelta(eventArg); if (delta) { if (Buffer.byteLength(JSON.stringify(eventArg), 'utf8') > controllerMaxLiveMessageEventBytes) { this.advanceFlexMessageStreamEpoch(true); return false; } const pending = this.latestPendingControllerDelta( this.pendingFlexControllerEvents, eventArg, generationKeySuffix, ); if (pending) { const merged = mergeControllerMessageDeltaEvents(pending.event, eventArg); if (!merged) { this.advanceFlexMessageStreamEpoch(true); return false; } const mergedBytes = this.flexControllerEventLiveBytes(merged); if ( mergedBytes.message <= controllerMaxLiveMessageEventBytes ) { const pendingBytes = this.flexControllerEventLiveBytes(pending.event); if ( this.latestFlexMessageUpdateBytes + this.pendingFlexMessageEventBytes - pendingBytes.message + mergedBytes.message > maxLatestFlexMessageUpdateBytes ) { this.advanceFlexMessageStreamEpoch(true); return false; } this.setPendingFlexControllerEvent(pending.key, merged); this.startFlexControllerEventDrain(); return true; } } } const key = `${this.controllerEventKey(eventArg)}${generationKeySuffix ?? ''}`; if (delta && this.pendingFlexControllerEvents.has(key)) { this.advanceFlexMessageStreamEpoch(true); return false; } if (!delta) this.deletePendingFlexControllerEvent(key); const liveBytes = this.flexControllerEventLiveBytes(eventArg); const exceedsCountBound = this.pendingFlexControllerEvents.size >= maxPendingFlexControllerEvents; const exceedsPendingBound = exceedsCountBound || this.latestFlexToolExecutionBytes + this.pendingFlexToolEventBytes + liveBytes.tool > maxLatestFlexToolExecutionBytes || this.latestFlexMessageUpdateBytes + this.pendingFlexMessageEventBytes + liveBytes.message > maxLatestFlexMessageUpdateBytes; if (exceedsPendingBound) { const requiresCountCollapse = exceedsCountBound || this.pendingFlexControllerEvents.size > maxPendingFlexControllerEvents - 3; if (requiresCountCollapse) { this.advanceFlexStreams(true); } else if ( eventArg.reasoningUpdate || eventArg.reasoningDelta || eventArg.textUpdate || eventArg.textDelta ) { this.advanceFlexMessageStreamEpoch(true); } else if (eventArg.toolExecution) { this.advanceFlexToolStreamEpoch(true); } else { const timestamp = Date.now(); this.advanceFlexStreams(true); const settingsChanged: IControllerEvent = { type: 'settings.changed', timestamp }; this.setPendingFlexControllerEvent( this.controllerEventKey(settingsChanged), settingsChanged, ); } if (eventArg.toolExecution || eventArg.reasoningUpdate || eventArg.textUpdate) { const rebasedEvent = this.rebaseFlexLiveControllerEvent(eventArg, generationArg); this.setPendingFlexControllerEvent( `${this.controllerEventKey(rebasedEvent)}${generationKeySuffix ?? ''}`, rebasedEvent, ); } this.startFlexControllerEventDrain(); return false; } this.setPendingFlexControllerEvent(key, eventArg); this.startFlexControllerEventDrain(); return true; } private startFlexControllerEventDrain(): void { if ( this.flexControllerEventDrainTask || (this.lifecycleState !== 'starting' && this.lifecycleState !== 'ready') ) { return; } let trackedTask: Promise<void>; trackedTask = this.drainFlexControllerEvents() .catch(() => undefined) .finally(() => { if (this.flexControllerEventDrainTask === trackedTask) { this.flexControllerEventDrainTask = undefined; } if (this.pendingFlexControllerEvents.size > 0) { this.startFlexControllerEventDrain(); } }); this.flexControllerEventDrainTask = trackedTask; } private async drainFlexControllerEvents(): Promise<void> { while (this.lifecycleState === 'starting' || this.lifecycleState === 'ready') { const next = this.pendingFlexControllerEvents.entries().next().value; if (!next) return; const [key, event] = next; this.deletePendingFlexControllerEvent(key); await this.pushControllerEvent(event).catch(() => undefined); } } private queueFlexAutoAcceptSweep( projectIdArg: string, sessionIdArg?: string, authorityArg?: TFlexPermissionAuthority, ): void { if (this.lifecycleState !== 'ready') return; const authorityProjectId = authorityArg?.type === 'managed' ? authorityArg.run.projectId : authorityArg?.lease.parentAuthority.projectId; const authoritySessionId = authorityArg?.type === 'managed' ? authorityArg.run.sessionId : authorityArg?.lease.parentAuthority.sessionId; if ( authorityArg !== undefined && ( sessionIdArg === undefined || authorityProjectId !== projectIdArg || authoritySessionId !== sessionIdArg ) ) return; const sweep: IFlexAutoAcceptSweep = { projectId: projectIdArg, ...(authorityArg === undefined && (this.autoAcceptPermissions || sessionIdArg === undefined) ? {} : { sessionId: sessionIdArg, ...(authorityArg === undefined ? {} : { authority: authorityArg }), }), }; const authorityKey = sweep.authority?.type === 'managed' ? [ 'managed', sweep.authority.run.sessionGenerationId, sweep.authority.run.sessionGenerationSequence, sweep.authority.run.queueId, sweep.authority.run.runId, sweep.authority.run.peerId, ] : sweep.authority?.type === 'delegated' ? ['delegated', sweep.authority.lease.leaseId] : null; const key = JSON.stringify([ sweep.projectId, sweep.sessionId ?? null, authorityKey, ]); if (this.pendingFlexAutoAcceptSweeps.has(key)) { this.pendingFlexAutoAcceptSweeps.delete(key); } else if (this.pendingFlexAutoAcceptSweeps.size >= maxPendingFlexAutoAcceptSweeps) { if (!this.autoAcceptPermissions) return; const projectIds = new Set(this.projectIdsByDirectory.values()); this.pendingFlexAutoAcceptSweeps.clear(); for (const projectId of projectIds) { this.pendingFlexAutoAcceptSweeps.set( JSON.stringify([projectId, null, null]), { projectId }, ); } this.startFlexAutoAcceptDrain(); return; } this.pendingFlexAutoAcceptSweeps.set(key, sweep); this.startFlexAutoAcceptDrain(); } private startFlexAutoAcceptDrain(): void { if (this.flexAutoAcceptDrainTask || this.lifecycleState !== 'ready') return; let trackedTask: Promise<void>; trackedTask = this.trackOperationPromise( this.drainFlexAutoAcceptSweeps().catch(() => undefined), ) .finally(() => { if (this.flexAutoAcceptDrainTask === trackedTask) { this.flexAutoAcceptDrainTask = undefined; } if (this.pendingFlexAutoAcceptSweeps.size > 0) { this.startFlexAutoAcceptDrain(); } }); this.flexAutoAcceptDrainTask = trackedTask; } private async drainFlexAutoAcceptSweeps(): Promise<void> { while (this.lifecycleState === 'ready') { const next = this.pendingFlexAutoAcceptSweeps.entries().next().value; if (!next) return; const [key, sweep] = next; this.pendingFlexAutoAcceptSweeps.delete(key); await this.autoAcceptFlexPendingPermissions( sweep.projectId, sweep.sessionId, sweep.authority, ).catch(() => undefined); } } private async autoAcceptFlexPendingPermissions( projectIdArg: string, sessionIdArg?: string, authorityArg?: TFlexPermissionAuthority, ): Promise<void> { const supervisor = this.flexSupervisor; if (!supervisor || !this.flexIsOperational()) return; if (authorityArg && !this.flexPermissionAuthorityIsCurrent(authorityArg)) return; if (sessionIdArg === undefined && !this.autoAcceptPermissions) { for (const entry of this.yoloSessionsByProjectKey.values()) { if (entry.projectId !== projectIdArg || entry.sessionId.harnessId !== 'flex') continue; await this.autoAcceptFlexPendingPermissions( projectIdArg, entry.sessionId.nativeId, ).catch(() => undefined); } return; } if (sessionIdArg !== undefined && !this.autoAcceptPermissions) { const requestedSessionId = flexRuntimeId(sessionIdArg); if (!this.isProjectAutoAcceptedSession(projectIdArg, requestedSessionId)) return; } const result = await supervisor.request('permission.list', { scopeId: projectIdArg, }, this.operationAbortController.signal); if (authorityArg && !this.flexPermissionAuthorityIsCurrent(authorityArg)) return; for (const permission of result.permissions) { if (this.lifecycleState !== 'ready') return; const authority = this.flexPermissionAuthority(projectIdArg, permission); if (!authority) continue; if (authorityArg && !this.flexPermissionAuthoritiesEqual(authority, authorityArg)) continue; const authorizationSessionId = flexRuntimeId( authority.type === 'managed' ? authority.run.sessionId : authority.lease.parentAuthority.sessionId, ); if (sessionIdArg !== undefined && authorizationSessionId.nativeId !== sessionIdArg) continue; if (!this.isProjectAutoAcceptedSession(projectIdArg, authorizationSessionId)) { continue; } const permissionId = flexRuntimeId(permission.permissionId); const permissionKey = JSON.stringify([ projectIdArg, permission.permissionId, permission.sessionGenerationId, permission.sessionGenerationSequence, authority.type === 'managed' ? [ authority.run.sessionGenerationId, authority.run.sessionGenerationSequence, authority.run.queueId, authority.run.runId, ] : authority.lease.leaseId, ]); if (this.autoRepliedPermissionIds.has(permissionKey)) continue; this.autoRepliedPermissionIds.add(permissionKey); while (this.autoRepliedPermissionIds.size > maxTrackedAutoRepliedPermissions) { const oldest = this.autoRepliedPermissionIds.values().next().value; if (oldest === undefined) break; this.autoRepliedPermissionIds.delete(oldest); } try { await this.runAudited( { type: 'permission.reply', requestId: permissionId, sessionId: flexRuntimeId(permission.sessionId), }, async () => { if (!this.isProjectAutoAcceptedSession(projectIdArg, authorizationSessionId)) { throw new FlexServiceError('STALE_RUN'); } if (authorityArg && !this.flexPermissionAuthorityIsCurrent(authorityArg)) { throw new FlexServiceError('STALE_RUN'); } await this.respondToFlexPermission( projectIdArg, permission, 'once', authority, this.operationAbortController.signal, ); }, ); } catch { this.autoRepliedPermissionIds.delete(permissionKey); } } } private isProjectAutoAcceptedSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): boolean { return this.autoAcceptPermissions || this.yoloSessionsByProjectKey.has( this.projectRuntimeIdKey(projectIdArg, sessionIdArg), ); } private hasFlexYoloSessions(): boolean { for (const entry of this.yoloSessionsByProjectKey.values()) { if (entry.sessionId.harnessId === 'flex') return true; } return false; } private projectRuntimeIdKey( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): string { return JSON.stringify([projectIdArg, controllerRuntimeIdKey(sessionIdArg)]); } private unusedSessionKey(projectIdArg: string, sessionIdArg: IControllerRuntimeId): string { return JSON.stringify([projectIdArg, controllerRuntimeIdKey(sessionIdArg)]); } private trimUnusedSessionStates(): void { if (this.unusedSessionStates.size <= maxTrackedSessionAdmissionStates) return; for (const [key, state] of this.unusedSessionStates) { if (state !== 'unused') continue; this.unusedSessionStates.delete(key); if (this.unusedSessionStates.size <= maxTrackedSessionAdmissionStates) return; } } private markUnusedSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, claimEvictionGenerationArg: number, ): void { const key = this.unusedSessionKey(projectIdArg, sessionIdArg); // A creation event can expose the ID to another client just before the // create request continuation records it here. A prior claim wins. if (this.claimedSessionKeys.delete(key)) return; // If any prior claim was evicted while creation was in flight, this ID may // have been that claim. Declining cleanup eligibility is the safe outcome. if (claimEvictionGenerationArg !== this.claimedSessionEvictionGeneration) return; this.unusedSessionStates.set(key, 'unused'); this.trimUnusedSessionStates(); } private claimUnusedSession(projectIdArg: string, sessionIdArg: IControllerRuntimeId): void { const key = this.unusedSessionKey(projectIdArg, sessionIdArg); const state = this.unusedSessionStates.get(key); if (state === 'discarding') { throw new plugins.typedrequest.TypedResponseError( 'The empty session is already being discarded.', { code: 'session_discarding' }, ); } this.unusedSessionStates.delete(key); if (state === 'unused') return; this.claimedSessionKeys.add(key); while (this.claimedSessionKeys.size > maxTrackedSessionAdmissionStates) { const oldest = this.claimedSessionKeys.values().next().value; if (oldest === undefined) break; this.claimedSessionKeys.delete(oldest); this.claimedSessionEvictionGeneration += 1; } } private async discardUnusedSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, discardArg: () => Promise<boolean>, ): Promise<IUnusedSessionDiscardResult> { const key = this.unusedSessionKey(projectIdArg, sessionIdArg); if (this.unusedSessionStates.get(key) !== 'unused') { return { discarded: false, retryable: false }; } this.unusedSessionStates.set(key, 'discarding'); let discarded = false; try { discarded = await discardArg(); return { discarded, retryable: !discarded }; } finally { if (this.unusedSessionStates.get(key) === 'discarding') { if (discarded) this.unusedSessionStates.delete(key); else { this.unusedSessionStates.set(key, 'unused'); this.trimUnusedSessionStates(); } } } } private isConfirmedArchivedSession( sessionArg: IControllerSession, sessionIdArg: IControllerRuntimeId, ): boolean { return controllerRuntimeIdKey(sessionArg.id) === controllerRuntimeIdKey(sessionIdArg) && Number.isFinite(sessionArg.archivedAt) && sessionArg.archivedAt! > 0; } private isConfirmedOpenCodeArchive( sessionArg: IControllerSession, sessionIdArg: IControllerRuntimeId, archivedAtArg: number, ): boolean { return this.isConfirmedArchivedSession(sessionArg, sessionIdArg) && sessionArg.archivedAt === archivedAtArg; } private readArchivePreflight( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise<IControllerProjectDocument> { const key = this.sessionStateMutationKey(projectIdArg, sessionIdArg); const existing = this.archivePreflightReads.get(key); if (existing) { throw new plugins.typedrequest.TypedResponseError( 'A prior archive check for this session is still pending.', { code: 'concurrent_change' }, ); } if (this.archivePreflightReads.size >= maxActiveControllerOperations) { throw new plugins.typedrequest.TypedResponseError( 'The controller is busy. Try again after current archive checks settle.', { code: 'operation_limit' }, ); } const preflight = (async () => { const project = await this.resolveProject(projectIdArg); await this.assertSessionExists(projectIdArg, sessionIdArg); return project; })(); this.archivePreflightReads.set(key, preflight); void preflight.finally(() => { if (this.archivePreflightReads.get(key) === preflight) { this.archivePreflightReads.delete(key); } }).catch(() => undefined); return preflight; } private async readMutableHarnessSession( projectIdArg: string, directoryArg: string, sessionIdArg: IControllerRuntimeId, signalArg?: AbortSignal, ): Promise<IControllerSession> { if (sessionIdArg.harnessId === 'codex') return this.readManagedCodexSession(projectIdArg, directoryArg, sessionIdArg.nativeId, signalArg); if (sessionIdArg.harnessId === 'opencode') { return this.requireOpenCodeClient().getSessionSnapshot( directoryArg, sessionIdArg.nativeId, signalArg, ); } if (sessionIdArg.harnessId === 'flex') { return this.toFlexSession(await this.requireFlexSupervisor().request( 'session.get', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId }, signalArg, )); } throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions cannot be archived.', { code: 'invalid_request' }, ); } private requireOpenCodeArchiveRuntime(): IOpenCodeArchiveRuntime { const client = this.requireOpenCodeClient(); const supervisor = this.openCodeSupervisor; const exitSignal = supervisor ? this.openCodeSupervisorExitSignals.get(supervisor)?.signal : undefined; if (!supervisor || !exitSignal) { throw new Error('The OpenCode runtime cannot own an archive mutation.'); } return { client, supervisor, exitSignal }; } private requireFlexArchiveRuntime(): IFlexArchiveRuntime { const supervisor = this.requireFlexSupervisor(); const exitSignal = this.flexSupervisorExitSignals.get(supervisor)?.signal; if (!exitSignal) throw new Error('The Flex runtime cannot own an archive mutation.'); return { supervisor, exitSignal }; } private async waitForArchiveRetryOrRuntimeExit( runtimeExitSignalArg: AbortSignal, ): Promise<boolean> { if (runtimeExitSignalArg.aborted) return true; return new Promise<boolean>((resolve) => { let settled = false; const finish = (exitedArg: boolean) => { if (settled) return; settled = true; clearTimeout(timer); runtimeExitSignalArg.removeEventListener('abort', onExit); resolve(exitedArg); }; const onExit = () => finish(true); const timer = setTimeout(() => finish(false), sessionArchiveReconciliationPollMs); runtimeExitSignalArg.addEventListener('abort', onExit, { once: true }); if (runtimeExitSignalArg.aborted) onExit(); }); } private async waitForArchiveRuntimeExit(runtimeExitSignalArg: AbortSignal): Promise<void> { if (runtimeExitSignalArg.aborted) return; await new Promise<void>((resolve) => { const onExit = () => { runtimeExitSignalArg.removeEventListener('abort', onExit); resolve(); }; runtimeExitSignalArg.addEventListener('abort', onExit, { once: true }); if (runtimeExitSignalArg.aborted) onExit(); }); } private waitForOpenCodeArchiveMutationOrRuntimeExit( mutationTaskArg: Promise<TOpenCodeArchiveMutationOutcome>, runtimeExitSignalArg: AbortSignal, ): Promise< | { type: 'mutation'; result: TOpenCodeArchiveMutationOutcome } | { type: 'runtime-exit' } > { if (runtimeExitSignalArg.aborted) return Promise.resolve({ type: 'runtime-exit' }); return new Promise((resolve) => { let settled = false; const finish = ( resultArg: | { type: 'mutation'; result: TOpenCodeArchiveMutationOutcome } | { type: 'runtime-exit' }, ) => { if (settled) return; settled = true; runtimeExitSignalArg.removeEventListener('abort', onExit); resolve(resultArg); }; const onExit = () => finish({ type: 'runtime-exit' }); runtimeExitSignalArg.addEventListener('abort', onExit, { once: true }); void mutationTaskArg.then((result) => finish({ type: 'mutation', result })); if (runtimeExitSignalArg.aborted) onExit(); }); } private async reconcileUnknownOpenCodeArchive( directoryArg: string, sessionIdArg: IControllerRuntimeId, archivedAtArg: number, runtimeArg: IOpenCodeArchiveRuntime, mutationStateArg: IOpenCodeArchiveMutationState, deadlineArg: number, ): Promise<IControllerSession | undefined> { for (let attempt = 0; attempt < maxSessionArchiveReconciliationReads; attempt += 1) { const mutationOutcome = mutationStateArg.outcome; if ( mutationOutcome?.type === 'session' && this.isConfirmedOpenCodeArchive( mutationOutcome.session, sessionIdArg, archivedAtArg, ) ) return mutationOutcome.session; if ( runtimeArg.exitSignal.aborted || this.operationAbortController.signal.aborted || Date.now() >= deadlineArg ) return undefined; const signal = AbortSignal.any([ runtimeArg.exitSignal, this.operationAbortController.signal, AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ]); const read = this.runWithSignal( () => runtimeArg.client.getSessionSnapshot(directoryArg, sessionIdArg.nativeId, signal), signal, ).then( (session) => ({ type: 'session' as const, session }), () => ({ type: 'retry' as const }), ); let outcome: Awaited<typeof read>; try { if (mutationStateArg.outcome === undefined) { const first = await this.waitForPromiseWithSignal(Promise.race([ mutationStateArg.task.then((result) => ({ source: 'mutation' as const, result })), read.then((result) => ({ source: 'read' as const, result })), ]), signal); if (first.source === 'mutation') { if ( first.result.type === 'session' && this.isConfirmedOpenCodeArchive( first.result.session, sessionIdArg, archivedAtArg, ) ) return first.result.session; outcome = await this.waitForPromiseWithSignal(read, signal); } else { outcome = first.result; } } else { outcome = await this.waitForPromiseWithSignal(read, signal); } } catch { return undefined; } if ( outcome.type === 'session' && this.isConfirmedOpenCodeArchive(outcome.session, sessionIdArg, archivedAtArg) ) return outcome.session; if (attempt + 1 < maxSessionArchiveReconciliationReads) { if ( runtimeArg.exitSignal.aborted || this.operationAbortController.signal.aborted || Date.now() >= deadlineArg ) return undefined; const retrySignal = AbortSignal.any([ runtimeArg.exitSignal, this.operationAbortController.signal, AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ]); try { if (await this.waitForPromiseWithSignal( this.waitForArchiveRetryOrRuntimeExit(runtimeArg.exitSignal), retrySignal, )) return undefined; } catch { return undefined; } } } return undefined; } private retainUnknownOpenCodeArchive( projectIdArg: string, sessionIdArg: IControllerRuntimeId, archivedAtArg: number, runtimeArg: IOpenCodeArchiveRuntime, mutationStateArg: IOpenCodeArchiveMutationState, releaseArchiveFenceArg: () => void, ): void { const background = (async () => { let outcome = mutationStateArg.outcome; if (!outcome) { const settled = await this.waitForOpenCodeArchiveMutationOrRuntimeExit( mutationStateArg.task, runtimeArg.exitSignal, ); if (settled.type === 'runtime-exit') return; outcome = settled.result; } if ( outcome.type === 'session' && this.isConfirmedOpenCodeArchive(outcome.session, sessionIdArg, archivedAtArg) ) { this.finalizeArchivedSession(projectIdArg, sessionIdArg); return; } await this.waitForArchiveRuntimeExit(runtimeArg.exitSignal); })().finally(releaseArchiveFenceArg); void this.trackOperationPromise(background).catch(() => undefined); } private async reconcileUnknownFlexArchive( projectIdArg: string, sessionIdArg: IControllerRuntimeId, runtimeArg: IFlexArchiveRuntime, deadlineArg: number, ): Promise<IControllerSession | undefined> { for (let attempt = 0; attempt < maxSessionArchiveReconciliationReads; attempt += 1) { if ( runtimeArg.exitSignal.aborted || this.operationAbortController.signal.aborted || Date.now() >= deadlineArg ) return undefined; const signal = AbortSignal.any([ runtimeArg.exitSignal, this.operationAbortController.signal, AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ]); const read = runtimeArg.supervisor.requestToCompletion( 'session.get', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId }, signal, ).then( (session) => ({ type: 'session' as const, session: this.toFlexSession(session) }), (error) => ({ type: 'error' as const, error }), ); let outcome: Awaited<typeof read>; try { outcome = await this.waitForPromiseWithSignal(read, signal); } catch { return undefined; } if (outcome.type === 'session') { return this.isConfirmedArchivedSession(outcome.session, sessionIdArg) ? outcome.session : undefined; } if ( outcome.error instanceof FlexServiceError && runtimeArg.supervisor.getStatus().ready ) return undefined; if (attempt + 1 < maxSessionArchiveReconciliationReads) { if ( runtimeArg.exitSignal.aborted || this.operationAbortController.signal.aborted || Date.now() >= deadlineArg ) return undefined; if (await this.waitForArchiveRetryOrRuntimeExit(runtimeArg.exitSignal)) return undefined; } } return undefined; } private retainFlexArchiveFenceUntilRuntimeExit( runtimeArg: IFlexArchiveRuntime, releaseArchiveFenceArg: () => void, ): void { const background = (async () => { let attempt = 0; while (!runtimeArg.exitSignal.aborted) { if (this.operationAbortController.signal.aborted) { await this.waitForArchiveRuntimeExit(runtimeArg.exitSignal); return; } await waitForSignalOrDelay( controllerFlexRestartDelayMs(attempt), AbortSignal.any([ runtimeArg.exitSignal, this.operationAbortController.signal, ]), ); if (runtimeArg.exitSignal.aborted) return; if (this.operationAbortController.signal.aborted) { await this.waitForArchiveRuntimeExit(runtimeArg.exitSignal); return; } try { await this.stopOwnedFlexSupervisor(runtimeArg.supervisor); } catch (errorArg) { console.error('Retrying uncertain Flex archive generation cleanup failed.', errorArg); attempt = Math.min(attempt + 1, 30); } } })().finally(releaseArchiveFenceArg); void this.trackOperationPromise(background).catch(() => undefined); } private finalizeArchivedSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): void { this.invalidateHarnessSessionEnumeration(projectIdArg); if (sessionIdArg.harnessId === 'opencode') { this.purgeOpenCodeStreamStateForSession(projectIdArg, sessionIdArg); } else if (sessionIdArg.harnessId === 'flex') { this.purgeFlexToolExecutionsForSession(projectIdArg, sessionIdArg); } this.yoloSessionsByProjectKey.delete( this.projectRuntimeIdKey(projectIdArg, sessionIdArg), ); // The harness mutation is authoritative. Layout pruning remains tracked // controller cleanup, but does not retain the archive fence or response. void this.pruneSessionFromLayout(projectIdArg, sessionIdArg); void this.pushControllerEvent({ type: 'session.changed', projectId: projectIdArg, sessionId: sessionIdArg, timestamp: Date.now(), }).catch(() => undefined); } private invalidateHarnessSessionEnumeration(projectIdArg: string): void { const enumeration = this.harnessSessionEnumerationTasks.get(projectIdArg); if (enumeration) enumeration.invalidated = true; this.harnessSessionEnumerationTasks.delete(projectIdArg); } private async archiveSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal, ): Promise<IControllerSession> { this.assertSessionMutationAdmission(projectIdArg, sessionIdArg); const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.archivingSessionKeys.has(sessionKey)) { throw new plugins.typedrequest.TypedResponseError( 'The session is already being archived.', { code: 'concurrent_change' }, ); } const releaseProjectArchiveAdmission = this.reserveAdmissionEntry( this.projectArchiveAdmissions, projectIdArg, ); this.archivingSessionKeys.add(sessionKey); this.revokeChildScopesForParent(projectIdArg, sessionIdArg); let archiveFenceReleased = false; const releaseArchiveFence = () => { if (archiveFenceReleased) return; archiveFenceReleased = true; this.archivingSessionKeys.delete(sessionKey); releaseProjectArchiveAdmission(); }; let archiveFenceRetained = false; const archiveSignal = AbortSignal.any([ signalArg, AbortSignal.timeout(sessionArchiveOperationTimeoutMs), ]); try { archiveSignal.throwIfAborted(); if ( this.sessionHarnessSubmissionAdmissions.has(sessionKey) || this.sessionIntelligenceAdmissions.has(sessionKey) || this.sessionStateMutationTails.has(sessionKey) || this.harnessOperationManager.listSessionActive(projectIdArg, sessionIdArg).length > 0 || this.activeSessionBackgroundOperations.has(sessionKey) || this.sessionIntelligenceJobs.has(sessionKey) ) { throw new plugins.typedrequest.TypedResponseError( 'The session has active work and cannot be archived yet.', { code: 'concurrent_change' }, ); } const project = await this.runWithSignal( () => this.readArchivePreflight(projectIdArg, sessionIdArg), archiveSignal, ); let session = await this.runWithSignal( () => this.readMutableHarnessSession( projectIdArg, project.directory, sessionIdArg, archiveSignal, ), archiveSignal, ); if (!this.isConfirmedArchivedSession(session, sessionIdArg)) { archiveSignal.throwIfAborted(); if (sessionIdArg.harnessId === 'opencode') { const runtime = this.requireOpenCodeArchiveRuntime(); const archivedAt = Date.now(); const postDispatchDeadline = Date.now() + sessionArchivePostDispatchTimeoutMs; const mutationSignal = AbortSignal.any([ runtime.exitSignal, this.operationAbortController.signal, ]); const mutationState = {} as IOpenCodeArchiveMutationState; mutationState.task = this.trackOperationPromise( Promise.resolve().then(() => runtime.client.updateSession( project.directory, sessionIdArg.nativeId, { archivedAt }, mutationSignal, )).then( (result) => ({ type: 'session' as const, session: result }), (error) => ({ type: 'error' as const, error }), ).then((outcome) => { mutationState.outcome = outcome; return outcome; }), ); let mutationError: unknown; const observationSignal = AbortSignal.any([ runtime.exitSignal, this.operationAbortController.signal, AbortSignal.timeout(Math.min( sessionArchiveMutationObservationMs, Math.max(1, postDispatchDeadline - Date.now()), )), ]); try { const outcome = await this.waitForPromiseWithSignal( mutationState.task, observationSignal, ); if (outcome.type === 'session') session = outcome.session; else mutationError = outcome.error; } catch (errorArg) { mutationError = errorArg; } if (!this.isConfirmedOpenCodeArchive(session, sessionIdArg, archivedAt)) { const reconciled = await this.reconcileUnknownOpenCodeArchive( project.directory, sessionIdArg, archivedAt, runtime, mutationState, postDispatchDeadline, ); if (!reconciled) { this.retainUnknownOpenCodeArchive( projectIdArg, sessionIdArg, archivedAt, runtime, mutationState, releaseArchiveFence, ); archiveFenceRetained = true; throw mutationError ?? new Error('OpenCode returned an unconfirmed archived session.'); } session = reconciled; } } else if (sessionIdArg.harnessId === 'codex') { session = await this.requireCodexClient(sessionIdArg.nativeId).archive(sessionIdArg.nativeId, archiveSignal); } else if (sessionIdArg.harnessId === 'flex') { const runtime = this.requireFlexArchiveRuntime(); const postDispatchDeadline = Date.now() + sessionArchivePostDispatchTimeoutMs; const mutationSignal = AbortSignal.any([ runtime.exitSignal, this.operationAbortController.signal, AbortSignal.timeout(Math.min( sessionArchiveMutationObservationMs, Math.max(1, postDispatchDeadline - Date.now()), )), ]); let response: TFlexSession | undefined; try { response = await runtime.supervisor.requestToCompletion( 'session.update', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId, options: { archived: true }, }, mutationSignal, ); } catch (errorArg) { if (!(errorArg instanceof FlexServiceError) || errorArg.code !== 'OUTCOME_UNKNOWN') { throw errorArg; } const reconciled = await this.reconcileUnknownFlexArchive( projectIdArg, sessionIdArg, runtime, postDispatchDeadline, ); if (!reconciled) { try { await this.stopOwnedFlexSupervisor(runtime.supervisor); } catch (stopErrorArg) { this.retainFlexArchiveFenceUntilRuntimeExit(runtime, releaseArchiveFence); archiveFenceRetained = true; throw new AggregateError( [errorArg, stopErrorArg], 'The uncertain Flex archive generation could not be stopped.', ); } throw errorArg; } session = reconciled; } if (response !== undefined) session = this.toFlexSession(response); } if (!this.isConfirmedArchivedSession(session, sessionIdArg)) { throw new Error('The harness returned an invalid archived session.'); } } this.finalizeArchivedSession(projectIdArg, sessionIdArg); return session; } finally { if (!archiveFenceRetained) releaseArchiveFence(); } } private async deleteSession( projectIdArg: string, sessionIdArg: IControllerRuntimeId, signalArg: AbortSignal, operationIdArg = plugins.crypto.randomBytes(24).toString('base64url'), ): Promise<boolean> { const sessionKey = this.sessionStateMutationKey(projectIdArg, sessionIdArg); if (this.archivingSessionKeys.has(sessionKey)) { throw new plugins.typedrequest.TypedResponseError( 'The session is being archived.', { code: 'concurrent_change' }, ); } if (this.retiringSessionKeys.has(sessionKey)) { throw new plugins.typedrequest.TypedResponseError( 'The session is already being deleted.', { code: 'concurrent_change' }, ); } this.retiringSessionKeys.add(sessionKey); if (sessionIdArg.harnessId === 'flex') { this.fenceFlexDelegatedAuthorityForParentScope(projectIdArg, sessionIdArg.nativeId); } this.revokeChildScopesForParent(projectIdArg, sessionIdArg); let durableDeletionCompleted = false; let retiredAuthorityToken: IRetiredSessionAuthorityToken | undefined; try { await this.waitForSessionHarnessSubmissionAdmissions(sessionKey); await this.waitForSessionIntelligenceAdmissions(sessionKey); await this.waitForFlexSessionHostRequestAdmissions(sessionKey, signalArg); await this.abortSessionBackgroundOperation( sessionKey, new Error('The source session was deleted.'), ); const job = this.sessionIntelligenceJobs.get( sessionKey, ); job?.abortController.abort(new Error('The source session was deleted.')); if (job?.task) await Promise.allSettled([job.task]); try { const deleted = await this.requireResourceCoordinator().withSessionDeletion( projectIdArg, sessionIdArg as import('../ts_interfaces/index.js').TControllerSessionId, signalArg, async () => this.withSessionStateMutation(projectIdArg, sessionIdArg, async () => { const project = await this.resolveProject(projectIdArg); if (sessionIdArg.harnessId === 'controller') return false; await this.abortActiveHarnessOperation(this.toPublicProject(project), sessionIdArg, signalArg); await this.waitForFlexSessionHostRequestAdmissions(sessionKey, signalArg); const deletion = await this.beginManagedSessionDeletion( projectIdArg, project.directory, sessionIdArg, operationIdArg, signalArg, ); let dispatchStarted = deletion.dispatchStartedAt !== undefined; try { const alreadyTombstoned = await this.requireAuthStore() .isSessionStateTombstoned(projectIdArg, sessionIdArg); if (!alreadyTombstoned) { const cleanupObligations = await this.requireAuthStore() .getSessionIntelligenceCleanupObligations(projectIdArg, sessionIdArg); for (const obligation of cleanupObligations) { await this.requireOpenCodeClient().deleteSessionIntelligenceSession( project.directory, obligation.nativeId, signalArg, ); await this.requireAuthStore().clearSessionIntelligenceTemporarySession( obligation.projectId, obligation.sessionId, obligation.exchangeId, obligation.nativeId, ); } } await this.assertManagedDeletionProviderGenerationCurrent( projectIdArg, project.directory, deletion, signalArg, ); if (!dispatchStarted) { await this.requireAuthStore().markManagedSessionDeletionDispatched({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), operationId: operationIdArg, dispatchStartedAt: new Date(), }); dispatchStarted = true; } if (sessionIdArg.harnessId === 'opencode') { await this.requireOpenCodeClient().deleteSessionIfPresent( project.directory, sessionIdArg.nativeId, signalArg, ); } else if (sessionIdArg.harnessId === 'codex') { await this.requireCodexClient(sessionIdArg.nativeId).deleteThread(this.codexDirectory(sessionIdArg.nativeId, project.directory), sessionIdArg.nativeId, signalArg); } else { signalArg.throwIfAborted(); const cleanupCohort = deletion.flexCleanupCohort; const cleanupRoot = cleanupCohort?.find((entry) => entry.cleanupRoot); if (!cleanupCohort || !cleanupRoot) { throw new Error('The Flex deletion obligation has no exact cleanup cohort.'); } await this.requireFlexSupervisor().request('session.delete', { scopeId: projectIdArg, root: { sessionId: cleanupRoot.sessionId, sessionGenerationId: cleanupRoot.sessionGenerationId, sessionGenerationSequence: cleanupRoot.sessionGenerationSequence, }, authorizedCohort: cleanupCohort.map((entry) => ({ sessionId: entry.sessionId, sessionGenerationId: entry.sessionGenerationId, sessionGenerationSequence: entry.sessionGenerationSequence, })), }, signalArg); } if (!alreadyTombstoned) { await this.requireAuthStore().tombstoneSessionState(projectIdArg, sessionIdArg); } this.yoloSessionsByProjectKey.delete( this.projectRuntimeIdKey(projectIdArg, sessionIdArg), ); if (sessionIdArg.harnessId === 'opencode') { this.purgeOpenCodeStreamStateForSession(projectIdArg, sessionIdArg); } else if (sessionIdArg.harnessId === 'flex') { this.purgeFlexToolExecutionsForSession(projectIdArg, sessionIdArg); } await this.pruneSessionFromLayout(projectIdArg, sessionIdArg); await this.harnessOperationManager.purgeSession(projectIdArg, sessionIdArg); if (sessionIdArg.harnessId === 'flex') { this.sealedFlexSessionHostRequestAdmissions.add(sessionKey); } try { if (sessionIdArg.harnessId === 'flex') { await this.waitForFlexSessionHostRequestAdmissions(sessionKey, signalArg); } await this.requireAuthStore().completeManagedSessionDeletion({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), operationId: operationIdArg, retiredAt: new Date(), }); if (sessionIdArg.harnessId === 'codex') await this.requireAuthStore().completeCodexCreationDeletion(projectIdArg, sessionIdArg.nativeId); // The durable deletion is complete; removing the sidebar entry is cleanup, and a // failure there must never turn a completed deletion into a failed operation. try { await this.requireAuthStore() .untrackConversation(projectIdArg, requireManagedRuntimeId(sessionIdArg)); } catch (errorArg) { console.error('Untracking the deleted conversation failed.', errorArg); } durableDeletionCompleted = true; retiredAuthorityToken = this.markRetiredSessionAuthority( projectIdArg, sessionIdArg, ); } finally { if (sessionIdArg.harnessId === 'flex') { this.sealedFlexSessionHostRequestAdmissions.delete(sessionKey); } } this.invalidateHarnessSessionEnumeration(projectIdArg); const changedEvent: IControllerEvent = { type: 'sessions.changed', harnessId: sessionIdArg.harnessId, projectId: projectIdArg, timestamp: Date.now(), }; if (sessionIdArg.harnessId === 'opencode') this.queueOpenCodeControllerEvent(changedEvent); else if (sessionIdArg.harnessId === 'codex') void this.pushControllerEvent(changedEvent).catch(() => undefined); else this.queueFlexControllerEvent(changedEvent); return true; } catch (errorArg) { if (!durableDeletionCompleted) { this.setManagedSessionAuthority(projectIdArg, sessionIdArg, 'uncertain'); } if (!dispatchStarted) { await this.requireAuthStore().cancelManagedSessionDeletion({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), operationId: operationIdArg, cancelledAt: new Date(), }).then((observation) => { this.setManagedSessionAuthority( projectIdArg, sessionIdArg, 'managed', observation.identity.sessionIdentityId, ); }).catch(() => undefined); } throw errorArg; } }), ); if (retiredAuthorityToken) { this.clearRetiredSessionAuthorityIfCurrent(retiredAuthorityToken); } return deleted; } catch (errorArg) { if (durableDeletionCompleted && retiredAuthorityToken) { this.scheduleProjectResourceReconciliationRetry( projectIdArg, retiredAuthorityToken, ); } throw errorArg; } } finally { this.retiringSessionKeys.delete(sessionKey); } } private async beginManagedSessionDeletion( projectIdArg: string, directoryArg: string, sessionIdArg: IControllerRuntimeId, operationIdArg: string, signalArg: AbortSignal, ): Promise<IControllerManagedSessionDeletionObligation> { if (sessionIdArg.harnessId === 'codex') { const session = await this.readManagedCodexSession(projectIdArg, directoryArg, sessionIdArg.nativeId, signalArg); const client = this.requireCodexClient(sessionIdArg.nativeId); const deletion = await this.requireAuthStore().beginManagedSessionDeletion({ projectIdentityId: projectIdArg, runtimeId: codexRuntimeId(sessionIdArg.nativeId), supervisorGeneration: client.supervisor.generation, providerSessionGeneration: codexSessionProviderGeneration(sessionIdArg.nativeId, session.createdAt), operationId: operationIdArg, startedAt: new Date(), }); this.setManagedSessionAuthority(projectIdArg, sessionIdArg, 'uncertain'); return deletion; } if (sessionIdArg.harnessId === 'opencode') { await this.assertSessionIsNotIntelligenceTemporary(projectIdArg, sessionIdArg, signalArg); const supervisor = this.openCodeSupervisor; const client = this.requireOpenCodeClient(); if (!supervisor) throw new Error('The OpenCode runtime is unavailable.'); const authority = this.requireOpenCodeRuntimeExitAuthority(supervisor); const observation = await client.getSessionAuthorityObservation( directoryArg, sessionIdArg.nativeId, AbortSignal.any([signalArg, authority.signal]), ); this.assertOpenCodeSessionIdentityGenerationCurrent(supervisor, client, authority); const deletion = await this.requireAuthStore().beginManagedSessionDeletion({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration: observation.providerSessionGeneration, operationId: operationIdArg, startedAt: new Date(), }); this.setManagedSessionAuthority(projectIdArg, sessionIdArg, 'uncertain'); return deletion; } if (sessionIdArg.harnessId === 'flex') { const supervisor = this.requireFlexSupervisor(); const authority = this.requireFlexRuntimeExitAuthority(supervisor); const raw = await supervisor.request('session.get', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId, }, AbortSignal.any([signalArg, authority.signal])); this.assertFlexSessionIdentityGenerationCurrent(supervisor, authority); const providerSessionGeneration = requireFlexSessionProviderGeneration(raw); const flexCleanupCohort = await this.captureFlexCleanupCohort( projectIdArg, [{ sessionId: sessionIdArg.nativeId, ...requireFlexSessionGeneration(raw), providerSessionGeneration, cleanupRoot: true, }], AbortSignal.any([signalArg, authority.signal]), ); if (!flexCleanupCohort.some((entry) => ( entry.sessionId === sessionIdArg.nativeId && entry.providerSessionGeneration === providerSessionGeneration && entry.cleanupRoot ))) throw new Error('The exact Flex deletion root left the complete snapshot fence.'); const deletion = await this.requireAuthStore().beginManagedSessionDeletion({ projectIdentityId: projectIdArg, runtimeId: requireManagedRuntimeId(sessionIdArg), supervisorGeneration: authority.sessionIdentityGeneration, providerSessionGeneration, flexCleanupCohort, operationId: operationIdArg, startedAt: new Date(), }); this.setManagedSessionAuthority(projectIdArg, sessionIdArg, 'uncertain'); return deletion; } throw new plugins.typedrequest.TypedResponseError( 'Controller-internal sessions cannot be deleted.', { code: 'invalid_request' }, ); } private async assertManagedDeletionProviderGenerationCurrent( projectIdArg: string, directoryArg: string, deletionArg: IControllerManagedSessionDeletionObligation, signalArg: AbortSignal, ): Promise<boolean> { if (deletionArg.runtimeId.harnessId === 'codex') { const current = await this.requireCodexClient(deletionArg.runtimeId.nativeId).readIfPresent(this.codexDirectory(deletionArg.runtimeId.nativeId, directoryArg), deletionArg.runtimeId.nativeId, signalArg); if (current && codexSessionProviderGeneration(current.id.nativeId, current.createdAt) !== deletionArg.providerSessionGeneration) throw new Error('The Codex thread generation changed before deletion dispatch.'); return current !== undefined; } if (deletionArg.runtimeId.harnessId === 'opencode') { const current = await this.requireOpenCodeClient().getSessionIfPresent( directoryArg, deletionArg.runtimeId.nativeId, signalArg, ); if ( current && openCodeSessionProviderGeneration(current.id.nativeId, current.createdAt) !== deletionArg.providerSessionGeneration ) throw new Error('The OpenCode session generation changed before deletion dispatch.'); return current !== undefined; } // Flex deletion is fenced by the persisted raw generation cohort itself. return true; } private readonly autoAcceptSweepTimers = new Map<string, ReturnType<typeof setTimeout>>(); private readonly autoAcceptSweepTasks = new Map<string, Promise<void>>(); private readonly autoAcceptSweepReplayDirectories = new Set<string>(); private async drainAutoAcceptSweepTasksForShutdown(): Promise<void> { const deadline = AbortSignal.timeout(operationDrainBeforeChildStopMs); try { while (this.autoAcceptSweepTasks.size > 0) { await this.waitForPromiseWithSignal( Promise.allSettled([...this.autoAcceptSweepTasks.values()]).then(() => undefined), deadline, ); } } finally { this.autoAcceptSweepTasks.clear(); this.autoAcceptSweepReplayDirectories.clear(); } } /** * Collapses permission-event bursts into one sweep per directory: every * auto-reply itself emits permission events, so sweeping per event goes * quadratic under load. */ private scheduleAutoAcceptSweep(directoryArg: string): void { if (!this.openCodeAutoAcceptSweepCanRun()) return; if (this.autoAcceptSweepTimers.has(directoryArg)) return; const timer = setTimeout(() => { this.autoAcceptSweepTimers.delete(directoryArg); void this.autoAcceptPendingPermissions(directoryArg).catch(() => undefined); }, 300); timer.unref?.(); this.autoAcceptSweepTimers.set(directoryArg, timer); } private async autoAcceptPendingPermissions( directoryArg: string, sessionIdArg?: string, ): Promise<void> { const existing = this.autoAcceptSweepTasks.get(directoryArg); if (existing) { if (this.openCodeAutoAcceptSweepCanRun()) { this.autoAcceptSweepReplayDirectories.add(directoryArg); } return existing; } if (!this.openCodeAutoAcceptSweepCanRun()) return; let nextSessionId = sessionIdArg; let firstError: unknown; const task = (async () => { while (this.openCodeAutoAcceptSweepCanRun()) { this.autoAcceptSweepReplayDirectories.delete(directoryArg); try { await this.performAutoAcceptPendingPermissions(directoryArg, nextSessionId); } catch (errorArg) { firstError ??= errorArg; } nextSessionId = undefined; if (!this.autoAcceptSweepReplayDirectories.delete(directoryArg)) break; } if (firstError !== undefined) throw firstError; })(); this.autoAcceptSweepTasks.set(directoryArg, task); const cleanup = () => { if (this.autoAcceptSweepTasks.get(directoryArg) !== task) return; this.autoAcceptSweepTasks.delete(directoryArg); if ( this.autoAcceptSweepReplayDirectories.delete(directoryArg) && this.openCodeAutoAcceptSweepCanRun() ) void this.autoAcceptPendingPermissions(directoryArg).catch(() => undefined); }; void task.then(cleanup, cleanup); return task; } private openCodeAutoAcceptSweepCanRun(): boolean { return this.lifecycleState === 'ready' && !this.stopRequested && !this.operationAbortController.signal.aborted; } private async performAutoAcceptPendingPermissions( directoryArg: string, sessionIdArg?: string, ): Promise<void> { const openCodeClient = this.openCodeClient; const projectId = this.projectIdsByDirectory.get(directoryArg); if ( !openCodeClient || projectId === undefined || !this.openCodeAutoAcceptSweepCanRun() || (!this.autoAcceptPermissions && this.yoloSessionsByProjectKey.size === 0) ) { return; } const permissions = await openCodeClient.listPermissions( directoryArg, sessionIdArg, this.operationAbortController.signal, ); for (const permission of permissions) { if (!this.openCodeAutoAcceptSweepCanRun()) return; try { const initialAuthority = await this.resolveOpenCodeFamilyAuthority( projectId, permission.sessionId as IControllerRuntimeId & { harnessId: 'opencode' }, this.operationAbortController.signal, ); const parentSessionId = initialAuthority.parent.session.id; if (!this.isProjectAutoAcceptedSession(projectId, parentSessionId)) continue; await this.withHarnessSubmissionAdmission( projectId, parentSessionId, this.operationAbortController.signal, async () => { const currentAuthority = await this.resolveOpenCodeFamilyAuthority( projectId, permission.sessionId as IControllerRuntimeId & { harnessId: 'opencode' }, this.operationAbortController.signal, ); if (!this.openCodeFamilyAuthoritiesEqual(initialAuthority, currentAuthority)) { throw new Error('The OpenCode family authority changed before automatic permission admission.'); } const releaseAutoAccept = this.reserveOpenCodeAutoAcceptAdmission( projectId, currentAuthority, ); try { const pending = await openCodeClient.listPermissions( currentAuthority.parent.project.directory, permission.sessionId.nativeId, this.operationAbortController.signal, ); this.assertAttentionRequestOwned(pending, permission.id, 'permission'); const finalAuthority = await this.resolveOpenCodeFamilyAuthority( projectId, permission.sessionId as IControllerRuntimeId & { harnessId: 'opencode' }, this.operationAbortController.signal, ); if ( !this.openCodeFamilyAuthoritiesEqual(currentAuthority, finalAuthority) || !this.isProjectAutoAcceptedSession(projectId, parentSessionId) || (finalAuthority.child !== undefined && this.childScopeObservedMode(finalAuthority.child.session) !== 'active') || this.openCodeAutoAcceptAdmissionSealCount > 0 || this.sealedOpenCodeAutoAcceptFamilyKeys.has( this.openCodeFamilyAdmissionKey(projectId, parentSessionId), ) ) throw new Error('The OpenCode family authority changed before automatic permission reply.'); const permissionKey = JSON.stringify([ projectId, controllerRuntimeIdKey(permission.id), this.openCodeExactFamilyAdmissionKey(projectId, finalAuthority), ]); if (this.autoRepliedPermissionIds.has(permissionKey)) return; // Claim before dispatch so overlapping sweeps cannot reply twice. this.autoRepliedPermissionIds.add(permissionKey); while (this.autoRepliedPermissionIds.size > maxTrackedAutoRepliedPermissions) { const oldest = this.autoRepliedPermissionIds.values().next().value; if (oldest === undefined) break; this.autoRepliedPermissionIds.delete(oldest); } try { await this.runAudited( { type: 'permission.reply', requestId: permission.id, sessionId: permission.sessionId }, async () => openCodeClient.replyPermission( currentAuthority.parent.project.directory, permission.id.nativeId, 'once', undefined, this.operationAbortController.signal, ), ); } catch (errorArg) { this.autoRepliedPermissionIds.delete(permissionKey); throw errorArg; } } finally { releaseAutoAccept(); } }, ); } catch { // Pending requests and authority may disappear while a sweep is running. } } } private async pruneSessionFromLayout( projectIdArg: string, sessionIdArg: IControllerRuntimeId, ): Promise<void> { try { const prune = async () => { const changed = await this.requireAuthStore().removeSessionFromLayout( projectIdArg, sessionIdArg, ); if (changed) this.pushSessionLayoutChanged(); }; const operation = this.runSessionLayoutOperation(prune); await this.trackOperationPromise(operation); } catch (errorArg) { console.error( `Removing a session from project ${projectIdArg} layout failed.`, errorArg, ); } } /** * Serializes every write to the one controller-wide layout document. The queue is shared by * all projects because the document is. */ private runSessionLayoutOperation<T>(operationArg: () => Promise<T>): Promise<T> { const projectIdArg = controllerSessionLayoutScopeKey; const depth = this.sessionLayoutOperationDepths.get(projectIdArg) ?? 0; if (depth >= maxQueuedSessionLayoutOperationsPerProject) { return Promise.reject(new Error('The session-layout queue is full.')); } this.sessionLayoutOperationDepths.set(projectIdArg, depth + 1); const previous = this.sessionLayoutOperationTails.get(projectIdArg) ?? Promise.resolve(); const operation = previous.then(operationArg); const tail = operation.then(() => undefined, () => undefined); this.sessionLayoutOperationTails.set(projectIdArg, tail); void tail.finally(() => { const remainingDepth = (this.sessionLayoutOperationDepths.get(projectIdArg) ?? 1) - 1; if (remainingDepth <= 0) this.sessionLayoutOperationDepths.delete(projectIdArg); else this.sessionLayoutOperationDepths.set(projectIdArg, remainingDepth); if (this.sessionLayoutOperationTails.get(projectIdArg) === tail) { this.sessionLayoutOperationTails.delete(projectIdArg); } }); return operation; } /** The layout is controller-wide, so its change event names no project. */ private pushSessionLayoutChanged(): void { void this.pushControllerEvent({ type: 'sessiongroups.changed', timestamp: Date.now(), }).catch(() => undefined); } private layoutSessionIdsByProject( layoutArg: IReq_ControllerSessionGroupsGet['response'], ): Map<string, IControllerRuntimeId[]> { const byProject = new Map<string, IControllerRuntimeId[]>(); for (const itemRef of [ ...layoutArg.groups.flatMap((group) => group.itemIds), ...layoutArg.ungroupedItemIds, ]) { const entries = byProject.get(itemRef.projectId) ?? []; if (itemRef.kind === 'session') entries.push(itemRef.id); byProject.set(itemRef.projectId, entries); } return byProject; } /** * Three outcomes, because the layout must treat two of them differently. * * A project that is retiring or deregistered owns no layout rows any more: keeping them would * leave rows the sidebar can never resolve again. A registered project whose directory is * merely unavailable — unmounted, renamed, not yet attached — comes back, and its remembered * positions have to survive that, so its rows are kept and only withheld until it returns. * `resolveProject` maps both onto `project_not_found` for every other caller, which is why * the two halves are asked separately here rather than being told apart from its error. */ private async resolveLayoutProjectAvailability( projectIdArg: string, ): Promise<TControllerLayoutProjectAvailability> { let project: IControllerProjectDocument; try { project = await this.resolveProjectIdentity(projectIdArg); } catch (errorArg) { if ( errorArg instanceof plugins.typedrequest.TypedResponseError && (errorArg.errorData as { code?: string } | undefined)?.code === 'project_not_found' ) return { status: 'gone', error: errorArg }; throw errorArg; } try { await this.assertProjectDirectory(project); } catch (errorArg) { if (!(errorArg instanceof CanonicalDirectoryError)) throw errorArg; return { status: 'unavailable' }; } return { status: 'available', project }; } /** * Rows of a withheld project stay stored; a client that cannot resolve them never sees them. * The revision is deliberately untouched, so the withholding is invisible to optimistic * concurrency and the rows reappear at their stored positions once the directory is back. */ private withoutLayoutProjects( layoutArg: IControllerSessionLayout, projectIdsArg: ReadonlySet<string>, ): IControllerSessionLayout { const keepItem = (itemRefArg: TControllerLayoutItemRef): boolean => ( !projectIdsArg.has(itemRefArg.projectId) ); return { groups: layoutArg.groups.map((group) => ({ ...group, itemIds: group.itemIds.filter(keepItem), })), ungroupedItemIds: layoutArg.ungroupedItemIds.filter(keepItem), revision: layoutArg.revision, }; } /** * A layout update is a full replacement, and a client can only replace what it was shown, so * the rows withheld while a project directory is unavailable are put back at their stored * positions. Without this the withholding would merely delay the deletion it prevents. */ private retainWithheldLayoutItemRefs( storedArg: IControllerSessionLayout, groupsArg: IControllerSessionGroup[], ungroupedItemIdsArg: TControllerLayoutItemRef[] | undefined, withheldProjectIdsArg: ReadonlySet<string>, ): { groups: IControllerSessionGroup[]; ungroupedItemIds: TControllerLayoutItemRef[] | undefined } { if (withheldProjectIdsArg.size === 0) { return { groups: groupsArg, ungroupedItemIds: ungroupedItemIdsArg }; } const isWithheld = (itemRefArg: TControllerLayoutItemRef): boolean => ( withheldProjectIdsArg.has(itemRefArg.projectId) ); const merge = ( itemRefsArg: readonly TControllerLayoutItemRef[], storedItemRefsArg: readonly TControllerLayoutItemRef[], ): TControllerLayoutItemRef[] => { const merged = [...itemRefsArg]; storedItemRefsArg.forEach((itemRef, index) => { if (!isWithheld(itemRef)) return; merged.splice(Math.min(index, merged.length), 0, itemRef); }); return merged; }; const requestedGroupIds = new Set(groupsArg.map((group) => group.id)); // A group the client deleted while it could not see these rows must not take them with it. const orphaned = storedArg.groups .filter((group) => !requestedGroupIds.has(group.id)) .flatMap((group) => group.itemIds.filter(isWithheld)); const groups = groupsArg.map((group) => { const storedGroup = storedArg.groups.find((candidate) => candidate.id === group.id); return storedGroup ? { ...group, itemIds: merge(group.itemIds, storedGroup.itemIds) } : group; }); if (ungroupedItemIdsArg === undefined) { // An absent order keeps the stored ungrouped items, which already carry the withheld ones; // only the rows of a deleted group need an explicit home. if (orphaned.length === 0) return { groups, ungroupedItemIds: undefined }; const groupedKeys = new Set( groups.flatMap((group) => group.itemIds.map(controllerLayoutItemRefKey)), ); return { groups, ungroupedItemIds: [ ...storedArg.ungroupedItemIds.filter( (itemRef) => !groupedKeys.has(controllerLayoutItemRefKey(itemRef)), ), ...orphaned, ], }; } return { groups, ungroupedItemIds: [...merge(ungroupedItemIdsArg, storedArg.ungroupedItemIds), ...orphaned], }; } private pushResourceChanged(projectIdArg: string): void { void this.pushControllerEvent({ type: 'resources.changed', projectId: projectIdArg, timestamp: Date.now(), }).catch(() => undefined); } private async openBrowserView( peerArg: IControllerPeer, transportConnectionArg: plugins.typedsocket.ISmartServeConnectionWrapper, requestArg: IReq_ControllerBrowserViewOpen['request'], signalArg: AbortSignal, ): Promise<plugins.typedrequest.TReverseVirtualStreamDirections< IReq_ControllerBrowserViewOpen['response'] >> { return this.runBrowserViewSlotOperation(peerArg.id, async (slotArg) => { const transportPeer = transportConnectionArg.peer; signalArg.throwIfAborted(); if (slotArg.view) { await this.cleanupBrowserViewAsSlotOwner( slotArg, slotArg.view, new Error('Browser view replaced.'), ); } await this.resolveProject(requestArg.projectId); const resource = await awaitWithAbortSignal(this.requireAuthStore().getResource( requestArg.projectId, requestArg.resourceId, ), signalArg); if ( !resource || resource.kind !== 'browser' || resource.lifecycle !== 'active' || resource.pendingAttachment || resource.attachmentRevision !== requestArg.expectedAttachmentRevision ) { throw new plugins.typedrequest.TypedResponseError( 'The requested browser resource revision is unavailable.', { code: 'resource_unavailable' }, ); } const credentialId = peerArg.context.state[authenticatedPeerStateKey]; const authenticationGeneration = this.authenticatedPeerGenerations.get(peerArg); if ( typeof credentialId !== 'string' || authenticationGeneration === undefined || transportPeer.id !== peerArg.id || transportPeer !== peerArg || transportPeer.context.state[authenticatedPeerStateKey] !== credentialId || this.resourceCoordinator?.isResourceRetiring(resource.projectId, resource.id) ) { throw new ControllerResourceUnavailableError('The browser peer is no longer authenticated.'); } const viewId = plugins.crypto.randomBytes(18).toString('base64url'); let view: IControllerBrowserView | undefined; let handle: IControllerBrowserView['handle'] | undefined; let disconnectedBeforePublication = false; try { handle = await this.requireBrowserResourceHost().openHumanView({ resource, actorId: credentialId, peerId: peerArg.id, signal: signalArg, onDisconnect: () => { if (!view) { disconnectedBeforePublication = true; return; } this.scheduleBrowserViewClose( view, new Error('BrowserRuntime revoked the view capability.'), controllerBrowserViewResourceChangedErrorCode, ); }, }); view = { id: viewId, peerId: peerArg.id, credentialId, authenticationGeneration, transportConnection: transportConnectionArg, transportPeer, resource, handle, authority: handle.lease.getAuthority(), lifecycle: 'inactive', activationAbortController: new AbortController(), streamGeneration: 1, stateRevision: 0, recoveryAttempts: 0, closing: false, }; slotArg.view = view; signalArg.throwIfAborted(); if ( disconnectedBeforePublication || view.closing || !this.authenticatedPeerIds.has(peerArg.id) || transportPeer.context.state[authenticatedPeerStateKey] !== credentialId || this.authenticatedPeerGenerations.get(transportPeer) !== authenticationGeneration || this.resourceCoordinator?.isResourceRetiring(resource.projectId, resource.id) ) { throw new ControllerResourceUnavailableError( 'The browser peer disconnected before the view was published.', ); } this.browserViews.set(view.id, view); const transport = await this.createBrowserViewTransport(view, 1); this.beginBrowserViewTransportOpening(view, transport); signalArg.throwIfAborted(); view.activationTimer = setTimeout(() => { this.scheduleBrowserViewClose(view!, new Error('Browser view activation timed out.')); }, browserViewActivationTimeoutMs); view.activationTimer.unref?.(); return { viewId, resourceId: resource.id, attachmentRevision: resource.attachmentRevision, streamGeneration: transport.generation, events: transport.events, operations: transport.operations, state: this.nextControllerBrowserViewState(view, handle.lease.getState()), }; } catch (errorArg) { let cleanupError: unknown; if (view) { await this.cleanupBrowserViewAsSlotOwner(slotArg, view, errorArg).catch((candidate) => { cleanupError = candidate; }); } else if (handle) { await handle.close().catch((candidate) => { cleanupError = candidate; }); } if (cleanupError !== undefined) { throw new AggregateError( [errorArg, cleanupError], 'Browser view publication cleanup is incomplete.', ); } throw errorArg; } }); } private async activateBrowserView( viewIdArg: string, peerArg: IControllerPeer, streamGenerationArg: number, signalArg: AbortSignal, ): Promise<void> { await this.runBrowserViewSlotOperation(peerArg.id, async (slotArg) => { const view = slotArg.view; const transport = view?.transport; if ( !view || view.id !== viewIdArg || view.closing || view.transportPeer !== peerArg || view.credentialId !== peerArg.context.state[authenticatedPeerStateKey] || view.streamGeneration !== streamGenerationArg || !transport || transport.generation !== streamGenerationArg || this.browserViews.get(viewIdArg) !== view ) { throw new plugins.typedrequest.TypedResponseError('The browser view is unavailable.', { code: 'resource_unavailable', }); } await this.assertBrowserViewAuthority( view, streamGenerationArg, transport.identity, signalArg, ); if (view.lifecycle === 'active' && transport.lifecycle === 'active') return; if (view.activationPromise) { await awaitWithAbortSignal(view.activationPromise, signalArg); return; } const recovering = view.lifecycle === 'recovering'; if (!recovering && view.lifecycle !== 'inactive') { throw new plugins.typedrequest.TypedResponseError('The browser view cannot be activated.', { code: 'concurrent_change', }); } if (!recovering) view.lifecycle = 'activating'; const activation = this.activateBrowserViewTransport(view, transport, recovering, signalArg); view.activationPromise = activation; try { await activation; if (view.closing || slotArg.view !== view) { throw new Error('The browser view changed during activation.'); } if (view.activationTimer) clearTimeout(view.activationTimer); view.activationTimer = undefined; this.armBrowserViewRenewal(view); } catch (errorArg) { this.scheduleBrowserViewClose(view, errorArg); throw asTypedResponseError(errorArg, 'controller.browser.view.activate'); } finally { if (view.activationPromise === activation) view.activationPromise = undefined; } }); } private async activateBrowserViewTransport( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, recoveringArg: boolean, signalArg: AbortSignal, ): Promise<void> { const signals = [ signalArg, viewArg.activationAbortController.signal, transportArg.abortController.signal, ]; if (recoveringArg && viewArg.recoveryDeadlineAt !== undefined) { const remainingMs = viewArg.recoveryDeadlineAt - Date.now(); if (remainingMs <= 0) throw new Error('Browser view recovery timed out.'); signals.push(AbortSignal.timeout(remainingMs)); } const activationSignal = AbortSignal.any(signals); await awaitWithAbortSignal(transportArg.openingTask, activationSignal); activationSignal.throwIfAborted(); if (viewArg.transport !== transportArg || transportArg.lifecycle !== 'ready') { throw new Error('The browser view transport changed during activation.'); } transportArg.lifecycle = 'active'; if (!recoveringArg) { await awaitWithAbortSignal(viewArg.handle.activate((eventArg) => { const currentTransport = viewArg.transport; if (viewArg.closing || !currentTransport) return; this.queueBrowserViewEvent(viewArg, currentTransport, eventArg); }), activationSignal); activationSignal.throwIfAborted(); viewArg.lifecycle = 'active'; this.admitBrowserOperations(viewArg, transportArg); return; } const runtimeState = viewArg.handle.lease.getState(); const activeTab = runtimeState.tabs.find((tabArg) => tabArg.id === runtimeState.activeTabId); if (runtimeState.status !== 'running' || !activeTab || !activeTab.active || activeTab.status !== 'open') { throw new Error('The active browser tab is unavailable.'); } const stateDelivery = this.queueBrowserServerPacket(viewArg, transportArg, { protocol: controllerBrowserTransportProtocol, streamGeneration: transportArg.generation, type: 'state', state: this.nextControllerBrowserViewState(viewArg, runtimeState), }); if (!stateDelivery) throw new Error('The recovered browser state could not be queued.'); await awaitWithAbortSignal(stateDelivery, activationSignal); viewArg.lifecycle = 'active'; viewArg.recoveryDeadlineAt = undefined; if (viewArg.recoveryTimer) clearTimeout(viewArg.recoveryTimer); viewArg.recoveryTimer = undefined; this.admitBrowserOperations(viewArg, transportArg); } private async recoverBrowserView( peerArg: IControllerPeer, transportConnectionArg: plugins.typedsocket.ISmartServeConnectionWrapper, requestArg: IReq_ControllerBrowserViewRecover['request'], signalArg: AbortSignal, ): Promise<plugins.typedrequest.TReverseVirtualStreamDirections< IReq_ControllerBrowserViewRecover['response'] >> { return this.runBrowserViewSlotOperation(peerArg.id, async (slotArg) => { const view = slotArg.view; const oldTransport = view?.transport; if ( !view || view.id !== requestArg.viewId || view.closing || view.transportConnection.peer !== transportConnectionArg.peer || view.transportPeer !== peerArg || view.credentialId !== peerArg.context.state[authenticatedPeerStateKey] || view.streamGeneration !== requestArg.expectedStreamGeneration || !oldTransport || oldTransport.generation !== requestArg.expectedStreamGeneration || (view.lifecycle !== 'active' && view.lifecycle !== 'recoverable') ) { throw new plugins.typedrequest.TypedResponseError('The browser view cannot be recovered.', { code: 'resource_unavailable', }); } await this.assertBrowserViewAuthority( view, oldTransport.generation, oldTransport.identity, signalArg, ); const now = Date.now(); view.recoveryDeadlineAt ??= now + controllerBrowserTransportRecoveryGraceMs; if ( now >= view.recoveryDeadlineAt || view.recoveryAttempts >= controllerBrowserTransportMaximumRecoveryAttempts ) { throw new plugins.typedrequest.TypedResponseError('Browser view recovery capacity was reached.', { code: 'operation_limit', }); } await this.closeBrowserDevTools(view); view.recoveryAttempts += 1; view.lifecycle = 'recovering'; this.armBrowserViewRecoveryDeadline(view); try { await this.retireBrowserViewTransport( view, oldTransport, Math.min( view.recoveryDeadlineAt, Date.now() + browserViewTransportCleanupTimeoutMs, ), ); signalArg.throwIfAborted(); if (Date.now() >= view.recoveryDeadlineAt) { throw new Error('Browser view recovery timed out.'); } const streamGeneration = oldTransport.generation + 1; view.streamGeneration = streamGeneration; const transport = await this.createBrowserViewTransport(view, streamGeneration); this.beginBrowserViewTransportOpening(view, transport); signalArg.throwIfAborted(); return { viewId: view.id, resourceId: view.resource.id, attachmentRevision: view.resource.attachmentRevision, streamGeneration, events: transport.events, operations: transport.operations, state: this.nextControllerBrowserViewState(view, view.handle.lease.getState()), }; } catch (errorArg) { this.scheduleBrowserViewClose(view, errorArg); throw asTypedResponseError(errorArg, 'controller.browser.view.recover'); } }); } private async closeBrowserView( viewIdArg: string, peerArg: string | IControllerPeer, expectedStreamGenerationArg?: number, ): Promise<boolean> { const peerId = typeof peerArg === 'string' ? peerArg : peerArg.id; const slot = this.browserViewSlots.get(peerId); const view = slot?.view; if ( !slot || !view || view.id !== viewIdArg || (typeof peerArg !== 'string' && view.transportPeer !== peerArg) || ( expectedStreamGenerationArg !== undefined && view.streamGeneration !== expectedStreamGenerationArg ) ) return false; await this.requestBrowserViewCleanup(slot, view, new Error('Browser view closed.')); return true; } private async closeBrowserViewsForPeer(peerIdArg: string): Promise<void> { const slot = this.browserViewSlots.get(peerIdArg); const view = slot?.view; if (!slot || !view) return; await this.requestBrowserViewCleanup(slot, view, new Error('Browser peer disconnected.')); } /** * Closes the views a resource owns before its runtime is torn down or moved * under them, announcing why. The browser host calls this for every such * teardown. `view_closed` is for a resource on its way down, so the client * must not reopen from a resource list that has not caught up yet; * `resource_changed` is for a resource that stays available under a new * attachment, which the client reopens once its list refreshes. */ private async closeBrowserViewsForResource( projectIdArg: string, resourceIdArg: string, closeCodeArg: TControllerBrowserViewCloseCode, signalArg: AbortSignal, ): Promise<void> { const ownedViews = [...this.browserViewSlots.values()].flatMap((slot) => ( slot.view && slot.view.resource.projectId === projectIdArg && slot.view.resource.id === resourceIdArg ? [{ slot, view: slot.view }] : [] )); if (ownedViews.length === 0) { // Not necessarily wrong -- the resource may own no view -- but a teardown // that closes nothing while a viewer is watching is exactly how an // unannounced reset reaches the client, so it is worth being able to see. const live = [...this.browserViewSlots.values()].flatMap((slot) => ( slot.view ? [`${slot.view.resource.projectId}/${slot.view.resource.id}`] : [] )); if (live.length > 0) { console.error( `Browser resource ${projectIdArg}/${resourceIdArg} closed as ${closeCodeArg} but owns no view;` + ` live views: ${live.join(', ')}.`, ); } // The waited path checks the deadline before it waits, so an already // expired teardown must still fail here rather than proceed into the // runtime just because this resource happened to own no view. signalArg.throwIfAborted(); return; } const closing = Promise.allSettled(ownedViews.map(({ slot, view }) => ( this.closeBrowserViewAnnounced( slot, view, new Error(controllerBrowserViewCloseReasons[closeCodeArg]), closeCodeArg, ) ))); // The teardown that asked for this close holds its project's mutation // queue, so the close stays inside the caller's deadline instead of the // delivery and cleanup timeouts. Giving up on the wait abandons only the // wait: each view was already fenced as failing with its operations // terminalized when the announce was queued, and the close it started // runs to cleanup on its own. The teardown then aborts before it reaches // the runtime, so the view never outlives its resource. const results = await awaitWithAbortSignal(closing, signalArg); const errors = results .filter((result): result is PromiseRejectedResult => result.status === 'rejected') .map((result) => result.reason); if (errors.length > 0) { throw new AggregateError(errors, 'Browser resource view cleanup is incomplete.'); } } private runBrowserViewSlotOperation<T>( peerIdArg: string, operationArg: (slotArg: IControllerBrowserViewSlot) => Promise<T>, ): Promise<T> { let slot = this.browserViewSlots.get(peerIdArg); if (!slot) { slot = { tail: Promise.resolve(), pendingOperations: 0 }; this.browserViewSlots.set(peerIdArg, slot); } return this.enqueueBrowserViewSlotOperation(peerIdArg, slot, operationArg, true); } private enqueueBrowserViewSlotOperation<T>( peerIdArg: string, slotArg: IControllerBrowserViewSlot, operationArg: (slotArg: IControllerBrowserViewSlot) => Promise<T>, enforceAdmissionArg: boolean, ): Promise<T> { if ( enforceAdmissionArg && slotArg.pendingOperations >= maxActiveControllerOperationsPerPeer ) { return Promise.reject(new plugins.typedrequest.TypedResponseError( 'The browser view lifecycle queue is full.', { code: 'operation_limit' }, )); } slotArg.pendingOperations += 1; const operation = slotArg.tail.then(() => operationArg(slotArg)); const tail = operation.then(() => undefined, () => undefined); slotArg.tail = tail; void tail.finally(() => { slotArg.pendingOperations -= 1; if ( slotArg.pendingOperations === 0 && slotArg.view === undefined && this.browserViewSlots.get(peerIdArg) === slotArg ) this.browserViewSlots.delete(peerIdArg); }); return operation; } private markBrowserViewClosing(viewArg: IControllerBrowserView, reasonArg: unknown): void { if (!viewArg.closing) { viewArg.closing = true; viewArg.lifecycle = 'closing'; if (this.browserViews.get(viewArg.id) === viewArg) this.browserViews.delete(viewArg.id); if (viewArg.activationTimer) clearTimeout(viewArg.activationTimer); viewArg.activationTimer = undefined; if (viewArg.renewalTimer) clearTimeout(viewArg.renewalTimer); viewArg.renewalTimer = undefined; if (viewArg.recoveryTimer) clearTimeout(viewArg.recoveryTimer); viewArg.recoveryTimer = undefined; if (!viewArg.activationAbortController.signal.aborted) { viewArg.activationAbortController.abort(reasonArg); } } viewArg.devTools?.stop(reasonArg); const transport = viewArg.transport; if (transport) { if (!transport.abortController.signal.aborted) transport.abortController.abort(reasonArg); for (const queued of transport.submittedOperations.keys()) queued.abortController?.abort(reasonArg); void this.retireBrowserEventStream(transport, reasonArg); void transport.operations.abort(reasonArg).catch(() => undefined); } } private scheduleBrowserViewClose( viewArg: IControllerBrowserView, reasonArg: unknown, closeCodeArg: TControllerBrowserViewCloseCode = controllerBrowserViewFailedErrorCode, ): void { const slot = this.browserViewSlots.get(viewArg.peerId); if (!slot || slot.view !== viewArg) return; void this.closeBrowserViewAnnounced(slot, viewArg, reasonArg, closeCodeArg).catch((errorArg) => { console.error(`Browser view ${viewArg.id} cleanup remains pending.`, errorArg); }); } private armBrowserViewRenewal(viewArg: IControllerBrowserView): void { if (viewArg.closing || viewArg.lifecycle === 'failing' || viewArg.renewalTimer || viewArg.renewalTask) return; const remainingMs = viewArg.handle.lease.expiresAt - Date.now(); if (!Number.isFinite(remainingMs) || remainingMs <= 0) { this.scheduleBrowserViewClose(viewArg, new Error('The browser view authority expired.')); return; } const timer = setTimeout(() => { if (viewArg.renewalTimer !== timer) return; viewArg.renewalTimer = undefined; const renewal = this.renewBrowserViewAuthority(viewArg); viewArg.renewalTask = renewal; void renewal.catch((errorArg) => { if (!viewArg.closing) this.scheduleBrowserViewClose(viewArg, errorArg); }).finally(() => { if (viewArg.renewalTask === renewal) viewArg.renewalTask = undefined; this.armBrowserViewRenewal(viewArg); }); }, Math.max(1, Math.min(60_000, Math.floor(remainingMs / 2)))); viewArg.renewalTimer = timer; timer.unref?.(); } private async renewBrowserViewAuthority(viewArg: IControllerBrowserView): Promise<void> { const slot = this.browserViewSlots.get(viewArg.peerId); if (!slot || slot.view !== viewArg || viewArg.closing || viewArg.lifecycle === 'failing') return; // Lifecycle serialization keeps a transport replacement from racing the authority read. // One internally scheduled renewal per view cannot consume unbounded slot admission. await this.enqueueBrowserViewSlotOperation(viewArg.peerId, slot, async (currentSlot) => { if (currentSlot.view !== viewArg || viewArg.closing || viewArg.lifecycle === 'failing') return; const transport = viewArg.transport; if (!transport) throw new Error('The browser view transport is unavailable.'); const signal = AbortSignal.any([ viewArg.activationAbortController.signal, this.operationAbortController.signal, ]); await this.assertBrowserViewAuthority(viewArg, transport.generation, transport.identity, signal); await viewArg.handle.lease.renew({ signal }); }, false); } /** * Announces a controller-initiated close on the event stream before the * streams are aborted, so the client can tell an intentional close from a * transport fault and never spends a recovery attempt on it. The caller says * what kind of close it is; see the close codes for what each one asks of * the client. The view is fenced as failing at once; returns the delivery, * or undefined when nothing can be announced. The close proceeds either way. */ private announceBrowserViewClose( viewArg: IControllerBrowserView, reasonArg: unknown, closeCodeArg: TControllerBrowserViewCloseCode, ): Promise<void> | undefined { const transport = viewArg.transport; if (!viewArg.closeDiagnostic) { viewArg.closeDiagnostic = { code: closeCodeArg, message: truncateBrowserErrorMessage(reasonArg instanceof Error ? reasonArg.message : String(reasonArg)), }; if (closeCodeArg === controllerBrowserViewFailedErrorCode) { this.reportBrowserDeliveryFailure(viewArg, transport, reasonArg, 'view_close'); } } if ( !transport || viewArg.closing || viewArg.lifecycle === 'failing' || viewArg.lifecycle === 'closing' ) { // A view whose stream already carried an announce owes the client nothing // more, so a second close on it is expected. Anything else closing with no // announce behind it is worth saying out loud -- including an announce // that was queued but dropped at send time, which is exactly the failure // this report exists to catch. if (!transport?.announceDelivered) { this.reportUnannouncedBrowserViewClose( viewArg, closeCodeArg, transport ? `view is ${viewArg.closing ? 'closing' : viewArg.lifecycle}` : 'view has no transport', ); } return undefined; } let delivery: Promise<void> | undefined; try { delivery = this.queueBrowserServerPacket(viewArg, transport, { protocol: controllerBrowserTransportProtocol, streamGeneration: transport.generation, type: 'error', error: { code: closeCodeArg, message: reasonArg instanceof Error ? reasonArg.message : String(reasonArg), fatal: true, }, }, { terminal: true, onSent: () => { transport.announceDelivered = true; }, onSkipped: (reasonArg) => { transport.announceSkipped = reasonArg; }, }); } catch (errorArg) { this.reportUnannouncedBrowserViewClose( viewArg, closeCodeArg, `delivery queue rejected the packet: ${errorArg instanceof Error ? errorArg.message : String(errorArg)}`, ); return undefined; } if (!delivery) { this.reportUnannouncedBrowserViewClose( viewArg, closeCodeArg, `transport is ${transport.lifecycle} and cannot carry the packet`, ); return undefined; } // The delivery chain suppresses its own faults once a close is announced, so // the reason a missing announce failed is otherwise lost. Keep it. delivery.catch((errorArg) => { transport.announceFailure = errorArg; }); transport.announcedClose = true; viewArg.lifecycle = 'failing'; this.terminalizeBrowserTransportOperations(transport, reasonArg); return delivery; } /** * Says so when a close the controller made itself could not be announced. * The close proceeds either way, but the client will read the stream reset * that follows as a transport fault and spend its recovery budget on it, so * the reason belongs in the log rather than nowhere. */ private reportUnannouncedBrowserViewClose( viewArg: IControllerBrowserView, closeCodeArg: TControllerBrowserViewCloseCode, reasonArg: string, ): void { console.error( `Browser view ${viewArg.id} closed as ${closeCodeArg} without announcing it: ${reasonArg}.` + ` Original close: ${JSON.stringify(viewArg.closeDiagnostic)}.`, ); } private reportBrowserDeliveryFailure( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport | undefined, reasonArg: unknown, phaseArg: 'transport' | 'protocol' | 'view_close', ): void { console.error(`Browser delivery failure: ${JSON.stringify({ timestamp: new Date().toISOString(), viewId: viewArg.id, streamGeneration: transportArg?.generation, phase: phaseArg, reason: truncateBrowserErrorMessage(reasonArg instanceof Error ? reasonArg.message : String(reasonArg)), viewState: viewArg.lifecycle, transportState: transportArg?.lifecycle, pendingEvents: transportArg?.pendingEventCount, pendingBytes: transportArg?.pendingEventBytes, highWater: transportArg?.deliveryHighWater, })}`); } /** Announces the close when possible, waits a bounded time for delivery, then cleans up. */ private async closeBrowserViewAnnounced( slotArg: IControllerBrowserViewSlot, viewArg: IControllerBrowserView, reasonArg: unknown, closeCodeArg: TControllerBrowserViewCloseCode, ): Promise<void> { const transport = viewArg.transport; const announcement = this.announceBrowserViewClose(viewArg, reasonArg, closeCodeArg); if (announcement) { await awaitWithAbortSignal( announcement, AbortSignal.timeout(browserViewTransportCleanupTimeoutMs), ).catch(() => undefined); // Delivery resolving is not the same as the packet going out, and the // difference is invisible from here unless it is checked: the stream then // ends cleanly with nothing in it and the client reads an unexplained // close. Saying so here names the drop, rather than leaving it to surface // later as a second close that merely reports a missing transport. if (transport && !transport.announceDelivered) { this.reportUnannouncedBrowserViewClose( viewArg, closeCodeArg, `${transport.announceFailure !== undefined ? `the send failed: ${transport.announceFailure instanceof Error ? transport.announceFailure.message : String(transport.announceFailure)}` : transport.announceSkipped !== undefined ? `the delivery skipped it because ${transport.announceSkipped}` : 'the queued packet never reached the wire and never failed'}` + ` (transport ${transport.lifecycle},` + ` view ${viewArg.closing ? 'closing' : viewArg.lifecycle},` + ` ${viewArg.transport === transport ? 'still attached' : 'detached'},` + ` ${transport.pendingEventCount} packets / ${transport.pendingEventBytes} bytes still queued)`, ); } } await this.requestBrowserViewCleanup(slotArg, viewArg, reasonArg); } private requestBrowserViewCleanup( slotArg: IControllerBrowserViewSlot, viewArg: IControllerBrowserView, reasonArg: unknown, ): Promise<void> { this.markBrowserViewClosing(viewArg, reasonArg); if (slotArg.view !== viewArg) return Promise.resolve(); if (viewArg.cleanupRetryTimer) { clearTimeout(viewArg.cleanupRetryTimer); viewArg.cleanupRetryTimer = undefined; } if (viewArg.cleanupTask) return viewArg.cleanupTask; const operation = this.enqueueBrowserViewSlotOperation( viewArg.peerId, slotArg, async (currentSlot) => { if (currentSlot.view !== viewArg) return; await this.attemptBrowserViewCleanup(currentSlot, viewArg, reasonArg); }, false, ); let cleanupTask!: Promise<void>; cleanupTask = operation.catch((errorArg) => { this.scheduleBrowserViewCleanupRetry(slotArg, viewArg, errorArg); throw errorArg; }).finally(() => { if (viewArg.cleanupTask === cleanupTask) viewArg.cleanupTask = undefined; }); viewArg.cleanupTask = cleanupTask; return cleanupTask; } private async cleanupBrowserViewAsSlotOwner( slotArg: IControllerBrowserViewSlot, viewArg: IControllerBrowserView, reasonArg: unknown, ): Promise<void> { try { await this.attemptBrowserViewCleanup(slotArg, viewArg, reasonArg); } catch (errorArg) { this.scheduleBrowserViewCleanupRetry(slotArg, viewArg, errorArg); throw errorArg; } } private async attemptBrowserViewCleanup( slotArg: IControllerBrowserViewSlot, viewArg: IControllerBrowserView, reasonArg: unknown, ): Promise<void> { this.markBrowserViewClosing(viewArg, reasonArg); if (slotArg.view !== viewArg) return; const errors: unknown[] = []; const constructionCleanup = viewArg.transportConstructionCleanup; if (constructionCleanup && !constructionCleanup.settled) { errors.push(new Error('Browser stream pair construction cleanup remains pending.')); } if (viewArg.transport) { await this.retireBrowserViewTransport( viewArg, viewArg.transport, Date.now() + browserViewTransportCleanupTimeoutMs, ).catch((errorArg) => errors.push(errorArg)); } await this.closeBrowserDevTools(viewArg).catch((errorArg) => errors.push(errorArg)); await viewArg.handle.close().catch((errorArg) => errors.push(errorArg)); if (errors.length === 1) throw errors[0]; if (errors.length > 1) { throw new AggregateError(errors, 'Browser view cleanup is incomplete.'); } if (viewArg.cleanupRetryTimer) clearTimeout(viewArg.cleanupRetryTimer); viewArg.cleanupRetryTimer = undefined; if (slotArg.view === viewArg) slotArg.view = undefined; } private scheduleBrowserViewCleanupRetry( slotArg: IControllerBrowserViewSlot, viewArg: IControllerBrowserView, reasonArg: unknown, ): void { if (slotArg.view !== viewArg || viewArg.cleanupRetryTimer) return; if ( viewArg.transportConstructionCleanup || ( viewArg.transport?.retirementSettlement && !viewArg.transport.retirementSettled ) ) return; const timer = setTimeout(() => { if (viewArg.cleanupRetryTimer === timer) viewArg.cleanupRetryTimer = undefined; if (slotArg.view !== viewArg) return; void this.requestBrowserViewCleanup(slotArg, viewArg, reasonArg).catch((errorArg) => { console.error(`Browser view ${viewArg.id} cleanup retry failed.`, errorArg); }); }, browserViewCleanupRetryMs); viewArg.cleanupRetryTimer = timer; timer.unref?.(); } private queueBrowserViewEvent( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, eventArg: plugins.browserRuntime.TBrowserRuntimeEvent, ): void { try { if (viewArg.lifecycle === 'failing' || viewArg.lifecycle === 'closing') return; if (eventArg.type === 'state') { if (viewArg.transport === transportArg && viewArg.devTools && viewArg.devTools.tabId !== viewArg.handle.lease.getState().activeTabId) { viewArg.devTools.stop(new Error('The selected browser tab changed.')); void this.closeBrowserDevTools(viewArg).catch(error => { console.error(`Browser view ${viewArg.id} inspector cleanup remains pending.`, error); }); } if (viewArg.lifecycle === 'recoverable') return; this.queueBrowserServerPacket(viewArg, transportArg, { protocol: controllerBrowserTransportProtocol, streamGeneration: transportArg.generation, type: 'state', state: this.nextControllerBrowserViewState(viewArg, eventArg.state), }); return; } if (eventArg.type === 'error') { const error = controllerBrowserError(eventArg.error); const delivery = this.queueBrowserServerPacket(viewArg, transportArg, { protocol: controllerBrowserTransportProtocol, streamGeneration: transportArg.generation, type: 'error', error, }); if (error.fatal) { const reason = new Error(error.message); viewArg.lifecycle = 'failing'; this.terminalizeBrowserTransportOperations(transportArg, reason); if (!delivery) { this.scheduleBrowserViewClose(viewArg, reason); } else { void awaitWithAbortSignal( delivery, AbortSignal.timeout(browserViewTransportCleanupTimeoutMs), ).then( () => this.scheduleBrowserViewClose(viewArg, reason), () => this.scheduleBrowserViewClose(viewArg, reason), ); } } return; } throw new Error('An image frame was delivered to a native video subscription.'); } catch (errorArg) { this.scheduleBrowserViewClose(viewArg, errorArg); throw errorArg; } } private async createBrowserViewTransport( viewArg: IControllerBrowserView, streamGenerationArg: number, ): Promise<IControllerBrowserViewTransport> { const typedSocket = this.typedServer?.typedsocket; if (!typedSocket) throw new Error('The controller transport is unavailable.'); const transportIdentity = Symbol('controller.browser.transport'); if (viewArg.pendingTransportIdentity || viewArg.transportConstructionCleanup) { throw new Error('A browser stream pair is already being constructed.'); } viewArg.pendingTransportIdentity = transportIdentity; const createAuthorization = ( creatorDirectionArg: plugins.typedrequestInterfaces.TVirtualStreamDirection, contentTypeArg: string, ): IControllerBrowserStreamAuthorization => Object.freeze({ marker: browserStreamAuthorizationMarker, view: viewArg, authority: viewArg.authority, transportPeer: viewArg.transportPeer, transportIdentity, streamGeneration: streamGenerationArg, creatorDirection: creatorDirectionArg, contentType: contentTypeArg, }); try { const events = typedSocket.createVirtualStream({ target: viewArg.transportConnection, creatorDirection: 'send', contentType: controllerBrowserTransportEventContentType, authorization: createAuthorization('send', controllerBrowserTransportEventContentType), }); let operations: plugins.typedrequestInterfaces.TVirtualStream<'receive'>; try { operations = typedSocket.createVirtualStream({ target: viewArg.transportConnection, creatorDirection: 'receive', contentType: controllerBrowserTransportOperationContentType, authorization: createAuthorization('receive', controllerBrowserTransportOperationContentType), }); } catch (errorArg) { let cleanup!: IControllerBrowserTransportConstructionCleanup; const settlement = Promise.allSettled([ Promise.resolve().then(() => events.abort(errorArg)), events.opened, events.completion, events.closed, ]).then(() => { cleanup.settled = true; const slot = this.browserViewSlots.get(viewArg.peerId); if ( !cleanup.deadlineExpired || !viewArg.closing || slot?.view !== viewArg ) { if (viewArg.transportConstructionCleanup === cleanup) { viewArg.transportConstructionCleanup = undefined; } return; } const activeCleanupTask = viewArg.cleanupTask; void (activeCleanupTask ? activeCleanupTask.catch(() => undefined) : Promise.resolve() ).then(() => { const currentSlot = this.browserViewSlots.get(viewArg.peerId); if ( viewArg.transportConstructionCleanup !== cleanup || !viewArg.closing || currentSlot?.view !== viewArg ) return; viewArg.transportConstructionCleanup = undefined; void this.requestBrowserViewCleanup(currentSlot, viewArg, cleanup.reason).catch( (cleanupErrorArg) => { console.error(`Browser view ${viewArg.id} late construction cleanup failed.`, cleanupErrorArg); }, ); }); }); cleanup = { settlement, reason: errorArg, settled: false, deadlineExpired: false, }; viewArg.transportConstructionCleanup = cleanup; try { await this.waitForBrowserTransportSettlement( settlement, Date.now() + browserViewTransportCleanupTimeoutMs, ); } catch (cleanupErrorArg) { cleanup.deadlineExpired = true; throw new AggregateError( [errorArg, cleanupErrorArg], 'Browser stream pair construction cleanup is incomplete.', ); } throw errorArg; } const transport: IControllerBrowserViewTransport = { identity: transportIdentity, generation: streamGenerationArg, lifecycle: 'opening', events, operations, abortController: new AbortController(), openingTask: Promise.resolve(), deliveryTail: Promise.resolve(), eventQueue: [], pendingEventCount: 0, pendingEventBytes: 0, submittedOperations: new Map(), operationQueue: [], pendingOperationBytes: 0, highestOperationId: 0, cancellableOperationIds: new Set(), }; viewArg.transport = transport; return transport; } finally { if (viewArg.pendingTransportIdentity === transportIdentity) { viewArg.pendingTransportIdentity = undefined; } } } private beginBrowserViewTransportOpening( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, ): void { if (viewArg.transport !== transportArg) { throw new Error('The browser stream pair cannot open before publication.'); } const openingTask = this.openBrowserViewTransport(viewArg, transportArg); transportArg.openingTask = openingTask; void openingTask.catch((errorArg) => { this.noteBrowserTransportFault(viewArg, transportArg, errorArg, true); }); } private async openBrowserViewTransport( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, ): Promise<void> { let timeout: ReturnType<typeof setTimeout> | undefined; try { await Promise.race([ Promise.all([transportArg.events.opened, transportArg.operations.opened]), new Promise<never>((_resolve, reject) => { timeout = setTimeout(() => reject(new Error('Browser stream pair opening timed out.')), controllerBrowserTransportPairOpenTimeoutMs); timeout.unref?.(); }), ]); if ( viewArg.transport !== transportArg || viewArg.closing || viewArg.streamGeneration !== transportArg.generation || transportArg.lifecycle !== 'opening' ) throw new Error('The browser stream pair became stale while opening.'); transportArg.lifecycle = 'ready'; const receiveTask = this.receiveBrowserOperations(viewArg, transportArg); transportArg.receiveTask = receiveTask; void receiveTask.catch((errorArg) => { this.noteBrowserTransportFault(viewArg, transportArg, errorArg, true); }); } finally { if (timeout) clearTimeout(timeout); } } private async receiveBrowserOperations( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, ): Promise<void> { while (transportArg.lifecycle !== 'retiring' && transportArg.lifecycle !== 'retired') { const chunk = await transportArg.operations.receive(); if (chunk === undefined) { if (transportArg.abortController.signal.aborted) return; await transportArg.operations.accept(); throw new Error('The browser operation stream closed unexpectedly.'); } let packet: TControllerBrowserClientPacket; try { packet = assertControllerBrowserClientPacket( decodeControllerBrowserTransportPacket(chunk), ); } catch (errorArg) { this.failBrowserTransportProtocol(viewArg, transportArg, errorArg); return; } if (packet.streamGeneration !== transportArg.generation) { this.failBrowserTransportProtocol( viewArg, transportArg, new Error('The browser operation belongs to a stale stream generation.'), ); return; } this.admitBrowserClientPacket(viewArg, transportArg, packet, chunk.byteLength); } } private admitBrowserClientPacket( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, packetArg: TControllerBrowserClientPacket, byteLengthArg: number, ): void { if (packetArg.type === 'cancel') { if (packetArg.operationId > transportArg.highestOperationId) { this.failBrowserTransportProtocol(viewArg, transportArg, new Error('cancel targeted a future operation.')); return; } if (transportArg.cancellableOperationIds.has(packetArg.operationId)) { this.cancelBrowserOperation(viewArg, transportArg, packetArg.operationId); } return; } if (packetArg.operationId <= transportArg.highestOperationId) { this.failBrowserTransportProtocol(viewArg, transportArg, new Error('operationId was reused.')); return; } transportArg.highestOperationId = packetArg.operationId; const queued: IControllerBrowserQueuedOperation = { operationId: packetArg.operationId, operation: packetArg.operation, byteLength: byteLengthArg, cancelled: false, terminal: false, }; if ( transportArg.operationQueue.length + transportArg.submittedOperations.size >= controllerBrowserTransportMaximumPendingOperations || transportArg.pendingOperationBytes + byteLengthArg > controllerBrowserTransportMaximumQueuedOperationBytes ) { this.settleBrowserOperation(viewArg, transportArg, queued, { status: 'failed', error: { code: 'operation_limit', message: 'The browser operation queue is full.' }, }); return; } transportArg.cancellableOperationIds.add(queued.operationId); const tail = transportArg.operationQueue.at(-1); if (tail && this.browserOperationsCanCoalesce(tail.operation, queued.operation)) { transportArg.operationQueue.pop(); transportArg.pendingOperationBytes -= tail.byteLength; this.settleBrowserOperation(viewArg, transportArg, tail, { status: 'superseded' }); } transportArg.operationQueue.push(queued); transportArg.pendingOperationBytes += byteLengthArg; if (viewArg.lifecycle === 'active' && transportArg.lifecycle === 'active') { this.admitBrowserOperations(viewArg, transportArg); } } private browserOperationsCanCoalesce( existingArg: TControllerBrowserViewOperation, candidateArg: TControllerBrowserViewOperation, ): boolean { // Runtime owns input coalescing at the queue where native work actually waits. return existingArg.type === 'setViewport' && candidateArg.type === 'setViewport'; } private cancelBrowserOperation( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, operationIdArg: number, ): void { if (!transportArg.cancellableOperationIds.delete(operationIdArg)) return; const queuedIndex = transportArg.operationQueue.findIndex( (candidateArg) => candidateArg.operationId === operationIdArg, ); if (queuedIndex >= 0) { const [queued] = transportArg.operationQueue.splice(queuedIndex, 1); if (!queued) return; transportArg.pendingOperationBytes -= queued.byteLength; queued.cancelled = true; this.settleBrowserOperation(viewArg, transportArg, queued, { status: 'cancelled' }); return; } const active = [...transportArg.submittedOperations.keys()].find(queued => queued.operationId === operationIdArg); if (active?.operationId === operationIdArg && !active.terminal) { active.cancelled = true; active.abortController?.abort(new Error('The browser operation was cancelled.')); return; } } private admitBrowserOperations( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, ): void { if (transportArg.admittingOperations) return; transportArg.admittingOperations = true; try { while (viewArg.lifecycle === 'active' && transportArg.lifecycle === 'active' && viewArg.transport === transportArg && transportArg.operationQueue.length > 0) { // Transport admission is FIFO. BrowserRuntime alone decides execution // concurrency, semantic barriers, telemetry independence and dialog priority. const queued = transportArg.operationQueue.shift()!; if (queued.terminal) { transportArg.pendingOperationBytes -= queued.byteLength; continue; } const task = this.runQueuedBrowserOperation(viewArg, transportArg, queued).finally(() => { transportArg.submittedOperations.delete(queued); transportArg.pendingOperationBytes -= queued.byteLength; this.admitBrowserOperations(viewArg, transportArg); }); transportArg.submittedOperations.set(queued, task); transportArg.drainTask = Promise.allSettled([...transportArg.submittedOperations.values()]).then(() => undefined); void task.catch(error => this.noteBrowserTransportFault(viewArg, transportArg, error, false)); } } finally { transportArg.admittingOperations = false; } } private async runQueuedBrowserOperation(viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, queued: IControllerBrowserQueuedOperation): Promise<void> { queued.abortController = new AbortController(); const peerAbortController = viewArg.transportPeer.context.state[operationAbortControllerPeerStateKey]; const operationSignal = AbortSignal.any([ queued.abortController.signal, viewArg.activationAbortController.signal, transportArg.abortController.signal, this.operationAbortController.signal, ...(peerAbortController instanceof AbortController ? [peerAbortController.signal] : []), ]); try { const result = await this.runBrowserOperation(viewArg, transportArg, queued.operation, operationSignal); this.settleBrowserOperation(viewArg, transportArg, queued, { status: 'succeeded', ...result }); } catch (error) { this.settleBrowserOperation(viewArg, transportArg, queued, queued.cancelled || operationSignal.aborted ? { status: 'cancelled' } : error instanceof plugins.browserRuntime.BrowserRuntimeError && error.code === 'SUPERSEDED' ? { status: 'superseded' } : { status: 'failed', error: this.browserOperationError(error) }); } } private async runBrowserOperation( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, operationArg: TControllerBrowserViewOperation, signalArg: AbortSignal, ): Promise<Pick<Extract<TControllerBrowserOperationResult, { status: 'succeeded' }>, 'state' | 'viewportResult' | 'videoOffer' | 'videoStatistics'>> { const execute = async (operationSignalArg: AbortSignal) => { this.assertBrowserViewLiveAuthority(viewArg, transportArg, operationSignalArg); return this.executeBrowserOperation(viewArg, operationArg, operationSignalArg); }; if (this.isBrowserSemanticOperation(operationArg)) { await this.runOperation(viewArg.transportPeer, execute, signalArg); return { state: this.nextControllerBrowserViewState(viewArg, viewArg.handle.lease.getState()) }; } if (this.lifecycleState !== 'ready' || this.stopRequested) { throw new Error('The controller is not ready to accept browser input.'); } // runOperation also admits in a microtask. Use the same admission boundary // so a later raw input cannot overtake a semantic command before Runtime. return await Promise.resolve().then(() => execute(signalArg)) ?? {}; } private isBrowserSemanticOperation(operationArg: TControllerBrowserViewOperation): boolean { return operationArg.type === 'navigate' || operationArg.type === 'createTab' || operationArg.type === 'activateTab' || operationArg.type === 'closeTab' || operationArg.type === 'back' || operationArg.type === 'forward' || operationArg.type === 'reload'; } private isBrowserRawInputOperation(operationArg: TControllerBrowserViewOperation): boolean { return operationArg.type === 'dispatchMouse' || operationArg.type === 'dispatchWheel' || operationArg.type === 'dispatchKey' || operationArg.type === 'insertText'; } private assertBrowserViewLiveAuthority( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, signalArg: AbortSignal, ): void { signalArg.throwIfAborted(); if (this.lifecycleState !== 'ready' || this.stopRequested || viewArg.lifecycle !== 'active' || transportArg.lifecycle !== 'active' || transportArg.abortController.signal.aborted || viewArg.transportPeer.context.headers.get('origin') !== this.requireRuntimeConfig().publicOrigin || this.resourceCoordinator?.isResourceRetiring(viewArg.resource.projectId, viewArg.resource.id) || !this.isBrowserViewInMemoryAuthorityCurrent(viewArg, transportArg.generation, transportArg.identity)) { throw new ControllerResourceUnavailableError('The browser view authority is unavailable.'); } } private assertHumanBrowserLiveAuthority( operationArg: plugins.browserRuntime.TBrowserRuntimeOperationIdentity, signalArg: AbortSignal, ): IControllerBrowserView { const view = this.browserViewSlots.get(operationArg.peerId)?.view; const transport = view?.transport; if (!view || !transport || operationArg.source !== 'human' || operationArg.role !== 'human' || operationArg.actorId !== view.credentialId || operationArg.projectId !== view.resource.projectId || operationArg.browserResourceId !== view.resource.id || operationArg.attachmentAuthorityId !== view.resource.attachmentAuthorityId || operationArg.leaseId !== view.handle.lease.leaseId || operationArg.capabilityId !== view.handle.lease.capabilityId) { throw new ControllerResourceUnavailableError('The browser view lease is unavailable.'); } this.assertBrowserViewLiveAuthority(view, transport, signalArg); return view; } private async executeBrowserOperation( viewArg: IControllerBrowserView, operationArg: TControllerBrowserViewOperation, signalArg: AbortSignal, ): Promise<Pick<Extract<TControllerBrowserOperationResult, { status: 'succeeded' }>, 'viewportResult' | 'videoOffer' | 'videoStatistics'> | undefined> { const lease = viewArg.handle.lease; switch (operationArg.type) { case 'respondToDialog': await lease.respondToDialog(operationArg.input, { signal: signalArg }); return; case 'openVideoPeer': return { videoOffer: await lease.openVideoPeer({ signal: signalArg }) }; case 'answerVideoPeer': await lease.answerVideoPeer(operationArg.negotiationId, operationArg.description, { signal: signalArg }); return; case 'closeVideoPeer': await lease.closeVideoPeer({ signal: signalArg }); return; case 'getVideoStatistics': return { videoStatistics: await lease.getVideoStatistics({ signal: signalArg }) }; case 'setViewport': return { viewportResult: await lease.setViewport(operationArg.viewport, { signal: signalArg }) }; case 'dispatchMouse': await lease.dispatchMouse(operationArg.input, { signal: signalArg, coalesceHoverMoves: true }); return; case 'dispatchWheel': await lease.dispatchWheel(operationArg.input, { signal: signalArg }); return; case 'dispatchKey': await lease.dispatchKey(operationArg.input, { signal: signalArg }); return; case 'insertText': await lease.insertText(operationArg.input, { signal: signalArg }); return; case 'navigate': await lease.executeHumanAction({ action: 'navigate', url: operationArg.url, ...(operationArg.tabId === undefined ? {} : { tabId: operationArg.tabId }), ...(operationArg.timeoutMs === undefined ? {} : { timeoutMs: operationArg.timeoutMs }), }, { signal: signalArg }); return; case 'createTab': await lease.createTab({ ...(operationArg.url === undefined ? {} : { url: operationArg.url }), ...(operationArg.activate === undefined ? {} : { activate: operationArg.activate }), }, { signal: signalArg }); return; case 'activateTab': await lease.activateTab(operationArg.tabId, { signal: signalArg }); return; case 'closeTab': await lease.closeTab(operationArg.tabId, { signal: signalArg }); return; case 'back': await lease.back( operationArg.tabId === undefined ? {} : { tabId: operationArg.tabId }, { signal: signalArg }, ); return; case 'forward': await lease.forward( operationArg.tabId === undefined ? {} : { tabId: operationArg.tabId }, { signal: signalArg }, ); return; case 'reload': await lease.reload( operationArg.tabId === undefined ? {} : { tabId: operationArg.tabId }, { signal: signalArg }, ); } } private settleBrowserOperation( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, queuedArg: IControllerBrowserQueuedOperation, resultArg: IControllerBrowserOperationResultPacket['result'], ): void { if (queuedArg.terminal) return; queuedArg.terminal = true; this.queueBrowserOperationResult(viewArg, transportArg, queuedArg.operationId, resultArg); } private queueBrowserOperationResult( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, operationIdArg: number, resultArg: IControllerBrowserOperationResultPacket['result'], ): void { transportArg.cancellableOperationIds.delete(operationIdArg); try { this.queueBrowserServerPacket(viewArg, transportArg, { protocol: controllerBrowserTransportProtocol, streamGeneration: transportArg.generation, type: 'operationResult', operationId: operationIdArg, result: resultArg, }); } catch (errorArg) { this.noteBrowserTransportFault(viewArg, transportArg, errorArg, false); } } /** Reliable control and signaling packets retain FIFO order with reserved close capacity. */ private queueBrowserServerPacket( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, packetArg: TControllerBrowserServerPacket, optionsArg?: { terminal?: boolean; onSent?: () => void; onSkipped?: (reasonArg: string) => void }, ): Promise<void> | undefined { const terminal = optionsArg?.terminal === true; if ( viewArg.transport !== transportArg || (terminal ? transportArg.announcedClose || transportArg.lifecycle === 'retired' || transportArg.lifecycle === 'opening' : transportArg.announcedClose || viewArg.closing || !this.browserTransportCanDeliverPacket(transportArg, packetArg)) ) return undefined; const packetBytes = encodeControllerBrowserTransportPacket(packetArg); const eventBytes = packetBytes.byteLength; if ( !terminal && ( (transportArg.pendingEventCount >= controllerBrowserTransportMaximumPendingEvents) || transportArg.pendingEventBytes + eventBytes > controllerBrowserTransportMaximumPendingEventBytes ) ) throw new Error('The browser view delivery queue is full.'); let resolve!: () => void; let reject!: (reasonArg: unknown) => void; const delivery = new Promise<void>((resolveArg, rejectArg) => { resolve = resolveArg; reject = rejectArg; }); // Event subscribers do not await delivery. The drain reports each failure // before considering the next packet, and callers can still await rejection. void delivery.catch(() => undefined); transportArg.eventQueue.push({ packet: packetArg, packetBytes, byteLength: eventBytes, terminal, onSent: optionsArg?.onSent, onSkipped: optionsArg?.onSkipped, resolve, reject, }); transportArg.pendingEventCount += 1; transportArg.pendingEventBytes += eventBytes; transportArg.deliveryHighWater = { events: Math.max(transportArg.deliveryHighWater?.events ?? 0, transportArg.pendingEventCount), bytes: Math.max(transportArg.deliveryHighWater?.bytes ?? 0, transportArg.pendingEventBytes), }; this.ensureBrowserEventDrain(viewArg, transportArg); return delivery; } private ensureBrowserEventDrain( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, ): void { if (transportArg.eventDeliveryTask) return; const task = transportArg.deliveryTail.then(async () => { const pending = new Set<Promise<void>>(); const deliver = async (entry: IControllerBrowserQueuedEvent): Promise<void> => { try { const detached = viewArg.transport !== transportArg; const refused = entry.terminal ? transportArg.lifecycle === 'retired' || transportArg.lifecycle === 'opening' : transportArg.announcedClose || viewArg.closing || !this.browserTransportCanDeliverPacket(transportArg, entry.packet); if (detached || refused) { entry.onSkipped?.(detached ? 'the stream is no longer the view\'s' : `the transport is ${transportArg.lifecycle}`); } else { await transportArg.events.send(entry.packetBytes); entry.onSent?.(); } entry.resolve(); } catch (errorArg) { entry.reject(errorArg); this.noteBrowserTransportFault(viewArg, transportArg, errorArg, true); } finally { transportArg.pendingEventCount -= 1; transportArg.pendingEventBytes -= entry.byteLength; } }; try { while (transportArg.eventQueue.length || pending.size) { while (transportArg.eventQueue.length && pending.size < 4) { const entry = transportArg.eventQueue.shift()!; // VirtualStream orders concurrent sends and acknowledges each at // the receiver. Keep a bounded sliding window instead of paying // a complete network round trip for every wheel result. if (entry.terminal) { await Promise.all(pending); await deliver(entry); } else { const delivery = deliver(entry).finally(() => pending.delete(delivery)); pending.add(delivery); } } if (pending.size) await Promise.race(pending); } } finally { await Promise.allSettled(pending); transportArg.eventDeliveryTask = undefined; } }); transportArg.eventDeliveryTask = task; transportArg.deliveryTail = task; } private browserTransportCanDeliverPacket( transportArg: IControllerBrowserViewTransport, packetArg: TControllerBrowserServerPacket, ): boolean { return transportArg.lifecycle === 'active' || (transportArg.lifecycle === 'ready' && packetArg.type === 'operationResult'); } /** * Tears down an event stream, and is the only place allowed to: a stream * that carried an announced close is ended in order behind it, because a * reset discards the very packet that tells the client the close was * intentional and leaves it reading an unexplained stream failure. Ending * is asynchronous, so callers that tear the transport down around it must * await the returned promise rather than race it. Everything else is reset, * because nothing is owed on it. */ private retireBrowserEventStream( transportArg: IControllerBrowserViewTransport, reasonArg: unknown, ): Promise<void> { return transportArg.announcedClose ? transportArg.events.close().then(() => undefined, () => undefined) : transportArg.events.abort(reasonArg).catch(() => undefined); } private failBrowserTransportProtocol( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, reasonArg: unknown, ): void { if (transportArg.announcedClose) return; if (transportArg.lifecycle === 'failed' || transportArg.lifecycle === 'retiring' || transportArg.lifecycle === 'retired') return; this.reportBrowserDeliveryFailure(viewArg, transportArg, reasonArg, 'protocol'); transportArg.lifecycle = 'failed'; transportArg.failure = reasonArg; this.terminalizeBrowserTransportOperations(transportArg, reasonArg); void this.retireBrowserEventStream(transportArg, reasonArg); void transportArg.operations.abort(reasonArg).catch(() => undefined); if (viewArg.transport !== transportArg) return; this.scheduleBrowserViewClose(viewArg, reasonArg); } private noteBrowserTransportFault( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, reasonArg: unknown, recoverableArg: boolean, ): void { if ( transportArg.lifecycle === 'failed' || transportArg.lifecycle === 'retiring' || transportArg.lifecycle === 'retired' ) return; // The announce terminalizes this transport's own in-flight work, so the // failures that follow are the close happening, not a new fault. Treating // them as one would reset the stream out from under the announce and open // a recovery window the controller refuses anyway. if (transportArg.announcedClose) return; this.reportBrowserDeliveryFailure(viewArg, transportArg, reasonArg, 'transport'); transportArg.lifecycle = 'failed'; transportArg.failure = reasonArg; this.terminalizeBrowserTransportOperations(transportArg, reasonArg); void this.retireBrowserEventStream(transportArg, reasonArg); void transportArg.operations.abort(reasonArg).catch(() => undefined); if (viewArg.transport !== transportArg) return; if ( recoverableArg && !viewArg.closing && viewArg.transport === transportArg && viewArg.lifecycle === 'active' ) { viewArg.lifecycle = 'recoverable'; viewArg.recoveryDeadlineAt = Date.now() + controllerBrowserTransportRecoveryGraceMs; this.armBrowserViewRecoveryDeadline(viewArg); return; } this.scheduleBrowserViewClose(viewArg, reasonArg); } private terminalizeBrowserTransportOperations( transportArg: IControllerBrowserViewTransport, reasonArg: unknown, ): void { if (!transportArg.abortController.signal.aborted) { transportArg.abortController.abort(reasonArg); } for (const queued of transportArg.operationQueue.splice(0)) { queued.terminal = true; transportArg.pendingOperationBytes -= queued.byteLength; } transportArg.cancellableOperationIds.clear(); for (const queued of transportArg.submittedOperations.keys()) { queued.cancelled = true; queued.abortController?.abort(reasonArg); } } private armBrowserViewRecoveryDeadline(viewArg: IControllerBrowserView): void { if (viewArg.recoveryTimer || viewArg.recoveryDeadlineAt === undefined) return; const delayMs = Math.max(0, viewArg.recoveryDeadlineAt - Date.now()); const timer = setTimeout(() => { if (viewArg.recoveryTimer === timer) viewArg.recoveryTimer = undefined; if (viewArg.closing || viewArg.lifecycle === 'active') return; this.scheduleBrowserViewClose(viewArg, new Error('Browser view recovery timed out.')); }, delayMs); viewArg.recoveryTimer = timer; timer.unref?.(); } private retireBrowserViewTransport( viewArg: IControllerBrowserView, transportArg: IControllerBrowserViewTransport, deadlineAtArg: number, ): Promise<void> { if (transportArg.lifecycle === 'retired') return Promise.resolve(); if (transportArg.retirementTask) return transportArg.retirementTask; if (transportArg.retirementSettlement && !transportArg.retirementSettled) { return Promise.reject(new Error('Browser stream generation cleanup remains pending.')); } if (!transportArg.retirementSettlement) { transportArg.lifecycle = 'retiring'; const retirementReason = new Error('The browser stream generation was retired.'); this.terminalizeBrowserTransportOperations(transportArg, retirementReason); const settlement = Promise.allSettled([ Promise.resolve().then(() => this.retireBrowserEventStream( transportArg, retirementReason, )), Promise.resolve().then(() => transportArg.operations.abort(retirementReason)), transportArg.openingTask, transportArg.receiveTask, transportArg.drainTask, transportArg.deliveryTail, ].filter((candidateArg): candidateArg is Promise<void> => candidateArg !== undefined)) .then(() => { transportArg.retirementSettled = true; transportArg.lifecycle = 'retired'; const stillAssociated = viewArg.transport === transportArg; const slot = this.browserViewSlots.get(viewArg.peerId); if (stillAssociated && viewArg.closing) viewArg.transport = undefined; if ( transportArg.retirementDeadlineExpired && stillAssociated && viewArg.closing && slot?.view === viewArg ) { void this.requestBrowserViewCleanup(slot, viewArg, retirementReason).catch((errorArg) => { console.error(`Browser view ${viewArg.id} late cleanup failed.`, errorArg); }); } }); transportArg.retirementSettlement = settlement; } if (transportArg.retirementSettled) return Promise.resolve(); const retirement = this.waitForBrowserTransportSettlement( transportArg.retirementSettlement, deadlineAtArg, ).catch((errorArg) => { transportArg.retirementDeadlineExpired = true; throw errorArg; }); let tracked!: Promise<void>; tracked = retirement.finally(() => { if (transportArg.retirementTask === tracked) transportArg.retirementTask = undefined; }); transportArg.retirementTask = tracked; return tracked; } private async waitForBrowserTransportSettlement( settlementArg: Promise<void>, deadlineAtArg: number, ): Promise<void> { const remainingMs = deadlineAtArg - Date.now(); if (remainingMs <= 0) throw new Error('Browser stream generation cleanup timed out.'); let timeout: ReturnType<typeof setTimeout> | undefined; try { await Promise.race([ settlementArg, new Promise<never>((_resolve, reject) => { timeout = setTimeout( () => reject(new Error('Browser stream generation cleanup timed out.')), remainingMs, ); timeout.unref?.(); }), ]); } finally { if (timeout) clearTimeout(timeout); } } private browserOperationError(errorArg: unknown): { code: string; message: string } { if (errorArg instanceof plugins.browserRuntime.BrowserRuntimeError && ['STALE_INPUT', 'DIALOG_PENDING'].includes(errorArg.code)) { return { code: errorArg.code.toLowerCase(), message: errorArg.publicMessage }; } const typedError = asTypedResponseError(errorArg, 'controller.browser.operation'); const code = typeof typedError.errorData?.code === 'string' ? typedError.errorData.code.slice(0, 128) : 'browser_operation_failed'; return { code, message: typedError.message.slice(0, 512) }; } private async closeBrowserDevTools(view: IControllerBrowserView): Promise<void> { const transport = view.devTools; if (!transport) return; await transport.close(); if (view.devTools === transport) view.devTools = undefined; } private async openBrowserDevTools( peer: IControllerPeer, connection: plugins.typedsocket.ISmartServeConnectionWrapper, request: IReq_ControllerBrowserDevToolsOpen['request'], signal: AbortSignal, ): Promise<plugins.typedrequest.TReverseVirtualStreamDirections<IReq_ControllerBrowserDevToolsOpen['response']>> { return this.runBrowserViewSlotOperation(peer.id, async slot => { const view = slot.view; const primary = view?.transport; if (!view || !primary || view.id !== request.viewId || view.streamGeneration !== request.streamGeneration || view.transportPeer !== peer || view.transportConnection.peer !== connection.peer || view.credentialId !== peer.context.state[authenticatedPeerStateKey] || view.lifecycle !== 'active') { throw new ControllerResourceUnavailableError('The browser view is unavailable.'); } await this.assertBrowserViewAuthority(view, request.streamGeneration, primary.identity, signal); if (view.handle.lease.getState().activeTabId !== request.tabId) { throw new ControllerResourceUnavailableError('The inspected tab is unavailable.'); } await this.closeBrowserDevTools(view); signal.throwIfAborted(); const transport = new ControllerBrowserDevToolsTransport(request.tabId, primary.abortController.signal); view.devTools = transport; const authorization = (creatorDirection: plugins.typedrequestInterfaces.TVirtualStreamDirection, contentType: string): IControllerBrowserDevToolsAuthorization => Object.freeze({ marker: browserDevToolsAuthorizationMarker, view, transport, primaryIdentity: primary.identity, authority: view.authority, transportPeer: view.transportPeer, streamGeneration: request.streamGeneration, creatorDirection, contentType, }); const cancelOpening = () => transport.stop(signal.reason); signal.addEventListener('abort', cancelOpening, { once: true }); try { const socket = this.requireTypedServer().typedsocket; if (!socket) throw new Error('The controller transport is unavailable.'); transport.events = socket.createVirtualStream({ target: view.transportConnection, creatorDirection: 'send', contentType: devToolsProtocol.controllerDevToolsEventContentType, authorization: authorization('send', devToolsProtocol.controllerDevToolsEventContentType) }); transport.watchStream(transport.events); transport.commands = socket.createVirtualStream({ target: view.transportConnection, creatorDirection: 'receive', contentType: devToolsProtocol.controllerDevToolsCommandContentType, authorization: authorization('receive', devToolsProtocol.controllerDevToolsCommandContentType) }); transport.watchStream(transport.commands); const inspector = await transport.open(options => view.handle.lease.openDevTools(options, { signal: AbortSignal.any([signal, transport.abortController.signal]), })); const assets = plugins.smartbrowser.getDevToolsFrontendAssets(); const major = Number(/(?:Chrome|Chromium)\/(\d+)/.exec(inspector.browserVersion)?.[1]); if (major !== assets.chromeMajor) throw new Error( `This DevTools frontend requires Chromium ${assets.chromeMajor}; the managed browser reports ${inspector.browserVersion}.`, ); await this.assertBrowserViewAuthority(view, request.streamGeneration, primary.identity, signal); if (view.handle.lease.getState().activeTabId !== request.tabId) { throw new ControllerResourceUnavailableError('The selected browser tab changed.'); } transport.start(); return { devToolsId: transport.id, viewId: view.id, tabId: request.tabId, streamGeneration: request.streamGeneration, browserVersion: inspector.browserVersion, entrypointUrl: assets.entrypointUrl, commands: transport.commands, events: transport.events }; } catch (error) { transport.stop(error); await this.closeBrowserDevTools(view); throw error; } finally { signal.removeEventListener('abort', cancelOpening); } }); } private bindBrowserDevToolsAuthorization( authorization: IControllerBrowserDevToolsAuthorization, context: Readonly<plugins.typedsocket.IVirtualStreamAuthorizationContext>, ): plugins.typedsocket.IVirtualStreamAuthorizationBinding { const { view, transport } = authorization; const current = (): boolean => { try { return Boolean(view && transport && view.devTools === transport && !transport.abortController.signal.aborted && view.authority === authorization.authority && view.transportPeer === authorization.transportPeer && this.isBrowserViewInMemoryAuthorityCurrent(view, authorization.streamGeneration, authorization.primaryIdentity) && view.lifecycle !== 'failing' && !this.resourceCoordinator?.isResourceRetiring(view.resource.projectId, view.resource.id) && view.handle.lease.getState().activeTabId === transport.tabId); } catch { return false; } }; if (!current() || context.target !== authorization.transportPeer || context.creatorDirection !== authorization.creatorDirection || context.contentType !== authorization.contentType || context.integrity !== undefined || !((authorization.creatorDirection === 'send' && authorization.contentType === devToolsProtocol.controllerDevToolsEventContentType) || (authorization.creatorDirection === 'receive' && authorization.contentType === devToolsProtocol.controllerDevToolsCommandContentType))) { throw new Error('The DevTools stream authority is unavailable.'); } return { revalidate: candidate => { if (!current() || candidate.abortSignal.aborted || candidate.connection.side !== 'server' || candidate.connection.peer !== authorization.transportPeer || authorization.transportPeer.context.headers.get('origin') !== this.requireRuntimeConfig().publicOrigin) return false; return true; } }; } private bindBrowserStreamAuthorization( authorizationArg: unknown, contextArg: Readonly<plugins.typedsocket.IVirtualStreamAuthorizationContext>, ): plugins.typedsocket.IVirtualStreamAuthorizationBinding { if (typeof authorizationArg !== 'object' || authorizationArg === null) { throw new Error('Unknown browser stream authority.'); } if ((authorizationArg as { marker?: unknown }).marker === browserDevToolsAuthorizationMarker) { return this.bindBrowserDevToolsAuthorization(authorizationArg as IControllerBrowserDevToolsAuthorization, contextArg); } const authorization = authorizationArg as Partial<IControllerBrowserStreamAuthorization>; const view = authorization.view; if ( authorization.marker !== browserStreamAuthorizationMarker || !view || authorization.authority !== view.authority || authorization.transportPeer !== view.transportPeer || typeof authorization.transportIdentity !== 'symbol' || contextArg.target !== view.transportPeer || authorization.streamGeneration !== view.streamGeneration || contextArg.creatorDirection !== authorization.creatorDirection || contextArg.contentType !== authorization.contentType || contextArg.integrity !== undefined || view.closing || this.browserViews.get(view.id) !== view || this.browserViewSlots.get(view.peerId)?.view !== view || !this.isBrowserStreamBindingAuthorityCurrent( view, authorization.streamGeneration, authorization.transportIdentity, ) ) throw new Error('The browser stream authority is unavailable.'); const bound = authorization as IControllerBrowserStreamAuthorization; return { revalidate: (revalidationArg) => this.revalidateBrowserStream( bound, revalidationArg, ), }; } private revalidateBrowserStream( authorizationArg: IControllerBrowserStreamAuthorization, contextArg: Readonly<plugins.typedsocket.IVirtualStreamRevalidationContext>, ): boolean { const view = authorizationArg.view; if ( contextArg.abortSignal.aborted || contextArg.connection.side !== 'server' || contextArg.connection.peer !== authorizationArg.transportPeer || view.transportPeer !== authorizationArg.transportPeer || view.authority !== authorizationArg.authority || view.streamGeneration !== authorizationArg.streamGeneration || !this.isBrowserViewInMemoryAuthorityCurrent( view, authorizationArg.streamGeneration, authorizationArg.transportIdentity, ) || view.transportPeer.context.headers.get('origin') !== this.requireRuntimeConfig().publicOrigin ) return false; // The exact outbound stream may announce its own revocation after resource // admission closes. Queued nonterminal packets yield once this is set; // inbound input remains fenced even though both streams share an identity. if ( contextArg.operation === 'chunk' && authorizationArg.creatorDirection === 'send' && view.transport?.identity === authorizationArg.transportIdentity && view.transport.announcedClose === true ) return true; // Resource retirement fences local authority before writing durable state. // Open, activation, renewal, recovery and semantic commands still read the // store; transport chunks carry only this exact live view's authority. return view.lifecycle !== 'failing' && !this.resourceCoordinator?.isResourceRetiring(view.resource.projectId, view.resource.id); } private isBrowserStreamBindingAuthorityCurrent( viewArg: IControllerBrowserView, streamGenerationArg: number, transportIdentityArg: symbol, ): boolean { return !viewArg.closing && viewArg.lifecycle !== 'closing' && viewArg.streamGeneration === streamGenerationArg && viewArg.pendingTransportIdentity === transportIdentityArg && this.browserViews.get(viewArg.id) === viewArg && this.browserViewSlots.get(viewArg.peerId)?.view === viewArg && this.authenticatedPeerIds.has(viewArg.peerId) && viewArg.transportPeer.context.state[authenticatedPeerStateKey] === viewArg.credentialId && typeof viewArg.authenticationGeneration === 'symbol' && this.authenticatedPeerGenerations.get(viewArg.transportPeer) === viewArg.authenticationGeneration && !this.resourceCoordinator?.isResourceRetiring(viewArg.resource.projectId, viewArg.resource.id) && this.isBrowserRuntimeAuthorityCurrent(viewArg); } private isBrowserViewInMemoryAuthorityCurrent( viewArg: IControllerBrowserView, streamGenerationArg: number, transportIdentityArg: symbol, ): boolean { return !viewArg.closing && viewArg.lifecycle !== 'closing' && viewArg.streamGeneration === streamGenerationArg && viewArg.transport?.identity === transportIdentityArg && this.browserViews.get(viewArg.id) === viewArg && this.browserViewSlots.get(viewArg.peerId)?.view === viewArg && this.authenticatedPeerIds.has(viewArg.peerId) && viewArg.transportPeer.context.state[authenticatedPeerStateKey] === viewArg.credentialId && typeof viewArg.authenticationGeneration === 'symbol' && this.authenticatedPeerGenerations.get(viewArg.transportPeer) === viewArg.authenticationGeneration && this.isBrowserRuntimeAuthorityCurrent(viewArg); } private async assertBrowserViewAuthority( viewArg: IControllerBrowserView, streamGenerationArg: number, transportIdentityArg: symbol, signalArg: AbortSignal, ): Promise<void> { signalArg.throwIfAborted(); if (this.resourceCoordinator?.isResourceRetiring(viewArg.resource.projectId, viewArg.resource.id) || !this.isBrowserViewInMemoryAuthorityCurrent( viewArg, streamGenerationArg, transportIdentityArg, )) { throw new ControllerResourceUnavailableError('The browser view authority is unavailable.'); } const current = await awaitWithAbortSignal(this.requireAuthStore().getResource( viewArg.resource.projectId, viewArg.resource.id, ), signalArg); signalArg.throwIfAborted(); if ( !current || current.kind !== 'browser' || current.lifecycle !== 'active' || current.attachmentAuthorityId !== viewArg.resource.attachmentAuthorityId || this.resourceCoordinator?.isResourceRetiring(viewArg.resource.projectId, viewArg.resource.id) || !this.isBrowserViewInMemoryAuthorityCurrent( viewArg, streamGenerationArg, transportIdentityArg, ) ) throw new ControllerResourceUnavailableError('The browser resource changed.'); } private isBrowserRuntimeAuthorityCurrent(viewArg: IControllerBrowserView): boolean { try { return viewArg.handle.lease.isAuthorityCurrent(viewArg.authority); } catch { return false; } } private nextControllerBrowserViewState( viewArg: IControllerBrowserView, stateArg: plugins.browserRuntime.IBrowserRuntimeState, ): IControllerBrowserViewState { const { lastError, ...state } = stateArg; viewArg.stateRevision += 1; return { revision: viewArg.stateRevision, ...state, ...(lastError === undefined ? {} : { lastError: controllerBrowserError(lastError), }), }; } private async resolveAttentionRootSessionIds( openCodeClientArg: OpenCodeClientAdapter, directoryArg: string, rootSessionsArg: IControllerSession[], pendingSessionIdsArg: readonly IControllerRuntimeId[], signalArg?: AbortSignal, ): Promise<Set<string>> { const rootSessionIds = new Set( rootSessionsArg.map((session) => controllerRuntimeIdKey(session.id)), ); const result = new Set<string>(); const sessionCache = new Map( rootSessionsArg.map((session) => [controllerRuntimeIdKey(session.id), session]), ); const resolveRoot = async ( sessionIdArg: IControllerRuntimeId, ): Promise<string | undefined> => { let sessionId = sessionIdArg; for (let depth = 0; depth < 16; depth += 1) { const sessionKey = controllerRuntimeIdKey(sessionId); if (rootSessionIds.has(sessionKey)) return sessionKey; let session = sessionCache.get(sessionKey); if (!session) { try { session = await openCodeClientArg.getSession( directoryArg, sessionId.nativeId, signalArg, ); } catch { // The pending item may disappear between the list and parent lookup. return undefined; } sessionCache.set(sessionKey, session); } if (!session.parentId) return undefined; sessionId = session.parentId; } return undefined; }; const pendingSessionIds = [...pendingSessionIdsArg]; for (let index = 0; index < pendingSessionIds.length; index += 8) { const roots = await Promise.all( pendingSessionIds.slice(index, index + 8).map(resolveRoot), ); for (const rootSessionId of roots) { if (rootSessionId) result.add(rootSessionId); } } return result; } private sweepAutoAcceptAcrossProjects(): void { for (const [directory, projectId] of this.projectIdsByDirectory) { void this.autoAcceptPendingPermissions(directory).catch(() => undefined); this.queueFlexAutoAcceptSweep(projectId); } for (const runtime of this.codexConnections?.all() ?? []) if (runtime.supervisor.getStatus().healthy) { for (const scope of runtime.client.pendingPermissionScopes()) this.queueCodexAutoAccept(scope.projectId, scope.sessionId); } } private async resolveProject(projectIdArg: string): Promise<IControllerProjectDocument> { const project = await this.resolveProjectIdentity(projectIdArg); try { await this.assertProjectDirectory(project); } catch (errorArg) { if (!(errorArg instanceof CanonicalDirectoryError)) throw errorArg; throw new plugins.typedrequest.TypedResponseError( 'The registered project directory is unavailable.', { code: 'project_not_found', cause: errorArg }, ); } return project; } private async resolveProjectIdentity(projectIdArg: string): Promise<IControllerProjectDocument> { if (this.retiringProjectIds.has(projectIdArg)) { throw new plugins.typedrequest.TypedResponseError( 'The requested project is being removed.', { code: 'project_not_found' }, ); } const project = await this.requireAuthStore().getProject(projectIdArg); if (!project || this.retiringProjectIds.has(projectIdArg)) { throw new plugins.typedrequest.TypedResponseError( 'The requested project is not registered.', { code: 'project_not_found' }, ); } return project; } private async assertProjectDirectory(projectArg: IControllerProjectDocument): Promise<string> { if ( projectArg.directoryIdentityState !== 'bound' || projectArg.directoryIdentity === undefined ) { throw new CanonicalDirectoryError('The project directory has no trusted filesystem identity.'); } return assertCanonicalDirectory(projectArg.directory, { ancestry: projectArg.directoryIdentity.ancestry, }); } private async resolveAvailableProjects( projectsArg: IControllerProjectDocument[], ): Promise<IControllerProjectDocument[]> { const available: IControllerProjectDocument[] = []; for (const project of projectsArg) { try { await this.assertProjectDirectory(project); available.push(project); } catch (errorArg) { if (!(errorArg instanceof CanonicalDirectoryError)) throw errorArg; } } return available; } private toPublicProject(projectArg: IControllerProjectDocument): IControllerProject { return { id: projectArg.id, name: projectArg.name, directory: projectArg.directory, createdAt: projectArg.createdAt.getTime(), }; } private async resolveExistingProjectDirectory( directoryArg: string, ): Promise<ICanonicalDirectoryResolution> { let resolvedDirectory: string; try { resolvedDirectory = await plugins.fs.promises.realpath(directoryArg); } catch { throw new plugins.typedrequest.TypedResponseError( 'The project path does not exist.', { code: 'project_path_missing' }, ); } try { return await resolveCanonicalDirectory(resolvedDirectory); } catch { throw new plugins.typedrequest.TypedResponseError( 'The project path is not a directory.', { code: 'project_path_missing' }, ); } } /** * Display name for a registered directory: relative when it lives under the * projects root, else the absolute path itself. */ private deriveProjectName(directoryArg: string): string { const projectsRoot = this.requireProjectsRoot(); if (directoryArg === projectsRoot) { return plugins.path.basename(projectsRoot) || 'workspace'; } if (directoryArg.startsWith(`${projectsRoot}${plugins.path.sep}`)) { return directoryArg.slice(projectsRoot.length + 1); } return directoryArg; } private async listProjectPathSuggestions( directoryPathArg: string, prefixArg: string, absoluteArg: boolean, ): Promise<Array<{ path: string; name: string }>> { const projectsRoot = this.requireProjectsRoot(); const baseDirectory = absoluteArg ? directoryPathArg : (directoryPathArg === '' ? projectsRoot : plugins.path.resolve(projectsRoot, directoryPathArg)); let entries: plugins.fs.Dirent[]; try { entries = await plugins.fs.promises.readdir(baseDirectory, { withFileTypes: true }); } catch { return []; } const loweredPrefix = prefixArg.toLowerCase(); return entries .filter((entry) => entry.isDirectory() || entry.isSymbolicLink()) .map((entry) => entry.name) .filter((name) => ( !name.startsWith('.') // eslint-disable-next-line no-control-regex && !/[\x00-\x1f\x7f\\]/.test(name) && Buffer.byteLength(name, 'utf8') <= 255 && name.toLowerCase().startsWith(loweredPrefix) )) .sort((left, right) => left.localeCompare(right)) .slice(0, 20) .map((name) => ({ path: absoluteArg ? (directoryPathArg === '/' ? `/${name}` : `${directoryPathArg}/${name}`) : (directoryPathArg === '' ? name : `${directoryPathArg}/${name}`), name, })); } private async sendTerminalOutput( peerIdArg: string, payloadArg: ITerminalOutputPayload, ): Promise<void> { const typedSocket = this.typedServer?.typedsocket; if (!typedSocket) throw new Error('The controller transport is unavailable.'); const target = await typedSocket.findTargetConnection(async (connectionArg) => ( connectionArg.peer.id === peerIdArg && typeof connectionArg.peer.context.state[authenticatedPeerStateKey] === 'string' && connectionArg.peer.context.headers.get('origin') === this.requireRuntimeConfig().publicOrigin )); if (!target) throw new Error('The terminal peer is no longer connected.'); // The deadline is a retry trigger, not a kill trigger: the terminal manager owns the bounded // retry budget because only it knows the peer cursor, which is what makes a re-send // idempotent. typedrequest-level retries would re-deliver blindly, so they stay off. const request = typedSocket.createTypedRequest<IReq_ControllerTerminalOutput>( 'controller.terminal.output', target, { timeoutMs: 15_000 }, ); await request.fire(payloadArg, { timeoutMs: 15_000, maxRetries: 0 }); } /** * Best effort by construction: it rides the same transport whose failure usually produced the * detach, so failures are swallowed and the browser also recovers from an output gap or an * explicit re-selection. */ private async sendTerminalDetached( peerIdArg: string, terminalIdArg: IReq_ControllerTerminalDetached['request']['terminalId'], reasonArg: TControllerTerminalDetachReason, ): Promise<void> { const typedSocket = this.typedServer?.typedsocket; if (!typedSocket) return; const target = await typedSocket.findTargetConnection(async (connectionArg) => ( connectionArg.peer.id === peerIdArg && typeof connectionArg.peer.context.state[authenticatedPeerStateKey] === 'string' && connectionArg.peer.context.headers.get('origin') === this.requireRuntimeConfig().publicOrigin )); if (!target) return; const request = typedSocket.createTypedRequest<IReq_ControllerTerminalDetached>( 'controller.terminal.detached', target, { timeoutMs: 2_000 }, ); await request.fire( { terminalId: { ...terminalIdArg }, reason: reasonArg }, { timeoutMs: 2_000, maxRetries: 0 }, ); } private async pushControllerEvent(eventArg: IControllerEvent): Promise<void> { const typedSocket = this.typedServer?.typedsocket; if (!typedSocket) return; const targets = await typedSocket.findAllTargetConnections(async (connectionArg) => ( typeof connectionArg.peer.context.state[authenticatedPeerStateKey] === 'string' && connectionArg.peer.context.headers.get('origin') === this.requireRuntimeConfig().publicOrigin )); await Promise.allSettled(targets.map(async (targetArg) => { const request = typedSocket.createTypedRequest<IReq_ControllerEvent>( 'controller.event', targetArg, { timeoutMs: 2_000 }, ); await request.fire(eventArg, { timeoutMs: 2_000, maxRetries: 0 }); })); } private runUpgradePreparationAdmission<T>(operationArg: () => Promise<T>): Promise<T> { const previous = this.upgradePreparationAdmissionTail; const task = previous.then(async () => { if ( this.lifecycleState !== 'ready' || this.stopRequested || this.operationAbortController.signal.aborted ) throw new plugins.typedrequest.TypedResponseError( 'The controller is not ready to accept upgrade preparation.', { code: 'controller_unavailable' }, ); return await operationArg(); }); this.upgradePreparationAdmissionTail = task.then(() => undefined, () => undefined); return task; } private async upgradePreparationReplayIsAccepted( coordinatorArg: UpgradeCoordinator, tokenArg: string, targetVersionArg: string, gracePeriodMsArg: number, ): Promise<boolean> { if (this.upgradeToken !== tokenArg || !this.upgradePreparationTask) return false; const transaction = await coordinatorArg.readTransaction(tokenArg); if (transaction.preparationAcceptedAt === undefined) return false; if ( transaction.targetVersion !== targetVersionArg || transaction.gracePeriodMs !== gracePeriodMsArg || transaction.preparationDeadlineAt !== transaction.preparationAcceptedAt + gracePeriodMsArg ) throw new plugins.typedrequest.TypedResponseError( 'The upgrade preparation request does not match the admitted transaction.', { code: 'concurrent_change' }, ); return true; } private createUpgradePreparationContext( coordinatorArg: UpgradeCoordinator, tokenArg: string, targetVersionArg: string, gracePeriodMsArg: number, operationSignalArg: AbortSignal, ): IUpgradePreparationContext { if (this.upgradeBarrier) { throw new plugins.typedrequest.TypedResponseError( 'An upgrade handoff is already active.', { code: 'concurrent_change' }, ); } if (this.codexCreationAdmissions > 0) throw new plugins.typedrequest.TypedResponseError('Codex conversation creation is still settling; retry the upgrade shortly.', { code: 'concurrent_change' }); const barrier = this.harnessOperationManager.beginUpgradeBarrier(); const preparationAbortController = new AbortController(); const preparationSignal = AbortSignal.any([ operationSignalArg, preparationAbortController.signal, ]); if (this.upgradeWorkerWatchdog) { clearInterval(this.upgradeWorkerWatchdog); this.upgradeWorkerWatchdog = undefined; } this.upgradeBarrier = barrier; this.upgradePreparationAbortController = preparationAbortController; this.upgradeCoordinator = coordinatorArg; this.upgradeToken = tokenArg; this.upgradeStatus = { fromVersion: commitinfo.version, toVersion: targetVersionArg, phase: 'preparing', }; this.startUpgradeWorkerWatchdog(); const acceptedAt = Date.now(); return { coordinator: coordinatorArg, token: tokenArg, targetVersion: targetVersionArg, gracePeriodMs: gracePeriodMsArg, barrier, preparationAbortController, preparationSignal, acceptedAt, deadline: acceptedAt + gracePeriodMsArg, }; } private async persistUpgradePreparation( contextArg: IUpgradePreparationContext, asynchronousArg: boolean, ): Promise<void> { await contextArg.coordinator.mutateTransaction(contextArg.token, (current) => { if (current.version === upgradePackageTransitionTransactionVersion) { if (contextArg.targetVersion !== current.targetVersion) { throw new Error('The package-transition preparation target is not exact.'); } return { ...current, phase: 'preparing', message: `Preparing ${contextArg.barrier.sessions.length} active session${contextArg.barrier.sessions.length === 1 ? '' : 's'} for upgrade.`, sessions: contextArg.barrier.sessions.map((session) => ({ projectId: session.projectId, sessionId: { ...session.sessionId }, pauseState: 'pending' as const, })), ...(asynchronousArg ? { preparationAcceptedAt: contextArg.acceptedAt, preparationDeadlineAt: contextArg.deadline, } : {}), }; } return { ...current, targetVersion: contextArg.targetVersion, phase: 'preparing', message: `Preparing ${contextArg.barrier.sessions.length} active session${contextArg.barrier.sessions.length === 1 ? '' : 's'} for upgrade.`, sessions: contextArg.barrier.sessions.map((session) => ({ projectId: session.projectId, sessionId: { ...session.sessionId }, pauseState: 'pending' as const, })), ...(asynchronousArg ? { preparationAcceptedAt: contextArg.acceptedAt, preparationDeadlineAt: contextArg.deadline, } : {}), }; }); } private pushUpgradePreparationEvent(): void { if (!this.upgradeStatus) return; void this.pushControllerEvent({ type: 'upgrade.changed', upgrade: { ...this.upgradeStatus }, timestamp: Date.now(), }).catch(() => undefined); } private async completeUpgradePreparation( contextArg: IUpgradePreparationContext, ): Promise<IReq_ControllerUpgradePrepare['response']> { const { barrier, coordinator, token, deadline, preparationSignal, } = contextArg; if (!await this.harnessOperationManager.waitForUpgradeBarrier( barrier, Math.max(1, deadline - Date.now()), preparationSignal, )) throw new Error('Timed out waiting for active harness operations to settle.'); this.upgradeStatus = { ...this.upgradeStatus!, phase: 'pausing' }; await coordinator.mutateTransaction(token, (current) => ({ ...current, phase: 'pausing', message: `Pausing ${barrier.sessions.length} active session${barrier.sessions.length === 1 ? '' : 's'}.`, })); await this.pushControllerEvent({ type: 'upgrade.changed', upgrade: { ...this.upgradeStatus }, timestamp: Date.now(), }).catch(() => undefined); const pauseResults = await Promise.allSettled(barrier.sessions.map((session) => ( this.sendUpgradeControlPrompt( coordinator, token, session.projectId, session.sessionId, 'pauseState', upgradePauseMessage, true, deadline, preparationSignal, ) ))); const failures = pauseResults.filter((result) => result.status === 'rejected'); if (failures.length > 0) { throw new AggregateError( failures.map((result) => (result as PromiseRejectedResult).reason), 'One or more active sessions could not pause for the upgrade.', ); } if (Date.now() > deadline) { throw new Error('The upgrade preparation deadline expired before completion.'); } return { prepared: true, sessionCount: barrier.sessions.length }; } private clearUpgradePreparationAbortController(contextArg: IUpgradePreparationContext): void { if (this.upgradePreparationAbortController === contextArg.preparationAbortController) { this.upgradePreparationAbortController = undefined; } } private async compensateUpgradePreparationFailure( contextArg: IUpgradePreparationContext, errorArg: unknown, finishTransactionArg: boolean, ): Promise<void> { let errorMessage = errorArg instanceof Error ? errorArg.message : String(errorArg); try { await this.finalizeUpgrade(contextArg.coordinator, contextArg.token, 'compensate'); } catch (cleanupErrorArg) { const cleanupMessage = cleanupErrorArg instanceof Error ? cleanupErrorArg.message : String(cleanupErrorArg); errorMessage = `${errorMessage} Cleanup failed: ${cleanupMessage}`; } finally { this.clearUpgradePreparationAbortController(contextArg); } if (finishTransactionArg) { await contextArg.coordinator.finishTransaction( contextArg.token, false, 'Upgrade preparation failed.', errorMessage, ).catch(() => undefined); } } private async prepareUpgrade( coordinatorArg: UpgradeCoordinator, tokenArg: string, targetVersionArg: string, gracePeriodMsArg: number, operationSignalArg: AbortSignal, ): Promise<IReq_ControllerUpgradePrepare['response']> { const context = this.createUpgradePreparationContext( coordinatorArg, tokenArg, targetVersionArg, gracePeriodMsArg, operationSignalArg, ); const operation = Promise.resolve().then(async () => { try { await this.persistUpgradePreparation(context, false); this.pushUpgradePreparationEvent(); const response = await this.completeUpgradePreparation(context); this.clearUpgradePreparationAbortController(context); return response; } catch (errorArg) { await this.compensateUpgradePreparationFailure(context, errorArg, false); throw errorArg; } }); this.upgradePreparationTask = operation.then(() => undefined, () => undefined); return await operation; } private async beginUpgradePreparation( coordinatorArg: UpgradeCoordinator, tokenArg: string, targetVersionArg: string, gracePeriodMsArg: number, ): Promise<IReq_ControllerUpgradePrepareBegin['response']> { const context = this.createUpgradePreparationContext( coordinatorArg, tokenArg, targetVersionArg, gracePeriodMsArg, this.operationAbortController.signal, ); let releaseGate!: () => void; let rejectGate!: (errorArg: unknown) => void; const gate = new Promise<void>((resolve, reject) => { releaseGate = resolve; rejectGate = reject; }); const background = (async () => { try { await gate; await this.completeUpgradePreparation(context); await coordinatorArg.mutateTransaction(tokenArg, (current) => { const completedAt = Date.now(); if (completedAt > context.deadline) { throw new Error('The upgrade preparation deadline expired before its durable commit.'); } if ( current.terminal || current.targetVersion !== targetVersionArg || current.gracePeriodMs !== gracePeriodMsArg || current.preparationAcceptedAt !== context.acceptedAt || current.preparationDeadlineAt !== context.deadline ) throw new Error('The admitted upgrade preparation transaction changed unexpectedly.'); return { ...current, phase: 'stopping', preparationCompletedAt: completedAt, message: 'Graceful session pause completed; stopping the previous controller.', }; }); this.clearUpgradePreparationAbortController(context); } catch (errorArg) { const transaction = await coordinatorArg.readTransaction(tokenArg).catch(() => undefined); if ( transaction?.preparationCompletedAt !== undefined && transaction.preparationDeadlineAt !== undefined && transaction.preparationCompletedAt <= transaction.preparationDeadlineAt ) { this.clearUpgradePreparationAbortController(context); return; } await this.compensateUpgradePreparationFailure(context, errorArg, true); } })(); const trackedBackground = this.trackOperationPromise(background); this.upgradePreparationTask = trackedBackground; try { await this.persistUpgradePreparation(context, true); releaseGate(); this.pushUpgradePreparationEvent(); return { accepted: true }; } catch (errorArg) { rejectGate(errorArg); await trackedBackground; throw errorArg; } } private async abortAndWaitForUpgradePreparation(reasonArg: Error): Promise<void> { const task = this.upgradePreparationTask; if (!task) return; this.upgradePreparationAbortController?.abort(reasonArg); await task; } private finalizeUpgrade( coordinatorArg: UpgradeCoordinator, tokenArg: string, modeArg: 'continue' | 'compensate' | 'reopen', ): Promise<IReq_ControllerUpgradeFinalize['response']> { const previous = this.upgradeFinalizeTail; let task: Promise<IReq_ControllerUpgradeFinalize['response']>; task = previous.then(async () => { const transaction = await coordinatorArg.readTransaction(tokenArg); const sessions = transaction.sessions.filter((session) => ( modeArg !== 'reopen' && ['submitting', 'accepted', 'paused', 'outcomeUnknown'].includes(session.pauseState) )); if (sessions.length > 0) { const cleanupSessions = sessions.filter((session) => ( session.cleanupState === 'submitting' || session.cleanupState === 'outcomeUnknown' || (session.pauseState !== 'paused' && session.cleanupState !== 'accepted') )); const cleanupResults = await Promise.allSettled(cleanupSessions.map(async (session) => { const updateCleanupState = async ( stateArg: TUpgradeSessionTransitionState, errorArg?: string, ): Promise<void> => { await coordinatorArg.mutateTransaction(tokenArg, (current) => ({ ...current, sessions: current.sessions.map((candidate) => ( candidate.projectId === session.projectId && controllerRuntimeIdKey(candidate.sessionId) === controllerRuntimeIdKey(session.sessionId) ? { ...candidate, cleanupState: stateArg, ...(errorArg === undefined ? {} : { error: errorArg.slice(0, 2_048) }), } : candidate )), })); }; await updateCleanupState('submitting'); try { if (session.sessionId.harnessId === 'flex') { const cleanupSignal = AbortSignal.any([ this.operationAbortController.signal, AbortSignal.timeout(10_000), ]); if (!session.pauseQueueId) { throw new Error('The accepted Flex pause queue identity is unavailable.'); } cleanupSignal.throwIfAborted(); const cancellation = await this.requireFlexSupervisor().request('prompt.cancel', { scopeId: session.projectId, sessionId: session.sessionId.nativeId, queueId: session.pauseQueueId, }); if (!cancellation.accepted) { const response = await this.requireFlexSupervisor().request('prompt.get', { scopeId: session.projectId, sessionId: session.sessionId.nativeId, queueId: session.pauseQueueId, }, cleanupSignal); if (!['completed', 'failed', 'cancelled'].includes(response.entry.status)) { throw new Error('The Flex pause queue did not accept cancellation or settle.'); } } } else if (session.sessionId.harnessId === 'codex') { const cleanupSignal = AbortSignal.any([this.operationAbortController.signal, AbortSignal.timeout(10_000)]); const project = await this.resolveProject(session.projectId); await this.readManagedCodexSession(session.projectId, project.directory, session.sessionId.nativeId, cleanupSignal); const client = this.requireCodexClient(session.sessionId.nativeId); await this.abortObservedCodexTurn(client, session.sessionId.nativeId, cleanupSignal); await client.waitForIdle(session.sessionId.nativeId, cleanupSignal); } else { const project = await this.resolveProject(session.projectId); const aborted = await this.requireOpenCodeClient().abortSession( project.directory, session.sessionId.nativeId, AbortSignal.timeout(10_000), ); if (!aborted) { const detail = await this.requireOpenCodeClient().getSessionDetail( project.directory, session.sessionId.nativeId, AbortSignal.timeout(10_000), ); if (detail.session.status === 'busy' || detail.session.status === 'retry') { throw new Error('The OpenCode pause turn did not abort or settle.'); } } } await updateCleanupState('accepted'); } catch (errorArg) { await updateCleanupState( 'outcomeUnknown', errorArg instanceof Error ? errorArg.message : String(errorArg), ).catch(() => undefined); throw errorArg; } })); const cleanupFailed = cleanupResults.some((result) => result.status === 'rejected'); const afterCleanup = await coordinatorArg.readTransaction(tokenArg); const sessionsReadyToContinue = cleanupFailed ? [] : afterCleanup.sessions.filter((session) => ( ['submitting', 'accepted', 'paused', 'outcomeUnknown'].includes(session.pauseState) && (session.pauseState === 'paused' || session.cleanupState === 'accepted') && (session.continueState === undefined || session.continueState === 'pending' || session.continueState === 'failed') )); const targetVersion = upgradeTransactionTargetVersion(transaction); this.upgradeStatus = targetVersion ? { fromVersion: transaction.sourceVersion, toVersion: targetVersion, phase: 'continuing', } : this.upgradeStatus; await coordinatorArg.mutateTransaction(tokenArg, (current) => ({ ...current, phase: current.terminal || modeArg === 'compensate' ? current.phase : 'continuing', message: modeArg === 'compensate' ? 'Recovering sessions after an interrupted upgrade.' : 'Continuing sessions after the upgrade.', })); const deadline = Date.now() + Math.min(transaction.gracePeriodMs, 60_000); await Promise.allSettled(sessionsReadyToContinue.map((session) => this.sendUpgradeControlPrompt( coordinatorArg, tokenArg, session.projectId, session.sessionId, 'continueState', upgradeContinueMessage, false, deadline, this.operationAbortController.signal, ))); } const finalTransaction = await coordinatorArg.readTransaction(tokenArg); const continued = finalTransaction.sessions.filter( (session) => session.continueState === 'accepted', ).length; const failed = finalTransaction.sessions.filter( (session) => session.cleanupState === 'failed' || session.cleanupState === 'outcomeUnknown' || session.continueState === 'failed' || session.continueState === 'outcomeUnknown' || session.continueState === 'submitting', ).length; const cleanupSettled = modeArg === 'reopen' || finalTransaction.sessions.every((session) => ( session.pauseState === 'pending' || session.pauseState === 'failed' || (session.pauseState === 'paused' && (session.cleanupState === undefined || session.cleanupState === 'accepted')) || session.cleanupState === 'accepted' )); if (cleanupSettled && this.upgradeBarrier) { this.harnessOperationManager.endUpgradeBarrier(this.upgradeBarrier); this.upgradeBarrier = undefined; } const targetVersion = upgradeTransactionTargetVersion(transaction); this.upgradeStatus = targetVersion ? { fromVersion: transaction.sourceVersion, toVersion: targetVersion, phase: failed > 0 || modeArg === 'compensate' ? 'failed' : 'completed', } : this.upgradeStatus; if (this.upgradeStatus) { await this.pushControllerEvent({ type: 'upgrade.changed', upgrade: { ...this.upgradeStatus }, timestamp: Date.now(), }).catch(() => undefined); } return { finalized: true, continued, failed }; }); this.upgradeFinalizeTail = task.then(() => undefined, () => undefined); return task; } private async sendUpgradeControlPrompt( coordinatorArg: UpgradeCoordinator, tokenArg: string, projectIdArg: string, sessionIdArg: IControllerRuntimeId, stateFieldArg: 'pauseState' | 'continueState', promptArg: string, waitForTerminalArg: boolean, deadlineArg: number, signalArg?: AbortSignal, ): Promise<void> { const updateState = async ( stateArg: TUpgradeSessionTransitionState, errorArg?: string, ): Promise<void> => { await coordinatorArg.mutateTransaction(tokenArg, (current) => ({ ...current, sessions: current.sessions.map((session) => ( session.projectId === projectIdArg && controllerRuntimeIdKey(session.sessionId) === controllerRuntimeIdKey(sessionIdArg) ? { ...session, [stateFieldArg]: stateArg, ...(errorArg === undefined ? {} : { error: errorArg.slice(0, 2_048) }), } : session )), })); }; if (Date.now() >= deadlineArg) { await updateState('failed', 'The upgrade control prompt deadline expired.'); throw new Error('The upgrade control prompt deadline expired.'); } signalArg?.throwIfAborted(); await updateState('submitting'); try { const project = await this.resolveProject(projectIdArg); if (sessionIdArg.harnessId === 'opencode') { if (waitForTerminalArg) { await this.requireOpenCodeClient().sendMessageAndWait( project.directory, sessionIdArg.nativeId, promptArg, signalArg ? AbortSignal.any([ signalArg, AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ]) : AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ); await updateState('paused'); return; } await this.requireOpenCodeClient().sendMessage( project.directory, sessionIdArg.nativeId, promptArg, undefined, undefined, signalArg ? AbortSignal.any([ signalArg, AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ]) : AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ); await updateState('accepted'); return; } const requestSignal = signalArg ? AbortSignal.any([ signalArg, AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())), ]) : AbortSignal.timeout(Math.max(1, deadlineArg - Date.now())); requestSignal.throwIfAborted(); if (sessionIdArg.harnessId === 'codex') { const client = this.requireCodexClient(sessionIdArg.nativeId); await this.readManagedCodexSession(projectIdArg, project.directory, sessionIdArg.nativeId, requestSignal); await this.finalizeCodexCreation(projectIdArg, sessionIdArg.nativeId, requestSignal); if (waitForTerminalArg) { await this.abortObservedCodexTurn(client, sessionIdArg.nativeId, requestSignal); await client.waitForIdle(sessionIdArg.nativeId, requestSignal); } const arm = await client.startTurn(sessionIdArg.nativeId, promptArg, `agl_upgrade_${plugins.crypto.randomUUID()}_${stateFieldArg}`, undefined, async () => { await this.requireAuthStore().prepareCodexTurn(projectIdArg, sessionIdArg.nativeId); }, () => {}, requestSignal, undefined, () => this.requireAuthStore().cancelUndispatchedCodexTurn(projectIdArg, sessionIdArg.nativeId)); arm(); await this.requireAuthStore().markCodexMaterialized(projectIdArg, sessionIdArg.nativeId); await updateState('accepted'); if (waitForTerminalArg) { await client.waitForIdle(sessionIdArg.nativeId, requestSignal); await updateState('paused'); } return; } const dispatched = await this.withEffectiveFlexModelChoice( projectIdArg, undefined, undefined, requestSignal, async (model) => ({ model, admission: await this.requireFlexSupervisor().request('prompt.start', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId, prompt: promptArg, model, }, requestSignal), }), ); const { admission } = dispatched; if (stateFieldArg === 'pauseState') { await coordinatorArg.mutateTransaction(tokenArg, (current) => ({ ...current, sessions: current.sessions.map((session) => ( session.projectId === projectIdArg && controllerRuntimeIdKey(session.sessionId) === controllerRuntimeIdKey(sessionIdArg) ? { ...session, pauseQueueId: admission.queueId } : session )), })); } await this.acknowledgeFlexRun( projectIdArg, sessionIdArg.nativeId, admission.queueId, admission.runId, dispatched.model, requestSignal, ); await updateState('accepted'); if (!waitForTerminalArg) return; while (Date.now() < deadlineArg) { signalArg?.throwIfAborted(); const response = await this.requireFlexSupervisor().request('prompt.get', { scopeId: projectIdArg, sessionId: sessionIdArg.nativeId, queueId: admission.queueId, }, requestSignal); if (['completed', 'failed', 'cancelled'].includes(response.entry.status)) { if (response.entry.status !== 'completed') { throw new Error(`The Flex pause prompt ended with ${response.entry.status}.`); } await updateState('paused'); return; } await new Promise((resolve) => setTimeout(resolve, upgradePromptPollMs)); } throw new Error('Timed out waiting for the Flex pause prompt to settle.'); } catch (errorArg) { const message = errorArg instanceof Error ? errorArg.message : String(errorArg); const transaction = await coordinatorArg.readTransaction(tokenArg).catch(() => undefined); const session = transaction?.sessions.find((candidate) => ( candidate.projectId === projectIdArg && controllerRuntimeIdKey(candidate.sessionId) === controllerRuntimeIdKey(sessionIdArg) )); const submitted = session?.[stateFieldArg] === 'submitting' || session?.[stateFieldArg] === 'accepted'; await updateState(submitted ? 'outcomeUnknown' : 'failed', message).catch(() => undefined); throw errorArg; } } private async retainUpgradeOwnership(cliPathArg: string): Promise<void> { if (!this.upgradeCoordinator || !this.upgradeToken) { throw new Error('The retained upgrade target has no coordination authority.'); } if (!this.retainedUpgradeOwnershipTask) { const coordinator = this.upgradeCoordinator; const token = this.upgradeToken; let task: Promise<void>; task = (async () => { let lock = this.retainedUpgradeLock; if (lock) await lock.assertOwned(); if (!lock) { let retryDelayMs = retainedUpgradeRetryInitialMs; while (!this.operationAbortController.signal.aborted) { const transaction = await coordinator.readTransaction(token); if (transaction.terminal) return; try { lock = await coordinator.acquireUpgradeLock({ token, cliPath: cliPathArg, command: this.options.processMode === 'detached' ? '__serve' : 'foreground', }); this.retainedUpgradeLock = lock; break; } catch (errorArg) { if ( errorArg instanceof Error && (errorArg.message.includes('already running') || errorArg.message.includes('initializing')) ) { await waitForSignalOrDelay( retryDelayMs, this.operationAbortController.signal, ); retryDelayMs = Math.min( retryDelayMs * 2, retainedUpgradeRetryMaximumMs, ); continue; } throw errorArg; } } } if (!lock) return; let releaseAttempted = false; const releaseLock = async (): Promise<void> => { releaseAttempted = true; await lock.release(); if (this.retainedUpgradeLock === lock) this.retainedUpgradeLock = undefined; }; try { let current = await coordinator.readTransaction(token); if (this.operationAbortController.signal.aborted) return; if (current.terminal) { await releaseLock(); return; } await coordinator.waitForStartLeasesToDrain(lock); current = await coordinator.readTransaction(token); if (this.operationAbortController.signal.aborted) return; if (current.terminal) { await releaseLock(); return; } } catch (errorArg) { if (releaseAttempted) throw errorArg; if (this.stopRequested || this.operationAbortController.signal.aborted) throw errorArg; try { await releaseLock(); } catch (releaseErrorArg) { throw new AggregateError( [errorArg, releaseErrorArg], 'Retained upgrade ownership failed and could not be released.', ); } throw errorArg; } })().finally(() => { if (this.retainedUpgradeOwnershipTask === task) { this.retainedUpgradeOwnershipTask = undefined; } }); this.retainedUpgradeOwnershipTask = task; } await this.retainedUpgradeOwnershipTask; } private async releaseRetainedUpgradeOwnership(allowDuringStopArg = false): Promise<void> { const activeRelease = this.retainedUpgradeReleaseTask; if (activeRelease) { try { await activeRelease; } catch (errorArg) { if (!allowDuringStopArg || !this.retainedUpgradeLock) throw errorArg; } if (allowDuringStopArg && this.retainedUpgradeLock) { await this.releaseRetainedUpgradeOwnership(true); } return; } let task: Promise<void>; task = (async () => { let ownershipError: unknown; try { await this.retainedUpgradeOwnershipTask; } catch (errorArg) { ownershipError = errorArg; } if (this.stopRequested && !allowDuringStopArg) { if (ownershipError !== undefined) throw ownershipError; return; } const lock = this.retainedUpgradeLock; let releaseError: unknown; if (lock) { try { await lock.release(allowDuringStopArg ? undefined : () => !this.stopRequested); if (this.retainedUpgradeLock === lock) this.retainedUpgradeLock = undefined; } catch (errorArg) { releaseError = errorArg; } } if (ownershipError !== undefined && releaseError !== undefined) { throw new AggregateError( [ownershipError, releaseError], 'Retained upgrade ownership failed and could not be released.', ); } if (ownershipError !== undefined) throw ownershipError; if (releaseError !== undefined) throw releaseError; })().finally(() => { if (this.retainedUpgradeReleaseTask === task) { this.retainedUpgradeReleaseTask = undefined; } }); this.retainedUpgradeReleaseTask = task; await task; } private startUpgradeWorkerWatchdog(): void { if (this.upgradeWorkerWatchdog || !this.upgradeCoordinator || !this.upgradeToken) return; const coordinator = this.upgradeCoordinator; const token = this.upgradeToken; let checking = false; let lastFailureLogAt = 0; this.upgradeWorkerWatchdog = setInterval(() => { if (checking || this.lifecycleState !== 'ready') return; checking = true; const task = (async () => { let transaction = await coordinator.readTransaction(token); this.upgradeStatus = await coordinator.readSanitizedUpgradeStatus(token); if (transaction.terminal) { try { await this.abortAndWaitForUpgradePreparation( new Error('The upgrade transaction became terminal during preparation.'), ); transaction = await coordinator.readTransaction(token); if (this.upgradeBarrier) await this.finalizeUpgrade(coordinator, token, 'compensate'); if (!this.upgradeStatus && this.upgradeWorkerWatchdog) { clearInterval(this.upgradeWorkerWatchdog); this.upgradeWorkerWatchdog = undefined; } } finally { await this.releaseRetainedUpgradeOwnership(); } return; } if (upgradeTransactionIsStalled(transaction)) { await coordinator.terminateTransactionWorker(transaction); } else if (await coordinator.transactionWorkerIsLive(transaction)) { return; } if (this.upgradePreparationAbortController) { await this.abortAndWaitForUpgradePreparation( new Error('The upgrade worker exited during session preparation.'), ); transaction = await coordinator.readTransaction(token); if (transaction.terminal) { if (this.upgradeBarrier) await this.finalizeUpgrade(coordinator, token, 'compensate'); return; } } const afterTargetPackageCommitStarted = transaction.version === upgradePackageTransitionTransactionVersion ? transaction.targetPackageCommitStarted === true : transaction.targetStartupInvoked === true; const finalization = await this.finalizeUpgrade( coordinator, token, afterTargetPackageCommitStarted ? (transaction.continueSessions ? 'continue' : 'reopen') : 'compensate', ); const success = transaction.targetStartupInvoked === true && finalization.failed === 0; await coordinator.finishTransaction( token, success, success ? 'The target controller completed recovery after worker loss.' : afterTargetPackageCommitStarted ? 'The target controller recovery remained incomplete after worker loss.' : 'The source controller compensated sessions after worker loss.', success ? undefined : finalization.failed > 0 ? `${finalization.failed} session recovery transition(s) did not settle.` : 'The detached upgrade worker is no longer running.', ); await this.releaseRetainedUpgradeOwnership(); })().catch((errorArg) => { if (Date.now() - lastFailureLogAt >= 30_000) { lastFailureLogAt = Date.now(); console.error('Upgrade worker-loss recovery failed.', errorArg); } }).finally(() => { checking = false; if (this.upgradeWorkerWatchdogTask === task) { this.upgradeWorkerWatchdogTask = undefined; } }); this.upgradeWorkerWatchdogTask = task; }, 5_000); this.upgradeWorkerWatchdog.unref?.(); } private pushSessionDraftChanged( projectIdArg: string, sessionIdArg: IControllerRuntimeId, updateArg: IControllerSessionDraftUpdate, ): void { if (sessionIdArg.harnessId === 'controller') return; void this.pushControllerEvent({ type: 'session.draft.changed', projectId: projectIdArg, harnessId: sessionIdArg.harnessId, sessionId: { ...sessionIdArg }, sessionDraftUpdate: { ...updateArg, ...(updateArg.attachments ? { attachments: updateArg.attachments.map((attachment) => ({ ...attachment })) } : {}), }, timestamp: Date.now(), }).catch(() => undefined); } private isHarnessSubmissionOutcomeUnknown( errorArg: unknown, harnessIdArg: TControllerSessionHarnessId, ): boolean { if (harnessIdArg === 'flex') { return this.isFlexPromptAdmissionTerminated(errorArg) || (errorArg instanceof AggregateError && errorArg.errors.some( (nestedError) => this.isFlexPromptAdmissionTerminated(nestedError), )); } if (!(errorArg instanceof Error)) return false; return errorArg.name === 'AbortError' || errorArg.name === 'TimeoutError' || /(?:exceeded \d+ms|fetch failed|ECONNRESET|ECONNREFUSED|socket|terminated)/iu.test( errorArg.message, ); } private isFlexPromptAdmissionTerminated(errorArg: unknown): boolean { return errorArg instanceof FlexServiceError && (errorArg.code === 'TIMEOUT' || errorArg.code === 'OUTCOME_UNKNOWN'); } private assertStartActive(): void { if (this.stopRequested || this.operationAbortController.signal.aborted) { throw new Error('Controller startup was cancelled by shutdown.'); } } private runOperation<T>( peerArg: IControllerPeer, operationArg: (signalArg: AbortSignal) => Promise<T>, requestSignalArg?: AbortSignal, ): Promise<T> { const peerId = peerArg.id; const storedPeerAbortController = peerArg.context.state[operationAbortControllerPeerStateKey]; const peerAbortController = storedPeerAbortController instanceof AbortController && !storedPeerAbortController.signal.aborted ? storedPeerAbortController : new AbortController(); if (peerAbortController !== storedPeerAbortController) { peerArg.context.state[operationAbortControllerPeerStateKey] = peerAbortController; } return this.runPrincipalOperation( 'browser', peerId, operationArg, requestSignalArg, peerAbortController.signal, typeof peerArg.context.state[authenticatedPeerStateKey] === 'string', ); } private runPrincipalOperation<T>( principalArg: TControllerOperationPrincipal, peerIdArg: string, operationArg: (signalArg: AbortSignal) => Promise<T>, requestSignalArg?: AbortSignal, principalAbortSignalArg?: AbortSignal, protectedOperationArg = false, ): Promise<T> { this.assertControllerOperationPrincipal(principalArg); if ( this.lifecycleState !== 'ready' || this.stopRequested || this.operationAbortController.signal.aborted ) { throw new plugins.typedrequest.TypedResponseError( 'The controller is not ready to accept this operation.', { code: 'controller_unavailable' }, ); } // Every identified MCP chat gets its own operation budget instead of sharing one MCP bucket. const peerId = principalArg === 'mcp' ? this.mcpCaller().auditPeerId : peerIdArg; const activeForPeer = this.activeOperationCountsByPeer.get(peerId) ?? 0; if ( this.activeOperations.size >= maxActiveControllerOperations || activeForPeer >= maxActiveControllerOperationsPerPeer ) { throw new plugins.typedrequest.TypedResponseError( 'The controller is busy. Try again after current operations settle.', { code: 'operation_limit' }, ); } if (protectedOperationArg) this.recordProtectedOperation(peerId); const operationSignal = AbortSignal.any([ this.operationAbortController.signal, ...(principalAbortSignalArg === undefined ? [] : [principalAbortSignalArg]), ...(requestSignalArg === undefined ? [] : [requestSignalArg]), ]); this.activeOperationCountsByPeer.set(peerId, activeForPeer + 1); const operation = Promise.resolve().then(() => ( operationArg(operationSignal) )); return this.trackOperationPromise(operation, () => { const remainingForPeer = (this.activeOperationCountsByPeer.get(peerId) ?? 1) - 1; if (remainingForPeer <= 0) { this.activeOperationCountsByPeer.delete(peerId); } else { this.activeOperationCountsByPeer.set(peerId, remainingForPeer); } }); } private assertControllerOperationPrincipal( principalArg: TControllerOperationPrincipal, ): void { if (principalArg !== 'browser' && principalArg !== 'mcp') { throw new Error('The controller operation principal is invalid.'); } } private auditPeerId( principalArg: TControllerOperationPrincipal, peerIdArg: string, ): string { this.assertControllerOperationPrincipal(principalArg); return principalArg === 'mcp' ? this.mcpCaller().auditPeerId : peerIdArg; } private async abortBusySessionsBeforeExit(): Promise<void> { const client = this.openCodeClient; if (!client) return; const deadline = AbortSignal.timeout(abortConversationsOnStopTimeoutMs); const projects = new Map<string, string>(); try { for (const project of (await this.authStore?.listProjects()) ?? []) { projects.set(project.id, project.directory); } } catch { // Project registry unavailable; shutdown continues with owned resources. } for (const [projectId, directory] of projects) { try { const listedSessions = await client.listSessions(directory, deadline); const sessions = await this.filterManagedSessions( projectId, listedSessions, [{ harnessId: 'opencode', liveRuntimeIds: listedSessions.map((session) => session.id), }], deadline, ); for (const session of sessions) { if (session.status !== 'busy') continue; try { await client.abortSession(directory, session.id.nativeId, deadline); } catch (error) { console.error(`Aborting session ${session.id} before exit failed.`, error); } } } catch (error) { console.error(`Listing sessions in ${directory} before exit failed.`, error); } } } private mintResumeToken( credentialIdArg: string, maxExpiresAtArg?: number, ): { resumeToken: string; resumeExpiresAt: number } { const now = Date.now(); for (const [hash, entry] of this.resumeTokens) { if (entry.expiresAt <= now) this.resumeTokens.delete(hash); } while (this.resumeTokens.size >= maxResumeTokens) { const oldest = this.resumeTokens.keys().next().value; if (oldest === undefined) break; this.resumeTokens.delete(oldest); } const token = plugins.crypto.randomBytes(32).toString('base64url'); const resumeExpiresAt = Math.min( now + resumeTokenTtlMs, maxExpiresAtArg ?? Number.MAX_SAFE_INTEGER, ); this.resumeTokens.set(hashResumeToken(token), { credentialId: credentialIdArg, expiresAt: resumeExpiresAt, }); return { resumeToken: token, resumeExpiresAt }; } private consumeResumeToken( tokenArg: string, ): { credentialId: string; expiresAt: number } | undefined { const hash = hashResumeToken(tokenArg); const entry = this.resumeTokens.get(hash); if (!entry) return undefined; this.resumeTokens.delete(hash); if (entry.expiresAt <= Date.now()) return undefined; return entry; } private recordProtectedOperation(peerIdArg: string): void { const now = Date.now(); if (now - this.protectedOperationWindowStartedAt >= protectedOperationWindowMs) { this.protectedOperationWindowStartedAt = now; this.protectedOperationCount = 0; this.protectedOperationCountsByPeer.clear(); } const peerCount = this.protectedOperationCountsByPeer.get(peerIdArg) ?? 0; if ( this.protectedOperationCount >= maxProtectedOperationsPerWindow || peerCount >= maxProtectedOperationsPerPeerPerWindow ) { throw new plugins.typedrequest.TypedResponseError( 'The authenticated operation rate limit was reached.', { code: 'rate_limited' }, ); } this.protectedOperationCount += 1; this.protectedOperationCountsByPeer.set(peerIdArg, peerCount + 1); } private recordUnauthenticatedAuditOperation(peerIdArg: string): void { const now = Date.now(); if (now - this.unauthenticatedAuditWindowStartedAt >= unauthenticatedAuditWindowMs) { this.unauthenticatedAuditWindowStartedAt = now; this.unauthenticatedAuditCount = 0; this.unauthenticatedAuditCountsByPeer.clear(); } const peerCount = this.unauthenticatedAuditCountsByPeer.get(peerIdArg) ?? 0; if ( this.unauthenticatedAuditCount >= maxUnauthenticatedAuditsPerWindow || peerCount >= maxUnauthenticatedAuditsPerPeerPerWindow ) { throw new plugins.typedrequest.TypedResponseError( 'The unauthenticated audit admission limit was reached.', { code: 'rate_limited' }, ); } this.unauthenticatedAuditCount += 1; this.unauthenticatedAuditCountsByPeer.set(peerIdArg, peerCount + 1); } private recordUnauthenticatedPreflightOperation(peerIdArg: string): void { const now = Date.now(); if (now - this.unauthenticatedPreflightWindowStartedAt >= unauthenticatedPreflightWindowMs) { this.unauthenticatedPreflightWindowStartedAt = now; this.unauthenticatedPreflightCount = 0; this.unauthenticatedPreflightCountsByPeer.clear(); } const peerCount = this.unauthenticatedPreflightCountsByPeer.get(peerIdArg) ?? 0; // the per-peer limit is best-effort only — peer ids are random per // connection, so reconnecting resets them; the global cap is the real bound if ( this.unauthenticatedPreflightCount >= maxUnauthenticatedPreflightsPerWindow || peerCount >= maxUnauthenticatedPreflightsPerPeerPerWindow ) { throw new plugins.typedrequest.TypedResponseError( 'The unauthenticated preflight limit was reached.', { code: 'rate_limited' }, ); } this.unauthenticatedPreflightCount += 1; if ( !this.unauthenticatedPreflightCountsByPeer.has(peerIdArg) && this.unauthenticatedPreflightCountsByPeer.size >= maxTrackedUnauthenticatedPreflightPeers ) { const oldestPeerId = this.unauthenticatedPreflightCountsByPeer.keys().next().value; if (typeof oldestPeerId === 'string') { this.unauthenticatedPreflightCountsByPeer.delete(oldestPeerId); } } this.unauthenticatedPreflightCountsByPeer.set(peerIdArg, peerCount + 1); } private trackOperationPromise<T>( operationArg: Promise<T>, onSettledArg?: () => void, ): Promise<T> { let trackedOperation: Promise<T>; trackedOperation = operationArg.finally(() => { this.activeOperations.delete(trackedOperation); onSettledArg?.(); }); this.activeOperations.add(trackedOperation); return trackedOperation; } private async waitForActiveOperations(timeoutMsArg: number): Promise<boolean> { if (this.activeOperations.size === 0) return true; let timeout: NodeJS.Timeout | undefined; try { return await Promise.race([ Promise.allSettled([...this.activeOperations]).then(() => this.activeOperations.size === 0), new Promise<false>((resolve) => { timeout = setTimeout(() => resolve(false), timeoutMsArg); }), ]); } finally { if (timeout) clearTimeout(timeout); } } private async waitForOwnedProcessGroupChildren(timeoutMsArg: number): Promise<boolean> { const deadline = Date.now() + timeoutMsArg; while (Date.now() < deadline) { const members = await readProcessGroupMemberPids(this.processGroupId); if (members.every((processIdArg) => processIdArg === process.pid)) return true; await new Promise((resolve) => setTimeout(resolve, 100)); } const members = await readProcessGroupMemberPids(this.processGroupId); return members.every((processIdArg) => processIdArg === process.pid); } private requireRuntimeConfig() { if (!this.runtimeConfig) throw new Error('Controller runtime configuration is unavailable.'); return this.runtimeConfig; } private requireAuthStore(): SmartDataAuthStore { if (!this.authStore) throw new Error('Controller authentication store is unavailable.'); return this.authStore; } private requireTypedServer(): plugins.typedserver.TypedServer { if (!this.typedServer) throw new Error('Controller transport is unavailable.'); return this.typedServer; } private requirePasskeyManager(): PasskeyManager { if (!this.passkeyManager) throw new Error('Controller passkey manager is unavailable.'); return this.passkeyManager; } private requireOpenCodeClient(): OpenCodeClientAdapter { if (!this.openCodeClient) throw new Error('Controller OpenCode client is unavailable.'); return this.openCodeClient; } private requireFlexSupervisor(): FlexSupervisor { if (!this.flexSupervisor || !this.flexIsOperational()) throw new FlexServiceError('NOT_READY'); return this.flexSupervisor; } private requireFlexProjectionReader(): FlexProjectionReader { if (!this.flexProjectionReader) throw new FlexProjectionNotFoundError(); return this.flexProjectionReader; } private requireGitReversion(): ControllerGitReversion { if (!this.gitReversion) throw new Error('Controller Git reversion is unavailable.'); return this.gitReversion; } private async resolveFlexHostGitProject(projectIdArg: string): Promise<{ projectId: string; directory: string; }> { const project = await this.requireAuthStore().getProject(projectIdArg) ?? await this.requireAuthStore().getPendingProjectRemoval(projectIdArg); if (!project) throw new Error('The Flex Git project is not registered.'); return { projectId: project.id, directory: project.directory }; } private terminalPeerLifecycle(peerArg: IControllerPeer): ITerminalPeerLifecycleState { const existing = peerArg.context.state[terminalPeerLifecycleStateKey]; if ( existing && typeof existing === 'object' && typeof (existing as Partial<ITerminalPeerLifecycleState>).connected === 'boolean' ) { return existing as ITerminalPeerLifecycleState; } const lifecycle: ITerminalPeerLifecycleState = { connected: true }; peerArg.context.state[terminalPeerLifecycleStateKey] = lifecycle; return lifecycle; } private requireTerminalManager(): ControllerTerminalManager { if (!this.terminalManager) throw new Error('Controller terminal manager is unavailable.'); return this.terminalManager; } private requireResourceCoordinator(): ControllerResourceCoordinator { if (!this.resourceCoordinator) throw new Error('Controller resource coordinator is unavailable.'); return this.resourceCoordinator; } private requireBrowserResourceHost(): ControllerBrowserResourceHost { if (!this.browserResourceHost) throw new Error('Controller browser runtime is unavailable.'); return this.browserResourceHost; } }