feat(runner): qualify native Codex platforms and cache hosted builds

Extract the tested runtime and installed-package prerequisite without changing agent creation defaults or experimental gates.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-12 15:15:01 -05:00
1 parent 7d9b93aee2
commit ddcd746eb6
40 files changed
+3474 -565

No files matched your search

+97 -2
View File
@@ -1,11 +1,11 @@
import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdtemp, mkdir, realpath, rm, symlink, writeFile } from "node:fs/promises";
import { chmod, lstat, mkdtemp, mkdir, readFile, realpath, rm, symlink, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { afterEach, expect, it } from "vitest";
import { runnerMatrix } from "./catalog.js";
import { assertInstalledCliSelection, installedCliKeys, verifyInstalledCli } from "./installed-cli.js";
import { assertInstalledCliSelection, installedCliKeys, probeInstalledCodexStartup, verifyInstalledCli } from "./installed-cli.js";
import { buildPaperclipServerEnvironment, buildRunnerE2EProcessEnvironment } from "./harness-env.js";
const cells = runnerMatrix.filter(e => e.profile.id === "runner-acpx-pi");
const roots: string[] = [];
@@ -25,6 +25,20 @@ it("admits all26 current Pi cases through pinned installed public CLI/server wit
const { env, server } = await fixture(); expect(cells).toHaveLength(26);
for (const cell of cells) expect(await verifyInstalledCli(env, [cell])).toMatchObject({ serverRoot: server, defaultRuntimeResolution: true, qualificationOverride: false });
});
it("admits existing Codex onboarding, useful-file revision and restart journeys without broadening other cells", async () => {
const { env, server } = await fixture();
for (const id of ["first-task.runner-codex.local.task-card-accept", "everyday-workflows.runner-codex.local.build-revise", "agent-chat.runner-codex.local.continuity-restart"]) {
const cell = runnerMatrix.find(e => e.id === id)!;
expect(cell).toBeDefined();
expect(await verifyInstalledCli(env, [cell])).toMatchObject({ serverRoot: server, defaultRuntimeResolution: true, qualificationOverride: false });
for (const key of ["PAPERCLIP_RUNNER_BINARY", "NODE_PATH"]) expect(() => assertInstalledCliSelection({ ...env, [key]: "foreign" }, [cell])).toThrow("forbids");
}
for (const id of ["first-task.legacy-codex.local.task-card-accept", "first-task.runner-codex.local.interview-first-response", "everyday-workflows.runner-codex.daytona.build-revise"]) {
const cell = runnerMatrix.find(e => e.id === id)!;
expect(cell).toBeDefined();
expect(() => assertInstalledCliSelection(env, [cell])).toThrow("explicit supported Pi or Codex");
}
});
it("qualified providers drop ambient admission while pending Copilot retains its exact gate", () => {
for (const cell of cells) expect(buildRunnerE2EProcessEnvironment({ PAPERCLIP_RUNNER_ACPX_QUALIFICATION: "ambient" }, [cell]).PAPERCLIP_RUNNER_ACPX_QUALIFICATION).toBeUndefined();
for (const agent of ["copilot"]) {
@@ -92,3 +106,84 @@ it("startup-only accepts only explicit uncredentialed local hello and zero retri
expect(()=>assertInstalledStartupOnly({...f.env,KIMI_MODEL_API_KEY:"dummy-not-a-real-key"},[cell],options)).toThrow("credential inputs");
expect(()=>assertInstalledStartupOnly({},[cell],options)).toThrow("startup-only");
});
async function startupFixture(mode: "normal" | "wrong-model" | "ambient-codex" | "startup-and-cleanup-failed" | "cleanup-failed" = "normal") {
const f = await fixture();
const vendor = join(f.server, "dist/vendor/paperclip-runner");
const daemon = join(vendor, "bin", `${process.platform}-${process.arch}`, "paperclip-runnerd");
const codex = join(f.root, "node_modules/@openai/codex/bin/codex");
const observations = join(f.root, "startup-observations.json");
await mkdir(join(vendor, "bin", `${process.platform}-${process.arch}`), { recursive: true });
await mkdir(join(f.server, "dist/services/native-runtime"), { recursive: true });
await mkdir(join(f.root, "node_modules/@openai/codex/bin"), { recursive: true });
await writeFile(daemon, "fixture-daemon");
await writeFile(codex, "#!/bin/sh\nprintf 'codex-cli 0.149.5\\n'\n");
await chmod(codex, 0o755);
await writeFile(join(f.server, "dist/services/native-runtime/native-codex-runner.js"), `export function resolvePaperclipRunnerBinary() { return ${JSON.stringify(daemon)}; }`);
await writeFile(join(vendor, "index.js"), `
import {writeFileSync} from "node:fs";
const state={calls:[],environmentKeys:[],fileAuthPinned:false};let retired=false;
const save=()=>writeFileSync(${JSON.stringify(observations)},JSON.stringify(state));
export function resolveCodexCommand(){return ${JSON.stringify(mode === "ambient-codex" ? process.execPath : codex)};}
export function createIsolatedCodexAppServerArgs(){return ["app-server"];}
export function createRunnerdCodexTransport(options){
state.environmentKeys=Object.keys(options.environment).sort();
state.fileAuthPinned=options.codexArgs.includes('cli_auth_credentials_store="file"');
state.privateStateDirectory=options.stateDirectory;
save();return {transport:{async close(){retired=true;state.calls.push("transport-close");save();${mode === "startup-and-cleanup-failed" ? 'throw new Error("fixture secondary transport cleanup failure");' : ''}}},
evidence:()=>({runnerPid:111,codexPid:222,providerDriver:"codex_app_server",runnerExited:retired,diagnostics:["fixture provider bootstrap failed"],providerSessionId:"PRIVATE-PROVIDER-ID"})};}
export class CodexAppServerDriver {
constructor(options){this.options=options;}
async openSession(){state.calls.push("open");save();
${mode === "startup-and-cleanup-failed" ? 'throw new Error("fixture original provider startup failure");' : ''}
const model=${JSON.stringify(mode === "wrong-model" ? "wrong-model" : "gpt-5.6-sol")};
return {ids:()=>({driverSessionId:"PRIVATE-DRIVER-ID",providerSessionId:"PRIVATE-PROVIDER-ID"}),
events:()=>({async *[Symbol.asyncIterator](){yield {eventType:"session.started",payload:{context:{model,sandbox:{permissionProfile:{id:"paperclip-runner-workspace-read-only"}}}}};}}),
startTurn(){state.calls.push("FORBIDDEN-TURN");save();throw new Error("No provider turn is permitted");},
async close(){retired=true;state.calls.push("session-close");save();${mode === "cleanup-failed" ? 'throw new Error("fixture session cleanup failure");' : ''}}};}
}
`);
return { ...f, observations, expectedRunnerSha256: hash("fixture-daemon") };
}
it.skipIf(process.platform === "win32")("installed native startup uses closed homes, file-only auth and no turn or version equality gate", async () => {
const f = await startupFixture();
const receipt = await probeInstalledCodexStartup({ consumerRoot: f.root, serverRoot: f.server, expectedRunnerSha256: f.expectedRunnerSha256 });
expect(receipt).toMatchObject({ nativeSessionStarted: true, cleanupConfirmed: true, providerTurns: 0, credentialsUsed: false,
codexVersion: "codex-cli 0.149.5", defaultRuntimeResolution: true, reasoningEffortExercised: false });
expect(await readFile(f.observations, "utf8").then(JSON.parse)).toMatchObject({ calls: ["open", "session-close"], fileAuthPinned: true,
environmentKeys: ["CODEX_HOME", "HOME", "LC_ALL", "PAPERCLIP_RUNNER_NETWORK_ACCESS", "PATH", "TMPDIR", "TZ"] });
expect(JSON.stringify(receipt)).not.toMatch(/PRIVATE-(?:DRIVER|PROVIDER)-ID/);
});
it.skipIf(process.platform === "win32")("installed startup preserves the primary cause, records bounded diagnostics and attempts independent cleanup", async () => {
const f = await startupFixture("startup-and-cleanup-failed");
const error = await probeInstalledCodexStartup({ consumerRoot: f.root, serverRoot: f.server, expectedRunnerSha256: f.expectedRunnerSha256 }).catch(error => error);
expect(error).toBeInstanceOf(AggregateError);
expect(error.message).toContain("fixture original provider startup failure");
expect(error.message).toContain("fixture secondary transport cleanup failure");
expect(error.message).toContain("fixture provider bootstrap failed");
expect(error.cause.message).toBe("fixture original provider startup failure");
expect(error.errors.map((error: Error) => error.message)).toEqual(["fixture original provider startup failure", "fixture secondary transport cleanup failure"]);
expect(error.message).not.toContain("PRIVATE-PROVIDER-ID");
const observations = JSON.parse(await readFile(f.observations, "utf8"));
expect(observations.calls).toEqual(["open", "transport-close"]);
await expect(lstat(observations.privateStateDirectory)).rejects.toThrow();
});
it.skipIf(process.platform === "win32")("installed startup cannot qualify when session cleanup fails after successful open", async () => {
const f = await startupFixture("cleanup-failed");
await expect(probeInstalledCodexStartup({ consumerRoot: f.root, serverRoot: f.server, expectedRunnerSha256: f.expectedRunnerSha256 })).rejects.toThrow("fixture session cleanup failure");
expect(JSON.parse(await readFile(f.observations, "utf8")).calls).toEqual(["open", "session-close", "session-close"]);
});
it.skipIf(process.platform === "win32")("installed startup rejects stale daemon/ambient Codex and cleans a mismatched native session", async () => {
const f = await startupFixture();
await expect(probeInstalledCodexStartup({ consumerRoot: f.root, serverRoot: f.server, expectedRunnerSha256: "0".repeat(64) })).rejects.toThrow("reviewed pin");
await expect(readFile(f.observations)).rejects.toThrow();
const ambient = await startupFixture("ambient-codex");
await expect(probeInstalledCodexStartup({ consumerRoot: ambient.root, serverRoot: ambient.server, expectedRunnerSha256: ambient.expectedRunnerSha256 })).rejects.toThrow("ambient Codex");
const wrong = await startupFixture("wrong-model");
await expect(probeInstalledCodexStartup({ consumerRoot: wrong.root, serverRoot: wrong.server, expectedRunnerSha256: wrong.expectedRunnerSha256 })).rejects.toThrow("matching native identity");
expect(await readFile(wrong.observations, "utf8").then(JSON.parse)).toMatchObject({ calls: ["open", "session-close"] });
});
+124 -3
View File
@@ -1,21 +1,33 @@
/** Optional published-install lane. No production hook or admission override. */
import { constants } from "node:fs";
import { lstat, open, realpath } from "node:fs/promises";
import { lstat, mkdir, mkdtemp, open, realpath, rm } from "node:fs/promises";
import { createHash } from "node:crypto";
import { execFileSync } from "node:child_process";
import { findPackageJSON } from "node:module";
import { dirname, isAbsolute, join, resolve } from "node:path";
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
import { tmpdir } from "node:os";
import { pathToFileURL } from "node:url";
import { CREDENTIAL_NAMES, type MatrixExecution } from "./types.js";
export const installedCliKeys = ["PAPERCLIP_RUNNER_E2E_INSTALLED_CLI", "PAPERCLIP_RUNNER_E2E_INSTALLED_CLI_SHA256", "PAPERCLIP_RUNNER_E2E_INSTALLED_SERVER_ROOT", "PAPERCLIP_RUNNER_E2E_INSTALLED_SERVER_SHA256"] as const;
const overrideKeys = ["PAPERCLIP_RUNNER_BINARY", "PAPERCLIP_RUNNER_REMOTE_BINARY_PATH", "PAPERCLIP_RUNNER_REMOTE_PROVIDER_PACK_PATH", "PAPERCLIP_RUNNER_ACPX_QUALIFICATION", "PAPERCLIP_ACPX_BUILTIN_ROOT", "PAPERCLIP_ACPX_PROVIDER_PACKAGE_ROOT", "PAPERCLIP_ACPX_PROVIDER_PACKAGE_MANIFEST"] as const;
const suites = new Set(["extended-harnesses", "pi-native", "pi-controls", "rich-acp-warm-continuity"]);
// Reuse the production-onboarding, independent file/revision, and restart
// journeys. Installed proof does not add cases or relax their existing graders.
const installedCodexCells = new Set([
"first-task.runner-codex.local.task-card-accept",
"everyday-workflows.runner-codex.local.build-revise",
"agent-chat.runner-codex.local.continuity-restart",
]);
export function usesInstalledCli(env: NodeJS.ProcessEnv): boolean {
return installedCliKeys.some(key => env[key] !== undefined);
}
export function assertInstalledCliSelection(env: NodeJS.ProcessEnv, executions: readonly MatrixExecution[]): void {
if (!usesInstalledCli(env)) return;
if (!installedCliKeys.every(key => Boolean(env[key])) || executions.length === 0 || executions.some(e => e.profile.id !== "runner-acpx-pi" || e.profile.qualificationCandidate !== "pi" || !e.suite.manualOnly || !suites.has(e.suite.id))) throw new Error("Installed CLI proof requires complete pins and explicit supported Pi cases");
if (!installedCliKeys.every(key => Boolean(env[key])) || executions.length === 0 || executions.some(e => !(
(e.profile.id === "runner-acpx-pi" && e.profile.qualificationCandidate === "pi" && e.suite.manualOnly && suites.has(e.suite.id)) ||
(e.profile.id === "runner-codex" && e.profile.provider === "codex" && e.environment.id === "local" && installedCodexCells.has(e.id))
))) throw new Error("Installed CLI proof requires complete pins and explicit supported Pi or Codex cases");
for (const key of overrideKeys) if (env[key] !== undefined) throw new Error(`Installed CLI proof forbids ${key}`);
if (env.NODE_OPTIONS !== undefined || env.NODE_PATH !== undefined) throw new Error("Installed CLI proof forbids ambient Node injection");
}
@@ -51,6 +63,115 @@ export async function verifyInstalledCli(env: NodeJS.ProcessEnv, executions: rea
return { schema: "paperclip.e2e.installed-cli-admission/v1", entry, cliRoot, cliSha256, serverRoot, serverEntry, serverSha256, version: cli.version, defaultRuntimeResolution: true, qualificationOverride: false };
}
/** Clean-install protocol proof only: no credentials, task, or provider turn. */
export async function probeInstalledCodexStartup(input: {
consumerRoot: string;
serverRoot: string;
expectedRunnerSha256: string;
timeoutMs?: number;
}) {
for (const key of [...overrideKeys, "NODE_OPTIONS", "NODE_PATH"]) {
if (process.env[key] !== undefined) throw new Error(`Installed Codex startup forbids ${key}`);
}
const inside = (root: string, filename: string) => {
const name = relative(root, filename);
return name !== "" && name !== ".." && !name.startsWith(`..${sep}`) && !isAbsolute(name);
};
for (const filename of [input.consumerRoot, input.serverRoot]) {
if (!isAbsolute(filename) || resolve(filename) !== filename || await realpath(filename) !== filename) throw new Error("Installed Codex startup paths must be canonical and unlinked");
}
if (!inside(join(input.consumerRoot, "node_modules"), input.serverRoot) || !/^[a-f0-9]{64}$/.test(input.expectedRunnerSha256)) throw new Error("Installed Codex startup requires a contained server and reviewed daemon digest");
const manifest = JSON.parse((await readOwned(join(input.serverRoot, "package.json"), 65536)).toString());
if (manifest.name !== "@paperclipai/server" || manifest.exports?.["."]?.import !== "./dist/index.js") throw new Error("Installed Codex startup requires the published server");
const vendorRoot = join(input.serverRoot, "dist/vendor/paperclip-runner");
const entries = [join(vendorRoot, "index.js"), join(input.serverRoot, "dist/services/native-runtime/native-codex-runner.js")];
for (const filename of entries) if (await realpath(filename) !== filename) throw new Error("Installed Codex startup runtime escapes its package");
const [runtime, production] = await Promise.all(entries.map(filename => import(pathToFileURL(filename).href)));
const runnerBinary = await realpath(production.resolvePaperclipRunnerBinary());
if (runnerBinary !== join(vendorRoot, "bin", `${process.platform}-${process.arch}`, "paperclip-runnerd")) throw new Error("Installed Codex startup requires the normal platform daemon; no source or flat fallback");
const digest = (bytes: Buffer) => createHash("sha256").update(bytes).digest("hex");
const runnerSha256 = digest(await readOwned(runnerBinary, 512 * 1024 * 1024));
if (runnerSha256 !== input.expectedRunnerSha256) throw new Error("Installed Codex startup daemon differs from its reviewed pin");
const directory = await realpath(await mkdtemp(join(tmpdir(), "pc-codex-startup-")));
const home = join(directory, "home"), codexHome = join(directory, "codex-home"), workspace = join(directory, "workspace");
for (const filename of [home, codexHome, workspace]) await mkdir(filename, { mode: 0o700 });
const environment = { PATH: `${dirname(process.execPath)}:/usr/bin:/bin:/usr/sbin`, HOME: home, CODEX_HOME: codexHome,
TMPDIR: directory, LC_ALL: "C", TZ: "UTC", PAPERCLIP_RUNNER_NETWORK_ACCESS: "disabled" };
let session: any;
let bundle: any;
let cleanupConfirmed = false;
let startupFailed = false;
let startupFailure: unknown;
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(new Error("Installed Codex startup timed out")), Math.max(1, Math.min(input.timeoutMs ?? 45000, 120000)));
try {
const codexCommand = await realpath(runtime.resolveCodexCommand(undefined, environment, workspace));
if (!inside(join(input.consumerRoot, "node_modules"), codexCommand)) throw new Error("Installed Codex startup resolved an ambient Codex executable");
const codexSha256 = digest(await readOwned(codexCommand, 512 * 1024 * 1024));
const codexVersion = execFileSync(codexCommand, ["--version"], { cwd: workspace, env: environment, encoding: "utf8", timeout: 10000, maxBuffer: 16384 }).trim();
if (!codexVersion || codexVersion.length > 200) throw new Error("Installed Codex startup returned no bounded version identity");
// Keep the production security arguments; pin file-only auth for this
// credential-free fixture so macOS keychain accounts cannot be consulted.
const args = runtime.createIsolatedCodexAppServerArgs(environment);
args.splice(args.indexOf("app-server"), 0, "-c", 'cli_auth_credentials_store="file"');
bundle = runtime.createRunnerdCodexTransport({ provider: "codex", stateDirectory: directory, environment,
sourceCodexHome: "", runnerBinary, codexCommand, codexArgs: args, closeGraceMs: 10000,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } });
const driver = new runtime.CodexAppServerDriver({
taskEnvelope: { schema: "paperclip.skillless_task.v1", objective: "Credential-free installed startup proof.",
completionContract: { revision: "installed-startup-v1", criteria: [] }, constraints: [], expectedResultSchema: "paperclip.run_result.v1" },
model: "gpt-5.6-sol", conversationMode: "direct", requestedCollaborationMode: "plan", approvalPolicy: "never",
includeCollaborationModeInstructions: false, dynamicTools: [], environment,
transportFactory: () => bundle.transport, requireProviderSessionIdentity: true,
});
session = await driver.openSession({ runId: "installed-startup", normalizedSessionId: "installed-startup", workingDirectory: workspace, signal: controller.signal });
const event = await session.events()[Symbol.asyncIterator]().next();
const identity = session.ids();
const evidence = bundle.evidence();
if (event.value?.eventType !== "session.started" || event.value.payload?.context?.model !== "gpt-5.6-sol" ||
event.value.payload?.context?.sandbox?.permissionProfile?.id !== "paperclip-runner-workspace-read-only" ||
!identity.driverSessionId || !identity.providerSessionId || evidence.providerDriver !== "codex_app_server" ||
!(evidence.runnerPid > 0) || !(evidence.codexPid > 0)) throw new Error("Installed Codex startup returned no matching native identity or read-only model context");
await session.close({ reason: "credential-free installed startup complete" });
session = undefined;
if (!bundle.evidence().runnerExited) throw new Error("Installed Codex startup cleanup is incomplete");
cleanupConfirmed = true;
return { schema: "paperclip.e2e.installed-codex-startup/v1", platform: `${process.platform}-${process.arch}`,
runnerSha256, codexSha256, codexVersion, providerDriver: "codex_app_server", model: "gpt-5.6-sol",
nativeSessionStarted: true, cleanupConfirmed: true, providerTurns: 0, credentialsUsed: false,
defaultRuntimeResolution: true, reasoningEffortExercised: false };
} catch (error) {
startupFailed = true;
startupFailure = error;
throw error;
} finally {
clearTimeout(timer);
const cleanupFailures: unknown[] = [];
try {
if (session) await session.close({ reason: "installed startup cleanup" });
else if (bundle && !cleanupConfirmed) await bundle.transport.close("installed startup cleanup");
} catch (error) { cleanupFailures.push(error); }
try {
if (!bundle || bundle.evidence().runnerExited) await rm(directory, { recursive: true, force: true });
} catch (error) { cleanupFailures.push(error); }
if (startupFailed) {
const evidence = bundle?.evidence();
// This zero-turn fixture has empty homes and no provider credentials.
// Report only bounded failure text and lifecycle state, never identities
// or raw durable state. A close failure cannot hide the startup cause.
const bounded = (value: unknown) => String(value).replaceAll(directory, "[private-startup]").slice(0, 2000);
const primaryMessage = bounded(startupFailure instanceof Error ? startupFailure.message : startupFailure);
const diagnostic = { schema: "paperclip.e2e.installed-codex-startup-failure/v1", platform: `${process.platform}-${process.arch}`,
runnerExited: evidence?.runnerExited ?? null, runnerExitCode: evidence?.runnerExitCode ?? null,
runnerSignal: evidence?.runnerSignal ?? null,
diagnostics: (Array.isArray(evidence?.diagnostics) ? evidence.diagnostics : []).slice(-16).map(bounded),
cleanupFailures: cleanupFailures.map(error => bounded(error instanceof Error ? error.message : error)) };
throw new AggregateError([startupFailure, ...cleanupFailures], `Installed Codex startup failed: ${primaryMessage}\n${JSON.stringify(diagnostic)}`, { cause: startupFailure });
}
if (cleanupFailures.length) throw new AggregateError(cleanupFailures, "Installed Codex startup cleanup failed", { cause: cleanupFailures[0] });
}
}
/** This prep lane cannot create agents or read the normal local credential file. */
export function assertInstalledStartupOnly(env: NodeJS.ProcessEnv, executions: readonly MatrixExecution[], options: { ids: string[]; maxParallel: number; maxAutomaticRetries: number; headed: boolean; ui: boolean; debug: boolean; list: boolean; matrixJson: boolean }) {
assertInstalledCliSelection(env, executions);