fix(pi): keep active runs alive during background sleep
This commit is contained in:
@@ -22,7 +22,7 @@ import {
|
||||
clearWorksSquareAIGatewayCredential,
|
||||
seedWorksSquareAIGatewayCredential,
|
||||
} from '../../services/works-square-ai-gateway';
|
||||
import type { PrepareConversationInput } from '../contracts';
|
||||
import type { ConversationPatchEnvelope, PrepareConversationInput } from '../contracts';
|
||||
import { PiManagedExtensionHost } from './extension-host';
|
||||
import { PiManagedInputRevisionCoordinator } from './managed-input-revision';
|
||||
import { runPiReleasePressureCleanup } from './release-proof-cleanup';
|
||||
@@ -273,6 +273,23 @@ export interface PiReleaseResilienceProof extends PiReleaseResilienceStatus {
|
||||
realTurnVerified: false;
|
||||
}
|
||||
|
||||
export interface PiReleaseResilienceIdleStatus {
|
||||
workers: ReturnType<CodingProductComposition['runtime']['getDiagnostics']>['workers'];
|
||||
resources: ReturnType<PiConversationRuntime['getResilienceProofDiagnostics']>;
|
||||
processes: { supported: boolean; parent: number[]; child: number[] };
|
||||
backgroundSleepReasoned: boolean;
|
||||
realTurnVerified: false;
|
||||
}
|
||||
|
||||
export interface PiReleaseIntentionalDisposeProof {
|
||||
terminal: { observed: boolean; errorCode: string | null; recoverable: boolean };
|
||||
other: { runStatus: string; bindingPreserved: boolean };
|
||||
targetBindingPreserved: boolean;
|
||||
providerRequestsUnchanged: boolean;
|
||||
resources: ReturnType<PiConversationRuntime['getResilienceProofDiagnostics']>;
|
||||
realTurnVerified: false;
|
||||
}
|
||||
|
||||
const PROOF_ACCOUNT_ID = 'release-proof-account';
|
||||
const PROOF_AGENT_ID = 'release-proof-agent';
|
||||
const PROOF_MODEL_ID = 'release-proof-model';
|
||||
@@ -1843,6 +1860,90 @@ export function releaseFinalAsarResilienceParents(): void {
|
||||
run.provider.releaseParents();
|
||||
}
|
||||
|
||||
export async function restartFinalAsarResilienceOther(): Promise<PiReleaseResilienceStatus> {
|
||||
const run = resilienceCompositionRun;
|
||||
if (!run) throw new Error('PI resilience proof is not running');
|
||||
const accepted = await run.composition.conversations.acceptPrompt({
|
||||
conversationId: run.otherConversationId,
|
||||
clientRequestId: 'release-proof-other-active-restarted',
|
||||
mode: 'prompt',
|
||||
text: 'RESILIENCE_OTHER_ACTIVE_RESTARTED',
|
||||
attachments: [],
|
||||
});
|
||||
if (!accepted.accepted) throw new Error('Resilience isolation restart was not accepted');
|
||||
return await waitForResilienceStatus(
|
||||
(status) => status.other.runStatus === 'running'
|
||||
&& status.activeProviderRequests.parent === 1,
|
||||
'Resilience isolation restart did not become active',
|
||||
);
|
||||
}
|
||||
|
||||
export async function getFinalAsarResilienceIdleStatus(): Promise<PiReleaseResilienceIdleStatus> {
|
||||
const run = resilienceCompositionRun;
|
||||
if (!run) throw new Error('PI resilience proof is not running');
|
||||
const runtime = resilienceRuntime(run);
|
||||
const processInspection = await inspectWindowsPiProcesses(run.hostToken);
|
||||
const lifecycleLogs = getRecentLogs().filter((line) => (
|
||||
line.includes('[PiWorkerLifecycle]')
|
||||
&& (line.includes(run.targetConversationId) || line.includes(run.otherConversationId))
|
||||
));
|
||||
return {
|
||||
workers: runtime.getDiagnostics().workers,
|
||||
resources: runtime.getResilienceProofDiagnostics(),
|
||||
processes: {
|
||||
supported: processInspection.supported,
|
||||
parent: processInspection.processes
|
||||
.filter(({ role }) => role === 'parent')
|
||||
.map(({ processId }) => processId)
|
||||
.sort((left, right) => left - right),
|
||||
child: processInspection.processes
|
||||
.filter(({ role }) => role === 'child')
|
||||
.map(({ processId }) => processId)
|
||||
.sort((left, right) => left - right),
|
||||
},
|
||||
backgroundSleepReasoned: lifecycleLogs.some((line) => (
|
||||
line.includes('"classification": "intentional_stop"')
|
||||
&& line.includes('"reason": "background_sleep"')
|
||||
)),
|
||||
realTurnVerified: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function disposeFinalAsarResilienceTarget(): Promise<PiReleaseIntentionalDisposeProof> {
|
||||
const run = resilienceCompositionRun;
|
||||
if (!run) throw new Error('PI resilience proof is not running');
|
||||
const runtime = resilienceRuntime(run);
|
||||
const beforeRequests = providerRequestCounts(run.provider);
|
||||
let errorCode: string | null = null;
|
||||
let recoverable = false;
|
||||
const unsubscribe = runtime.subscribe((envelope: ConversationPatchEnvelope) => {
|
||||
if (envelope.conversationId !== run.targetConversationId
|
||||
|| envelope.patch.op !== 'run.state'
|
||||
|| envelope.patch.run.status !== 'error') return;
|
||||
errorCode = envelope.patch.run.error?.code ?? null;
|
||||
recoverable = envelope.patch.run.error?.recoverable ?? false;
|
||||
});
|
||||
try {
|
||||
await runtime.dispose(run.targetConversationId, 'test_injection');
|
||||
} finally {
|
||||
unsubscribe();
|
||||
}
|
||||
const [other, targetBindingPreserved, otherBindingPreserved] = await Promise.all([
|
||||
run.composition.conversations.getSnapshot(run.otherConversationId),
|
||||
resilienceBindingPreserved(run, run.targetConversationId, run.targetBinding),
|
||||
resilienceBindingPreserved(run, run.otherConversationId, run.otherBinding),
|
||||
]);
|
||||
return {
|
||||
terminal: { observed: errorCode !== null, errorCode, recoverable },
|
||||
other: { runStatus: other.run.status, bindingPreserved: otherBindingPreserved },
|
||||
targetBindingPreserved,
|
||||
providerRequestsUnchanged: JSON.stringify(beforeRequests)
|
||||
=== JSON.stringify(providerRequestCounts(run.provider)),
|
||||
resources: runtime.getResilienceProofDiagnostics(),
|
||||
realTurnVerified: false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function finishFinalAsarResilienceProof(): Promise<PiReleaseResilienceProof> {
|
||||
const run = resilienceCompositionRun;
|
||||
if (!run) throw new Error('PI resilience proof is not running');
|
||||
@@ -1883,6 +1984,7 @@ export async function finishFinalAsarResilienceProof(): Promise<PiReleaseResilie
|
||||
|| released.resources.pool.processBudget.waiting !== 0
|
||||
|| released.resources.pool.runs.active !== 0
|
||||
|| released.resources.pool.runs.waiting !== 0
|
||||
|| released.resources.backgroundLeases.active !== 0
|
||||
|| released.resources.subagents?.activeChildPermits !== 0
|
||||
|| released.resources.subagents?.waitingChildPermits !== 0
|
||||
|| released.resources.subagents?.activeDispatches !== 0
|
||||
|
||||
Reference in New Issue
Block a user