import type * as interfaces from '../ts_interfaces/index.js'; type TAccount = interfaces.TAuthSwitchAccounts['harnesses'][number]['accounts'][number]; type THarness = interfaces.TAuthSwitchAccounts['harnesses'][number]; export const accountLimitsRefreshIntervalMs = 5 * 60_000; export const accountLimitsOperationPollMs = 600; export const accountLimitsOperationObservationMs = 4 * 60_000; export interface IAccountLimitsServiceResponse { operation: interfaces.TAuthSwitchOperation; harnesses: interfaces.IControllerAuthSwitchHarness[]; } export type TAccountLimitsRequest = ( requestArg: interfaces.TAuthSwitchRequest, signalArg: AbortSignal, ) => Promise; export type TAccountLimitsCodexAccountRequest = ( profileIdArg: string, signalArg: AbortSignal, ) => Promise; export interface IAccountLimitsRuntimeIdentity { harnessId: 'codex'; profileId: string; providerAccountId: string; } export interface IAccountLimitsScheduler { now(): number; schedule(callbackArg: () => void, delayMsArg: number): unknown; cancel(handleArg: unknown): void; } export interface IAccountLimitsReadState { accounts?: interfaces.TAuthSwitchAccounts; harnesses: interfaces.IControllerAuthSwitchHarness[]; loadingCurrent: boolean; loadingAll: boolean; currentError?: string; allError?: string; currentUpdatedAt?: number; allUpdatedAt?: number; staleHarnessIds: string[]; runtimeIdentity?: IAccountLimitsRuntimeIdentity; } export type TResolvedAccountLimitsCurrent = | { available: true; harness: THarness; account: TAccount; } | { available: false; message: string; }; const defaultScheduler: IAccountLimitsScheduler = { now: () => Date.now(), schedule: (callbackArg, delayMsArg) => globalThis.setTimeout(callbackArg, delayMsArg), cancel: (handleArg) => globalThis.clearTimeout(handleArg as ReturnType), }; const requestErrorMessage = (errorArg: unknown): string => { if (errorArg instanceof Error && errorArg.message.trim()) return errorArg.message.trim(); return 'The controller did not return an account-limits response.'; }; const contextIdentity = ( contextArg: interfaces.TControllerAuthSwitchLimitsContext | undefined, contextKeyArg: string, ): string => { if (!contextArg) return `${contextKeyArg}\0none`; if (!contextArg.available) return `${contextKeyArg}\0unavailable\0${contextArg.reason}\0${contextArg.message}`; return contextArg.harnessId === 'opencode' ? `${contextKeyArg}\0opencode\0${contextArg.slotId}` : `${contextKeyArg}\0codex\0${contextArg.profileId}`; }; const refreshableHarnessId = ( contextArg: interfaces.TControllerAuthSwitchLimitsContext | undefined, ): 'opencode' | 'codex' | undefined => ( contextArg?.available ? contextArg.harnessId : undefined ); /** Resolve only the controller's authoritative current-account identity. */ export const resolveAccountLimitsCurrent = ( accountsArg: interfaces.TAuthSwitchAccounts | undefined, contextArg: interfaces.TControllerAuthSwitchLimitsContext | undefined, runtimeIdentityArg?: IAccountLimitsRuntimeIdentity, ): TResolvedAccountLimitsCurrent => { if (!contextArg) { return { available: false, message: 'Select a session to show its current account limits.' }; } if (!contextArg.available) return { available: false, message: contextArg.message }; if (!accountsArg) return { available: false, message: 'Current account limits have not loaded yet.' }; const harness = accountsArg.harnesses.find((candidateArg) => candidateArg.id === contextArg.harnessId); if (!harness) { return { available: false, message: `No ${contextArg.harnessId} account data was returned.` }; } if (contextArg.harnessId === 'codex') { if (!runtimeIdentityArg || runtimeIdentityArg.profileId !== contextArg.profileId) { return { available: false, message: 'The running Codex account has not been verified yet.' }; } const matches = harness.accounts.filter((accountArg) => ( accountArg.providerAccountId === runtimeIdentityArg.providerAccountId )); if (matches.length !== 1) { return { available: false, message: matches.length === 0 ? 'No authswitch account matches the running Codex account ID.' : 'More than one authswitch account matches the running Codex account ID, so none was selected.', }; } return { available: true, harness, account: matches[0]! }; } const matches = harness.accounts.filter((accountArg) => ( accountArg.isActive && accountArg.slotId === contextArg.slotId )); if (matches.length !== 1) { const accountKind = `active account for slot ${contextArg.slotId}`; return { available: false, message: matches.length === 0 ? `No ${accountKind} was reported.` : `More than one ${accountKind} was reported, so none was selected.`, }; } return { available: true, harness, account: matches[0]! }; }; /** The provider's headline window, otherwise its highest-used general account window. */ export const accountLimitsSummaryWindow = ( windowsArg: readonly interfaces.TAuthSwitchUsageWindow[], ): interfaces.TAuthSwitchUsageWindow | undefined => ( windowsArg.find((windowArg) => windowArg.headline === true) ?? windowsArg .filter((windowArg) => windowArg.scope !== 'feature') .reduce((selectedArg, windowArg) => ( !selectedArg || windowArg.usedPercent > selectedArg.usedPercent ? windowArg : selectedArg ), undefined) ); type TCodexRuntimeIdentityResult = | { available: true; identity: IAccountLimitsRuntimeIdentity } | { available: false; message: string }; /** * Owns account-list reads, operation polling, refresh cadence and response fencing. It never * infers an identity: the UI resolves the current account only through the controller context. */ export class AccountLimitsReadModel { private request: TAccountLimitsRequest | undefined; private codexAccountRequest: TAccountLimitsCodexAccountRequest | undefined; private context: interfaces.TControllerAuthSwitchLimitsContext | undefined; private contextKey = ''; private contextIdentity = contextIdentity(undefined, ''); private sourceGeneration = 0; private currentGeneration = 0; private requestSequence = 0; private readonly harnessSequence = new Map(); private readonly abortControllers = new Map<'all' | 'current', AbortController>(); private readonly inFlight = new Map<'all' | 'current', Promise>(); private refreshTimer: unknown; private started = false; private disposed = false; private stateValue: IAccountLimitsReadState = { harnesses: [], loadingCurrent: false, loadingAll: false, staleHarnessIds: [], }; public constructor( private readonly changed: (stateArg: IAccountLimitsReadState) => void, private readonly scheduler: IAccountLimitsScheduler = defaultScheduler, ) {} public get state(): IAccountLimitsReadState { return this.stateValue; } public setRequest(requestArg: TAccountLimitsRequest | undefined): void { if (this.request === requestArg) return; const staleHarnessIds = this.stateValue.accounts?.harnesses.map((harnessArg) => harnessArg.id) ?? []; this.sourceGeneration++; this.currentGeneration++; this.abortAll(); this.request = requestArg; this.publish({ loadingCurrent: false, loadingAll: false, currentError: undefined, allError: undefined, staleHarnessIds, runtimeIdentity: undefined, }); if (this.started && requestArg) void this.refreshCurrent(); } public setCodexAccountRequest(requestArg: TAccountLimitsCodexAccountRequest | undefined): void { if (this.codexAccountRequest === requestArg) return; this.sourceGeneration++; this.currentGeneration++; this.abortAll(); this.codexAccountRequest = requestArg; const harnessId = refreshableHarnessId(this.context); const hasRetainedHarness = harnessId !== undefined && this.stateValue.accounts?.harnesses.some((harnessArg) => harnessArg.id === harnessId); this.publish({ loadingCurrent: false, loadingAll: false, currentError: undefined, allError: undefined, staleHarnessIds: hasRetainedHarness ? [...new Set([...this.stateValue.staleHarnessIds, harnessId])] : this.stateValue.staleHarnessIds, runtimeIdentity: undefined, }); if (this.started && this.request) void this.refreshCurrent(); } public setContext( contextArg: interfaces.TControllerAuthSwitchLimitsContext | undefined, contextKeyArg: string, ): void { const nextIdentity = contextIdentity(contextArg, contextKeyArg); if (nextIdentity === this.contextIdentity) return; this.context = contextArg; this.contextKey = contextKeyArg; this.contextIdentity = nextIdentity; this.currentGeneration++; this.abortAll(); const harnessId = refreshableHarnessId(contextArg); const hasRetainedHarness = harnessId !== undefined && this.stateValue.accounts?.harnesses.some((harnessArg) => harnessArg.id === harnessId); this.publish({ loadingCurrent: false, currentError: undefined, staleHarnessIds: hasRetainedHarness ? [...new Set([...this.stateValue.staleHarnessIds, harnessId])] : this.stateValue.staleHarnessIds, runtimeIdentity: undefined, }); if (this.started) void this.refreshCurrent(); } public start(): void { if (this.started) return; this.disposed = false; this.started = true; this.schedulePeriodicRefresh(); void this.refreshCurrent(); } public stop(): void { this.started = false; this.disposed = true; this.sourceGeneration++; this.currentGeneration++; if (this.refreshTimer !== undefined) this.scheduler.cancel(this.refreshTimer); this.refreshTimer = undefined; this.abortAll(); this.publish({ loadingCurrent: false, loadingAll: false, runtimeIdentity: undefined }); } public invalidate(): void { this.currentGeneration++; this.abortAll(); const harnessId = refreshableHarnessId(this.context); this.publish({ loadingCurrent: false, currentError: undefined, staleHarnessIds: harnessId ? [...new Set([...this.stateValue.staleHarnessIds, harnessId])] : this.stateValue.staleHarnessIds, runtimeIdentity: undefined, }); if (this.started) void this.refreshCurrent(); } public refreshCurrent(): Promise { const harnessId = refreshableHarnessId(this.context); if (!this.request || !harnessId || this.disposed) return Promise.resolve(); return this.startRead('current', harnessId); } /** Every completed call starts a new unqualified read; only an identical in-flight read coalesces. */ public refreshAll(): Promise { if (!this.request || this.disposed) return Promise.resolve(); return this.startRead('all'); } private startRead(scopeArg: 'all' | 'current', harnessIdArg?: string): Promise { const existing = this.inFlight.get(scopeArg); if (existing) return existing; const request = this.request; if (!request) return Promise.resolve(); const sourceGeneration = this.sourceGeneration; const currentGeneration = this.currentGeneration; const currentContext = this.context; const codexContext = currentContext?.available && currentContext.harnessId === 'codex' ? currentContext : undefined; const codexAccountRequest = this.codexAccountRequest; const sequence = ++this.requestSequence; const abortController = new AbortController(); this.abortControllers.set(scopeArg, abortController); const retainedHarnessIds = this.stateValue.accounts?.harnesses.map((harnessArg) => harnessArg.id) ?? []; const affectedHarnessIds = scopeArg === 'current' ? [harnessIdArg!] : [...new Set([ ...retainedHarnessIds, ...(currentContext?.available ? [currentContext.harnessId] : []), ])]; for (const idArg of affectedHarnessIds) this.harnessSequence.set(idArg, sequence); this.publish(scopeArg === 'all' ? { loadingAll: true, allError: undefined, staleHarnessIds: [...new Set([...this.stateValue.staleHarnessIds, ...affectedHarnessIds])], runtimeIdentity: undefined, } : { loadingCurrent: true, currentError: undefined, staleHarnessIds: [...new Set([...this.stateValue.staleHarnessIds, ...affectedHarnessIds])], runtimeIdentity: undefined, }); const promise = (async (): Promise => { const response = await this.performRead(request, harnessIdArg, abortController.signal); if (!response.operation.accounts) { throw new Error('The account operation completed without an account snapshot.'); } if ( abortController.signal.aborted || sourceGeneration !== this.sourceGeneration || (scopeArg === 'current' && currentGeneration !== this.currentGeneration) ) return; let codexIdentityResult: TCodexRuntimeIdentityResult | undefined; if (codexContext) { if (!codexAccountRequest) { codexIdentityResult = { available: false, message: 'The running Codex account reader is unavailable.', }; } else { try { const account = await codexAccountRequest(codexContext.profileId, abortController.signal); codexIdentityResult = this.verifyCodexRuntimeIdentity( response.operation.accounts, codexContext.profileId, account, ); } catch (error) { codexIdentityResult = { available: false, message: requestErrorMessage(error), }; } } } if ( abortController.signal.aborted || sourceGeneration !== this.sourceGeneration || (scopeArg === 'current' && currentGeneration !== this.currentGeneration) ) return; if (scopeArg === 'all') { this.applyAll(response, sequence, currentGeneration, codexIdentityResult); } else { this.applyCurrent(response, harnessIdArg!, sequence, codexIdentityResult); } })() .catch((errorArg: unknown) => { if ( abortController.signal.aborted || sourceGeneration !== this.sourceGeneration || (scopeArg === 'current' && currentGeneration !== this.currentGeneration) ) return; const message = requestErrorMessage(errorArg); const existingHarnessIds = this.stateValue.accounts?.harnesses.map((harnessArg) => harnessArg.id) ?? []; const failedHarnessIds = scopeArg === 'all' ? existingHarnessIds : [harnessIdArg!]; for (const idArg of failedHarnessIds) { if ((this.harnessSequence.get(idArg) ?? 0) <= sequence) { this.harnessSequence.set(idArg, sequence); } } const staleHarnessIds = scopeArg === 'all' ? [...new Set([...this.stateValue.staleHarnessIds, ...existingHarnessIds])] : [...new Set([...this.stateValue.staleHarnessIds, harnessIdArg!])]; this.publish(scopeArg === 'all' ? { allError: message, staleHarnessIds } : { currentError: message, staleHarnessIds }); }) .finally(() => { if (this.inFlight.get(scopeArg) === promise) this.inFlight.delete(scopeArg); if (this.abortControllers.get(scopeArg) === abortController) { this.abortControllers.delete(scopeArg); } if ( sourceGeneration === this.sourceGeneration && (scopeArg === 'all' || currentGeneration === this.currentGeneration) ) { this.publish(scopeArg === 'all' ? { loadingAll: false } : { loadingCurrent: false }); } }); this.inFlight.set(scopeArg, promise); return promise; } private async performRead( requestArg: TAccountLimitsRequest, harnessIdArg: string | undefined, signalArg: AbortSignal, ): Promise { const observationStartedAt = this.scheduler.now(); let response = await requestArg({ protocolVersion: 1, action: 'list', ...(harnessIdArg ? { harnessId: harnessIdArg } : {}), }, signalArg); while (response.operation.state === 'pending') { await this.wait(accountLimitsOperationPollMs, signalArg); if (this.scheduler.now() - observationStartedAt >= accountLimitsOperationObservationMs) { throw new Error( 'AGL stopped observing this read after four minutes. It may still finish in the controller; refresh to start a new read.', ); } response = await requestArg({ protocolVersion: 1, action: 'get', operationId: response.operation.id, }, signalArg); } if (response.operation.state === 'failed') { throw new Error(response.operation.error ?? 'The account operation failed.'); } return response; } private verifyCodexRuntimeIdentity( accountsArg: interfaces.TAuthSwitchAccounts, profileIdArg: string, accountArg: interfaces.IControllerCodexAccount, ): TCodexRuntimeIdentityResult { if (accountArg.type !== 'chatgpt') { return { available: false, message: 'The running Codex profile is not signed in with a ChatGPT account.', }; } if (!accountArg.accountId) { return { available: false, message: 'The running Codex profile did not report an exact account ID.', }; } const harness = accountsArg.harnesses.find((harnessArg) => harnessArg.id === 'codex'); const matches = harness?.accounts.filter((account) => ( account.providerAccountId === accountArg.accountId )) ?? []; if (matches.length !== 1) { return { available: false, message: matches.length === 0 ? 'No authswitch account matches the running Codex account ID.' : 'More than one authswitch account matches the running Codex account ID, so none was selected.', }; } return { available: true, identity: { harnessId: 'codex', profileId: profileIdArg, providerAccountId: accountArg.accountId, }, }; } private applyAll( responseArg: IAccountLimitsServiceResponse, sequenceArg: number, currentGenerationArg: number, codexIdentityResultArg: TCodexRuntimeIdentityResult | undefined, ): void { const incoming = responseArg.operation.accounts!; const previous = this.stateValue.accounts; const currentHarnessId = refreshableHarnessId(this.context); const currentWasSuperseded = currentHarnessId !== undefined && (this.harnessSequence.get(currentHarnessId) ?? 0) > sequenceArg; const refreshedHarnessIds = new Set(); const retainedNewer = previous?.harnesses.filter((harnessArg) => ( (this.harnessSequence.get(harnessArg.id) ?? 0) > sequenceArg && !incoming.harnesses.some((candidateArg) => candidateArg.id === harnessArg.id) )) ?? []; const harnesses = incoming.harnesses.map((harnessArg) => { if ((this.harnessSequence.get(harnessArg.id) ?? 0) <= sequenceArg) { this.harnessSequence.set(harnessArg.id, sequenceArg); refreshedHarnessIds.add(harnessArg.id); return harnessArg; } return previous?.harnesses.find((candidateArg) => candidateArg.id === harnessArg.id) ?? harnessArg; }); for (const harnessArg of previous?.harnesses ?? []) { if ( !incoming.harnesses.some((candidateArg) => candidateArg.id === harnessArg.id) && (this.harnessSequence.get(harnessArg.id) ?? 0) <= sequenceArg ) { refreshedHarnessIds.add(harnessArg.id); this.harnessSequence.delete(harnessArg.id); } } const behaviourById = new Map(responseArg.harnesses.map((itemArg) => [itemArg.id, itemArg])); for (const itemArg of this.stateValue.harnesses) { if ((this.harnessSequence.get(itemArg.id) ?? 0) > sequenceArg) behaviourById.set(itemArg.id, itemArg); } const now = this.scheduler.now(); const refreshedCurrent = currentHarnessId !== undefined && refreshedHarnessIds.has(currentHarnessId); const sameCurrentGeneration = currentGenerationArg === this.currentGeneration; const currentContext = this.context; const codexCurrentResult = sameCurrentGeneration && !currentWasSuperseded && currentContext?.available && currentContext.harnessId === 'codex' ? codexIdentityResultArg : undefined; const verifiedCodexIdentity = codexCurrentResult?.available ? codexCurrentResult.identity : undefined; const codexCurrentFailure = codexCurrentResult && !codexCurrentResult.available ? codexCurrentResult : undefined; const currentIsFresh = sameCurrentGeneration && ( currentContext?.available && currentContext.harnessId === 'codex' ? verifiedCodexIdentity !== undefined && !currentWasSuperseded : refreshedCurrent ); let staleHarnessIds = this.stateValue.staleHarnessIds.filter((idArg) => ( !refreshedHarnessIds.has(idArg) || (idArg === currentHarnessId && !currentIsFresh) )); if (currentHarnessId && codexCurrentFailure && !staleHarnessIds.includes(currentHarnessId)) { staleHarnessIds = [...staleHarnessIds, currentHarnessId]; } this.publish({ accounts: { ...incoming, harnesses: [...harnesses, ...retainedNewer] }, harnesses: [...behaviourById.values()], currentError: currentIsFresh ? undefined : codexCurrentFailure?.message ?? this.stateValue.currentError, allError: undefined, allUpdatedAt: now, currentUpdatedAt: currentIsFresh ? now : this.stateValue.currentUpdatedAt, staleHarnessIds, ...(verifiedCodexIdentity ? { runtimeIdentity: verifiedCodexIdentity } : {}), }); } private applyCurrent( responseArg: IAccountLimitsServiceResponse, harnessIdArg: string, sequenceArg: number, codexIdentityResultArg: TCodexRuntimeIdentityResult | undefined, ): void { if ((this.harnessSequence.get(harnessIdArg) ?? 0) > sequenceArg) return; const incoming = responseArg.operation.accounts!; const incomingHarness = incoming.harnesses.find((harnessArg) => harnessArg.id === harnessIdArg); if (!incomingHarness) throw new Error(`The account operation returned no ${harnessIdArg} harness.`); this.harnessSequence.set(harnessIdArg, sequenceArg); const previous = this.stateValue.accounts; const harnesses = previous ? previous.harnesses.some((harnessArg) => harnessArg.id === harnessIdArg) ? previous.harnesses.map((harnessArg) => harnessArg.id === harnessIdArg ? incomingHarness : harnessArg) : [...previous.harnesses, incomingHarness] : [incomingHarness]; const incomingBehaviour = responseArg.harnesses.find((itemArg) => itemArg.id === harnessIdArg); const behaviours = incomingBehaviour ? this.stateValue.harnesses.some((itemArg) => itemArg.id === harnessIdArg) ? this.stateValue.harnesses.map((itemArg) => itemArg.id === harnessIdArg ? incomingBehaviour : itemArg) : [...this.stateValue.harnesses, incomingBehaviour] : this.stateValue.harnesses; const codexFailure = codexIdentityResultArg && !codexIdentityResultArg.available ? codexIdentityResultArg : undefined; const verifiedCodexIdentity = codexIdentityResultArg?.available ? codexIdentityResultArg.identity : undefined; const currentIsFresh = harnessIdArg !== 'codex' || verifiedCodexIdentity !== undefined; this.publish({ accounts: { schemaVersion: incoming.schemaVersion, generatedAt: incoming.generatedAt, complete: (previous?.complete ?? true) && incoming.complete, harnesses, }, harnesses: behaviours, currentError: codexFailure?.message, currentUpdatedAt: currentIsFresh ? this.scheduler.now() : this.stateValue.currentUpdatedAt, staleHarnessIds: currentIsFresh ? this.stateValue.staleHarnessIds.filter((idArg) => idArg !== harnessIdArg) : [...new Set([...this.stateValue.staleHarnessIds, harnessIdArg])], ...(verifiedCodexIdentity ? { runtimeIdentity: verifiedCodexIdentity } : {}), }); } private schedulePeriodicRefresh(): void { if (!this.started) return; if (this.refreshTimer !== undefined) this.scheduler.cancel(this.refreshTimer); this.refreshTimer = this.scheduler.schedule(() => { this.refreshTimer = undefined; this.schedulePeriodicRefresh(); void this.refreshCurrent(); }, accountLimitsRefreshIntervalMs); } private wait(delayMsArg: number, signalArg: AbortSignal): Promise { return new Promise((resolve, reject) => { if (signalArg.aborted) { reject(new DOMException('The account-limits request was cancelled.', 'AbortError')); return; } let handle: unknown; const abort = () => { this.scheduler.cancel(handle); reject(new DOMException('The account-limits request was cancelled.', 'AbortError')); }; handle = this.scheduler.schedule(() => { signalArg.removeEventListener('abort', abort); resolve(); }, delayMsArg); signalArg.addEventListener('abort', abort, { once: true }); }); } private abort(scopeArg: 'all' | 'current'): void { this.abortControllers.get(scopeArg)?.abort(); this.abortControllers.delete(scopeArg); this.inFlight.delete(scopeArg); } private abortAll(): void { this.abort('current'); this.abort('all'); } private publish(patchArg: Partial): void { this.stateValue = { ...this.stateValue, ...patchArg, harnesses: patchArg.harnesses ? [...patchArg.harnesses] : this.stateValue.harnesses, staleHarnessIds: patchArg.staleHarnessIds ? [...patchArg.staleHarnessIds] : this.stateValue.staleHarnessIds, }; this.changed(this.stateValue); } }