import * as plugins from '../ts/plugins.js'; import { ControllerManagedSessionModel, type IControllerManagedSessionDocument } from '../ts/classes.managedsessionmodels.js'; import { ControllerCodexCreationModel, type IControllerCodexCreationDocument } from '../ts/classes.codexcreationmodels.js'; import { type ControllerCodexConnectionStore } from '../ts/classes.codexconnectionstore.js'; import { isCodexCreationToken, isCodexThreadId } from '../ts/functions.codexidentity.js'; import { validateCodexServerUrl } from '../ts/classes.codexconnectionmodels.js'; interface IControllerCodexOriginMigrationDocument { id: string; issuerId: string; version: 29; legacyProfileId: string; startedAt: Date; completedAt?: Date; } const assertMarker = (valueArg: unknown): asserts valueArg is IControllerCodexOriginMigrationDocument => { if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) throw new Error('Invalid Codex origin migration marker.'); const value = valueArg as Record; if (!['id', 'issuerId', 'version', 'legacyProfileId', 'startedAt'].every(key => Object.hasOwn(value, key)) || Object.keys(value).some(key => !['id', 'issuerId', 'version', 'legacyProfileId', 'startedAt', 'completedAt'].includes(key)) || !isCodexCreationToken(value.issuerId, 32) || value.id !== value.issuerId || !isCodexCreationToken(value.legacyProfileId, 32) || value.version !== 29 || !(value.startedAt instanceof Date) || !Number.isFinite(value.startedAt.getTime()) || (value.completedAt !== undefined && (!(value.completedAt instanceof Date) || value.completedAt < value.startedAt))) throw new Error('Invalid Codex origin migration marker.'); }; @plugins.smartdata.managed({ collectionName: 'agl_controller_codex_origin_migrations' }) @plugins.smartdata.exactPersistence({ assertDocument: assertMarker }) export class ControllerCodexOriginMigrationModel extends plugins.smartdata.SmartDataDbDoc { declare static exact: plugins.smartdata.TExact; @plugins.smartdata.unI() public id!: string; @plugins.smartdata.svDb() public issuerId!: string; @plugins.smartdata.svDb() public version!: 29; @plugins.smartdata.svDb() public legacyProfileId!: string; @plugins.smartdata.svDb() public startedAt!: Date; @plugins.smartdata.svDb() public completedAt?: Date; } export const codexMembershipWithLegacyOrigin = (bodyArg: IControllerManagedSessionDocument, profileIdArg: string, directoryArg: string): IControllerManagedSessionDocument => { if (bodyArg.harnessId !== 'codex' || bodyArg.codexOrigin) return structuredClone(bodyArg); if (!isCodexThreadId(bodyArg.runtimeId.nativeId)) throw new Error('A legacy Codex conversation has an unrecognized identity.'); return { ...structuredClone(bodyArg), codexOrigin: { version: 2, profileId: profileIdArg, rawThreadId: bodyArg.runtimeId.nativeId, remoteDirectory: directoryArg } }; }; export const codexCreationWithLegacyOrigin = (bodyArg: IControllerCodexCreationDocument, profileIdArg: string, directoryArg: string): IControllerCodexCreationDocument => { if (bodyArg.connection) return structuredClone(bodyArg); if (bodyArg.nativeId && !isCodexThreadId(bodyArg.nativeId)) throw new Error('A legacy Codex creation has an unrecognized identity.'); return { ...structuredClone(bodyArg), connection: { profileId: profileIdArg, remoteDirectory: directoryArg }, ...(bodyArg.nativeId ? { codexOrigin: { version: 2 as const, profileId: profileIdArg, rawThreadId: bodyArg.nativeId, remoteDirectory: directoryArg } } : {}) }; }; /** One-time attribution to the shipped local/environment configuration. Does not contact or mutate providers. */ export const migrateCodexOriginsV29 = async (inputArg: { connections: ControllerCodexConnectionStore; projects: Array<{ id: string; directory: string }>; pendingRemovalProjects?: Array<{ id: string; directory: string }>; legacyServerUrl?: string; legacyToken?: string; signal: AbortSignal; }): Promise<{ localProfileId: string; defaultProfileId: string }> => { const store = inputArg.connections; const localProfileId = store.stableId('local'); const profiles = await store.list(); if (!profiles.some(profile => profile.id === localProfileId)) await store.create({ id: localProfileId, name: 'Local Codex' }); let marker = await ControllerCodexOriginMigrationModel.exact.findStoredOne({ id: store.issuerId }); let persisted = marker ? ControllerCodexOriginMigrationModel.exact.toPersisted(marker) : undefined; if (persisted?.completedAt) return { localProfileId, defaultProfileId: persisted.legacyProfileId }; const url = inputArg.legacyServerUrl ? validateCodexServerUrl(inputArg.legacyServerUrl) : undefined; const legacyProfileId = url ? store.stableId(`legacy-remote:${url}`) : localProfileId; if (persisted && persisted.legacyProfileId !== legacyProfileId) throw new Error('The legacy Codex endpoint changed before its conversation origin migration completed.'); if (!profiles.some(profile => profile.id === legacyProfileId) && legacyProfileId !== localProfileId) { await store.create({ id: legacyProfileId, name: 'Imported Codex server', serverUrl: url, ...(inputArg.legacyToken ? { token: inputArg.legacyToken } : {}) }); } if (!marker) { try { marker = (await ControllerCodexOriginMigrationModel.exact.insert({ id: store.issuerId, issuerId: store.issuerId, version: 29, legacyProfileId, startedAt: new Date() })).document; } catch (error) { if (!(error instanceof plugins.smartdata.SmartdataExactPersistenceError) || error.code !== 'ambiguous_write') throw error; marker = await ControllerCodexOriginMigrationModel.exact.findStoredOne({ id: store.issuerId }); if (!marker) throw new Error('Codex origin migration could not establish its durable attribution.'); } persisted = ControllerCodexOriginMigrationModel.exact.toPersisted(marker); if (persisted.legacyProfileId !== legacyProfileId) throw new Error('Codex origin attribution changed concurrently.'); } const projects = new Map([...inputArg.projects, ...(inputArg.pendingRemovalProjects ?? [])].map(project => [project.id, project])); for (const project of inputArg.projects) { inputArg.signal.throwIfAborted(); if (!await store.mapping(project.id)) await store.setMapping(project.id, legacyProfileId, project.directory); } // Scan all states, including retired memberships: their origins outlive creation intent TTLs. let lastId: string | undefined; let count = 0; while (true) { const page = await ControllerManagedSessionModel.exact.findStored({ filter: { issuerId: store.issuerId, harnessId: 'codex', ...(lastId ? { id: { $gt: lastId } } : {}) }, sort: { id: 1 }, limit: 128, signal: inputArg.signal }); if (page.length === 0) break; if ((count += page.length) > 65536) throw new Error('Codex origin migration exceeds its bounded membership capacity.'); for (const stored of page) { const body = ControllerManagedSessionModel.exact.toPersisted(stored); if (lastId && body.id <= lastId) throw new Error('Codex origin migration did not advance.'); lastId = body.id; if (body.codexOrigin) continue; const project = projects.get(body.projectIdentityId); if (!project) { if (body.state === 'retired') continue; throw new Error('An active Codex conversation has no project for its origin migration.'); } const next = codexMembershipWithLegacyOrigin(body, legacyProfileId, project.directory); const result = await ControllerManagedSessionModel.exact.transition({ current: stored, change: model => { model.codexOrigin = next.codexOrigin; model.updateId = plugins.crypto.randomBytes(32).toString('base64url'); } }); if (result.status !== 'transitioned') throw new Error('Codex membership changed during origin migration.'); } } lastId = undefined; count = 0; while (true) { const page = await ControllerCodexCreationModel.exact.findStored({ filter: { issuerId: store.issuerId, ...(lastId ? { id: { $gt: lastId } } : {}) }, sort: { id: 1 }, limit: 128, signal: inputArg.signal }); if (page.length === 0) break; if ((count += page.length) > 65536) throw new Error('Codex origin migration exceeds its bounded creation capacity.'); for (const stored of page) { const body = ControllerCodexCreationModel.exact.toPersisted(stored); if (lastId && body.id <= lastId) throw new Error('Codex creation migration did not advance.'); lastId = body.id; if (body.connection) continue; const project = projects.get(body.projectIdentityId); if (!project) { if (body.state === 'retired' || body.state === 'materialized') continue; throw new Error('An outstanding Codex creation has no project for its origin migration.'); } const next = codexCreationWithLegacyOrigin(body, legacyProfileId, project.directory); const result = await ControllerCodexCreationModel.exact.transition({ current: stored, change: model => { model.connection = next.connection; if (next.codexOrigin) model.codexOrigin = next.codexOrigin; model.updateId = plugins.crypto.randomBytes(32).toString('base64url'); } }); if (result.status !== 'transitioned') throw new Error('Codex creation changed during origin migration.'); } } inputArg.signal.throwIfAborted(); const result = await ControllerCodexOriginMigrationModel.exact.transition({ current: marker, change: model => { model.completedAt = new Date(); } }); if (result.status !== 'transitioned') throw new Error('Codex origin migration completion changed concurrently.'); return { localProfileId, defaultProfileId: legacyProfileId }; };