export interface PiManagedInputRevision { provider: number; resources: number; } export type PiManagedInputAction = | { action: 'reuse'; revision: PiManagedInputRevision } | { action: 'rebuild-before-prompt'; revision: PiManagedInputRevision } | { action: 'rebuild-after-settled'; revision: PiManagedInputRevision } | { action: 'defer-until-settled'; revision: PiManagedInputRevision }; interface WorkerRevisionState { applied: PiManagedInputRevision; activeRun: PiManagedInputRevision | null; } function copyRevision(revision: PiManagedInputRevision): PiManagedInputRevision { return { provider: revision.provider, resources: revision.resources }; } function revisionsEqual(left: PiManagedInputRevision, right: PiManagedInputRevision): boolean { return left.provider === right.provider && left.resources === right.resources; } export class PiManagedInputRevisionCoordinator { private currentRevision: PiManagedInputRevision = { provider: 1, resources: 1 }; private readonly workers = new Map(); get current(): PiManagedInputRevision { return copyRevision(this.currentRevision); } markProviderStale(): PiManagedInputRevision { this.currentRevision = { provider: this.currentRevision.provider + 1, resources: this.currentRevision.resources, }; return this.current; } markResourcesStale(): PiManagedInputRevision { this.currentRevision = { provider: this.currentRevision.provider, resources: this.currentRevision.resources + 1, }; return this.current; } registerWorker(workerId: string, applied: PiManagedInputRevision = this.currentRevision): void { if (!workerId.trim()) throw new Error('Worker id is required'); if (this.workers.has(workerId)) throw new Error(`Worker is already registered: ${workerId}`); this.workers.set(workerId, { applied: copyRevision(applied), activeRun: null }); } removeWorker(workerId: string): void { this.workers.delete(workerId); } beforePrompt(workerId: string): PiManagedInputAction { const worker = this.requireWorker(workerId); if (worker.activeRun) { return revisionsEqual(worker.applied, this.currentRevision) ? { action: 'reuse', revision: copyRevision(worker.activeRun) } : { action: 'defer-until-settled', revision: copyRevision(worker.activeRun) }; } return revisionsEqual(worker.applied, this.currentRevision) ? { action: 'reuse', revision: copyRevision(worker.applied) } : { action: 'rebuild-before-prompt', revision: this.current }; } applyCurrentRevision(workerId: string): PiManagedInputRevision { const worker = this.requireWorker(workerId); if (worker.activeRun) throw new Error('Cannot rebuild managed input while a run is active'); worker.applied = this.current; return copyRevision(worker.applied); } beginRun(workerId: string): PiManagedInputRevision { const worker = this.requireWorker(workerId); if (worker.activeRun) throw new Error('Worker already has an active run'); if (!revisionsEqual(worker.applied, this.currentRevision)) { throw new Error('Worker managed input is stale'); } worker.activeRun = copyRevision(worker.applied); return copyRevision(worker.activeRun); } settleRun(workerId: string): PiManagedInputAction { const worker = this.requireWorker(workerId); if (!worker.activeRun) throw new Error('Worker has no active run'); worker.activeRun = null; return revisionsEqual(worker.applied, this.currentRevision) ? { action: 'reuse', revision: copyRevision(worker.applied) } : { action: 'rebuild-after-settled', revision: this.current }; } private requireWorker(workerId: string): WorkerRevisionState { const worker = this.workers.get(workerId); if (!worker) throw new Error(`Worker is not registered: ${workerId}`); return worker; } }