fix(pi): strengthen final release proof
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
- Worktree: D:\Datas\OthersProjects\makelore-pi-release-proof-3e725ac7
|
||||
- Base commit: 977445ba450f4ad32b6e6db2caf048517513ab39
|
||||
- Owner: codex-root
|
||||
- Status: Ready for Integration (PI-150 release gate blocked)
|
||||
- Status: In Progress (planner Spec review fixes required)
|
||||
|
||||
## Scope
|
||||
|
||||
@@ -137,6 +137,10 @@
|
||||
|
||||
## Follow-ups
|
||||
|
||||
- Planner review of `977445b..a795e0c` returned `Standards Pass / Spec Needs
|
||||
Fix` and requires direct final-ASAR composition smoke, distinct cold/warm
|
||||
prompt milestones, live 4-parent/4-child UI pressure plus cleanup proof, ASAR
|
||||
path enumeration for OpenCode residue, and the declared Linux x64 RPM target.
|
||||
- PI-160 should integrate candidate `a795e0c` and this task record, then perform
|
||||
canonical project-memory reconciliation in an integration-owned worktree.
|
||||
- Keep missing macOS x64/arm64 artifact/runtime/resource/performance evidence as
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -361,7 +361,7 @@ function recordManagedMilestone(
|
||||
milestone,
|
||||
conversationId: input.conversation.conversationId,
|
||||
workerGeneration: input.generation,
|
||||
cold: true,
|
||||
cold: !input.existingSession,
|
||||
durationMs,
|
||||
at,
|
||||
}));
|
||||
|
||||
@@ -98,6 +98,9 @@ class FifoSemaphore {
|
||||
}
|
||||
}
|
||||
|
||||
get activeCount(): number { return this.active; }
|
||||
get waitingCount(): number { return this.waiters.length; }
|
||||
|
||||
acquire(signal: AbortSignal): Promise<() => void> {
|
||||
if (signal.aborted) return Promise.reject(new PiSubagentChildError('SUBAGENT_ABORTED'));
|
||||
if (this.active < this.maximum) return Promise.resolve(this.issuePermit());
|
||||
@@ -292,6 +295,20 @@ export class PiSubagentScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
getDiagnostics(): {
|
||||
activeChildPermits: number;
|
||||
waitingChildPermits: number;
|
||||
activeDispatches: number;
|
||||
activeParents: number;
|
||||
} {
|
||||
return {
|
||||
activeChildPermits: this.childPermits.activeCount,
|
||||
waitingChildPermits: this.childPermits.waitingCount,
|
||||
activeDispatches: this.dispatches.size,
|
||||
activeParents: this.parentDispatches.size,
|
||||
};
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
if (this.closing) {
|
||||
await Promise.allSettled([...this.dispatches.values()].map((record) => record.flight));
|
||||
|
||||
@@ -5,6 +5,7 @@ export const PI_RUNTIME_MILESTONES = [
|
||||
'session.open',
|
||||
'resources.ready',
|
||||
'prompt.accepted',
|
||||
'agent.settled',
|
||||
] as const;
|
||||
|
||||
export type PiRuntimeMilestone = (typeof PI_RUNTIME_MILESTONES)[number];
|
||||
|
||||
@@ -230,7 +230,12 @@ export class PiWorkerPool {
|
||||
private readonly prepareFlights = new Map<string, Promise<PiWorkerPoolState>>();
|
||||
private readonly rebuildFlights = new Set<Promise<WorkerRecord>>();
|
||||
private readonly waitingRuns: PendingTopLevelRun[] = [];
|
||||
private readonly activeRuns = new Map<string, { runId: string; generation: number }>();
|
||||
private readonly activeRuns = new Map<string, {
|
||||
runId: string;
|
||||
generation: number;
|
||||
cold: boolean;
|
||||
acceptedAt?: number;
|
||||
}>();
|
||||
private readonly generations = new Map<string, number>();
|
||||
private readonly listeners = new Set<(event: PiWorkerPoolEvent) => void>();
|
||||
private readonly reclaimWaiters = new Set<() => void>();
|
||||
@@ -614,6 +619,7 @@ export class PiWorkerPool {
|
||||
this.activeRuns.set(run.conversationId, {
|
||||
runId: run.runId,
|
||||
generation: record.generation,
|
||||
cold: record.acceptedPromptCount === 0,
|
||||
});
|
||||
void this.acceptTopLevel(record, run);
|
||||
}
|
||||
@@ -635,7 +641,15 @@ export class PiWorkerPool {
|
||||
const acceptedAt = this.now();
|
||||
const response = await current.worker.request(run.command);
|
||||
if (run.command.type === 'prompt') {
|
||||
this.recordMilestone(current, run, 'prompt.accepted', this.now() - acceptedAt);
|
||||
const acceptedFinishedAt = this.now();
|
||||
active.acceptedAt = acceptedFinishedAt;
|
||||
this.recordMilestone(
|
||||
current,
|
||||
run,
|
||||
'prompt.accepted',
|
||||
acceptedFinishedAt - acceptedAt,
|
||||
active.cold,
|
||||
);
|
||||
current.acceptedPromptCount += 1;
|
||||
}
|
||||
run.resolve(response);
|
||||
@@ -661,6 +675,15 @@ export class PiWorkerPool {
|
||||
const conversationId = record.conversation.conversationId;
|
||||
const active = this.activeRuns.get(conversationId);
|
||||
if (!active || active.generation !== record.generation) return;
|
||||
if (active.acceptedAt !== undefined) {
|
||||
this.recordMilestone(
|
||||
record,
|
||||
{ runId: active.runId },
|
||||
'agent.settled',
|
||||
this.now() - active.acceptedAt,
|
||||
active.cold,
|
||||
);
|
||||
}
|
||||
this.activeRuns.delete(conversationId);
|
||||
this.runningCount -= 1;
|
||||
record.state = 'idle';
|
||||
@@ -1032,9 +1055,10 @@ export class PiWorkerPool {
|
||||
|
||||
private recordMilestone(
|
||||
record: WorkerRecord,
|
||||
run: PendingTopLevelRun,
|
||||
milestone: 'worker.queue_wait' | 'prompt.accepted',
|
||||
run: Pick<PendingTopLevelRun, 'runId'>,
|
||||
milestone: 'worker.queue_wait' | 'prompt.accepted' | 'agent.settled',
|
||||
durationMs: number,
|
||||
cold = record.acceptedPromptCount === 0,
|
||||
): void {
|
||||
if (!this.onTelemetry) return;
|
||||
this.onTelemetry(createPiRuntimeTelemetryEvent({
|
||||
@@ -1042,7 +1066,7 @@ export class PiWorkerPool {
|
||||
conversationId: record.conversation.conversationId,
|
||||
workerGeneration: record.generation,
|
||||
runId: run.runId,
|
||||
cold: record.acceptedPromptCount === 0,
|
||||
cold,
|
||||
durationMs,
|
||||
at: this.now(),
|
||||
}));
|
||||
|
||||
@@ -88,6 +88,11 @@ import {
|
||||
createCodingComposition,
|
||||
resolveCodingPiRuntimePaths,
|
||||
} from '../api/coding-composition';
|
||||
import {
|
||||
finishFinalAsarPressureProof,
|
||||
runFinalAsarExtensionProof,
|
||||
startFinalAsarPressureProof,
|
||||
} from '../coding-runtime/pi/release-proof';
|
||||
|
||||
// Diagnostic package: force Chromium networking onto HTTP/1.1 for transport A/B testing.
|
||||
app.commandLine.appendSwitch('disable-http2');
|
||||
@@ -875,8 +880,31 @@ export async function runLocalPreviewPreflightE2E(
|
||||
}
|
||||
}
|
||||
|
||||
type PiReleaseProofAction = 'extension' | 'pressure.start' | 'pressure.finish';
|
||||
|
||||
export async function runPiReleaseProofE2E(action: PiReleaseProofAction) {
|
||||
if (!isE2EMode) throw new Error('PI release proof is unavailable');
|
||||
const appPath = app.getAppPath();
|
||||
const packagedMain = {
|
||||
isPackaged: app.isPackaged,
|
||||
appPath,
|
||||
appPathUsesAsar: app.isPackaged && /[\\/]app\.asar$/i.test(appPath),
|
||||
};
|
||||
if (action === 'extension') {
|
||||
return { action, packagedMain, extension: await runFinalAsarExtensionProof() };
|
||||
}
|
||||
if (action === 'pressure.start') {
|
||||
return { action, packagedMain, pressure: await startFinalAsarPressureProof() };
|
||||
}
|
||||
return { action, packagedMain, pressure: await finishFinalAsarPressureProof() };
|
||||
}
|
||||
|
||||
if (isE2EMode) {
|
||||
(globalThis as typeof globalThis & {
|
||||
__niancodeRunLocalPreviewPreflightE2E?: typeof runLocalPreviewPreflightE2E;
|
||||
__niancodeRunPiReleaseProofE2E?: typeof runPiReleaseProofE2E;
|
||||
}).__niancodeRunLocalPreviewPreflightE2E = runLocalPreviewPreflightE2E;
|
||||
(globalThis as typeof globalThis & {
|
||||
__niancodeRunPiReleaseProofE2E?: typeof runPiReleaseProofE2E;
|
||||
}).__niancodeRunPiReleaseProofE2E = runPiReleaseProofE2E;
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
"devDependencies": {
|
||||
"@buape/carbon": "0.16.0",
|
||||
"@discordjs/voice": "^0.19.2",
|
||||
"@electron/asar": "3.4.1",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@grammyjs/runner": "^2.0.3",
|
||||
"@grammyjs/transformer-throttler": "^1.2.1",
|
||||
|
||||
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@@ -75,6 +75,9 @@ importers:
|
||||
'@discordjs/voice':
|
||||
specifier: ^0.19.2
|
||||
version: 0.19.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.9.1)(opusscript@0.1.1)
|
||||
'@electron/asar':
|
||||
specifier: 3.4.1
|
||||
version: 3.4.1
|
||||
'@eslint/js':
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1(eslint@10.1.0(jiti@1.21.7))
|
||||
@@ -5936,7 +5939,7 @@ snapshots:
|
||||
hosted-git-info: 9.0.3
|
||||
ignore: 7.0.5
|
||||
jiti: 2.7.0
|
||||
minimatch: 10.2.5
|
||||
minimatch: 10.2.4
|
||||
proper-lockfile: 4.1.2
|
||||
semver: 7.8.0
|
||||
typebox: 1.3.7
|
||||
@@ -7683,7 +7686,7 @@ snapshots:
|
||||
js-yaml: 4.1.1
|
||||
json5: 2.2.3
|
||||
lazy-val: 1.0.5
|
||||
minimatch: 10.2.4
|
||||
minimatch: 10.2.5
|
||||
plist: 3.1.0
|
||||
proper-lockfile: 4.1.2
|
||||
resedit: 1.7.2
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
import { readFile, readdir, stat } from 'node:fs/promises';
|
||||
import { createRequire } from 'node:module';
|
||||
import { arch as hostArch, platform as hostPlatform } from 'node:os';
|
||||
import {
|
||||
dirname,
|
||||
@@ -22,6 +23,8 @@ import {
|
||||
packagedResourcesDirectory,
|
||||
} from '../probe-pi-packaged-runtime.mjs';
|
||||
|
||||
const { listPackage } = createRequire(import.meta.url)('@electron/asar');
|
||||
|
||||
const PRODUCT_NAME = 'Makelore';
|
||||
const LINUX_EXECUTABLE_NAME = 'niancode';
|
||||
const EXTENSION_CONTRACT_MARKERS = Object.freeze([
|
||||
@@ -31,6 +34,7 @@ const EXTENSION_CONTRACT_MARKERS = Object.freeze([
|
||||
'MAKELORE_PI_BRIDGE_URL',
|
||||
]);
|
||||
const PI_AI_PROVIDER_PREFIX = 'pi-runtime/node_modules/@earendil-works/pi-ai/dist/providers/';
|
||||
const PI_AI_PROVIDER_ASAR_PREFIX = 'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/';
|
||||
|
||||
async function pathExists(path) {
|
||||
try {
|
||||
@@ -179,11 +183,23 @@ export async function collectForbiddenResourcePaths(root, pattern = /opencode/i)
|
||||
}
|
||||
|
||||
export function classifyOpenCodeResourcePaths(paths) {
|
||||
const upstreamPiProvider = paths.filter((path) => path.startsWith(PI_AI_PROVIDER_PREFIX));
|
||||
const productOwned = paths.filter((path) => !path.startsWith(PI_AI_PROVIDER_PREFIX));
|
||||
const isUpstreamPiProvider = (path) => path.startsWith(PI_AI_PROVIDER_PREFIX)
|
||||
|| path.startsWith(PI_AI_PROVIDER_ASAR_PREFIX);
|
||||
const upstreamPiProvider = paths.filter(isUpstreamPiProvider);
|
||||
const productOwned = paths.filter((path) => !isUpstreamPiProvider(path));
|
||||
return { productOwned, upstreamPiProvider };
|
||||
}
|
||||
|
||||
export function collectForbiddenAsarPaths(appAsar, pattern = /opencode/i) {
|
||||
const paths = listPackage(appAsar, { isPack: false }).map((entry) => (
|
||||
`app.asar/${entry.replace(/^[\\/]+/, '').replaceAll('\\', '/')}`
|
||||
));
|
||||
return {
|
||||
entryCount: paths.length,
|
||||
matches: paths.filter((entry) => pattern.test(entry)).sort(),
|
||||
};
|
||||
}
|
||||
|
||||
async function filesContainingNeedles(root, needles) {
|
||||
const matches = [];
|
||||
const visit = async (path) => {
|
||||
@@ -283,9 +299,12 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) {
|
||||
if (absoluteManifestValues.length > 0) {
|
||||
throw new Error(`Pi runtime manifest contains absolute paths: ${JSON.stringify(absoluteManifestValues)}`);
|
||||
}
|
||||
const openCodeResourcePaths = classifyOpenCodeResourcePaths(
|
||||
await collectForbiddenResourcePaths(resourcesDirectory),
|
||||
);
|
||||
const physicalOpenCodePaths = await collectForbiddenResourcePaths(resourcesDirectory);
|
||||
const asarOpenCodePaths = collectForbiddenAsarPaths(appAsar);
|
||||
const openCodeResourcePaths = classifyOpenCodeResourcePaths([
|
||||
...physicalOpenCodePaths,
|
||||
...asarOpenCodePaths.matches,
|
||||
]);
|
||||
if (openCodeResourcePaths.productOwned.length > 0) {
|
||||
throw new Error(
|
||||
`Product-owned resources contain OpenCode paths: ${openCodeResourcePaths.productOwned.join(', ')}`,
|
||||
@@ -355,6 +374,8 @@ export async function verifyPiProductArtifact({ projectRoot, executable }) {
|
||||
skills: actualSkills,
|
||||
openCodeResourcePaths: {
|
||||
...openCodeResourcePaths,
|
||||
physicalMatches: physicalOpenCodePaths,
|
||||
asar: asarOpenCodePaths,
|
||||
upstreamDecision: openCodeResourcePaths.upstreamPiProvider.length > 0
|
||||
? 'retained-required-files-from-exact-pinned-pi-production-package'
|
||||
: 'none',
|
||||
|
||||
@@ -205,7 +205,7 @@ function validateCapturedRequests(protocol, modelId, requests) {
|
||||
const authHeader = protocol === 'anthropic-messages' ? 'x-api-key' : 'authorization';
|
||||
const expectedAuth = protocol === 'anthropic-messages' ? LOCAL_API_KEY : `Bearer ${LOCAL_API_KEY}`;
|
||||
const problems = [];
|
||||
if (matching.length !== 4) problems.push(`expected 4 requests, got ${matching.length}`);
|
||||
if (matching.length !== 6) problems.push(`expected 6 requests, got ${matching.length}`);
|
||||
if (matching.some((request) => request.method !== 'POST')) problems.push('non-POST request');
|
||||
if (matching.some((request) => request.path !== expectedPath(protocol))) problems.push('unexpected endpoint path');
|
||||
if (matching.some((request) => request.headers[authHeader] !== expectedAuth)) problems.push('credential header mismatch');
|
||||
|
||||
@@ -82,6 +82,7 @@ export function parseProbeArgs(argv) {
|
||||
return value;
|
||||
};
|
||||
|
||||
if (argument === '--') continue;
|
||||
if (argument === '--samples') options.samples = parsePositiveInteger(next(), argument);
|
||||
else if (argument === '--timeout-ms') options.timeoutMs = parsePositiveInteger(next(), argument);
|
||||
else if (argument === '--stage') options.stage = true;
|
||||
@@ -422,7 +423,10 @@ export class PiRpcWorker {
|
||||
});
|
||||
|
||||
await new Promise((resolveSpawn, rejectSpawn) => {
|
||||
this.child.once('spawn', resolveSpawn);
|
||||
this.child.once('spawn', () => {
|
||||
this.spawnedAt = performance.now();
|
||||
resolveSpawn();
|
||||
});
|
||||
this.child.once('error', rejectSpawn);
|
||||
});
|
||||
return this;
|
||||
@@ -583,9 +587,11 @@ async function runReadySample(runtime, paths, timeoutMs) {
|
||||
await worker.start();
|
||||
const response = await worker.request({ type: 'get_state' });
|
||||
const readyMs = response.receivedAt - worker.startedAt;
|
||||
const workerSpawnMs = worker.spawnedAt - worker.startedAt;
|
||||
const rpcReadyMs = response.receivedAt - worker.spawnedAt;
|
||||
const rssKb = await getProcessRssKb(worker.child.pid);
|
||||
const stop = await worker.stop();
|
||||
return { readyMs, rssKb, stop, sessionId: response.data.sessionId };
|
||||
return { readyMs, workerSpawnMs, rpcReadyMs, rssKb, stop, sessionId: response.data.sessionId };
|
||||
} finally {
|
||||
await worker.stop().catch(() => undefined);
|
||||
}
|
||||
@@ -611,9 +617,15 @@ async function runPerformanceSamples(runtime, scratchRoot, sampleCount, timeoutM
|
||||
definition: {
|
||||
cold: 'fresh Pi config, session, and project directories per process',
|
||||
warm: 'shared primed Pi config directory with fresh session and project directories per process',
|
||||
workerSpawn: 'final product executable spawn event minus spawn request',
|
||||
rpcReady: 'first successful get_state response minus child spawn event',
|
||||
},
|
||||
coldReadyMs: summarizeMeasurements(cold.map((sample) => sample.readyMs)),
|
||||
warmReadyMs: summarizeMeasurements(warm.map((sample) => sample.readyMs)),
|
||||
coldWorkerSpawnMs: summarizeMeasurements(cold.map((sample) => sample.workerSpawnMs)),
|
||||
warmWorkerSpawnMs: summarizeMeasurements(warm.map((sample) => sample.workerSpawnMs)),
|
||||
coldRpcReadyMs: summarizeMeasurements(cold.map((sample) => sample.rpcReadyMs)),
|
||||
warmRpcReadyMs: summarizeMeasurements(warm.map((sample) => sample.rpcReadyMs)),
|
||||
rssKb: summarizeMeasurements([...cold, ...warm].flatMap((sample) => sample.rssKb == null ? [] : [sample.rssKb])),
|
||||
exitMs: summarizeMeasurements([...cold, ...warm].map((sample) => sample.stop.exitMs)),
|
||||
exitModes: [...new Set([...cold, ...warm].map((sample) => sample.stop.mode))],
|
||||
@@ -867,6 +879,7 @@ async function promptAndSettle(worker, message, images, timeoutMs) {
|
||||
providerFirstEventMs: firstProviderEvent
|
||||
? firstProviderEvent.receivedAt - agentStart.receivedAt
|
||||
: settled.receivedAt - agentStart.receivedAt,
|
||||
agentSettledMs: settled.receivedAt - accepted.receivedAt,
|
||||
startedAt: agentStart.receivedAt,
|
||||
settledAt: settled.receivedAt,
|
||||
stopReason: lastAssistantStopReason(messagesResponse),
|
||||
@@ -908,6 +921,20 @@ export async function runProviderQualification(runtime, scratchRoot, fixturePath
|
||||
throw new Error('Provider workers shared a session id');
|
||||
}
|
||||
|
||||
const [leftWarmTurn, rightWarmTurn] = await Promise.all([
|
||||
promptAndSettle(left, 'Reply with exactly PI_PROVIDER_LEFT_WARM_OK. Do not use tools.', undefined, timeoutMs),
|
||||
promptAndSettle(right, 'Reply with exactly PI_PROVIDER_RIGHT_WARM_OK. Do not use tools.', undefined, timeoutMs),
|
||||
]);
|
||||
if (!successfulReasons.has(leftWarmTurn.stopReason)
|
||||
|| !successfulReasons.has(rightWarmTurn.stopReason)) {
|
||||
throw new Error(
|
||||
`Warm provider turns did not succeed: left=${leftWarmTurn.stopReason}, right=${rightWarmTurn.stopReason}`,
|
||||
);
|
||||
}
|
||||
const warmOverlapMs = Math.min(leftWarmTurn.settledAt, rightWarmTurn.settledAt)
|
||||
- Math.max(leftWarmTurn.startedAt, rightWarmTurn.startedAt);
|
||||
if (warmOverlapMs <= 0) throw new Error('Warm provider turns did not overlap');
|
||||
|
||||
const abortStartIndex = left.events.length;
|
||||
const abortedPrompt = left.request({
|
||||
type: 'prompt',
|
||||
@@ -950,20 +977,49 @@ export async function runProviderQualification(runtime, scratchRoot, fixturePath
|
||||
apiKeyEnv: fixture.apiKeyEnv,
|
||||
imageInput: Boolean(imagePath),
|
||||
distinctSessionIds: true,
|
||||
cold: {
|
||||
promptAcceptedSamplesMs: [leftTurn.acceptedMs, rightTurn.acceptedMs].map(Math.round),
|
||||
promptAcceptedMs: summarizeMeasurements([leftTurn.acceptedMs, rightTurn.acceptedMs]),
|
||||
agentStartSamplesMs: [leftTurn.agentStartMs, rightTurn.agentStartMs].map(Math.round),
|
||||
agentStartMs: summarizeMeasurements([leftTurn.agentStartMs, rightTurn.agentStartMs]),
|
||||
providerFirstEventSamplesMs: [
|
||||
leftTurn.providerFirstEventMs,
|
||||
rightTurn.providerFirstEventMs,
|
||||
].map(Math.round),
|
||||
providerFirstEventMs: summarizeMeasurements([
|
||||
leftTurn.providerFirstEventMs,
|
||||
rightTurn.providerFirstEventMs,
|
||||
]),
|
||||
agentSettledSamplesMs: [leftTurn.agentSettledMs, rightTurn.agentSettledMs].map(Math.round),
|
||||
agentSettledMs: summarizeMeasurements([leftTurn.agentSettledMs, rightTurn.agentSettledMs]),
|
||||
},
|
||||
warm: {
|
||||
promptAcceptedSamplesMs: [leftWarmTurn.acceptedMs, rightWarmTurn.acceptedMs].map(Math.round),
|
||||
promptAcceptedMs: summarizeMeasurements([leftWarmTurn.acceptedMs, rightWarmTurn.acceptedMs]),
|
||||
agentStartSamplesMs: [leftWarmTurn.agentStartMs, rightWarmTurn.agentStartMs].map(Math.round),
|
||||
agentStartMs: summarizeMeasurements([leftWarmTurn.agentStartMs, rightWarmTurn.agentStartMs]),
|
||||
providerFirstEventSamplesMs: [
|
||||
leftWarmTurn.providerFirstEventMs,
|
||||
rightWarmTurn.providerFirstEventMs,
|
||||
].map(Math.round),
|
||||
providerFirstEventMs: summarizeMeasurements([
|
||||
leftWarmTurn.providerFirstEventMs,
|
||||
rightWarmTurn.providerFirstEventMs,
|
||||
]),
|
||||
agentSettledSamplesMs: [leftWarmTurn.agentSettledMs, rightWarmTurn.agentSettledMs].map(Math.round),
|
||||
agentSettledMs: summarizeMeasurements([
|
||||
leftWarmTurn.agentSettledMs,
|
||||
rightWarmTurn.agentSettledMs,
|
||||
]),
|
||||
},
|
||||
overlapMs: Math.round(overlapMs),
|
||||
promptAcceptedSamplesMs: [leftTurn.acceptedMs, rightTurn.acceptedMs].map(Math.round),
|
||||
promptAcceptedMs: summarizeMeasurements([leftTurn.acceptedMs, rightTurn.acceptedMs]),
|
||||
agentStartSamplesMs: [leftTurn.agentStartMs, rightTurn.agentStartMs].map(Math.round),
|
||||
agentStartMs: summarizeMeasurements([leftTurn.agentStartMs, rightTurn.agentStartMs]),
|
||||
providerFirstEventSamplesMs: [
|
||||
leftTurn.providerFirstEventMs,
|
||||
rightTurn.providerFirstEventMs,
|
||||
].map(Math.round),
|
||||
providerFirstEventMs: summarizeMeasurements([
|
||||
leftTurn.providerFirstEventMs,
|
||||
rightTurn.providerFirstEventMs,
|
||||
]),
|
||||
stopReasons: [leftTurn.stopReason, rightTurn.stopReason],
|
||||
warmOverlapMs: Math.round(warmOverlapMs),
|
||||
stopReasons: [
|
||||
leftTurn.stopReason,
|
||||
rightTurn.stopReason,
|
||||
leftWarmTurn.stopReason,
|
||||
rightWarmTurn.stopReason,
|
||||
],
|
||||
abortIsolation: {
|
||||
abortedStopReason,
|
||||
unaffectedStopReason: unaffected.stopReason,
|
||||
|
||||
@@ -9,6 +9,7 @@ import { pathToFileURL } from 'node:url';
|
||||
import { verifyPiProductArtifact } from './lib/pi-product-artifact.mjs';
|
||||
import { runLocalProviderContracts } from './probe-pi-provider-contracts.mjs';
|
||||
import { runProbe, summarizeMeasurements } from './probe-pi-runtime.mjs';
|
||||
import { runPackagedProductProof } from './run-pi-subagent-packaged-smoke.mjs';
|
||||
import { parsePiArtifactVerifierArgs } from './verify-pi-product-artifact.mjs';
|
||||
|
||||
function runCommand(executable, args, options = {}) {
|
||||
@@ -124,6 +125,27 @@ async function runComposerSamples(projectRoot, scratchRoot, samples) {
|
||||
return summarizeMeasurements(values);
|
||||
}
|
||||
|
||||
async function runProductProofSamples(artifact, projectRoot, samples) {
|
||||
const reports = [];
|
||||
for (let index = 0; index < samples; index += 1) {
|
||||
reports.push(await runPackagedProductProof({
|
||||
projectRoot,
|
||||
runtimeRoot: artifact.artifact.runtimeRoot,
|
||||
electronExecutable: artifact.artifact.executable,
|
||||
reportPath: undefined,
|
||||
}));
|
||||
}
|
||||
return reports;
|
||||
}
|
||||
|
||||
function summarizeManagedMilestone(reports, cold, milestone) {
|
||||
return summarizeMeasurements(reports.flatMap(({ extension }) => (
|
||||
extension.managedWorkerMilestones
|
||||
.filter((event) => event.cold === cold && event.milestone === milestone)
|
||||
.map(({ durationMs }) => durationMs)
|
||||
)));
|
||||
}
|
||||
|
||||
export async function runPiReleasePerformance(options) {
|
||||
const scratchRoot = await mkdtemp(join(tmpdir(), 'makelore-pi-release-performance-'));
|
||||
try {
|
||||
@@ -153,28 +175,52 @@ export async function runPiReleasePerformance(options) {
|
||||
scratchRoot,
|
||||
options.samples,
|
||||
);
|
||||
const promptAcceptedSamples = providerContracts.protocols.flatMap(
|
||||
({ qualification }) => qualification.promptAcceptedSamplesMs,
|
||||
const productProofs = await runProductProofSamples(
|
||||
artifact,
|
||||
options.projectRoot,
|
||||
options.samples,
|
||||
);
|
||||
const agentStartSamples = providerContracts.protocols.flatMap(
|
||||
({ qualification }) => qualification.agentStartSamplesMs,
|
||||
const providerMetric = (temperature, field) => summarizeMeasurements(
|
||||
providerContracts.protocols.flatMap(
|
||||
({ qualification }) => qualification[temperature][field],
|
||||
),
|
||||
);
|
||||
const providerFirstEventSamples = providerContracts.protocols.flatMap(
|
||||
({ qualification }) => qualification.providerFirstEventSamplesMs,
|
||||
const managedMilestones = {
|
||||
definition: 'Actual createPiManagedWorkerOpener execution from final app.asar Main against final pi-runtime; cold creates a session, warm reopens it.',
|
||||
cold: {
|
||||
resourcesReadyMs: summarizeManagedMilestone(productProofs, true, 'resources.ready'),
|
||||
workerSpawnMs: summarizeManagedMilestone(productProofs, true, 'worker.spawn'),
|
||||
rpcReadyMs: summarizeManagedMilestone(productProofs, true, 'rpc.ready'),
|
||||
sessionOpenMs: summarizeManagedMilestone(productProofs, true, 'session.open'),
|
||||
promptAcceptedMs: providerMetric('cold', 'promptAcceptedSamplesMs'),
|
||||
agentStartMs: providerMetric('cold', 'agentStartSamplesMs'),
|
||||
providerFirstEventMs: providerMetric('cold', 'providerFirstEventSamplesMs'),
|
||||
agentSettledMs: providerMetric('cold', 'agentSettledSamplesMs'),
|
||||
},
|
||||
warm: {
|
||||
resourcesReadyMs: summarizeManagedMilestone(productProofs, false, 'resources.ready'),
|
||||
workerSpawnMs: summarizeManagedMilestone(productProofs, false, 'worker.spawn'),
|
||||
rpcReadyMs: summarizeManagedMilestone(productProofs, false, 'rpc.ready'),
|
||||
sessionOpenMs: summarizeManagedMilestone(productProofs, false, 'session.open'),
|
||||
promptAcceptedMs: providerMetric('warm', 'promptAcceptedSamplesMs'),
|
||||
agentStartMs: providerMetric('warm', 'agentStartSamplesMs'),
|
||||
providerFirstEventMs: providerMetric('warm', 'providerFirstEventSamplesMs'),
|
||||
agentSettledMs: providerMetric('warm', 'agentSettledSamplesMs'),
|
||||
},
|
||||
};
|
||||
const pressureUiInteractiveMs = summarizeMeasurements(
|
||||
productProofs.map(({ pressure }) => pressure.ui.durationMs),
|
||||
);
|
||||
const promptAcceptedMs = summarizeMeasurements(promptAcceptedSamples);
|
||||
const agentStartMs = summarizeMeasurements(agentStartSamples);
|
||||
const providerFirstEventMs = summarizeMeasurements(providerFirstEventSamples);
|
||||
const rendererFirstCommitMs = fragments.pressure.mainToReactMs;
|
||||
const budgets = {
|
||||
projectMetadata: fragments.metadata.projectMetadataMs.p95 <= 1_000,
|
||||
agentMetadata: fragments.metadata.agentMetadataMs.p95 <= 500,
|
||||
conversationMetadata: fragments.metadata.conversationMetadataMs.p95 <= 500,
|
||||
composerInteractive: composerInteractiveMs.p95 <= 500,
|
||||
warmRpcReady: runtime.performance.warmReadyMs.p95 <= 1_500,
|
||||
coldRpcReady: runtime.performance.coldReadyMs.p95 <= 3_000,
|
||||
warmPromptAccepted: promptAcceptedMs.p95 <= 250,
|
||||
coldPromptAccepted: promptAcceptedMs.p95 <= 3_000,
|
||||
warmRpcReady: managedMilestones.warm.rpcReadyMs.p95 <= 1_500,
|
||||
coldRpcReady: managedMilestones.cold.rpcReadyMs.p95 <= 3_000,
|
||||
warmPromptAccepted: managedMilestones.warm.promptAcceptedMs.p95 <= 250,
|
||||
coldPromptAccepted: managedMilestones.cold.promptAcceptedMs.p95 <= 3_000,
|
||||
rendererFirstCommit: rendererFirstCommitMs.p95 <= 50,
|
||||
gracefulShutdown: runtime.performance.exitMs.p95 <= 3_000,
|
||||
};
|
||||
@@ -184,12 +230,23 @@ export async function runPiReleasePerformance(options) {
|
||||
const git = await gitEvidence(options.projectRoot);
|
||||
const scenarios = [
|
||||
{ id: 1, name: 'fresh userData metadata and Composer', evidence: ['metadata fragments', 'Electron Composer samples'], result: 'pass' },
|
||||
{ id: 2, name: 'first prompt milestone split', evidence: ['packaged rpc.ready', 'provider-shaped accepted/agent-start/first-event', 'Renderer commit'], result: 'pass' },
|
||||
{ id: 2, name: 'first prompt milestone split', evidence: ['final-ASAR resources.ready/worker.spawn/rpc.ready/session.open', 'separate cold prompt.accepted/agent.start/provider.first_event/agent.settled', 'Renderer commit'], result: 'pass' },
|
||||
{ id: 3, name: 'warm Conversation restore', evidence: ['packaged session stop/reopen/get_entries', 'warm rpc.ready samples'], result: 'pass' },
|
||||
{ id: 4, name: 'two projects provider-shaped overlap and abort isolation', evidence: providerContracts.protocols.map(({ protocol }) => protocol), result: 'pass' },
|
||||
{ id: 5, name: 'same-project read-only concurrency', evidence: ['pi-worker-pool.test.ts', 'pi-subagent.test.ts'], result: 'pass' },
|
||||
{ id: 6, name: 'same-project mutation lease serialization and cancellation', evidence: ['pi-write-lease.test.ts', 'pi-worker-pool.test.ts'], result: 'pass' },
|
||||
{ id: 7, name: 'four-child subagent pressure and global cap', evidence: ['pi-subagent.test.ts', 'pi-worker-pool.test.ts'], result: 'pass' },
|
||||
{
|
||||
id: 7,
|
||||
name: '4 parent + 4 child final-product pressure, interactive UI, and cleanup',
|
||||
evidence: {
|
||||
samples: productProofs.length,
|
||||
packagedMain: productProofs.every(({ packagedMain }) => packagedMain.appPathUsesAsar),
|
||||
active: productProofs[0]?.pressure.active,
|
||||
uiInteractiveMs: pressureUiInteractiveMs,
|
||||
released: productProofs[0]?.pressure.released,
|
||||
},
|
||||
result: 'pass',
|
||||
},
|
||||
{ id: 8, name: '100 KB mixed blocks and batch recovery', evidence: ['coding-chat-pressure.test.tsx', 'pi-session-projector.test.ts', 'pi-event-projector.test.ts'], result: 'pass' },
|
||||
{ id: 9, name: 'large image attachment references', evidence: ['repeated Electron large-image attachment E2E', 'four-protocol packaged image requests'], result: 'pass' },
|
||||
{ id: 10, name: 'worker crash, stream recovery, and shutdown cleanup', evidence: ['pi-worker-pool.test.ts', 'pi-session-projector.test.ts', 'packaged exit samples'], result: 'pass' },
|
||||
@@ -203,15 +260,18 @@ export async function runPiReleasePerformance(options) {
|
||||
localOverhead: {
|
||||
...fragments.metadata,
|
||||
composerInteractiveMs,
|
||||
managedMilestones,
|
||||
coldRpcReadyMs: runtime.performance.coldReadyMs,
|
||||
warmRpcReadyMs: runtime.performance.warmReadyMs,
|
||||
promptAcceptedMs,
|
||||
agentStartMs,
|
||||
pressureUiInteractiveMs,
|
||||
rendererFirstCommitMs,
|
||||
exitMs: runtime.performance.exitMs,
|
||||
},
|
||||
controlledProviderShaped: {
|
||||
firstEventMs: providerFirstEventMs,
|
||||
firstEventMs: {
|
||||
cold: managedMilestones.cold.providerFirstEventMs,
|
||||
warm: managedMilestones.warm.providerFirstEventMs,
|
||||
},
|
||||
protocols: providerContracts.protocols.map(({ protocol, qualification }) => ({
|
||||
protocol,
|
||||
overlapMs: qualification.overlapMs,
|
||||
@@ -231,7 +291,14 @@ export async function runPiReleasePerformance(options) {
|
||||
reactCommits: fragments.pressure.reactCommits,
|
||||
},
|
||||
scenarios,
|
||||
commands: fragments.commands,
|
||||
commands: {
|
||||
...fragments.commands,
|
||||
finalPackagedProductProof: {
|
||||
samples: productProofs.length,
|
||||
result: 'pass',
|
||||
executedFromFinalAsarMain: true,
|
||||
},
|
||||
},
|
||||
budgets,
|
||||
result: 'pass',
|
||||
crossPlatformReleaseReady: false,
|
||||
|
||||
@@ -1,103 +1,218 @@
|
||||
import { spawn } from 'node:child_process';
|
||||
import { mkdtemp, rm } from 'node:fs/promises';
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { _electron as electron } from '@playwright/test';
|
||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { createServer } from 'node:net';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, resolve } from 'node:path';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { performance } from 'node:perf_hooks';
|
||||
|
||||
import { bundlePiRuntime } from './bundle-pi-runtime.mjs';
|
||||
import { defaultPiBundleTarget } from './lib/pi-runtime-bundle.mjs';
|
||||
import { defaultProductExecutable } from './lib/pi-product-artifact.mjs';
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = { runtimeRoot: undefined, electronExecutable: undefined };
|
||||
function parseArgs(argv, projectRoot = process.cwd()) {
|
||||
const options = {
|
||||
projectRoot: resolve(projectRoot),
|
||||
runtimeRoot: undefined,
|
||||
electronExecutable: undefined,
|
||||
reportPath: undefined,
|
||||
};
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const argument = argv[index];
|
||||
const value = argv[index + 1];
|
||||
if (!value || value.startsWith('--')) throw new Error(`${argument} requires a value`);
|
||||
if (argument === '--runtime-root') options.runtimeRoot = resolve(value);
|
||||
else if (argument === '--electron-executable') options.electronExecutable = resolve(value);
|
||||
else if (argument === '--report') options.reportPath = resolve(value);
|
||||
else throw new Error(`Unknown argument: ${argument}`);
|
||||
index += 1;
|
||||
}
|
||||
if (Boolean(options.runtimeRoot) !== Boolean(options.electronExecutable)) {
|
||||
throw new Error('--runtime-root and --electron-executable must be provided together');
|
||||
}
|
||||
options.electronExecutable ??= defaultProductExecutable(options.projectRoot);
|
||||
return options;
|
||||
}
|
||||
|
||||
function runVitest(runtimeRoot, electronExecutable) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const child = spawn(process.execPath, [
|
||||
resolve('node_modules/vitest/vitest.mjs'),
|
||||
'run',
|
||||
'tests/unit/pi-worker-process-real.test.ts',
|
||||
], {
|
||||
cwd: process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
MAKELORE_PI_STAGED_RUNTIME_ROOT: runtimeRoot,
|
||||
...(electronExecutable ? { MAKELORE_PI_ELECTRON_EXECUTABLE: electronExecutable } : {}),
|
||||
},
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
});
|
||||
child.once('error', reject);
|
||||
child.once('exit', (code, signal) => {
|
||||
if (code === 0) resolvePromise();
|
||||
else {
|
||||
reject(new Error(
|
||||
`Packaged subagent smoke failed with code ${code ?? 'null'} signal ${signal ?? 'none'}`,
|
||||
));
|
||||
async function allocatePort() {
|
||||
return await new Promise((resolvePort, reject) => {
|
||||
const server = createServer();
|
||||
server.once('error', reject);
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
const address = server.address();
|
||||
if (!address || typeof address === 'string') {
|
||||
server.close(() => reject(new Error('Failed to allocate PI proof Host API port')));
|
||||
return;
|
||||
}
|
||||
server.close((error) => error ? reject(error) : resolvePort(address.port));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function runElectronProductTools(electronExecutable) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const child = spawn(process.execPath, [
|
||||
resolve('scripts/run-electron-vitest.mjs'),
|
||||
'tests/unit/pi-extension-bundle.test.ts',
|
||||
], {
|
||||
cwd: process.cwd(),
|
||||
env: {
|
||||
...process.env,
|
||||
...(electronExecutable ? {
|
||||
MAKELORE_ELECTRON_EXECUTABLE: electronExecutable,
|
||||
MAKELORE_PI_ELECTRON_EXECUTABLE: electronExecutable,
|
||||
} : {}),
|
||||
},
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
});
|
||||
child.once('error', reject);
|
||||
child.once('exit', (code, signal) => {
|
||||
if (code === 0) resolvePromise();
|
||||
else {
|
||||
reject(new Error(
|
||||
`Packaged product-tools Electron smoke failed with code ${code ?? 'null'} signal ${signal ?? 'none'}`,
|
||||
));
|
||||
}
|
||||
});
|
||||
});
|
||||
function assertPackagedMain(result) {
|
||||
if (result?.packagedMain?.isPackaged !== true || result?.packagedMain?.appPathUsesAsar !== true) {
|
||||
throw new Error(`PI proof did not execute from packaged app.asar Main: ${JSON.stringify(result?.packagedMain)}`);
|
||||
}
|
||||
}
|
||||
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const outputRoot = options.runtimeRoot
|
||||
? null
|
||||
: await mkdtemp(join(tmpdir(), 'makelore-pi-subagent-package-'));
|
||||
try {
|
||||
let runtimeRoot = options.runtimeRoot;
|
||||
if (!runtimeRoot) {
|
||||
const [bundle] = await bundlePiRuntime({
|
||||
outputRoot,
|
||||
targets: [defaultPiBundleTarget()],
|
||||
});
|
||||
if (!bundle) throw new Error('Pi runtime bundler returned no staged runtime');
|
||||
runtimeRoot = bundle.destination;
|
||||
}
|
||||
await runVitest(runtimeRoot, options.electronExecutable);
|
||||
await runElectronProductTools(options.electronExecutable);
|
||||
} finally {
|
||||
if (outputRoot) {
|
||||
await rm(outputRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
||||
function assertActivePressure(pressure) {
|
||||
const expected = {
|
||||
parentWorkers: 4,
|
||||
childWorkers: 4,
|
||||
liveProcesses: 8,
|
||||
processBudget: 8,
|
||||
childPermits: 4,
|
||||
dispatches: 4,
|
||||
writeLeases: 4,
|
||||
};
|
||||
const actual = {
|
||||
parentWorkers: pressure?.parentWorkers,
|
||||
childWorkers: pressure?.childWorkers,
|
||||
liveProcesses: pressure?.liveProcessIds?.length,
|
||||
processBudget: pressure?.processBudget?.active,
|
||||
childPermits: pressure?.childPermits?.active,
|
||||
dispatches: pressure?.dispatches?.active,
|
||||
writeLeases: pressure?.writeLeases?.active,
|
||||
};
|
||||
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
||||
throw new Error(`PI pressure did not reach 4 parent + 4 child state: ${JSON.stringify(actual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertReleasedPressure(pressure) {
|
||||
const counts = [
|
||||
pressure?.parentWorkers,
|
||||
pressure?.childWorkers,
|
||||
pressure?.liveProcessIds?.length,
|
||||
pressure?.processBudget?.active,
|
||||
pressure?.processBudget?.waiting,
|
||||
pressure?.childPermits?.active,
|
||||
pressure?.childPermits?.waiting,
|
||||
pressure?.dispatches?.active,
|
||||
pressure?.dispatches?.parents,
|
||||
pressure?.writeLeases?.active,
|
||||
pressure?.writeLeases?.waiting,
|
||||
];
|
||||
if (counts.some((value) => value !== 0)) {
|
||||
throw new Error(`PI pressure resources were not fully released: ${JSON.stringify(pressure)}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function evaluateProof(electronApplication, action) {
|
||||
return await electronApplication.evaluate(async (_electron, requestedAction) => {
|
||||
const proof = globalThis.__niancodeRunPiReleaseProofE2E;
|
||||
if (typeof proof !== 'function') throw new Error('Packaged Main has no PI release proof entry');
|
||||
return await proof(requestedAction);
|
||||
}, action);
|
||||
}
|
||||
|
||||
async function closeApplication(electronApplication) {
|
||||
await Promise.race([
|
||||
electronApplication.close().catch(() => undefined),
|
||||
new Promise((resolveTimeout) => setTimeout(resolveTimeout, 5_000)),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function runPackagedProductProof(options) {
|
||||
const scratchRoot = await mkdtemp(join(tmpdir(), 'makelore-pi-final-product-proof-'));
|
||||
const homeDir = join(scratchRoot, 'home');
|
||||
const userDataDir = join(scratchRoot, 'user-data');
|
||||
await Promise.all([
|
||||
mkdir(join(homeDir, '.config'), { recursive: true }),
|
||||
mkdir(join(homeDir, 'AppData', 'Local'), { recursive: true }),
|
||||
mkdir(join(homeDir, 'AppData', 'Roaming'), { recursive: true }),
|
||||
mkdir(userDataDir, { recursive: true }),
|
||||
]);
|
||||
const hostApiPort = await allocatePort();
|
||||
let electronApplication;
|
||||
let pressureActive = false;
|
||||
try {
|
||||
electronApplication = await electron.launch({
|
||||
executablePath: options.electronExecutable,
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: homeDir,
|
||||
USERPROFILE: homeDir,
|
||||
APPDATA: join(homeDir, 'AppData', 'Roaming'),
|
||||
LOCALAPPDATA: join(homeDir, 'AppData', 'Local'),
|
||||
XDG_CONFIG_HOME: join(homeDir, '.config'),
|
||||
NIANCODE_E2E: '1',
|
||||
NIANCODE_E2E_SKIP_SETUP: '1',
|
||||
NIANCODE_USER_DATA_DIR: userDataDir,
|
||||
NIANCODE_PORT_NIANCODE_HOST_API: String(hostApiPort),
|
||||
...(process.platform === 'linux' ? { ELECTRON_DISABLE_SANDBOX: '1' } : {}),
|
||||
},
|
||||
timeout: 90_000,
|
||||
});
|
||||
const page = await electronApplication.firstWindow();
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
|
||||
const extension = await evaluateProof(electronApplication, 'extension');
|
||||
assertPackagedMain(extension);
|
||||
if (extension.extension?.subagentStatus !== 'complete'
|
||||
|| extension.extension?.childToolNames?.length !== 0
|
||||
|| !extension.extension?.parentToolNames?.includes('subagent')
|
||||
|| extension.extension?.managedWorkerMilestones?.filter(({ cold }) => cold).length !== 4
|
||||
|| extension.extension?.managedWorkerMilestones?.filter(({ cold }) => !cold).length !== 4) {
|
||||
throw new Error(`Final ASAR extension/subagent proof failed: ${JSON.stringify(extension.extension)}`);
|
||||
}
|
||||
|
||||
const pressureStart = await evaluateProof(electronApplication, 'pressure.start');
|
||||
pressureActive = true;
|
||||
assertPackagedMain(pressureStart);
|
||||
assertActivePressure(pressureStart.pressure);
|
||||
|
||||
const uiStartedAt = performance.now();
|
||||
await page.getByTestId('ai-module-option-programming').click();
|
||||
await page.getByTestId('main-layout').waitFor({ state: 'visible', timeout: 10_000 });
|
||||
const uiInteractiveMs = Math.round(performance.now() - uiStartedAt);
|
||||
|
||||
const pressureFinish = await evaluateProof(electronApplication, 'pressure.finish');
|
||||
pressureActive = false;
|
||||
assertPackagedMain(pressureFinish);
|
||||
assertReleasedPressure(pressureFinish.pressure);
|
||||
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
generatedAt: new Date().toISOString(),
|
||||
executable: options.electronExecutable,
|
||||
runtimeRoot: options.runtimeRoot ?? null,
|
||||
packagedMain: extension.packagedMain,
|
||||
extension: extension.extension,
|
||||
pressure: {
|
||||
processKind: 'final-product-executable-with-ELECTRON_RUN_AS_NODE',
|
||||
active: pressureStart.pressure,
|
||||
ui: {
|
||||
action: 'select Makelore Code and render main layout',
|
||||
interactive: true,
|
||||
durationMs: uiInteractiveMs,
|
||||
},
|
||||
released: pressureFinish.pressure,
|
||||
},
|
||||
result: 'pass',
|
||||
};
|
||||
if (options.reportPath) {
|
||||
await mkdir(dirname(options.reportPath), { recursive: true });
|
||||
await writeFile(options.reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
||||
}
|
||||
return report;
|
||||
} finally {
|
||||
if (electronApplication && pressureActive) {
|
||||
await evaluateProof(electronApplication, 'pressure.finish').catch(() => undefined);
|
||||
}
|
||||
if (electronApplication) await closeApplication(electronApplication);
|
||||
await rm(scratchRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 });
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const report = await runPackagedProductProof(options);
|
||||
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
||||
}
|
||||
|
||||
const isMain = process.argv[1]
|
||||
&& pathToFileURL(resolve(process.argv[1])).href === import.meta.url;
|
||||
if (isMain) {
|
||||
main().catch((error) => {
|
||||
process.stderr.write(`${error.stack ?? error.message}\n`);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { arch, platform } from 'node:os';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
@@ -10,25 +9,14 @@ import { verifyPiProductArtifact } from './lib/pi-product-artifact.mjs';
|
||||
import { runLocalProviderContracts } from './probe-pi-provider-contracts.mjs';
|
||||
import { parsePiArtifactVerifierArgs } from './verify-pi-product-artifact.mjs';
|
||||
import { runProbe } from './probe-pi-runtime.mjs';
|
||||
import { runPackagedProductProof } from './run-pi-subagent-packaged-smoke.mjs';
|
||||
|
||||
function runExtensionSmoke(projectRoot, artifact) {
|
||||
return new Promise((resolvePromise, reject) => {
|
||||
const child = spawn(process.execPath, [
|
||||
resolve(projectRoot, 'scripts', 'run-pi-subagent-packaged-smoke.mjs'),
|
||||
'--runtime-root', artifact.artifact.runtimeRoot,
|
||||
'--electron-executable', artifact.artifact.executable,
|
||||
], {
|
||||
cwd: projectRoot,
|
||||
stdio: 'inherit',
|
||||
windowsHide: true,
|
||||
});
|
||||
child.once('error', reject);
|
||||
child.once('exit', (code, signal) => {
|
||||
if (code === 0) resolvePromise({ result: 'pass' });
|
||||
else reject(new Error(
|
||||
`Final product extension/subagent smoke failed with code ${code ?? 'null'} signal ${signal ?? 'none'}`,
|
||||
));
|
||||
});
|
||||
async function runExtensionSmoke(projectRoot, artifact) {
|
||||
return await runPackagedProductProof({
|
||||
projectRoot,
|
||||
runtimeRoot: artifact.artifact.runtimeRoot,
|
||||
electronExecutable: artifact.artifact.executable,
|
||||
reportPath: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,8 @@ describe('managed Pi worker opener', () => {
|
||||
'resources.ready', 'worker.spawn', 'rpc.ready', 'session.open',
|
||||
'resources.ready', 'worker.spawn', 'rpc.ready', 'session.open',
|
||||
]);
|
||||
expect(telemetry.slice(0, 4).every(({ cold }) => cold)).toBe(true);
|
||||
expect(telemetry.slice(4).every(({ cold }) => !cold)).toBe(true);
|
||||
expect(JSON.stringify(telemetry)).not.toContain(created.id);
|
||||
expect(JSON.stringify(telemetry)).not.toContain('PRIVATE MANAGED PROMPT');
|
||||
expect(JSON.stringify(telemetry)).not.toContain('provider-secret-value');
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @vitest-environment node
|
||||
|
||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { createRequire } from 'node:module';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
assertNodeEngineCompatible,
|
||||
classifyOpenCodeResourcePaths,
|
||||
collectAbsoluteManifestValues,
|
||||
collectForbiddenAsarPaths,
|
||||
collectForbiddenResourcePaths,
|
||||
defaultProductExecutable,
|
||||
validatePiArtifactMetadata,
|
||||
@@ -17,6 +19,7 @@ import { parsePiArtifactVerifierArgs } from '../../scripts/verify-pi-product-art
|
||||
|
||||
const roots: string[] = [];
|
||||
const PI_PACKAGE = '@earendil-works/pi-coding-agent';
|
||||
const { createPackage } = createRequire(import.meta.url)('@electron/asar');
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
@@ -85,14 +88,46 @@ describe('final Pi product artifact verification', () => {
|
||||
expect(classifyOpenCodeResourcePaths([
|
||||
'app.asar.unpacked/resources/opencode-runtime',
|
||||
'pi-runtime/node_modules/@earendil-works/pi-ai/dist/providers/opencode.js',
|
||||
'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/opencode-codex-responses.js',
|
||||
])).toEqual({
|
||||
productOwned: ['app.asar.unpacked/resources/opencode-runtime'],
|
||||
upstreamPiProvider: [
|
||||
'pi-runtime/node_modules/@earendil-works/pi-ai/dist/providers/opencode.js',
|
||||
'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/opencode-codex-responses.js',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('enumerates OpenCode-named paths inside app.asar instead of scanning only physical resources', async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-asar-paths-'));
|
||||
roots.push(root);
|
||||
const source = path.join(root, 'source');
|
||||
await mkdir(path.join(source, 'node_modules', '@earendil-works', 'pi-ai', 'dist', 'providers'), {
|
||||
recursive: true,
|
||||
});
|
||||
await mkdir(path.join(source, 'resources', 'opencode-runtime'), { recursive: true });
|
||||
await writeFile(
|
||||
path.join(source, 'node_modules', '@earendil-works', 'pi-ai', 'dist', 'providers', 'opencode.js'),
|
||||
'export {};',
|
||||
);
|
||||
await writeFile(path.join(source, 'resources', 'opencode-runtime', 'legacy.js'), 'export {};');
|
||||
const archive = path.join(root, 'app.asar');
|
||||
await createPackage(source, archive);
|
||||
|
||||
const inventory = collectForbiddenAsarPaths(archive);
|
||||
expect(inventory.entryCount).toBeGreaterThan(2);
|
||||
expect(inventory.matches).toEqual(expect.arrayContaining([
|
||||
'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/opencode.js',
|
||||
'app.asar/resources/opencode-runtime',
|
||||
]));
|
||||
expect(classifyOpenCodeResourcePaths(inventory.matches)).toMatchObject({
|
||||
productOwned: expect.arrayContaining(['app.asar/resources/opencode-runtime']),
|
||||
upstreamPiProvider: expect.arrayContaining([
|
||||
'app.asar/node_modules/@earendil-works/pi-ai/dist/providers/opencode.js',
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it('uses final unpacked-product paths and strictly parses verifier options', () => {
|
||||
expect(defaultProductExecutable('/repo', 'linux'))
|
||||
.toBe(path.resolve('/repo', 'release', 'linux-unpacked', 'niancode'));
|
||||
|
||||
@@ -86,6 +86,12 @@ describe('Pi subagent scheduler', () => {
|
||||
await expect.poll(() => running).toBe(4);
|
||||
expect(maxRunning).toBe(4);
|
||||
expect(processBudget.activeCount).toBe(4);
|
||||
expect(scheduler.getDiagnostics()).toEqual({
|
||||
activeChildPermits: 4,
|
||||
waitingChildPermits: 4,
|
||||
activeDispatches: 2,
|
||||
activeParents: 2,
|
||||
});
|
||||
gate.resolve();
|
||||
const [leftResult, rightResult] = await Promise.all([left, right]);
|
||||
expect(leftResult.details.tasks).toHaveLength(4);
|
||||
@@ -93,6 +99,12 @@ describe('Pi subagent scheduler', () => {
|
||||
expect(opened).toHaveLength(8);
|
||||
expect(maxRunning).toBe(4);
|
||||
expect(processBudget.activeCount).toBe(0);
|
||||
expect(scheduler.getDiagnostics()).toEqual({
|
||||
activeChildPermits: 0,
|
||||
waitingChildPermits: 0,
|
||||
activeDispatches: 0,
|
||||
activeParents: 0,
|
||||
});
|
||||
await scheduler.close();
|
||||
});
|
||||
|
||||
|
||||
@@ -714,7 +714,7 @@ describe('Pi worker pool', () => {
|
||||
expect(pool.getState('conversation-b')).toMatchObject({ state: 'ready', generation: 1 });
|
||||
});
|
||||
|
||||
it('records privacy-safe queue wait and RPC prompt acceptance spans', async () => {
|
||||
it('records privacy-safe queue, acceptance, and authoritative settled spans', async () => {
|
||||
let now = 0;
|
||||
const telemetry: PiRuntimeTelemetryEvent[] = [];
|
||||
const workers = new Map<string, FakeWorker>();
|
||||
@@ -757,10 +757,14 @@ describe('Pi worker pool', () => {
|
||||
|
||||
expect(telemetry.map(({ milestone }) => milestone)).toEqual([
|
||||
'prompt.accepted',
|
||||
'agent.settled',
|
||||
'worker.queue_wait',
|
||||
'prompt.accepted',
|
||||
]);
|
||||
expect(telemetry[1]).toMatchObject({ durationMs: 20, workerGeneration: 1, cold: true });
|
||||
expect(telemetry[1]).toMatchObject({
|
||||
milestone: 'agent.settled', durationMs: 20, workerGeneration: 1, cold: true,
|
||||
});
|
||||
expect(telemetry[2]).toMatchObject({ durationMs: 20, workerGeneration: 1, cold: true });
|
||||
const serialized = JSON.stringify(telemetry);
|
||||
expect(serialized).not.toContain('private first prompt');
|
||||
expect(serialized).not.toContain('private second prompt');
|
||||
|
||||
Reference in New Issue
Block a user