Merge branch 'codex/runner-rich-acp' into codex/runner-cursor-acp

* codex/runner-rich-acp:
  Admit the versioned Pi invocation identity profile
  test(runner): preserve prepared OpenCode failure diagnostics
This commit is contained in:
Dotta committed 2026-09-28 16:49:42 -05:00
commit 2fa142d665
4 files changed
+95 -20

No files matched your search

@@ -280,7 +280,7 @@ describe("NativeExecutionInputV1", () => {
})).toThrow("eventExpiryDays");
});
it.each([1, 2, 3] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
it.each([1, 2, 3, 4] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
const provider = {
kind: "acpx",
agent: "pi",
@@ -312,7 +312,7 @@ describe("NativeExecutionInputV1", () => {
profile: provider.profile,
});
expect(parseNativeExecutionInput(parsed)).toEqual(parsed);
for (const unsupportedVersion of [0, 4, 1.5, "3", null]) {
for (const unsupportedVersion of [0, 5, 1.5, "4", null]) {
expect(() => parseNativeExecutionInput({
...input,
session: { ...input.session, driverKind: "acpx_runtime" },
@@ -90,7 +90,7 @@ export interface NativeAcpxProfileSnapshot {
protocolVersion: 1;
acpxVersion: "0.13.1";
agent: NativeAcpxAgent;
agentProfileVersion: 1 | 2 | 3;
agentProfileVersion: 1 | 2 | 3 | 4;
agentServerPackage: string;
agentServerVersion: string;
agentRuntimePackage: string | null;
@@ -596,7 +596,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
|| profile.protocolVersion !== 1
|| profile.acpxVersion !== "0.13.1"
|| profile.agent !== provider.agent
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3)
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4)
) {
throw new NativeExecutionInputError("input.provider.profile does not match the qualified ACPX v1 profile");
}
@@ -11,7 +11,7 @@ export interface QualifiedAcpxProfile {
readonly protocolVersion: typeof ACPX_DRIVER_PROTOCOL_VERSION;
readonly acpxVersion: typeof QUALIFIED_ACPX_VERSION;
readonly agent: QualifiedAcpxAgent;
readonly agentProfileVersion: 1 | 2 | 3;
readonly agentProfileVersion: 1 | 2 | 3 | 4;
readonly qualificationStatus?: "pending";
readonly modelPolicy?: "explicit-provider-verified";
/** Wire identity: an npm package name or a runner-owned builtin: identifier. */
@@ -7379,6 +7379,79 @@ it("admits only exact live Pi turn controls", () => {
expect(() => parseAcpxTurnControlCapabilities({ steering: true, queuedFollowUp: false }, agent)).toThrow("cannot advertise");
}
});
async function withPreparedOpenCodeCleanup(input: {
run: () => Promise<void>;
closeSession: () => Promise<void>;
closeTransport: () => Promise<void>;
removeRoot: () => Promise<void>;
evidence: () => Pick<
ReturnType<ReturnType<typeof createCapabilityRunnerdCodexTransport>["evidence"]>,
"runnerPid" | "runnerExited" | "runnerExitCode" | "runnerSignal" | "diagnostics"
>;
}): Promise<void> {
const failures: { stage: string; error: unknown }[] = [];
for (const [stage, operation] of [
["run", input.run],
["session.close", input.closeSession],
["transport.close", input.closeTransport],
["removeRoot", input.removeRoot],
] as const) {
try {
await operation();
} catch (error) {
failures.push({ stage, error });
}
}
if (failures.length === 0) return;
const evidence = input.evidence();
// Never dump stderr, environment values, prompts, paths, or arbitrary
// diagnostic text. Keep only bounded process/settlement facts alongside
// the original errors, so a finally failure cannot hide the primary one.
const summary = {
failedStages: failures.map(({ stage }) => stage),
runnerStarted: evidence.runnerPid !== null,
runnerExited: evidence.runnerExited,
runnerExitCode: evidence.runnerExitCode,
runnerSignalled: evidence.runnerSignal !== null,
diagnosticCount: evidence.diagnostics.length,
suspensionRejected: evidence.diagnostics.includes(
"runner did not prove durable suspension before checkpoint",
),
};
throw new AggregateError(
failures.map(({ error }) => error),
`Prepared OpenCode fixture failed: ${JSON.stringify(summary)}`,
{ cause: failures[0]!.error },
);
}
it.each([true, false])("preserves prepared OpenCode cleanup errors (primary failure: %s)", async (failRun) => {
const primary = new Error("prepared input assertion failed");
const cleanup = new NativeSessionCloseUnrecoverableError();
const calls: string[] = [];
const evidence = {
runnerPid: 123,
runnerExited: true,
runnerExitCode: 1,
runnerSignal: null,
diagnostics: ["Bearer fixture-secret", "runner did not prove durable suspension before checkpoint"],
};
const failure = await withPreparedOpenCodeCleanup({
run: async () => { calls.push("run"); if (failRun) throw primary; },
closeSession: async () => { calls.push("session.close"); throw cleanup; },
closeTransport: async () => { calls.push("transport.close"); },
removeRoot: async () => { calls.push("removeRoot"); },
evidence: () => evidence,
}).catch((error: unknown) => error);
expect(failure).toBeInstanceOf(AggregateError);
expect((failure as AggregateError).errors).toEqual(failRun ? [primary, cleanup] : [cleanup]);
expect((failure as AggregateError).cause).toBe(failRun ? primary : cleanup);
expect(calls).toEqual(["run", "session.close", "transport.close", "removeRoot"]);
expect((failure as Error).message).toContain('"suspensionRejected":true');
expect((failure as Error).message).not.toContain("fixture-secret");
});
it("preserves prepared input through runnerd and the real OpenCode proxy boundary", async () => {
const root = await mkdtemp(join(tmpdir(), "runnerd-prepared-opencode-"));
// The qualified launch boundary unlinks its executable after exec. Use a
@@ -7432,19 +7505,21 @@ it("preserves prepared input through runnerd and the real OpenCode proxy boundar
task: { prompt: "Keep this request unchanged." },
completionContract: { revision: "prepared-v1", criteria: task.completionContract.criteria },
});
try {
session = await driver.openSession({ runId: "prepared-opencode", normalizedSessionId: "prepared-opencode", workingDirectory: root });
await session.startTurn({ message: { role: "user", text: prepared } });
for await (const event of session.events()) {
if (event.eventType === "turn.completed") break;
}
const sessionRoots = (await readdir(runtime, { withFileTypes: true })).filter((entry) => entry.isDirectory());
expect(sessionRoots).toHaveLength(1);
const requests = (await readFile(join(runtime, sessionRoots[0]!.name, "data/fake-prompt-requests.ndjson"), "utf8")).trim().split("\n").map((line) => JSON.parse(line));
expect(requests.map((request) => request.parts)).toEqual([[{ type: "text", text: prepared }]]);
} finally {
await session?.close();
await bundle.transport.close();
await rm(root, { recursive: true, force: true });
}
await withPreparedOpenCodeCleanup({
run: async () => {
session = await driver.openSession({ runId: "prepared-opencode", normalizedSessionId: "prepared-opencode", workingDirectory: root });
await session.startTurn({ message: { role: "user", text: prepared } });
for await (const event of session.events()) {
if (event.eventType === "turn.completed") break;
}
const sessionRoots = (await readdir(runtime, { withFileTypes: true })).filter((entry) => entry.isDirectory());
expect(sessionRoots).toHaveLength(1);
const requests = (await readFile(join(runtime, sessionRoots[0]!.name, "data/fake-prompt-requests.ndjson"), "utf8")).trim().split("\n").map((line) => JSON.parse(line));
expect(requests.map((request) => request.parts)).toEqual([[{ type: "text", text: prepared }]]);
},
closeSession: async () => { await session?.close(); },
closeTransport: () => bundle.transport.close(),
removeRoot: () => rm(root, { recursive: true, force: true }),
evidence: () => bundle.evidence(),
});
}, 30_000);