test(pi): hold packaged rpc confirmation past timeout

This commit is contained in:
2026-08-26 07:40:55 +08:00
parent 4b75d201b8
commit 26d616c9b6
8 changed files with 105 additions and 0 deletions

View File

@@ -1898,6 +1898,11 @@ export function armFinalAsarResilienceCompactDelay(): { delayMs: number } {
const run = resilienceCompositionRun;
if (!run) throw new Error('PI resilience proof is not running');
run.provider.armDelayedCompaction();
resilienceRuntime(run).delayNextTopLevelConfirmationForProof(
run.targetConversationId,
'compact',
PROOF_MUTATION_CONFIRMATION_DELAY_MS,
);
return { delayMs: PROOF_MUTATION_CONFIRMATION_DELAY_MS };
}
@@ -1905,6 +1910,11 @@ export function armFinalAsarResiliencePromptDelay(): { delayMs: number } {
const run = resilienceCompositionRun;
if (!run) throw new Error('PI resilience proof is not running');
run.provider.armDelayedPrompt();
resilienceRuntime(run).delayNextTopLevelConfirmationForProof(
run.targetConversationId,
'prompt',
PROOF_MUTATION_CONFIRMATION_DELAY_MS,
);
return { delayMs: PROOF_MUTATION_CONFIRMATION_DELAY_MS };
}

View File

@@ -78,6 +78,7 @@ export class PiRpcClient {
private readonly pending = new Map<string, PendingRequest>();
private readonly retiredIds = new Set<string>();
private readonly listeners = new Set<(event: PiRpcEvent) => void>();
private readonly proofResponseDelays = new Map<string, number>();
private sequence = 0;
private invalidated: PiProcessError | null = null;
@@ -111,6 +112,13 @@ export class PiRpcClient {
return () => this.listeners.delete(listener);
}
delayNextResponseForProof(commandType: string, delayMs: number): void {
if (!commandType || !Number.isSafeInteger(delayMs) || delayMs <= 0) {
throw new Error('Proof response delay requires a command type and positive safe delay');
}
this.proofResponseDelays.set(commandType, delayMs);
}
async request<T = unknown>(
command: PiRpcCommand,
options: PiRpcRequestOptions = {},
@@ -161,6 +169,22 @@ export class PiRpcClient {
if (!pending) {
throw new PiProcessError('PI_RPC_PROTOCOL_ERROR', `Pi RPC response used unknown id ${value.id}`);
}
const proofDelayMs = this.proofResponseDelays.get(pending.commandType);
if (proofDelayMs !== undefined) {
this.proofResponseDelays.delete(pending.commandType);
setTimeout(() => {
try {
this.accept(value);
} catch (error) {
try {
this.onEventListenerError?.(error);
} catch {
// Proof diagnostics must not affect transport state.
}
}
}, proofDelayMs);
return;
}
pending.cancel();
this.pending.delete(value.id);
if (value.success) {

View File

@@ -1086,6 +1086,14 @@ export class PiConversationRuntime implements CodingConversationRuntime {
return await this.pool.injectFailureForProof(conversationId, failure);
}
delayNextTopLevelConfirmationForProof(
conversationId: string,
commandType: 'prompt' | 'compact',
delayMs: number,
): void {
this.pool.delayNextTopLevelConfirmationForProof(conversationId, commandType, delayMs);
}
getResilienceProofDiagnostics(): {
pool: ReturnType<PiWorkerPool['getResilienceProofDiagnostics']>;
subagents: ReturnType<PiSubagentScheduler['getDiagnostics']> | null;

View File

@@ -38,6 +38,7 @@ export interface PiConversationWorker {
subscribeInvalidation(listener: (error: PiProcessError) => void): () => void;
stop(reason: PiWorkerStopReason): Promise<PiWorkerStopResult>;
injectFailureForProof?(failure: PiWorkerProofFailure): Promise<void>;
delayNextResponseForProof?(commandType: string, delayMs: number): void;
}
export interface PiWorkerSessionBinding {
@@ -677,6 +678,21 @@ export class PiWorkerPool {
return true;
}
delayNextTopLevelConfirmationForProof(
conversationId: string,
commandType: string,
delayMs: number,
): void {
const record = this.workers.get(conversationId);
if (!record || record.state === 'crashed') {
throw new Error('Conversation worker is not available for proof response delay');
}
if (!record.worker.delayNextResponseForProof) {
throw new Error('Conversation worker does not support proof response delay');
}
record.worker.delayNextResponseForProof(commandType, delayMs);
}
private async performShutdown(): Promise<void> {
this.shuttingDown = true;
this.shutdownController.abort();

View File

@@ -412,6 +412,11 @@ export class PiWorkerProcess {
return this.rpc.request<T>(command, options);
}
delayNextResponseForProof(commandType: string, delayMs: number): void {
if (!this.rpc) throw new Error('Pi worker has not started');
this.rpc.delayNextResponseForProof(commandType, delayMs);
}
send(command: PiRpcCommand): Promise<void> {
if (!this.rpc) {
return Promise.reject(new PiProcessError(