fix(pi): settle delayed compact ownership
This commit is contained in:
@@ -36,6 +36,8 @@ class LifecycleFakeWorker implements PiConversationWorker {
|
||||
private readonly events = new Set<(event: PiRpcEvent) => void>();
|
||||
private readonly invalidations = new Set<(error: PiProcessError) => void>();
|
||||
private timeoutType: string | null = null;
|
||||
private deferredType: string | null = null;
|
||||
private deferredReject: ((error: unknown) => void) | null = null;
|
||||
private compacted = false;
|
||||
private lateResult: PiRpcRequestOptions['onLateResult'];
|
||||
|
||||
@@ -57,6 +59,12 @@ class LifecycleFakeWorker implements PiConversationWorker {
|
||||
)), 10_000);
|
||||
});
|
||||
}
|
||||
if (this.deferredType === command.type) {
|
||||
this.deferredType = null;
|
||||
await new Promise<void>((_resolve, reject) => {
|
||||
this.deferredReject = reject;
|
||||
});
|
||||
}
|
||||
const data = command.type === 'get_state'
|
||||
? {
|
||||
sessionId: `session-${this.id}`,
|
||||
@@ -132,6 +140,15 @@ class LifecycleFakeWorker implements PiConversationWorker {
|
||||
this.timeoutType = type;
|
||||
}
|
||||
|
||||
deferNext(type: string): void {
|
||||
this.deferredType = type;
|
||||
}
|
||||
|
||||
rejectDeferred(error: unknown): void {
|
||||
this.deferredReject?.(error);
|
||||
this.deferredReject = null;
|
||||
}
|
||||
|
||||
completeLateSuccess(): void {
|
||||
this.lateResult?.({
|
||||
response: { type: 'response', id: 'late-proof', success: true },
|
||||
@@ -354,7 +371,7 @@ describe('Pi run background lifecycle lease', () => {
|
||||
willRetry: false,
|
||||
});
|
||||
expect((await runtime.getSnapshot(runningConversation.id)).context.compaction).toBe('idle');
|
||||
workersByConversation.get(runningConversation.id)!.emit({ type: 'agent_settled' });
|
||||
workersByConversation.get(runningConversation.id)!.completeLateSuccess();
|
||||
await expect.poll(async () => (
|
||||
await runtime.getSnapshot(runningConversation.id)
|
||||
).run.status).toBe('idle');
|
||||
@@ -363,6 +380,31 @@ describe('Pi run background lifecycle lease', () => {
|
||||
);
|
||||
expect(controller.getLeaseCount()).toBe(0);
|
||||
|
||||
workersByConversation.get(runningConversation.id)!.deferNext('compact');
|
||||
const rejectedCompact = runtime.compact(runningConversation.id);
|
||||
void rejectedCompact.catch(() => undefined);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
workersByConversation.get(runningConversation.id)!.emit({
|
||||
type: 'compaction_start',
|
||||
reason: 'manual',
|
||||
});
|
||||
workersByConversation.get(runningConversation.id)!.emit({
|
||||
type: 'compaction_end',
|
||||
reason: 'manual',
|
||||
aborted: false,
|
||||
willRetry: false,
|
||||
errorMessage: 'Compaction failed: controlled rejection',
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
expect((await runtime.getSnapshot(runningConversation.id)).run.status).toBe('error');
|
||||
workersByConversation.get(runningConversation.id)!.rejectDeferred(new PiProcessError(
|
||||
'PI_RPC_RESPONSE_ERROR',
|
||||
'controlled compact rejection',
|
||||
{ generation: 1 },
|
||||
));
|
||||
await expect(rejectedCompact).rejects.toBeInstanceOf(PiProcessError);
|
||||
expect(controller.getLeaseCount()).toBe(0);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(100);
|
||||
expect(onSleep).toHaveBeenCalledTimes(1);
|
||||
expect(onStopRuntime).toHaveBeenCalledTimes(1);
|
||||
|
||||
Reference in New Issue
Block a user