fix(pi): strengthen final release proof
This commit is contained in:
593
electron/coding-runtime/pi/release-proof.ts
Normal file
593
electron/coding-runtime/pi/release-proof.ts
Normal file
@@ -0,0 +1,593 @@
|
||||
import { spawn, type ChildProcess } from 'node:child_process';
|
||||
import { mkdtemp, rm } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
import { createCodingConversationStore } from '../../coding-projects/conversation-store';
|
||||
import { createCodingProjectAgent } from '../../coding-projects/project-config';
|
||||
import {
|
||||
createCodingProjectStore,
|
||||
createLocalCodingProject,
|
||||
createMemoryCodingProjectStorage,
|
||||
} from '../../coding-projects/project-store';
|
||||
import type { ProviderAccount } from '../../shared/providers/types';
|
||||
import type { PrepareConversationInput } from '../contracts';
|
||||
import type { PiProcessError } from './process-errors';
|
||||
import { PiManagedExtensionHost } from './extension-host';
|
||||
import type {
|
||||
PiRpcCommand,
|
||||
PiRpcEvent,
|
||||
PiRpcResponse,
|
||||
} from './rpc-client';
|
||||
import {
|
||||
PiSubagentScheduler,
|
||||
type PiSubagentChild,
|
||||
} from './subagent';
|
||||
import {
|
||||
PiProcessBudget,
|
||||
PiWorkerPool,
|
||||
type PiConversationWorker,
|
||||
} from './worker-pool';
|
||||
import { PiProjectWriteLeaseCoordinator, type PiProjectWriteLease } from './write-lease';
|
||||
import { createPiManagedWorkerOpener } from './runtime';
|
||||
import { PiSessionRegistry } from './session-registry';
|
||||
import type { PiRuntimeTelemetryEvent } from './telemetry';
|
||||
|
||||
type ExtensionHandler = (...arguments_: unknown[]) => Promise<unknown> | unknown;
|
||||
type ExtensionTool = {
|
||||
name: string;
|
||||
execute?: (...arguments_: unknown[]) => Promise<unknown>;
|
||||
};
|
||||
|
||||
type PressureProcess = {
|
||||
label: string;
|
||||
child: ChildProcess;
|
||||
pid: number;
|
||||
stop(): Promise<void>;
|
||||
};
|
||||
|
||||
type Deferred = {
|
||||
promise: Promise<void>;
|
||||
resolve(): void;
|
||||
};
|
||||
|
||||
type PressureRun = {
|
||||
active: PiReleasePressureSnapshot;
|
||||
finish(): Promise<PiReleasePressureSnapshot>;
|
||||
};
|
||||
|
||||
export interface PiReleasePressureSnapshot {
|
||||
parentWorkers: number;
|
||||
childWorkers: number;
|
||||
liveProcessIds: number[];
|
||||
processBudget: { active: number; waiting: number };
|
||||
childPermits: { active: number; waiting: number };
|
||||
dispatches: { active: number; parents: number };
|
||||
writeLeases: { active: number; waiting: number };
|
||||
}
|
||||
|
||||
export interface PiReleaseExtensionProof {
|
||||
parentToolNames: string[];
|
||||
childToolNames: string[];
|
||||
subagentStatus: string;
|
||||
subagentSummary: string;
|
||||
materializedExtension: string;
|
||||
managedWorkerMilestones: PiRuntimeTelemetryEvent[];
|
||||
released: {
|
||||
processBudget: { active: number; waiting: number };
|
||||
childPermits: { active: number; waiting: number };
|
||||
dispatches: { active: number; parents: number };
|
||||
};
|
||||
}
|
||||
|
||||
let pressureRun: PressureRun | null = null;
|
||||
|
||||
function deferred(): Deferred {
|
||||
let resolve!: () => void;
|
||||
const promise = new Promise<void>((done) => { resolve = done; });
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
async function waitFor(predicate: () => boolean, message: string): Promise<void> {
|
||||
const deadline = Date.now() + 10_000;
|
||||
while (Date.now() < deadline) {
|
||||
if (predicate()) return;
|
||||
await new Promise((resolveWait) => setTimeout(resolveWait, 20));
|
||||
}
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
async function spawnPressureProcess(label: string): Promise<PressureProcess> {
|
||||
const child = spawn(process.execPath, [
|
||||
'-e',
|
||||
`process.title=${JSON.stringify(`makelore-pi-proof-${label}`)};setInterval(()=>{},1000)`,
|
||||
], {
|
||||
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
|
||||
stdio: 'ignore',
|
||||
windowsHide: true,
|
||||
});
|
||||
await new Promise<void>((resolveSpawn, rejectSpawn) => {
|
||||
child.once('spawn', resolveSpawn);
|
||||
child.once('error', rejectSpawn);
|
||||
});
|
||||
if (!child.pid) throw new Error(`Pressure process ${label} has no pid`);
|
||||
let stopFlight: Promise<void> | null = null;
|
||||
return {
|
||||
label,
|
||||
child,
|
||||
pid: child.pid,
|
||||
stop: () => {
|
||||
if (stopFlight) return stopFlight;
|
||||
stopFlight = (async () => {
|
||||
if (child.exitCode !== null || child.signalCode !== null) return;
|
||||
const exited = new Promise<void>((resolveExit) => child.once('exit', () => resolveExit()));
|
||||
child.kill('SIGTERM');
|
||||
const graceful = await Promise.race([
|
||||
exited.then(() => true),
|
||||
new Promise<boolean>((resolveTimeout) => setTimeout(() => resolveTimeout(false), 2_000)),
|
||||
]);
|
||||
if (!graceful && child.exitCode === null && child.signalCode === null) {
|
||||
child.kill('SIGKILL');
|
||||
await exited;
|
||||
}
|
||||
})();
|
||||
return stopFlight;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
class PressureConversationWorker implements PiConversationWorker {
|
||||
readonly id: string;
|
||||
private readonly eventListeners = new Set<(event: PiRpcEvent) => void>();
|
||||
|
||||
constructor(
|
||||
readonly generation: number,
|
||||
readonly process: PressureProcess,
|
||||
) {
|
||||
this.id = process.label;
|
||||
}
|
||||
|
||||
async request<T = unknown>(command: PiRpcCommand): Promise<PiRpcResponse<T>> {
|
||||
return {
|
||||
type: 'response',
|
||||
id: `${this.id}-${command.type}`,
|
||||
command: command.type,
|
||||
success: true,
|
||||
};
|
||||
}
|
||||
|
||||
async send(_command: PiRpcCommand): Promise<void> {}
|
||||
|
||||
subscribe(listener: (event: PiRpcEvent) => void): () => void {
|
||||
this.eventListeners.add(listener);
|
||||
return () => this.eventListeners.delete(listener);
|
||||
}
|
||||
|
||||
subscribeInvalidation(_listener: (error: PiProcessError) => void): () => void {
|
||||
return () => undefined;
|
||||
}
|
||||
|
||||
emitSettled(): void {
|
||||
for (const listener of this.eventListeners) listener({ type: 'agent_settled' });
|
||||
}
|
||||
|
||||
async stop() {
|
||||
await this.process.stop();
|
||||
return { mode: 'forced-tree-kill' as const, code: 0, signal: null };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function conversation(index: number): PrepareConversationInput {
|
||||
return {
|
||||
conversationId: `release-proof-conversation-${index}`,
|
||||
projectId: `release-proof-project-${index}`,
|
||||
agentId: 'release-proof-agent',
|
||||
title: `Release proof ${index}`,
|
||||
model: {
|
||||
model: {
|
||||
accountId: 'release-proof-account',
|
||||
modelId: 'release-proof-model',
|
||||
thinkingLevel: 'medium',
|
||||
},
|
||||
modelResolution: 'resolved',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function pressureSnapshot(
|
||||
pool: PiWorkerPool,
|
||||
processBudget: PiProcessBudget,
|
||||
scheduler: PiSubagentScheduler,
|
||||
leases: PiProjectWriteLeaseCoordinator,
|
||||
processes: PressureProcess[],
|
||||
): PiReleasePressureSnapshot {
|
||||
const subagents = scheduler.getDiagnostics();
|
||||
return {
|
||||
parentWorkers: pool.getDiagnostics().workers.filter(({ state }) => state === 'running').length,
|
||||
childWorkers: subagents.activeChildPermits,
|
||||
liveProcessIds: processes
|
||||
.filter(({ child }) => child.exitCode === null && child.signalCode === null)
|
||||
.map(({ pid }) => pid)
|
||||
.sort((left, right) => left - right),
|
||||
processBudget: { active: processBudget.activeCount, waiting: processBudget.waitingCount },
|
||||
childPermits: {
|
||||
active: subagents.activeChildPermits,
|
||||
waiting: subagents.waitingChildPermits,
|
||||
},
|
||||
dispatches: {
|
||||
active: subagents.activeDispatches,
|
||||
parents: subagents.activeParents,
|
||||
},
|
||||
writeLeases: { active: leases.activeCount, waiting: leases.waitingCount() },
|
||||
};
|
||||
}
|
||||
|
||||
async function startPressureRun(): Promise<PressureRun> {
|
||||
const processBudget = new PiProcessBudget(8);
|
||||
const processes: PressureProcess[] = [];
|
||||
const parentWorkers = new Map<string, PressureConversationWorker>();
|
||||
const childGate = deferred();
|
||||
const pool = new PiWorkerPool({
|
||||
maxRunning: 4,
|
||||
maxIdle: 4,
|
||||
processBudget,
|
||||
openWorker: async ({ conversation: input, generation }) => {
|
||||
const pressureProcess = await spawnPressureProcess(`parent-${generation}-${input.conversationId}`);
|
||||
processes.push(pressureProcess);
|
||||
const worker = new PressureConversationWorker(generation, pressureProcess);
|
||||
parentWorkers.set(input.conversationId, worker);
|
||||
return {
|
||||
worker,
|
||||
session: {
|
||||
piSessionId: `release-proof-session-${input.conversationId}`,
|
||||
sessionKey: `release-proof-session-${input.conversationId}`,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
let childIndex = 0;
|
||||
const scheduler = new PiSubagentScheduler({
|
||||
processBudget,
|
||||
createId: (kind) => `release-proof-${kind}-${++childIndex}`,
|
||||
openChild: async (input): Promise<PiSubagentChild> => {
|
||||
const pressureProcess = await spawnPressureProcess(`child-${input.taskId}`);
|
||||
processes.push(pressureProcess);
|
||||
return {
|
||||
id: input.taskId,
|
||||
async run(_prompt, signal) {
|
||||
await Promise.race([
|
||||
childGate.promise,
|
||||
new Promise<void>((_resolve, reject) => {
|
||||
signal.addEventListener('abort', () => reject(new Error('Pressure child aborted')), {
|
||||
once: true,
|
||||
});
|
||||
}),
|
||||
]);
|
||||
return { summary: `completed ${input.agentId}` };
|
||||
},
|
||||
async stop() { await pressureProcess.stop(); },
|
||||
};
|
||||
},
|
||||
});
|
||||
const writeLeases = new PiProjectWriteLeaseCoordinator();
|
||||
const heldWriteLeases: PiProjectWriteLease[] = [];
|
||||
const dispatches: Array<Promise<unknown>> = [];
|
||||
try {
|
||||
const inputs = Array.from({ length: 4 }, (_, index) => conversation(index + 1));
|
||||
await Promise.all(inputs.map(async (input) => await pool.prepare(input)));
|
||||
await Promise.all(inputs.map(async (input, index) => {
|
||||
const ticket = pool.startTopLevel({
|
||||
conversationId: input.conversationId,
|
||||
runId: `release-proof-parent-run-${index + 1}`,
|
||||
command: { type: 'prompt', message: 'release qualification pressure' },
|
||||
});
|
||||
await ticket.accepted;
|
||||
}));
|
||||
for (let index = 0; index < 4; index += 1) {
|
||||
heldWriteLeases.push(await writeLeases.acquire(
|
||||
`release-proof-write-project-${index + 1}`,
|
||||
`release-proof-holder-${index + 1}`,
|
||||
));
|
||||
dispatches.push(scheduler.dispatch({
|
||||
conversationId: inputs[index]?.conversationId ?? `release-proof-conversation-${index + 1}`,
|
||||
workerGeneration: 1,
|
||||
runId: `release-proof-parent-run-${index + 1}`,
|
||||
projectId: inputs[index]?.projectId ?? `release-proof-project-${index + 1}`,
|
||||
request: {
|
||||
mode: 'single',
|
||||
tasks: [{
|
||||
agentId: `release-proof-child-agent-${index + 1}`,
|
||||
task: 'hold one child during UI interaction',
|
||||
toolProfile: 'read-only',
|
||||
}],
|
||||
},
|
||||
}));
|
||||
}
|
||||
await waitFor(
|
||||
() => processBudget.activeCount === 8
|
||||
&& scheduler.getDiagnostics().activeChildPermits === 4
|
||||
&& processes.length === 8,
|
||||
'4 parent + 4 child pressure did not become active',
|
||||
);
|
||||
const active = pressureSnapshot(pool, processBudget, scheduler, writeLeases, processes);
|
||||
if (active.liveProcessIds.length !== 8) {
|
||||
throw new Error(`Expected 8 live pressure processes, got ${active.liveProcessIds.length}`);
|
||||
}
|
||||
return {
|
||||
active,
|
||||
finish: async () => {
|
||||
childGate.resolve();
|
||||
await Promise.all(dispatches);
|
||||
for (const worker of parentWorkers.values()) worker.emitSettled();
|
||||
await scheduler.close();
|
||||
await pool.shutdown();
|
||||
for (const lease of heldWriteLeases.splice(0)) lease.release();
|
||||
await Promise.all(processes.map(async (entry) => await entry.stop()));
|
||||
return pressureSnapshot(pool, processBudget, scheduler, writeLeases, processes);
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
childGate.resolve();
|
||||
await scheduler.close().catch(() => undefined);
|
||||
await pool.shutdown().catch(() => undefined);
|
||||
for (const lease of heldWriteLeases.splice(0)) lease.release();
|
||||
await Promise.allSettled(processes.map(async (entry) => await entry.stop()));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function withWorkerEnvironment<T>(
|
||||
environment: NodeJS.ProcessEnv,
|
||||
operation: () => Promise<T>,
|
||||
): Promise<T> {
|
||||
const previous = {
|
||||
bridge: process.env.MAKELORE_PI_BRIDGE_URL,
|
||||
token: process.env.MAKELORE_PI_WORKER_TOKEN,
|
||||
context: process.env.MAKELORE_PI_CONTEXT_FILE,
|
||||
role: process.env.MAKELORE_PI_WORKER_ROLE,
|
||||
};
|
||||
Object.assign(process.env, environment);
|
||||
try {
|
||||
return await operation();
|
||||
} finally {
|
||||
const values: Array<[keyof typeof previous, string]> = [
|
||||
['bridge', 'MAKELORE_PI_BRIDGE_URL'],
|
||||
['token', 'MAKELORE_PI_WORKER_TOKEN'],
|
||||
['context', 'MAKELORE_PI_CONTEXT_FILE'],
|
||||
['role', 'MAKELORE_PI_WORKER_ROLE'],
|
||||
];
|
||||
for (const [key, environmentKey] of values) {
|
||||
const value = previous[key];
|
||||
if (value === undefined) delete process.env[environmentKey];
|
||||
else process.env[environmentKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadExtension(extensionPath: string, environment: NodeJS.ProcessEnv): Promise<{
|
||||
tools: Map<string, ExtensionTool>;
|
||||
handlers: Map<string, ExtensionHandler>;
|
||||
}> {
|
||||
return await withWorkerEnvironment(environment, async () => {
|
||||
const module = await import(
|
||||
/* @vite-ignore */ `${pathToFileURL(extensionPath).href}?release-proof=${Date.now()}-${Math.random()}`
|
||||
) as {
|
||||
default(factory: {
|
||||
registerTool(tool: ExtensionTool): void;
|
||||
on(event: string, handler: ExtensionHandler): void;
|
||||
}): void;
|
||||
};
|
||||
const tools = new Map<string, ExtensionTool>();
|
||||
const handlers = new Map<string, ExtensionHandler>();
|
||||
module.default({
|
||||
registerTool: (tool) => tools.set(tool.name, tool),
|
||||
on: (event, handler) => handlers.set(event, handler),
|
||||
});
|
||||
return { tools, handlers };
|
||||
});
|
||||
}
|
||||
|
||||
async function runManagedWorkerMilestoneProof(
|
||||
root: string,
|
||||
extensionHost: PiManagedExtensionHost,
|
||||
): Promise<PiRuntimeTelemetryEvent[]> {
|
||||
const now = new Date().toISOString();
|
||||
const projectPath = path.join(root, 'managed-project');
|
||||
const userDataDir = path.join(root, 'managed-user-data');
|
||||
const projectStore = createCodingProjectStore(createMemoryCodingProjectStorage(), {
|
||||
createId: () => 'release-proof-managed-project',
|
||||
now: () => now,
|
||||
});
|
||||
await createLocalCodingProject({ projectPath, now }, projectStore);
|
||||
await createCodingProjectAgent(projectPath, {
|
||||
id: 'release-proof-agent',
|
||||
avatarId: 'avatar-01',
|
||||
roleName: 'Release proof',
|
||||
name: 'Release proof agent',
|
||||
model: {
|
||||
accountId: 'release-proof-account',
|
||||
modelId: 'release-proof-model',
|
||||
thinkingLevel: 'medium',
|
||||
},
|
||||
modelResolution: 'resolved',
|
||||
responsibility: {
|
||||
mission: 'Exercise final packaged managed worker composition',
|
||||
owns: [],
|
||||
boundaries: [],
|
||||
collaborators: [],
|
||||
principles: [],
|
||||
},
|
||||
prompt: 'Release qualification only.',
|
||||
skillIds: [],
|
||||
}, { now });
|
||||
const conversationStore = createCodingConversationStore(projectPath, {
|
||||
createId: () => 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
|
||||
now: () => now,
|
||||
});
|
||||
const created = await conversationStore.create({
|
||||
agentId: 'release-proof-agent',
|
||||
title: 'Release proof managed conversation',
|
||||
model: {
|
||||
accountId: 'release-proof-account',
|
||||
modelId: 'release-proof-model',
|
||||
thinkingLevel: 'medium',
|
||||
},
|
||||
modelResolution: 'resolved',
|
||||
});
|
||||
const input: PrepareConversationInput = {
|
||||
conversationId: created.id,
|
||||
projectId: 'release-proof-managed-project',
|
||||
agentId: 'release-proof-agent',
|
||||
title: created.title,
|
||||
model: { model: created.model, modelResolution: created.modelResolution },
|
||||
};
|
||||
const account: ProviderAccount = {
|
||||
id: 'release-proof-account',
|
||||
vendorId: 'custom',
|
||||
label: 'Release proof account',
|
||||
authMode: 'api_key',
|
||||
apiProtocol: 'openai-completions',
|
||||
baseUrl: 'http://127.0.0.1:1/v1',
|
||||
model: 'release-proof-model',
|
||||
enabled: true,
|
||||
isDefault: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
const telemetry: PiRuntimeTelemetryEvent[] = [];
|
||||
const opener = createPiManagedWorkerOpener({
|
||||
registry: new PiSessionRegistry({ projectStore }),
|
||||
executablePath: process.execPath,
|
||||
cliPath: path.join(process.resourcesPath, 'pi-runtime', 'dist', 'cli.js'),
|
||||
userDataDir,
|
||||
bundledSkillsDir: path.join(process.resourcesPath, 'resources', 'coding-skills'),
|
||||
extensionHost,
|
||||
loadProviderInput: async () => ({ accounts: [account], modelSummaries: [] }),
|
||||
resolveCredential: async () => 'release-proof-local-only',
|
||||
createSessionKey: () => '8b1a9953-c461-4d88-9c3e-7e1f8f3f2c11',
|
||||
onTelemetry: (event) => telemetry.push(event),
|
||||
});
|
||||
const first = await opener({
|
||||
conversation: input,
|
||||
generation: 1,
|
||||
revision: { provider: 1, resources: 1 },
|
||||
});
|
||||
try {
|
||||
await first.worker.stop();
|
||||
const reopened = await opener({
|
||||
conversation: input,
|
||||
generation: 2,
|
||||
revision: { provider: 1, resources: 1 },
|
||||
existingSession: first.session,
|
||||
});
|
||||
await reopened.worker.stop();
|
||||
} finally {
|
||||
await first.worker.stop().catch(() => undefined);
|
||||
}
|
||||
const expected = ['resources.ready', 'worker.spawn', 'rpc.ready', 'session.open'];
|
||||
for (const cold of [true, false]) {
|
||||
const milestones = telemetry.filter((event) => event.cold === cold).map(({ milestone }) => milestone);
|
||||
if (JSON.stringify(milestones) !== JSON.stringify(expected)) {
|
||||
throw new Error(`Managed worker ${cold ? 'cold' : 'warm'} milestones are incomplete: ${milestones}`);
|
||||
}
|
||||
}
|
||||
return telemetry;
|
||||
}
|
||||
|
||||
export async function runFinalAsarExtensionProof(): Promise<PiReleaseExtensionProof> {
|
||||
const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-final-asar-extension-'));
|
||||
const processBudget = new PiProcessBudget(8);
|
||||
const scheduler = new PiSubagentScheduler({
|
||||
processBudget,
|
||||
openChild: async (input) => ({
|
||||
id: input.taskId,
|
||||
async run() { return { summary: `done ${input.agentId}` }; },
|
||||
async stop() {},
|
||||
}),
|
||||
});
|
||||
const host = new PiManagedExtensionHost();
|
||||
host.configureSubagents({ scheduler });
|
||||
try {
|
||||
const managedWorkerMilestones = await runManagedWorkerMilestoneProof(root, host);
|
||||
const parent = await host.registerWorker({
|
||||
conversationId: 'release-proof-extension-parent',
|
||||
generation: 1,
|
||||
projectId: 'release-proof-project',
|
||||
extensionsDir: root,
|
||||
});
|
||||
await host.bindRun('release-proof-extension-parent', 1, 'release-proof-run');
|
||||
const parentExtension = await loadExtension(parent.extensionPath, parent.env);
|
||||
const updates: unknown[] = [];
|
||||
const result = await withWorkerEnvironment(parent.env, async () => (
|
||||
await parentExtension.tools.get('subagent')?.execute?.(
|
||||
'release-proof-subagent',
|
||||
{
|
||||
mode: 'single',
|
||||
tasks: [{
|
||||
agentId: 'release-proof-agent',
|
||||
task: 'execute the final materialized extension',
|
||||
toolProfile: 'read-only',
|
||||
}],
|
||||
},
|
||||
new AbortController().signal,
|
||||
(update: unknown) => updates.push(update),
|
||||
)
|
||||
)) as { details?: { tasks?: Array<{ status?: string; summary?: string }> } } | undefined;
|
||||
const child = await host.registerWorker({
|
||||
conversationId: 'release-proof-extension-child',
|
||||
generation: 1,
|
||||
projectId: 'release-proof-project',
|
||||
extensionsDir: root,
|
||||
role: 'child',
|
||||
runId: 'release-proof-run',
|
||||
});
|
||||
const childExtension = await loadExtension(child.extensionPath, child.env);
|
||||
const task = result?.details?.tasks?.[0];
|
||||
if (task?.status !== 'complete' || updates.length === 0) {
|
||||
throw new Error('Final materialized extension did not execute its subagent bridge');
|
||||
}
|
||||
if (childExtension.tools.size !== 0) {
|
||||
throw new Error('Final child extension exposed parent-only product tools');
|
||||
}
|
||||
await scheduler.close();
|
||||
const diagnostics = scheduler.getDiagnostics();
|
||||
return {
|
||||
parentToolNames: [...parentExtension.tools.keys()].sort(),
|
||||
childToolNames: [...childExtension.tools.keys()].sort(),
|
||||
subagentStatus: task.status,
|
||||
subagentSummary: task.summary ?? '',
|
||||
materializedExtension: path.basename(parent.extensionPath),
|
||||
managedWorkerMilestones,
|
||||
released: {
|
||||
processBudget: { active: processBudget.activeCount, waiting: processBudget.waitingCount },
|
||||
childPermits: {
|
||||
active: diagnostics.activeChildPermits,
|
||||
waiting: diagnostics.waitingChildPermits,
|
||||
},
|
||||
dispatches: {
|
||||
active: diagnostics.activeDispatches,
|
||||
parents: diagnostics.activeParents,
|
||||
},
|
||||
},
|
||||
};
|
||||
} finally {
|
||||
await scheduler.close().catch(() => undefined);
|
||||
await host.close().catch(() => undefined);
|
||||
await rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function startFinalAsarPressureProof(): Promise<PiReleasePressureSnapshot> {
|
||||
if (pressureRun) throw new Error('PI release pressure proof is already running');
|
||||
pressureRun = await startPressureRun();
|
||||
return pressureRun.active;
|
||||
}
|
||||
|
||||
export async function finishFinalAsarPressureProof(): Promise<PiReleasePressureSnapshot> {
|
||||
const current = pressureRun;
|
||||
if (!current) throw new Error('PI release pressure proof is not running');
|
||||
pressureRun = null;
|
||||
return await current.finish();
|
||||
}
|
||||
Reference in New Issue
Block a user