fix(pi): settle delayed compact ownership
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';
|
||||
import { execFile } from 'node:child_process';
|
||||
import { mkdtemp, readFile, readdir, rm } from 'node:fs/promises';
|
||||
import { mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { promisify } from 'node:util';
|
||||
@@ -26,6 +26,7 @@ import type { ConversationPatchEnvelope, PrepareConversationInput } from '../con
|
||||
import { PiManagedExtensionHost } from './extension-host';
|
||||
import { PiManagedInputRevisionCoordinator } from './managed-input-revision';
|
||||
import { runPiReleasePressureCleanup } from './release-proof-cleanup';
|
||||
import { ensurePiManagedPaths } from './resource-loader';
|
||||
import type { PiRpcEvent } from './rpc-client';
|
||||
import { createPiManagedWorkerOpener, PiConversationRuntime } from './runtime';
|
||||
import { PiSessionRegistry } from './session-registry';
|
||||
@@ -510,7 +511,9 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
|
||||
return;
|
||||
}
|
||||
const toolNames = toolNamesFrom(body);
|
||||
const role: ProofWorkerRole = toolNames.includes('subagent') ? 'parent' : 'child';
|
||||
const role: ProofWorkerRole = mode === 'resilience' && delayedCompactionArmed
|
||||
? 'parent'
|
||||
: toolNames.includes('subagent') ? 'parent' : 'child';
|
||||
const toolResult = hasToolResult(body);
|
||||
const providerRequest: ProviderRequest = {
|
||||
role,
|
||||
@@ -552,7 +555,7 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
|
||||
respondWithSubagentCall(response, model, 'coding');
|
||||
return;
|
||||
}
|
||||
if (role === 'parent' && delayedCompactionArmed) {
|
||||
if (delayedCompactionArmed) {
|
||||
delayedCompactionArmed = false;
|
||||
const entry = { role, response } satisfies HeldProviderResponse;
|
||||
held.add(entry);
|
||||
@@ -1743,6 +1746,7 @@ async function waitForResilienceStatus(
|
||||
export async function startFinalAsarResilienceProof(input: {
|
||||
composition: CodingProductComposition;
|
||||
projectPath: string;
|
||||
userDataDir: string;
|
||||
hostProxyBaseUrl: string;
|
||||
hostToken: string;
|
||||
}): Promise<{
|
||||
@@ -1766,6 +1770,13 @@ export async function startFinalAsarResilienceProof(input: {
|
||||
const providerService = getProviderService();
|
||||
await providerService.createAccount(proxyProviderAccount(input.hostProxyBaseUrl));
|
||||
await providerService.setDefaultAccount(PROOF_ACCOUNT_ID);
|
||||
const managedPaths = await ensurePiManagedPaths(input.userDataDir);
|
||||
await writeFile(path.join(managedPaths.configDir, 'settings.json'), JSON.stringify({
|
||||
compaction: {
|
||||
reserveTokens: 128,
|
||||
keepRecentTokens: 1,
|
||||
},
|
||||
}, null, 2), 'utf8');
|
||||
const project = await input.composition.projects.createProject({ projectPath: input.projectPath });
|
||||
projectId = project.project.id;
|
||||
await createCodingProjectAgent(input.projectPath, {
|
||||
|
||||
@@ -1398,6 +1398,41 @@ export class PiConversationRuntime implements CodingConversationRuntime {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (event.type === 'top-level.settled') {
|
||||
void this.enqueueProjection(event.conversationId, async () => {
|
||||
const snapshot = this.states.get(event.conversationId)?.snapshot;
|
||||
const current = snapshot?.run;
|
||||
if (!snapshot
|
||||
|| snapshot.cursor.workerGeneration !== event.generation
|
||||
|| current?.runId !== event.runId
|
||||
|| runIsTerminal(current.status)) return;
|
||||
this.emit(event.conversationId, {
|
||||
op: 'run.state',
|
||||
run: {
|
||||
status: 'idle',
|
||||
runId: event.runId,
|
||||
settledAt: this.now(),
|
||||
terminalReason: 'completed',
|
||||
},
|
||||
}, event.runId);
|
||||
this.extensionUi.endRun(event.conversationId, event.runId);
|
||||
try {
|
||||
await Promise.allSettled([
|
||||
this.interactions.cancelRun(event.conversationId, event.runId, true),
|
||||
this.extensionHost?.clearRun(
|
||||
event.conversationId,
|
||||
event.generation,
|
||||
event.runId,
|
||||
),
|
||||
]);
|
||||
} finally {
|
||||
this.releaseRunBackgroundLease(event.conversationId, event.runId);
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.recordProjectionFailure(event.conversationId, event.generation, error);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (event.type === 'top-level.failed') {
|
||||
void this.enqueueProjection(event.conversationId, async () => {
|
||||
await this.failRun(
|
||||
@@ -1525,8 +1560,11 @@ export class PiConversationRuntime implements CodingConversationRuntime {
|
||||
const current = this.states.get(conversationId)?.snapshot?.run;
|
||||
const snapshotGeneration = this.states.get(conversationId)?.snapshot?.cursor.workerGeneration;
|
||||
if (current?.runId !== runId
|
||||
|| runIsTerminal(current.status)
|
||||
|| (generation !== undefined && snapshotGeneration !== generation)) return;
|
||||
if (runIsTerminal(current.status)) {
|
||||
if (releaseBackgroundLease) this.releaseRunBackgroundLease(conversationId, runId);
|
||||
return;
|
||||
}
|
||||
const publicError = runtimeFailure(error);
|
||||
this.emit(conversationId, {
|
||||
op: 'run.state',
|
||||
|
||||
@@ -117,6 +117,12 @@ export type PiWorkerPoolEvent =
|
||||
generation: number;
|
||||
runId: string;
|
||||
}
|
||||
| {
|
||||
type: 'top-level.settled';
|
||||
conversationId: string;
|
||||
generation: number;
|
||||
runId: string;
|
||||
}
|
||||
| {
|
||||
type: 'top-level.failed';
|
||||
conversationId: string;
|
||||
@@ -756,6 +762,15 @@ export class PiWorkerPool {
|
||||
onLateResult: (result) => this.handleLateTopLevelResult(current!, run, result),
|
||||
});
|
||||
this.confirmTopLevel(current, run);
|
||||
if (run.command.type === 'compact') {
|
||||
this.settleTopLevel(current);
|
||||
this.emit({
|
||||
type: 'top-level.settled',
|
||||
conversationId: run.conversationId,
|
||||
generation: current.generation,
|
||||
runId: run.runId,
|
||||
});
|
||||
}
|
||||
run.resolve(response);
|
||||
} catch (error) {
|
||||
const active = this.activeRuns.get(run.conversationId);
|
||||
@@ -823,6 +838,15 @@ export class PiWorkerPool {
|
||||
generation: record.generation,
|
||||
runId: run.runId,
|
||||
});
|
||||
if (run.command.type === 'compact') {
|
||||
this.settleTopLevel(record);
|
||||
this.emit({
|
||||
type: 'top-level.settled',
|
||||
conversationId: run.conversationId,
|
||||
generation: record.generation,
|
||||
runId: run.runId,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (result.error.code === 'PI_RPC_EXITED'
|
||||
|
||||
@@ -965,6 +965,7 @@ export async function runPiReleaseProofE2E(action: PiReleaseProofAction) {
|
||||
resilience: await startFinalAsarResilienceProof({
|
||||
composition: codingProducts,
|
||||
projectPath: join(app.getPath('userData'), 'pi-resilience-proof-project'),
|
||||
userDataDir: app.getPath('userData'),
|
||||
hostProxyBaseUrl: `http://127.0.0.1:${address.port}/api/ai-proxy/v1`,
|
||||
hostToken: getHostApiToken(),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user