diff --git a/electron/coding-runtime/pi/runtime.ts b/electron/coding-runtime/pi/runtime.ts index 46b3a6c..894d604 100644 --- a/electron/coding-runtime/pi/runtime.ts +++ b/electron/coding-runtime/pi/runtime.ts @@ -125,6 +125,7 @@ export interface PiWorkerProcessAdapter { subscribeInvalidation(listener: (error: PiProcessError) => void): () => void; stop(reason: PiWorkerStopReason): Promise; injectFailureForProof?(failure: PiWorkerProofFailure): Promise; + delayNextResponseForProof?(commandType: string, delayMs: number): void; } export interface PiManagedProviderInput { @@ -193,6 +194,13 @@ class ManagedPiConversationWorker implements PiConversationWorker { } await this.process.injectFailureForProof(failure); } + + delayNextResponseForProof(commandType: string, delayMs: number): void { + if (!this.process.delayNextResponseForProof) { + throw new Error('Managed Pi process does not support proof response delay'); + } + this.process.delayNextResponseForProof(commandType, delayMs); + } } export function createPiManagedWorkerOpener( diff --git a/tests/unit/pi-managed-worker-opener.test.ts b/tests/unit/pi-managed-worker-opener.test.ts index cf0457c..ff12cfe 100644 --- a/tests/unit/pi-managed-worker-opener.test.ts +++ b/tests/unit/pi-managed-worker-opener.test.ts @@ -34,6 +34,7 @@ const NOW = '2026-08-22T16:00:00.000Z'; class OpenerFakeProcess implements PiWorkerProcessAdapter { readonly generation = 1; + readonly proofResponseDelays: Array<{ commandType: string; delayMs: number }> = []; constructor( private readonly sessionId: string, private readonly options: PiWorkerProcessOptions, @@ -60,6 +61,9 @@ class OpenerFakeProcess implements PiWorkerProcessAdapter { subscribe(_listener: (event: PiRpcEvent) => void): () => void { return () => undefined; } subscribeInvalidation(_listener: (error: PiProcessError) => void): () => void { return () => undefined; } + delayNextResponseForProof(commandType: string, delayMs: number): void { + this.proofResponseDelays.push({ commandType, delayMs }); + } async stop() { return { mode: 'stdin-close' as const, code: 0, signal: null }; } } @@ -120,6 +124,7 @@ describe('managed Pi worker opener', () => { updatedAt: NOW, }; const processOptions: PiWorkerProcessOptions[] = []; + const processes: OpenerFakeProcess[] = []; const telemetry: PiRuntimeTelemetryEvent[] = []; const registry = new PiSessionRegistry({ projectStore }); const extensionHost = new PiManagedExtensionHost(); @@ -140,7 +145,9 @@ describe('managed Pi worker opener', () => { createProcess: (options) => { processOptions.push(options); const sessionIndex = options.additionalArgs?.indexOf('--session-id') ?? -1; - return new OpenerFakeProcess(options.additionalArgs?.[sessionIndex + 1] ?? '', options); + const process = new OpenerFakeProcess(options.additionalArgs?.[sessionIndex + 1] ?? '', options); + processes.push(process); + return process; }, }); const opener = createOpener(registry); @@ -200,6 +207,9 @@ describe('managed Pi worker opener', () => { expect(JSON.stringify(telemetry)).not.toContain(created.id); expect(JSON.stringify(telemetry)).not.toContain('PRIVATE MANAGED PROMPT'); expect(JSON.stringify(telemetry)).not.toContain('provider-secret-value'); + expect(first.worker.delayNextResponseForProof).toBeTypeOf('function'); + first.worker.delayNextResponseForProof?.('prompt', 12_000); + expect(processes[0]?.proofResponseDelays).toEqual([{ commandType: 'prompt', delayMs: 12_000 }]); await first.worker.stop('test_injection'); await reopened.worker.stop('test_injection'); await restarted.worker.stop('test_injection');