Merge current master before PR validation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
cryppadottaandPaperclip committed 2026-11-06 15:57:54 +00:00
commit 715cee88ce
200 files changed
+6595 -743

No files matched your search

+3
View File
@@ -446,6 +446,9 @@ jobs:
- name: Typecheck workspaces whose build scripts skip TypeScript - name: Typecheck workspaces whose build scripts skip TypeScript
run: pnpm run typecheck:build-gaps run: pnpm run typecheck:build-gaps
- name: Typecheck test files that no build reads
run: node scripts/typecheck-test-programs.mjs --low-memory
- name: Typecheck the server in low-memory layers - name: Typecheck the server in low-memory layers
env: env:
PAPERCLIP_TYPECHECK_LOW_MEMORY: "1" PAPERCLIP_TYPECHECK_LOW_MEMORY: "1"
+34
View File
@@ -1319,6 +1319,40 @@ that file, and it fails when a referenced config does not exist yet. The
program. That program peaks at about 15 GiB at default settings and about 8 GiB on program. That program peaks at about 15 GiB at default settings and about 8 GiB on
one thread. one thread.
#### Typecheck test files outside the build
Most package tsconfigs exclude tests, and Vitest strips types, so a type error in
one of those tests fails nothing. `scripts/typecheck-test-programs.mjs` reads them
through separate programs, usually a `tsconfig.test.json` beside the package's
`tsconfig.json`:
```sh
node scripts/typecheck-test-programs.mjs --low-memory
node scripts/typecheck-test-programs.mjs packages/paperclip-runner/tsconfig.test.json
```
The first form runs in the required `Typecheck + Release Registry` job. It checks
one program at a time and the largest peaks below 2 GiB. It then fails if a tracked
`*.test.ts(x)` or `*.spec.ts(x)` file is in no program. The compiler's file list
decides that, because `include` silently drops `foo.test.tsx` when `foo.test.ts`
exists.
To add a test directory, add it to the `include` of the package's
`tsconfig.test.json`. To add a package, add a program to `PROGRAMS` in the script.
Sandbox providers are outside the root workspace, so the root install has none of
their SDKs. `scripts/typecheck-sandbox-providers` is a small workspace that lists
them and holds one lockfile. Its install replaces any standalone `node_modules` in
those providers. After changing a provider's `package.json`, refresh that lockfile:
```sh
pnpm -C scripts/typecheck-sandbox-providers install --ignore-scripts --lockfile-only
```
Do not commit a lockfile inside a provider.
`scripts/build-standalone-public-packages.mjs` uses a frozen install when one
exists, which changes what a release resolves.
#### Reused Runner build in the server build #### Reused Runner build in the server build
The server build vendors `packages/paperclip-runner/dist`, so its The server build vendors `packages/paperclip-runner/dist`, so its
+15 -2
View File
@@ -2589,13 +2589,26 @@ Shutdown, process loss, and provider failure use the existing durable failure re
If shutdown or a recorded unexpected system interruption stops Paperclip Runner If shutdown or a recorded unexpected system interruption stops Paperclip Runner
preparation before native execution was selected, recovery verifies that no preparation before native execution was selected, recovery verifies that no
provider, process launch, native result or coordinator exists. The previous provider, process launch, native result or coordinator exists. The previous
controller must have settled or lost its lease. controller must have settled or lost its lease. A failed `process_lost` run also
qualifies when restart recovery committed its explicit bootstrap certificate
that provider work did not start. The error label alone grants no replay.
Required workspace recovery must have an exact saved-file receipt and stopped Required workspace recovery must have an exact saved-file receipt and stopped
leases before the task can continue. Once these checks pass, the server retires leases before the task can continue. Once these checks pass, the server retires
its own unchanged bootstrap-failure block and schedules one bounded retry. its own unchanged bootstrap-failure block and schedules one bounded retry.
Startup also discovers this obligation when file recovery completed before the Startup also discovers this obligation when file recovery completed before the
restart. A pending Retry or saved message owns its continuation instead of restart. A pending Retry or saved message owns its continuation instead of
starting a competing automatic attempt. Retry uses the same preparation proof starting a competing automatic attempt. If a replacement was already withheld
by the workspace gate before execution, continuation retains that cancelled row
and uses its existing predecessor claim and remaining failure budget.
Recovery adopts that withheld row only when it is the source's sole retry child.
Multiple-child history reuses the newest existing child, ordered by creation time
and run ID, without scheduling another replacement.
Clearing the file hold records a pending continuation before enqueue. Restart
reconstructs that obligation until a successor is recorded. Known admission
gates defer it without a polling deadline; eligibility changes wake the existing
recovery worker. An unresolved enqueue or receipt write keeps its shared retry
deadline. Delivery or exhaustion retires that obligation.
Retry uses the same preparation proof
and preserves the recovered workspace. Current task ownership, human blocks, and preserves the recovered workspace. Current task ownership, human blocks,
pauses, approvals, budgets and the existing retry limit remain effective. pauses, approvals, budgets and the existing retry limit remain effective.
Operator Stops retain their explicit continuation requirements. Other ambiguous Operator Stops retain their explicit continuation requirements. Other ambiguous
+1 -1
View File
@@ -61,7 +61,7 @@
"smoke:notion-generic-live": "node scripts/smoke/notion-generic-live.mjs", "smoke:notion-generic-live": "node scripts/smoke/notion-generic-live.mjs",
"smoke:posthog-live": "node scripts/smoke/posthog-live.mjs", "smoke:posthog-live": "node scripts/smoke/posthog-live.mjs",
"smoke:terminal-bench-loop-skill": "node scripts/smoke/terminal-bench-loop-skill-smoke.mjs", "smoke:terminal-bench-loop-skill": "node scripts/smoke/terminal-bench-loop-skill-smoke.mjs",
"test:release-registry": "node scripts/ci-profile/node-test.mjs scripts/ci-profile/reporters.test.mjs scripts/__tests__/canary-startup-playwright.test.mjs scripts/__tests__/cursor-public-install-sandbox.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/public-install-closure.test.mjs scripts/__tests__/public-install-registry.test.mjs scripts/__tests__/prefetch-child.test.mjs scripts/__tests__/stage-bundled-packages.test.mjs scripts/__tests__/pack-staged-packages.test.mjs scripts/__tests__/retain-runner-qualification-packages.test.mjs scripts/__tests__/runner-release-crate-cache.test.mjs scripts/__tests__/runner-debug-crate-cache.test.mjs scripts/__tests__/vitest-worker-isolation.test.mjs scripts/verify-release-registry-state.test.mjs scripts/release-package-map.test.mjs scripts/check-release-package-bootstrap.test.mjs scripts/check-no-git-push.test.mjs scripts/release-lib.test.mjs scripts/release-registry-versions.test.mjs scripts/link-plugin-dev-sdk.test.js scripts/acpx-patch-packaging.test.mjs scripts/service-onboard-smoke.test.mjs scripts/docker-onboard-smoke.test.mjs scripts/preview-artifacts.test.mjs scripts/cloud-migrator-artifacts.test.mjs scripts/__tests__/local-verify.test.mjs", "test:release-registry": "node scripts/ci-profile/node-test.mjs scripts/ci-profile/reporters.test.mjs scripts/__tests__/canary-startup-playwright.test.mjs scripts/__tests__/cursor-public-install-sandbox.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/public-install-closure.test.mjs scripts/__tests__/public-install-registry.test.mjs scripts/__tests__/prefetch-child.test.mjs scripts/__tests__/stage-bundled-packages.test.mjs scripts/__tests__/pack-staged-packages.test.mjs scripts/__tests__/retain-runner-qualification-packages.test.mjs scripts/__tests__/runner-release-crate-cache.test.mjs scripts/__tests__/runner-debug-crate-cache.test.mjs scripts/__tests__/vitest-worker-isolation.test.mjs scripts/verify-release-registry-state.test.mjs scripts/release-package-map.test.mjs scripts/check-release-package-bootstrap.test.mjs scripts/check-no-git-push.test.mjs scripts/release-lib.test.mjs scripts/release-registry-versions.test.mjs scripts/link-plugin-dev-sdk.test.js scripts/acpx-patch-packaging.test.mjs scripts/service-onboard-smoke.test.mjs scripts/docker-onboard-smoke.test.mjs scripts/preview-artifacts.test.mjs scripts/cloud-migrator-artifacts.test.mjs scripts/typecheck-test-programs.test.mjs scripts/__tests__/local-verify.test.mjs",
"storybook-visual:baseline": "node scripts/storybook-visual-baseline.mjs", "storybook-visual:baseline": "node scripts/storybook-visual-baseline.mjs",
"test:storybook-visual": "node scripts/storybook-visual-baseline.mjs download && node scripts/storybook-visual-baseline.mjs verify && pnpm build-storybook && npx playwright test --config tests/storybook-visual/playwright.config.ts", "test:storybook-visual": "node scripts/storybook-visual-baseline.mjs download && node scripts/storybook-visual-baseline.mjs verify && pnpm build-storybook && npx playwright test --config tests/storybook-visual/playwright.config.ts",
"test:storybook-visual:update": "node scripts/storybook-visual-baseline.mjs download && pnpm build-storybook && npx playwright test --config tests/storybook-visual/playwright.config.ts --update-snapshots && node scripts/storybook-visual-baseline.mjs pack", "test:storybook-visual:update": "node scripts/storybook-visual-baseline.mjs download && pnpm build-storybook && npx playwright test --config tests/storybook-visual/playwright.config.ts --update-snapshots && node scripts/storybook-visual-baseline.mjs pack",
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"rootDir": "."
},
"include": ["scripts/**/*.test.ts"]
}
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true
},
"exclude": []
}
@@ -3,7 +3,7 @@ import { describe, expect, it, vi } from "vitest";
import type { HarnessDriver, HarnessSession, PersistedHarnessSession } from "../contracts/harness-driver.js"; import type { HarnessDriver, HarnessSession, PersistedHarnessSession } from "../contracts/harness-driver.js";
import { PROVIDER_STATE_INCOMPATIBLE, runnerRecoveryFailureClass } from "../contracts/harness-driver.js"; import { PROVIDER_STATE_INCOMPATIBLE, runnerRecoveryFailureClass } from "../contracts/harness-driver.js";
import type { PrpEvent, PrpStructuredRunResult, PrpTerminalState } from "../protocol/replay-contract.js"; import type { PrpEvent, PrpStructuredRunResult, PrpTerminalState } from "../protocol/replay-contract.js";
import { NativeSessionProtocolIntegrityError } from "../contracts/native-session-backend.js"; import { NativeSessionProtocolIntegrityError, type NativeSession } from "../contracts/native-session-backend.js";
import { HarnessDriverBackend } from "./harness-driver-backend.js"; import { HarnessDriverBackend } from "./harness-driver-backend.js";
const result: PrpStructuredRunResult = { const result: PrpStructuredRunResult = {
@@ -55,8 +55,13 @@ function prpEvent(sourceSeq: number, eventType: PrpEvent["eventType"], payload:
const runtimeResolutions: unknown[] = []; const runtimeResolutions: unknown[] = [];
function cancelSession(session: NativeSession, input: Parameters<NonNullable<NativeSession["cancel"]>>[0]) {
if (!session.cancel) throw new Error("backend session does not support cancellation");
return session.cancel(input);
}
class FakeHarnessSession implements HarnessSession { class FakeHarnessSession implements HarnessSession {
ids() { return { driverSessionId: "driver-1", providerSessionId: "provider-1" }; } ids(): ReturnType<HarnessSession["ids"]> { return { driverSessionId: "driver-1", providerSessionId: "provider-1" }; }
async *events() { async *events() {
yield prpEvent(1, "run.result.proposed", result); yield prpEvent(1, "run.result.proposed", result);
yield prpEvent(2, "turn.completed", { status: "completed" }); yield prpEvent(2, "turn.completed", { status: "completed" });
@@ -942,7 +947,7 @@ describe("HarnessDriverBackend", () => {
); );
class TerminalThenIntegrityFailure extends FakeHarnessSession { class TerminalThenIntegrityFailure extends FakeHarnessSession {
goal = vi.fn(async () => null); goal = vi.fn(async () => null);
steer = vi.fn(async () => ({ correlationId: "blocked-steer" })); steer = vi.fn(async () => {});
override async *events() { override async *events() {
yield* super.events(); yield* super.events();
throw fault; throw fault;
@@ -1068,8 +1073,8 @@ describe("HarnessDriverBackend", () => {
const session = await backend.openSession({ const session = await backend.openSession({
identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" }, workingDirectory: "/workspace", identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" }, workingDirectory: "/workspace",
}); });
if (journalOnly) session.revokeTurnPublication(); if (journalOnly) session.revokeTurnPublication!();
else await session.cancel({ reason: "durable governed wait", signal: new AbortController().signal }).cleanup; else await cancelSession(session, { reason: "durable governed wait", signal: new AbortController().signal }).cleanup;
const events = []; const events = [];
for await (const event of session.events()) events.push(event); for await (const event of session.events()) events.push(event);
expect(events.filter(event => event.payload.kind === "agentMessage")).toHaveLength(0); expect(events.filter(event => event.payload.kind === "agentMessage")).toHaveLength(0);
@@ -1085,7 +1090,7 @@ describe("HarnessDriverBackend", () => {
identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" }, identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" },
workingDirectory: "/workspace", workingDirectory: "/workspace",
}); });
await session.cancel({ reason: "user stop during startup", signal: new AbortController().signal }).cleanup; await cancelSession(session, { reason: "user stop during startup", signal: new AbortController().signal }).cleanup;
await expect(session.startTurn({ message: { role: "user", text: "cancelled work" } })) await expect(session.startTurn({ message: { role: "user", text: "cancelled work" } }))
.rejects.toThrow("native_session_cancelled"); .rejects.toThrow("native_session_cancelled");
expect(start).not.toHaveBeenCalled(); expect(start).not.toHaveBeenCalled();
@@ -1108,7 +1113,7 @@ describe("HarnessDriverBackend", () => {
}); });
const iterator = session.events()[Symbol.asyncIterator](); const iterator = session.events()[Symbol.asyncIterator]();
await iterator.next(); await iterator.next();
await session.cancel({ reason: "operator stop", signal: new AbortController().signal }).cleanup; await cancelSession(session, { reason: "operator stop", signal: new AbortController().signal }).cleanup;
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.cancelled", sourceSeq: 3 } }); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.cancelled", sourceSeq: 3 } });
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "turn.failed", sourceSeq: 4 } }); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "turn.failed", sourceSeq: 4 } });
await expect(iterator.next()).resolves.toMatchObject({ done: true }); await expect(iterator.next()).resolves.toMatchObject({ done: true });
@@ -1133,7 +1138,7 @@ describe("HarnessDriverBackend", () => {
}); });
const iterator = session.events()[Symbol.asyncIterator](); const iterator = session.events()[Symbol.asyncIterator]();
await iterator.next(); await iterator.next();
await expect(session.cancel({ reason: "operator stop", signal: new AbortController().signal }).cleanup).rejects.toThrow("no active provider turn"); await expect(cancelSession(session, { reason: "operator stop", signal: new AbortController().signal }).cleanup).rejects.toThrow("no active provider turn");
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "turn.cancelled", sourceSeq: 2, await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "turn.cancelled", sourceSeq: 2,
payload: { status: "cancelled", providerTerminalState: "completed", reason: "cancelled_after_provider_completed" } } }); payload: { status: "cancelled", providerTerminalState: "completed", reason: "cancelled_after_provider_completed" } } });
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "run.terminal", sourceSeq: 3, await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "run.terminal", sourceSeq: 3,
@@ -1166,7 +1171,7 @@ describe("HarnessDriverBackend", () => {
}); });
const iterator = session.events()[Symbol.asyncIterator](); const iterator = session.events()[Symbol.asyncIterator]();
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.created" } }); await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.created" } });
await session.cancel({ await cancelSession(session, {
reason: "operator cancelled the run", reason: "operator cancelled the run",
signal: new AbortController().signal, signal: new AbortController().signal,
}).cleanup; }).cleanup;
@@ -5,7 +5,7 @@ import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import * as codexSecurity from "../drivers/codex/codex-security-config.js"; import * as codexSecurity from "../drivers/codex/codex-security-config.js";
import { buildNativeModelEnvelope, parseNativeExecutionInput, type NativeExecutionInput } from "../contracts/native-execution.js"; import { buildNativeModelEnvelope, parseNativeExecutionInput, type NativeExecutionInput, type NativeExecutionInputV1, type NativeExecutionInputV2 } from "../contracts/native-execution.js";
import { NATIVE_RUNTIME_ASSET_SCHEMA, PAPERCLIP_EXECUTION_PROMPT, PAPERCLIP_EXECUTION_PROMPT_REVISION, nativeRuntimePromptDigest, canonicalNativeRuntimeContextDigest, composeNativeSystemInstructions } from "../contracts/runtime-context.js"; import { NATIVE_RUNTIME_ASSET_SCHEMA, PAPERCLIP_EXECUTION_PROMPT, PAPERCLIP_EXECUTION_PROMPT_REVISION, nativeRuntimePromptDigest, canonicalNativeRuntimeContextDigest, composeNativeSystemInstructions } from "../contracts/runtime-context.js";
const contextRoots: string[] = []; const contextRoots: string[] = [];
@@ -36,12 +36,12 @@ import { createCodexNativeSessionBackend, createRunnerdNativeSessionBackend } fr
import { createOpenCodeNativeSessionBackend } from "./opencode-native-backend.js"; import { createOpenCodeNativeSessionBackend } from "./opencode-native-backend.js";
function execution( function execution(
provider: NativeExecutionInput["provider"] = { provider: NativeExecutionInputV1["provider"] = {
kind: "codex", kind: "codex",
model: null, model: null,
approvalPolicy: "never", approvalPolicy: "never",
}, },
): NativeExecutionInput { ): NativeExecutionInputV1 {
return { return {
schema: "paperclip.native-execution-input.v1", schema: "paperclip.native-execution-input.v1",
binding: { binding: {
@@ -88,7 +88,7 @@ function execution(
function acpxExecution( function acpxExecution(
agent: "codex" | "pi" | "claude" = "codex", agent: "codex" | "pi" | "claude" = "codex",
): NativeExecutionInput { ): NativeExecutionInputV1 {
return { return {
...execution(), ...execution(),
session: { session: {
@@ -107,7 +107,7 @@ function acpxExecution(
}; };
} }
function opencodeExecution(): NativeExecutionInput { function opencodeExecution(): NativeExecutionInputV1 {
return { return {
...execution(), ...execution(),
session: { session: {
@@ -124,7 +124,7 @@ function opencodeExecution(): NativeExecutionInput {
}; };
} }
function planningExecution(input: NativeExecutionInput): NativeExecutionInput { function planningExecution(input: NativeExecutionInputV1): NativeExecutionInputV2 {
return { return {
...input, ...input,
schema: "paperclip.native-execution-input.v2", schema: "paperclip.native-execution-input.v2",
@@ -143,7 +143,7 @@ function planningExecution(input: NativeExecutionInput): NativeExecutionInput {
function managedExecution( function managedExecution(
kind: "claude_managed" | "aws_agentcore", kind: "claude_managed" | "aws_agentcore",
): NativeExecutionInput { ): NativeExecutionInputV1 {
if (kind === "claude_managed") { if (kind === "claude_managed") {
return { return {
...execution(), ...execution(),
@@ -223,7 +223,7 @@ describe("native backend factory", () => {
}); });
try { try {
const backend = createNativeSessionBackend(input, { environment: {} }); const backend = createNativeSessionBackend(input, { environment: {} });
await expect(backend.openSession({ identity: { runId: "run", sessionId: "session", companyId: "company", agentId: "agent" }, await expect(backend.openSession({ identity: { runId: "run", sessionId: "session", companyId: "company", issueId: "issue", agentId: "agent" },
workingDirectory: WORKSPACE })).rejects.toThrow("offline-stop-before-process-spawn"); workingDirectory: WORKSPACE })).rejects.toThrow("offline-stop-before-process-spawn");
expect(generated).toBeDefined(); expect(generated).toBeDefined();
if (webSearch) expect(generated?.slice(-3)).toEqual(["-c", 'web_search="disabled"', "app-server"]); if (webSearch) expect(generated?.slice(-3)).toEqual(["-c", 'web_search="disabled"', "app-server"]);
@@ -404,7 +404,7 @@ describe("native backend factory", () => {
expect(recovered).toMatchObject({ recovered: true }); expect(recovered).toMatchObject({ recovered: true });
expect(factory).toHaveBeenCalledWith(expect.objectContaining({ baseInstructions: retainedSystemInstructions })); expect(factory).toHaveBeenCalledWith(expect.objectContaining({ baseInstructions: retainedSystemInstructions }));
expect(transport.calls.some(call => call.method === "thread/start" || call.method === "turn/start")).toBe(false); expect(transport.calls.some(call => call.method === "thread/start" || call.method === "turn/start")).toBe(false);
if (recovered.recovered) await recovered.session.close({ reason: "test complete" }); if (recovered.recovered) await recovered.session!.close({ reason: "test complete" });
}); });
describe("runner-prepared launch working directory", () => { describe("runner-prepared launch working directory", () => {
@@ -420,7 +420,7 @@ describe("native backend factory", () => {
return response; return response;
} }
} }
const identity = { runId: "run", sessionId: "session", companyId: "company", agentId: "agent" }; const identity = { runId: "run", sessionId: "session", companyId: "company", issueId: "issue", agentId: "agent" };
const providers = [ const providers = [
["Codex", execution()], ["Codex", execution()],
["OpenCode", opencodeExecution()], ["OpenCode", opencodeExecution()],
@@ -452,8 +452,8 @@ describe("native backend factory", () => {
}).recoverSession!({ ...snapshot, activeTurnId: null }, { signal: new AbortController().signal }); }).recoverSession!({ ...snapshot, activeTurnId: null }, { signal: new AbortController().signal });
expect(recovered).toMatchObject({ recovered: true }); expect(recovered).toMatchObject({ recovered: true });
if (recovered.recovered) { if (recovered.recovered) {
expect((await recovered.session.snapshot()).workingDirectory).toBe(cwd); expect((await recovered.session!.snapshot()).workingDirectory).toBe(cwd);
await recovered.session.close({ reason: "test complete" }); await recovered.session!.close({ reason: "test complete" });
} }
}); });
@@ -769,6 +769,7 @@ describe("native backend factory", () => {
await backend.openSession({ await backend.openSession({
identity: { identity: {
companyId: "company", companyId: "company",
issueId: "issue",
agentId: "agent", agentId: "agent",
runId: "run", runId: "run",
sessionId: "session", sessionId: "session",
@@ -807,7 +808,7 @@ describe("native backend factory", () => {
environment: { PAPERCLIP_WORKSPACE_CWD: WORKSPACE }, environment: { PAPERCLIP_WORKSPACE_CWD: WORKSPACE },
}); });
const session = await backend.openSession({ const session = await backend.openSession({
identity: { companyId: "company", agentId: "agent", runId: "run", sessionId: "session" }, identity: { companyId: "company", issueId: "issue", agentId: "agent", runId: "run", sessionId: "session" },
workingDirectory: WORKSPACE, workingDirectory: WORKSPACE,
}); });
const first = JSON.stringify(buildNativeModelEnvelope(input)); const first = JSON.stringify(buildNativeModelEnvelope(input));
@@ -21,7 +21,7 @@ import { inspectNativeCompletionSourceMetadata } from "../../../../tests/runner-
// It measures complete Paperclip-supplied instruction/tool/message projections, // It measures complete Paperclip-supplied instruction/tool/message projections,
// not provider-owned stock prompts, tokenization, billing or model behavior. // not provider-owned stock prompts, tokenization, billing or model behavior.
const tools = PAPERCLIP_SEMANTIC_ACTION_CATALOG const tools = PAPERCLIP_SEMANTIC_ACTION_CATALOG
.filter(tool => tool.placement === "core") .filter(tool => tool.placement === "always")
.map(tool => ({ name: tool.operationId, description: tool.description, inputSchema: tool.inputSchema })); .map(tool => ({ name: tool.operationId, description: tool.description, inputSchema: tool.inputSchema }));
const identity = { companyId: "company", agentId: "agent", issueId: "issue", runId: "run", sessionId: "session" }; const identity = { companyId: "company", agentId: "agent", issueId: "issue", runId: "run", sessionId: "session" };
const { qualificationModel: _qualificationModel, reportedModelId: _reportedModelId, permissionPolicy: _permissionPolicy, ...profile } = resolveQualifiedAcpxProfile("claude", "claude-sonnet-5"); const { qualificationModel: _qualificationModel, reportedModelId: _reportedModelId, permissionPolicy: _permissionPolicy, ...profile } = resolveQualifiedAcpxProfile("claude", "claude-sonnet-5");
@@ -182,7 +182,7 @@ describe("direct OpenCode HTTP instruction boundary", () => {
try { try {
const checkpoint = await session.snapshot(); const checkpoint = await session.snapshot();
const send = async (current: typeof session, phase: string, value: NativeExecutionInput) => { const send = async (current: typeof session, phase: string, value: NativeExecutionInput) => {
const envelope = buildNativeModelEnvelope(value, { resumedSession: phase === "continuation" }); const envelope = phase === "continuation" ? buildNativeModelEnvelope(value, { resumedSession: true }) : buildNativeModelEnvelope(value);
if ("requestedSkills" in envelope && backend.preparedTaskConstraints) envelope.constraints = [...backend.preparedTaskConstraints]; if ("requestedSkills" in envelope && backend.preparedTaskConstraints) envelope.constraints = [...backend.preparedTaskConstraints];
await current.startTurn({ message: { role: "user", text: JSON.stringify(envelope) } }); await current.startTurn({ message: { role: "user", text: JSON.stringify(envelope) } });
for await (const event of current.events()) { if (event.eventType === "turn.completed") break; if (["turn.failed", "turn.cancelled"].includes(event.eventType)) throw new Error(`Fixture turn failed: ${event.eventType}`); } for await (const event of current.events()) { if (event.eventType === "turn.completed") break; if (["turn.failed", "turn.cancelled"].includes(event.eventType)) throw new Error(`Fixture turn failed: ${event.eventType}`); }
@@ -54,16 +54,20 @@ describe("public runner browser client", () => {
fetchImpl: vi.fn() as unknown as typeof fetch, fetchImpl: vi.fn() as unknown as typeof fetch,
eventSourceFactory: (nextUrl) => { url = nextUrl; return source; }, eventSourceFactory: (nextUrl) => { url = nextUrl; return source; },
}); });
const event = { const event: PrpEvent = {
schema: "paperclip.prp.event.v1", schema: "paperclip.prp.event.v1",
sourceEventId: "event-9", sourceEventId: "event-9",
sourceKind: "harness", sourceKind: "runner",
sourceInstanceId: "driver", sourceInstanceId: "driver",
sourceSeq: 9, sourceSeq: 9,
runId: "run-1",
normalizedSessionId: "session/id",
schemaVersion: 1,
priority: 2,
emittedAt: "2026-08-08T00:00:00.000Z", emittedAt: "2026-08-08T00:00:00.000Z",
eventType: "item.delta", eventType: "item.delta",
payload: { text: "exact" }, payload: { text: "exact" },
} as PrpEvent; };
const onEvent = vi.fn(); const onEvent = vi.fn();
const onError = vi.fn(); const onError = vi.fn();
const handle = client.openEventStream("session/id", 8, { onEvent, onError }); const handle = client.openEventStream("session/id", 8, { onEvent, onError });
@@ -1,17 +1,17 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { LiveConsoleScriptedDriver } from "../mock-core/live-console-scripted-driver"; import { LiveConsoleScriptedDriver } from "../mock-core/live-console-scripted-driver.js";
import { import {
parseHarnessRuntimeRequestResolution, parseHarnessRuntimeRequestResolution,
type HarnessSession, type HarnessSession,
} from "../contracts/harness-driver"; } from "../contracts/harness-driver.js";
import type { PrpEvent } from "../protocol/replay-contract"; import type { PrpEvent } from "../protocol/replay-contract.js";
import { import {
applyPrpEvent, applyPrpEvent,
createSessionSnapshotFromMetadata, createSessionSnapshotFromMetadata,
reduceSessionEvents, reduceSessionEvents,
type SessionSnapshot, type SessionSnapshot,
} from "../reducer/session-reducer"; } from "../reducer/session-reducer.js";
import type { RunnerCapabilities } from "./protocol.js"; import type { RunnerCapabilities } from "./protocol.js";
import { import {
buildTranscript, buildTranscript,
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { PAPERCLIP_PROTOCOL_ACTIONS } from "../protocol-actions/index.js"; import { PAPERCLIP_PROTOCOL_ACTIONS } from "../protocol-actions/index.js";
@@ -50,8 +50,9 @@ describe("canonical Paperclip protocol action contracts", () => {
).toEqual([]); ).toEqual([]);
if (action.scenario.descriptor.idempotency === "required") { if (action.scenario.descriptor.idempotency === "required") {
expect(action.examples).toHaveProperty("scenarioCall"); expect(action.examples).toHaveProperty("scenarioCall");
expect(scenarioCall.idempotencyKey).toEqual(expect.any(String)); const { idempotencyKey } = scenarioCall as { idempotencyKey?: string };
expect(scenarioCall.idempotencyKey).not.toHaveLength(0); expect(idempotencyKey).toEqual(expect.any(String));
expect(idempotencyKey).not.toHaveLength(0);
} else { } else {
expect(scenarioCall).not.toHaveProperty("idempotencyKey"); expect(scenarioCall).not.toHaveProperty("idempotencyKey");
} }
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
import { dirname, resolve } from "node:path"; import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { createTaskAction } from "../protocol-actions/create-task.js"; import { createTaskAction } from "../protocol-actions/create-task.js";
import { createProjectAction } from "../protocol-actions/create-project.js"; import { createProjectAction } from "../protocol-actions/create-project.js";
@@ -449,7 +449,7 @@ describe("qualified ACPX runtime sidecar", () => {
// The receipt hashes the actual emitted input, not a second normalization. // The receipt hashes the actual emitted input, not a second normalization.
await loadActualToolCommands(tools).resolve({ callId: "3", turnId: "test-turn", result: { accepted: true }, error: null }); await loadActualToolCommands(tools).resolve({ callId: "3", turnId: "test-turn", result: { accepted: true }, error: null });
} }
const body = await (await response).json(); const body = await (await response).json() as ToolCallResponseBody;
const receipt = readNativeSemanticReceipt({ contents: body.result.content }); const receipt = readNativeSemanticReceipt({ contents: body.result.content });
expect(receipt).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(raw), expect(receipt).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(raw),
normalizedInputSha256: mode === "forwarded" ? semanticInputSha256(emitted[0].input) : null, normalizedInputSha256: mode === "forwarded" ? semanticInputSha256(emitted[0].input) : null,
@@ -490,7 +490,7 @@ describe("qualified ACPX runtime sidecar", () => {
expect(validatePrpStructuredRunResult(emitted[1].input).ok).toBe(true); expect(validatePrpStructuredRunResult(emitted[1].input).ok).toBe(true);
await commands.resolve({ callId: "3", turnId: "test-turn", result: { accepted: true }, error: null }); await commands.resolve({ callId: "3", turnId: "test-turn", result: { accepted: true }, error: null });
} }
const body = await (await response).json(); const body = await (await response).json() as ToolCallResponseBody;
const receipt = readNativeSemanticReceipt({ contents: body.result.content }); const receipt = readNativeSemanticReceipt({ contents: body.result.content });
expect(receipt).toMatchObject({ inputSha256: semanticInputSha256(raw), expect(receipt).toMatchObject({ inputSha256: semanticInputSha256(raw),
normalizedInputSha256: eligible ? semanticInputSha256(emitted[1].input) : null, outcome: eligible ? "returned" : "error" }); normalizedInputSha256: eligible ? semanticInputSha256(emitted[1].input) : null, outcome: eligible ? "returned" : "error" });
@@ -517,7 +517,7 @@ describe("qualified ACPX runtime sidecar", () => {
await vi.waitFor(() => expect(emitted).toHaveLength(1)); await vi.waitFor(() => expect(emitted).toHaveLength(1));
await expect(handler(call)).rejects.toThrow("duplicated"); await expect(handler(call)).rejects.toThrow("duplicated");
} finally { controller.abort(); } } finally { controller.abort(); }
expect((await result).message).toContain("cancelled"); expect((await result as Error).message).toContain("cancelled");
expect(capture).not.toHaveBeenCalled(); expect(capture).not.toHaveBeenCalled();
expect(tools.size).toBe(0); expect(tools.size).toBe(0);
expect(emitted[0].operationId).toBe("get_task_context"); expect(emitted[0].operationId).toBe("get_task_context");
@@ -547,7 +547,7 @@ describe("qualified ACPX runtime sidecar", () => {
const bridge = await startRunnerToolBridge({ handler, captureSemanticReceipt: () => () => {} }); const bridge = await startRunnerToolBridge({ handler, captureSemanticReceipt: () => () => {} });
try { try {
const body = await (await fetch(bridge.url, { method: "POST", headers: { Authorization: `Bearer ${bridge.secret}`, "Content-Type": "application/json" }, const body = await (await fetch(bridge.url, { method: "POST", headers: { Authorization: `Bearer ${bridge.secret}`, "Content-Type": "application/json" },
body: JSON.stringify(request) })).json(); body: JSON.stringify(request) })).json() as ToolCallResponseBody;
expect(body.result.isError).toBe(true); expect(body.result.isError).toBe(true);
expect(body.result.content[0].text).toBe("ACPX semantic tool call exceeds the sidecar frame limit"); expect(body.result.content[0].text).toBe("ACPX semantic tool call exceeds the sidecar frame limit");
expect(readNativeSemanticReceipt({ contents: body.result.content })).toMatchObject({ outcome: "error", normalizedInputSha256: null }); expect(readNativeSemanticReceipt({ contents: body.result.content })).toMatchObject({ outcome: "error", normalizedInputSha256: null });
@@ -561,7 +561,7 @@ describe("qualified ACPX runtime sidecar", () => {
} finally { await bridge.close(); } } finally { await bridge.close(); }
}); });
it.each(["backpressure", "throws"])("uses actual writer acceptance, preserving %s semantics", async mode => { it.each(["backpressure", "throws"] as const)("uses actual writer acceptance, preserving %s semantics", async mode => {
const wire = loadActualSidecarWriter(mode); const wire = loadActualSidecarWriter(mode);
const tools = new Map<string, any>(), emitted: any[] = [], commit = vi.fn(), capture = vi.fn((_input: unknown) => commit); const tools = new Map<string, any>(), emitted: any[] = [], commit = vi.fn(), capture = vi.fn((_input: unknown) => commit);
const raw = { reportedWorkDisposition: "done", summary: "Complete", evidence: [], verification: [], const raw = { reportedWorkDisposition: "done", summary: "Complete", evidence: [], verification: [],
@@ -727,7 +727,7 @@ describe("qualified ACPX runtime sidecar", () => {
expect(Buffer.byteLength(JSON.stringify(JSON.parse(wire.writes[0]!).payload))).toBeGreaterThan(256 * 1024); expect(Buffer.byteLength(JSON.stringify(JSON.parse(wire.writes[0]!).payload))).toBeGreaterThan(256 * 1024);
expect(Buffer.byteLength(wire.writes[0]!)).toBeLessThan(ACPX_SIDECAR_MAX_FRAME_BYTES); expect(Buffer.byteLength(wire.writes[0]!)).toBeLessThan(ACPX_SIDECAR_MAX_FRAME_BYTES);
} }
const body = await (await response).json(); const body = await (await response).json() as ToolCallResponseBody;
if (mode === "error") { if (mode === "error") {
expect(JSON.stringify(body)).not.toContain(credentialCanary); expect(JSON.stringify(body)).not.toContain(credentialCanary);
expect(JSON.stringify(body)).toContain("[REDACTED]"); expect(JSON.stringify(body)).toContain("[REDACTED]");
@@ -820,8 +820,9 @@ describe("qualified ACPX runtime sidecar", () => {
const sanitize = new Function("boundedOptionalText", "stableProviderIdentity", `return event => {${source.slice(start, end)} return null;}`)( const sanitize = new Function("boundedOptionalText", "stableProviderIdentity", `return event => {${source.slice(start, end)} return null;}`)(
(value: string) => value, (value: string) => `stable-${value}`, (value: string) => value, (value: string) => `stable-${value}`,
); );
const projection = createPiMessageProjection<{ type: string; text: string; stream: string; messageId: string; meta: Record<string, string> }>(); type PiTextFrame = { type: string; text: string; stream: string; messageId: string; meta: Record<string, string> };
const event = (kind: string) => ({ type: "text_delta", text: "", stream: "output", messageId: `pi-message-${"a".repeat(64)}`, const projection = createPiMessageProjection<PiTextFrame>();
const event = (kind: string): PiTextFrame => ({ type: "text_delta", text: "", stream: "output", messageId: `pi-message-${"a".repeat(64)}`,
meta: { origin: "pi-native-assistant", source: "pi-rpc-message-v1", kind, secret: "DROP_ME" } }); meta: { origin: "pi-native-assistant", source: "pi-rpc-message-v1", kind, secret: "DROP_ME" } });
const startFrame = sanitize(projection.normalize(event("start"))); const startFrame = sanitize(projection.normalize(event("start")));
const endFrame = sanitize(projection.normalize(event("end:toolUse"))); const endFrame = sanitize(projection.normalize(event("end:toolUse")));
@@ -830,7 +831,7 @@ describe("qualified ACPX runtime sidecar", () => {
expect(startFrame.messageId).toBe(endFrame.messageId); expect(startFrame.messageId).toBe(endFrame.messageId);
expect(JSON.stringify([startFrame, endFrame])).not.toContain("DROP_ME"); expect(JSON.stringify([startFrame, endFrame])).not.toContain("DROP_ME");
projection.settle(); projection.settle();
const loaded = createPiMessageProjection<ReturnType<typeof event>>(); const loaded = createPiMessageProjection<PiTextFrame>();
const history = sanitize(loaded.normalize({ ...event("history"), messageId: `pi-history-message-${"b".repeat(64)}`, const history = sanitize(loaded.normalize({ ...event("history"), messageId: `pi-history-message-${"b".repeat(64)}`,
text: "old reply", meta: { origin: "pi-history-assistant", source: "pi-session-history-v1", kind: "history" } })); text: "old reply", meta: { origin: "pi-history-assistant", source: "pi-session-history-v1", kind: "history" } }));
expect(history).toMatchObject({ text: "old reply", piMessageHistory: true }); loaded.settle(); expect(history).toMatchObject({ text: "old reply", piMessageHistory: true }); loaded.settle();
@@ -1744,6 +1745,8 @@ class SidecarProcess {
} }
} }
type ToolCallResponseBody = { result: { content: Array<{ text: string }>; isError?: boolean } };
function loadWaitForTool(input: { function loadWaitForTool(input: {
tools: Map<string, unknown>; tools: Map<string, unknown>;
emitted: unknown[]; emitted: unknown[];
@@ -54,7 +54,9 @@ describe("ACPX sidecar input sequencing", () => {
async () => { async () => {
events.push(command); events.push(command);
}, },
() => events.push(`${command}:error`), () => {
events.push(`${command}:error`);
},
); );
} }
const shutdown = pending.then(() => events.push("shutdown")); const shutdown = pending.then(() => events.push("shutdown"));
@@ -87,7 +89,9 @@ describe("ACPX sidecar input sequencing", () => {
async () => { async () => {
events.push("recovered"); events.push("recovered");
}, },
() => events.push("unexpected"), () => {
events.push("unexpected");
},
); );
await recovered; await recovered;
@@ -113,7 +117,9 @@ describe("ACPX sidecar input sequencing", () => {
async () => { async () => {
events.push("recovered"); events.push("recovered");
}, },
() => events.push("unexpected"), () => {
events.push("unexpected");
},
); );
await expect( await expect(
@@ -123,7 +123,7 @@ describe("eval-session request contract", () => {
const value = request({ provider: "acpx", acpxAgent: agent, model: "explicit-provider-model" }); const value = request({ provider: "acpx", acpxAgent: agent, model: "explicit-provider-model" });
expect(() => parseEvalSessionRequest(value)).toThrow("--candidate-profile"); expect(() => parseEvalSessionRequest(value)).toThrow("--candidate-profile");
expect(parseEvalSessionRequest(value, { candidateProfile: agent })).toMatchObject({ acpxAgent: agent, model: "explicit-provider-model" }); expect(parseEvalSessionRequest(value, { candidateProfile: agent })).toMatchObject({ acpxAgent: agent, model: "explicit-provider-model" });
expect(() => parseEvalSessionRequest(value, { candidateProfile: agent === "pi" ? "cursor" : "pi" })).toThrow("must match"); expect(() => parseEvalSessionRequest(value, { candidateProfile: "pi" })).toThrow("must match");
expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: agent, model: "" }), { candidateProfile: agent })).toThrow("request.model"); expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: agent, model: "" }), { candidateProfile: agent })).toThrow("request.model");
expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: "codex", session: { acpxAgent: agent } }))).toThrow("session.acpxAgent must match"); expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: "codex", session: { acpxAgent: agent } }))).toThrow("session.acpxAgent must match");
expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: agent, candidateProfile: agent }))).toThrow("--candidate-profile"); expect(() => parseEvalSessionRequest(request({ provider: "acpx", acpxAgent: agent, candidateProfile: agent }))).toThrow("--candidate-profile");
@@ -525,10 +525,10 @@ describe("eval-session usage", () => {
providerReported: { nanodollars: null, recordedAttempts: 0 }, providerReported: { nanodollars: null, recordedAttempts: 0 },
estimated: { nanodollars: 6_200_000, recordedAttempts: 1 }, estimated: { nanodollars: 6_200_000, recordedAttempts: 1 },
}); });
expect(snapshot.usageLedger[0]?.costNanodollars).toBe(legacyCost); expect(snapshot.usageLedger![0]?.costNanodollars).toBe(legacyCost);
const limitedRequest = parseEvalSessionRequest(request()); const limitedRequest = parseEvalSessionRequest(request());
limitedRequest.limits.maxEstimatedCostNanodollars = 10_000_000; limitedRequest.limits.maxEstimatedCostNanodollars = 10_000_000;
const costlySnapshot = { ...snapshot, usageLedger: [{ ...snapshot.usageLedger[0]!, costNanodollars: 10_000_001 }] }; const costlySnapshot = { ...snapshot, usageLedger: [{ ...snapshot.usageLedger![0]!, costNanodollars: 10_000_001 }] };
expect(() => boundedEvalSessionUsage(limitedRequest, { expect(() => boundedEvalSessionUsage(limitedRequest, {
turnId: "codex-turn", status: "completed", assistantText: "Completed", snapshot: costlySnapshot, turnId: "codex-turn", status: "completed", assistantText: "Completed", snapshot: costlySnapshot,
})).toThrow("legacy ledger cost limit exceeded"); })).toThrow("legacy ledger cost limit exceeded");
@@ -609,7 +609,7 @@ describe("eval-session budget settlement", () => {
}); });
const input = request({ provider: "acpx", acpxAgent: agent, model, const input = request({ provider: "acpx", acpxAgent: agent, model,
runnerd: { path: binary, sha256: createHash("sha256").update("unused fake runner").digest("hex") }, runnerd: { path: binary, sha256: createHash("sha256").update("unused fake runner").digest("hex") },
session: { workingDirectory: workspace, ...(agent === "pi" ? { piThinkingLevel: "low" } : {}) }, limits: { turnTimeoutMs: 1000, maxAgentTurns: 2, maxEstimatedCostNanodollars: 100_000_000 }, session: { workingDirectory: workspace }, limits: { turnTimeoutMs: 1000, maxAgentTurns: 2, maxEstimatedCostNanodollars: 100_000_000 },
}); });
const requestPath = join(workspace, "request.json"); const requestPath = join(workspace, "request.json");
const outputPath = join(workspace, "output.json"); const outputPath = join(workspace, "output.json");
@@ -17,14 +17,18 @@ describe("OpenCode proxy input sequencing", () => {
await firstBlocked; await firstBlocked;
events.push("initialize:complete"); events.push("initialize:complete");
}, },
() => events.push("initialize:error"), () => {
events.push("initialize:error");
},
); );
pending = enqueueOpenCodeProxyInput( pending = enqueueOpenCodeProxyInput(
pending, pending,
async () => { async () => {
events.push("thread:start"); events.push("thread:start");
}, },
() => events.push("thread:error"), () => {
events.push("thread:error");
},
); );
const shutdown = pending.then(() => { const shutdown = pending.then(() => {
events.push("shutdown"); events.push("shutdown");
@@ -50,7 +54,9 @@ describe("OpenCode proxy input sequencing", () => {
async () => { async () => {
throw new Error("initialize failed"); throw new Error("initialize failed");
}, },
(error) => events.push((error as Error).message), (error) => {
events.push((error as Error).message);
},
); );
await pending; await pending;
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import type { NativeExecutionInput } from "../contracts/native-execution.js"; import type { NativeExecutionInput, NativeExecutionInputV1 } from "../contracts/native-execution.js";
import type { import type {
NativeRunIdentity, NativeRunIdentity,
NativeSessionCapabilities, NativeSessionCapabilities,
@@ -29,8 +29,8 @@ type ProfileKey = "codex" | "opencode" | "acpx-claude" | "acpx-codex";
interface LocalNativeProfile { interface LocalNativeProfile {
key: ProfileKey; key: ProfileKey;
driverKind: NativeExecutionInput["session"]["driverKind"]; driverKind: NativeExecutionInputV1["session"]["driverKind"];
provider: NativeExecutionInput["provider"]; provider: NativeExecutionInputV1["provider"];
} }
/** /**
@@ -1,5 +1,5 @@
import { normalizeLegacyPrpStructuredRunResult } from "../protocol/result-normalization.js"; import { normalizeLegacyPrpStructuredRunResult } from "../protocol/result-normalization.js";
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { import {
PRP_BLOCK_RESULT_OUTPUT_SCHEMA, PRP_BLOCK_RESULT_OUTPUT_SCHEMA,
@@ -76,5 +76,5 @@ describe("ProviderTraceFileSink", () => {
}); });
function statMode(value: Awaited<ReturnType<typeof stat>>): number { function statMode(value: Awaited<ReturnType<typeof stat>>): number {
return value.mode & 0o777; return Number(value.mode) & 0o777;
} }
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { questionSetSchema } from "../protocol/generated/schema-bundle.js"; import { questionSetSchema } from "../protocol/generated/schema-bundle.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
@@ -101,7 +101,7 @@ it("settles a create rejected during real-facade Stop before cold recovery and t
PAPERCLIP_CURSOR_CLOUD_RELAY_EXPIRES_AT: String(Date.now() + 60_000) }; PAPERCLIP_CURSOR_CLOUD_RELAY_EXPIRES_AT: String(Date.now() + 60_000) };
const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand,
cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`,
lifecyclePolicy: { mode: "per_turn" as const }, environment }; lifecyclePolicy: { mode: "per_turn" as const, idleTimeoutMs: null }, environment };
const first = createRunnerdCodexTransport({ ...options, prpIdentity: identity }), bundles = [first]; const first = createRunnerdCodexTransport({ ...options, prpIdentity: identity }), bundles = [first];
let stop: Promise<unknown> | undefined; let stop: Promise<unknown> | undefined;
try { try {
@@ -169,7 +169,7 @@ it("opens and closes the real Cloud facade without a provider prompt", async ()
runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand,
cursorCloudProxyPath: proxyPath, cursorCloudProxyPath: proxyPath,
cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`,
lifecyclePolicy: { mode: "per_turn" }, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
prpIdentity: { runnerInstanceId: "runner-cloud-launch", environmentLeaseId: "lease-cloud-launch", prpIdentity: { runnerInstanceId: "runner-cloud-launch", environmentLeaseId: "lease-cloud-launch",
runId: "00000000-0000-4000-8000-000000000761", normalizedSessionId, turnId: "turn-cloud-launch", itemId: "item-cloud-launch" }, runId: "00000000-0000-4000-8000-000000000761", normalizedSessionId, turnId: "turn-cloud-launch", itemId: "item-cloud-launch" },
environment: { environment: {
@@ -249,7 +249,7 @@ it("preserves native Cloud tool identity and redacted output through the real Ru
provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"), provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"),
runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath,
cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`,
lifecyclePolicy: { mode: "per_turn" }, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
prpIdentity: { runnerInstanceId: "runner-cloud-tool", environmentLeaseId: "lease-cloud-tool", prpIdentity: { runnerInstanceId: "runner-cloud-tool", environmentLeaseId: "lease-cloud-tool",
runId: "00000000-0000-4000-8000-000000000762", normalizedSessionId, turnId: "turn-cloud-tool", itemId: "item-cloud-tool" }, runId: "00000000-0000-4000-8000-000000000762", normalizedSessionId, turnId: "turn-cloud-tool", itemId: "item-cloud-tool" },
environment: { environment: {
@@ -343,7 +343,7 @@ async function cloudQuestionHandoff(terminalDelayMs: number, governed: boolean,
provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"), provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"),
runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath,
cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`,
lifecyclePolicy: { mode: "per_turn" }, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
prpIdentity: { runnerInstanceId: "runner-cloud-question", environmentLeaseId: "lease-cloud-question", prpIdentity: { runnerInstanceId: "runner-cloud-question", environmentLeaseId: "lease-cloud-question",
runId: "00000000-0000-4000-8000-000000000763", normalizedSessionId, turnId: "turn-cloud-question", itemId: "item-cloud-question" }, runId: "00000000-0000-4000-8000-000000000763", normalizedSessionId, turnId: "turn-cloud-question", itemId: "item-cloud-question" },
environment: { environment: {
@@ -405,7 +405,7 @@ async function cloudQuestionHandoff(terminalDelayMs: number, governed: boolean,
const execution = executeNativeSession({ input, backend, runnerInstanceId: "runner-cloud-question", controlPlaneInstanceId: "control-cloud-question", timeoutMs: 120_000, const execution = executeNativeSession({ input, backend, runnerInstanceId: "runner-cloud-question", controlPlaneInstanceId: "control-cloud-question", timeoutMs: 120_000,
controlPlane: { controlPlane: {
async openRun() {}, async checkpointSession() {}, completeRun, async openRun() {}, async checkpointSession() {}, completeRun,
async appendEvent(event) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents(replay) { return { events: history.filter(event => event.sourceInstanceId === replay.sourceInstanceId && event.sourceSeq > replay.afterSourceSeq), highestContiguousSourceSeq: 0 }; }, async replayEvents(replay) { return { events: history.filter(event => event.sourceInstanceId === replay.sourceInstanceId && event.sourceSeq > replay.afterSourceSeq), highestContiguousSourceSeq: 0 }; },
}, },
resolveGovernedWait: ({ event }) => event.eventType === "item.completed" && event.payload.kind === "dynamicToolCall" ? yielded : null, resolveGovernedWait: ({ event }) => event.eventType === "item.completed" && event.payload.kind === "dynamicToolCall" ? yielded : null,
@@ -520,7 +520,7 @@ async function cloudColdAuthorityRotation(instructions: string, validShutdown =
PAPERCLIP_CURSOR_CLOUD_RELAY_URL: `https://relay.example.test/mcp/native-runs/${runId}`, PAPERCLIP_CURSOR_CLOUD_RELAY_EXPIRES_AT: String(Date.now() + 60_000) }); PAPERCLIP_CURSOR_CLOUD_RELAY_URL: `https://relay.example.test/mcp/native-runs/${runId}`, PAPERCLIP_CURSOR_CLOUD_RELAY_EXPIRES_AT: String(Date.now() + 60_000) });
const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand,
cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`, cursorCloudProxyPath: proxyPath, cursorCloudProxySha256: `sha256:${createHash("sha256").update(await readFile(proxyPath)).digest("hex")}`,
lifecyclePolicy: { mode: "per_turn" as const } }; lifecyclePolicy: { mode: "per_turn" as const, idleTimeoutMs: null } };
const mcp = async (token: string, body: Record<string, unknown>, sessionId?: string, priorScopeHeader?: string): Promise<{ status: number; sessionId?: string; value: any }> => { const mcp = async (token: string, body: Record<string, unknown>, sessionId?: string, priorScopeHeader?: string): Promise<{ status: number; sessionId?: string; value: any }> => {
const scopeHeader = priorScopeHeader ?? await currentCloudScopeHeader(runtime, normalizedSessionId); const scopeHeader = priorScopeHeader ?? await currentCloudScopeHeader(runtime, normalizedSessionId);
return new Promise((accept, reject) => { return new Promise((accept, reject) => {
@@ -39,6 +39,8 @@ import {
inspectRetainedRunInstructions, inspectRetainedRunInstructions,
inspectStagedRunAttachmentHeader, inspectStagedRunAttachmentHeader,
type DurablePrpCheckpoint, type DurablePrpCheckpoint,
type DurablePrpControlPlaneStore,
type RunnerProcessHandle,
durableRecoveryInternals, durableRecoveryInternals,
SemanticToolNotDispatchedError, SemanticToolNotDispatchedError,
inspectWarmRunTransition, inspectWarmRunTransition,
@@ -48,6 +50,16 @@ import {
} from "./durable-prp-control-plane.js"; } from "./durable-prp-control-plane.js";
import type { DurableRecoveryIdentity } from "./prp-transport-types.js"; import type { DurableRecoveryIdentity } from "./prp-transport-types.js";
type WritableCoreStore = DurablePrpControlPlaneStore & {
save(): void;
commit(candidate: DurablePrpControlPlaneStore["state"]): void;
};
function restartRunner(handle: RunnerProcessHandle, ticket: string): void {
if (!handle.restart) throw new Error("Runner handle does not support restart");
handle.restart(ticket);
}
const identity: DurableRecoveryIdentity = { const identity: DurableRecoveryIdentity = {
runnerInstanceId: "runner-test-1", runnerInstanceId: "runner-test-1",
environmentLeaseId: "environment-test-1", environmentLeaseId: "environment-test-1",
@@ -229,7 +241,7 @@ it("recovers only the matching run's admitted instructions from a valid checkpoi
expect(inspectRetainedRunInstructions(core.store.state, { ...targetExpected, runtimeContextDigest: digest })).toBeNull(); expect(inspectRetainedRunInstructions(core.store.state, { ...targetExpected, runtimeContextDigest: digest })).toBeNull();
expect(inspectRetainedRunInstructions(core.store.state, expected)).toBe(template.provider.instructions); expect(inspectRetainedRunInstructions(core.store.state, expected)).toBe(template.provider.instructions);
const corrupted = structuredClone(core.store.state); const corrupted = structuredClone(core.store.state);
corrupted.stagedRunAttachment!.templateDigest = `sha256:${"f".repeat(64)}`; corrupted.stagedRunAttachment = { ...corrupted.stagedRunAttachment!, templateDigest: `sha256:${"f".repeat(64)}` };
expect(inspectRetainedRunInstructions(corrupted, targetExpected)).toBeNull(); expect(inspectRetainedRunInstructions(corrupted, targetExpected)).toBeNull();
} finally { await core.stop(); rmSync(root, { recursive: true, force: true }); } } finally { await core.stop(); rmSync(root, { recursive: true, force: true }); }
}); });
@@ -545,7 +557,7 @@ it("excludes streamed output from checkpoints even when the live window is full"
anchor.eventType = "turn.started"; anchor.eventType = "turn.started";
(anchor.envelope.payload as Record<string, unknown>).eventType = "turn.started"; (anchor.envelope.payload as Record<string, unknown>).eventType = "turn.started";
core.store.state.ackedSourceSeq = 4096; core.store.state.ackedSourceSeq = 4096;
core.store.save(); (core.store as WritableCoreStore).save();
const snapshots: DurablePrpCheckpoint[] = []; const snapshots: DurablePrpCheckpoint[] = [];
core.setCheckpointPersistence(async saved => { snapshots.push(saved); }); core.setCheckpointPersistence(async saved => { snapshots.push(saved); });
try { try {
@@ -918,7 +930,7 @@ it.each([false, true])("dispatches a fresh semantic input once when recovery ove
if (JSON.parse(saved.records.get("cursor")!).ackedSourceSeq > 0) { writeHeld = true; await writeBarrier; } if (JSON.parse(saved.records.get("cursor")!).ackedSourceSeq > 0) { writeHeld = true; await writeBarrier; }
}); });
const pending: Promise<void>[] = []; const pending: Promise<void>[] = [];
let client: Awaited<ReturnType<typeof authenticate>>; let client: AuthenticatedClient | null = null;
try { try {
await core.start(); await core.start();
client = (await authenticate(core, core.issueBootstrapTicket()))!; client = (await authenticate(core, core.issueBootstrapTicket()))!;
@@ -1149,7 +1161,7 @@ it.each(["event", "progress", "lifecycle", "lifecycle_priority_one", "usage", "c
expect(() => core.queueCommand("session.snapshot")).toThrow("indeterminate"); expect(() => core.queueCommand("session.snapshot")).toThrow("indeterminate");
} }
if (mode === "authority_conflict") { if (mode === "authority_conflict") {
core.store.commit(structuredClone(core.store.state)); (core.store as WritableCoreStore).commit(structuredClone(core.store.state));
core.queueCommand("session.snapshot", {}, "command_after_authority"); core.queueCommand("session.snapshot", {}, "command_after_authority");
expect(JSON.parse(readFileSync(core.store.path, "utf8")).ackedSourceSeq).toBe(0); expect(JSON.parse(readFileSync(core.store.path, "utf8")).ackedSourceSeq).toBe(0);
expect(core.store.state.ackedSourceSeq).toBe(0); expect(core.store.state.ackedSourceSeq).toBe(0);
@@ -1817,7 +1829,7 @@ it("pins the ACPX launch profile in runner startup arguments and restarts", () =
}, },
}); });
handle.restart("replacement-ticket"); restartRunner(handle, "replacement-ticket");
expect(launches).toHaveLength(2); expect(launches).toHaveLength(2);
for (const launch of launches) { for (const launch of launches) {
expect(launch.args).toContain("--acpx-launch-authority-digest"); expect(launch.args).toContain("--acpx-launch-authority-digest");
@@ -1869,7 +1881,7 @@ it("pins the OpenCode launch profile in runner startup arguments and restarts",
}, },
}); });
handle.restart("replacement-ticket"); restartRunner(handle, "replacement-ticket");
expect(launches).toHaveLength(2); expect(launches).toHaveLength(2);
for (const launch of launches) { for (const launch of launches) {
expect(launch.args).toEqual( expect(launch.args).toEqual(
@@ -2057,7 +2069,7 @@ it("preserves only bounded GitHub credential projection at the runner spawn boun
it("passes structured inputs to an external owner and preserves its replacement veto", () => { it("passes structured inputs to an external owner and preserves its replacement veto", () => {
const process = { child: { exitCode: null, signalCode: null, kill: () => true }, const process = { child: { exitCode: null, signalCode: null, kill: () => true },
completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }), restart: null }; completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }), restart: null };
const launcher = vi.fn(() => process); const launcher = vi.fn((_spec: RunnerProcessLaunchSpec) => process);
const options = { connection: { mode: "external" as const }, stateDirectory: "/unused", identity, const options = { connection: { mode: "external" as const }, stateDirectory: "/unused", identity,
ticket: "synthetic-ticket", maxOutboxBytes: 262144, p0ReserveBytes: 65536, ticket: "synthetic-ticket", maxOutboxBytes: 262144, p0ReserveBytes: 65536,
runnerVersion: expectedRunnerVersion, runnerDigest: expectedRunnerDigest, runnerVersion: expectedRunnerVersion, runnerDigest: expectedRunnerDigest,
@@ -2065,7 +2077,7 @@ it("passes structured inputs to an external owner and preserves its replacement
expect(() => spawnRunner(options)).toThrow("requires a process owner"); expect(() => spawnRunner(options)).toThrow("requires a process owner");
expect(spawnRunner({ ...options, processLauncher: launcher })).toBe(process); expect(spawnRunner({ ...options, processLauncher: launcher })).toBe(process);
expect(launcher).toHaveBeenCalledWith(expect.objectContaining({ nativeRunner: expect.objectContaining(options) })); expect(launcher).toHaveBeenCalledWith(expect.objectContaining({ nativeRunner: expect.objectContaining(options) }));
expect(launcher.mock.calls[0][0].args).not.toContain("--connect-url"); expect(launcher.mock.calls[0]![0].args).not.toContain("--connect-url");
}); });
it("preserves the controller-selected ACPX provider package root", () => { it("preserves the controller-selected ACPX provider package root", () => {
@@ -2147,7 +2159,7 @@ it.each([
completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }) }; completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }) };
}, },
}); });
handle.restart("replacement-ticket"); restartRunner(handle, "replacement-ticket");
for (const launch of launches.splice(0)) { for (const launch of launches.splice(0)) {
expect(launch.environment[key]).toBe(explicit ? "explicit-fixture-credential" : undefined); expect(launch.environment[key]).toBe(explicit ? "explicit-fixture-credential" : undefined);
const receipt = launch.environment[ACPX_CREDENTIAL_BINDING_ENV]; const receipt = launch.environment[ACPX_CREDENTIAL_BINDING_ENV];
@@ -19,37 +19,41 @@ function snapshot(): CapabilityLiveSessionSnapshot {
current.lifecycle = "running"; current.lifecycle = "running";
current.artifacts.push({ current.artifacts.push({
id: "artifact-1", id: "artifact-1",
companyId: "company-1",
taskId: "task-1", taskId: "task-1",
runId: "run-1", filename: "result.txt",
kind: "text", contentType: "text/plain",
name: "result.txt", byteSize: 6,
sha256: "a".repeat(64),
contentRef: "file:///private/result.txt", contentRef: "file:///private/result.txt",
metadata: {},
createdAt: "2026-08-09T00:00:01.000Z", createdAt: "2026-08-09T00:00:01.000Z",
}); });
return { return {
id: "session-1", schema: "paperclip.capability.live-session.v1",
sessionId: "session-1",
revision: 1, revision: 1,
status: "running", status: "running",
activeTurnId: null, activeTurnId: null,
providerThreadId: "thread-1", providerThreadId: "thread-1",
providerSessionId: "provider-session-1", providerSessionId: "provider-session-1",
providerModel: "gpt-test", providerModel: { id: "gpt-test", provider: "openai" },
config: { config: {
seedState: JSON.stringify(seed), seedState: JSON.stringify(seed),
workingDirectory: "/private/worktree", workingDirectory: "/private/worktree",
scenario: "hb-baseline", scenario: { id: "hb-baseline" },
capabilities: ["task:read"], capabilities: ["task:read"],
explicitClaims: [], explicitClaims: [],
turnTimeoutMs: 1_000, turnTimeoutMs: 1_000,
}, },
authority: { authority: {
active: true,
companyId: "company-1", companyId: "company-1",
actorId: "actor-1", actorId: "actor-1",
taskId: "task-1", taskId: "task-1",
runId: "run-1", runId: "run-1",
sessionId: "session-1", sessionId: "session-1",
scenarioId: "hb-baseline",
capabilities: ["task:read"],
explicitClaims: [],
}, },
transcript: [], transcript: [],
evidence: [{ evidence: [{
@@ -64,8 +68,9 @@ function snapshot(): CapabilityLiveSessionSnapshot {
}, },
}], }],
mockState: JSON.stringify(current), mockState: JSON.stringify(current),
authorizationRecords: [],
process: null, process: null,
networkEvidence: [], networkEvidence: { realPaperclipRequests: 0, childPaperclipEnvironmentKeys: [] },
createdAt: "2026-08-09T00:00:00.000Z", createdAt: "2026-08-09T00:00:00.000Z",
updatedAt: "2026-08-09T00:00:01.000Z", updatedAt: "2026-08-09T00:00:01.000Z",
stateHistory: [{ stateHistory: [{
@@ -168,5 +168,6 @@ it("rejects an unacknowledged thinking-level change after a native family model
const f = await fixture("family-change"), owner = await f.open(); const f = await fixture("family-change"), owner = await f.open();
const result = await f.turn(owner); const result = await f.turn(owner);
expect(result.status).toBe("failed"); expect(result.status).toBe("failed");
if (result.status !== "failed") throw new Error("turn did not fail");
expect(result.error?.message).toMatch(/admission/i); expect(result.error?.message).toMatch(/admission/i);
}); });
@@ -22,7 +22,7 @@ function response(message: ReturnType<typeof request>, changes: Record<string, u
sessionId: "native-session", models: { currentModelId: model }, modes: { currentModeId: "default" }, sessionId: "native-session", models: { currentModelId: model }, modes: { currentModeId: "default" },
_meta: { paperclipAntigravityAdmission: { _meta: { paperclipAntigravityAdmission: {
...meta.antigravity.paperclipAdmission, sessionId: "native-session", connectionId: "native-connection", ...changes, ...meta.antigravity.paperclipAdmission, sessionId: "native-session", connectionId: "native-connection", ...changes,
} }, } as Record<string, unknown> },
} }; } };
} }
const prompt = () => ({ id: "turn", method: "session/prompt", params: { sessionId: "native-session", prompt: [{ type: "text", text: "Do the task" }] } }); const prompt = () => ({ id: "turn", method: "session/prompt", params: { sessionId: "native-session", prompt: [{ type: "text", text: "Do the task" }] } });
@@ -2,7 +2,7 @@ import { appendSemanticToolReceipt } from "../semantic-tool-receipt.js";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { cursorToolIdentity } from "./cursor-plan-tool-identity.js"; import { cursorToolIdentity } from "./cursor-plan-tool-identity.js";
import { describe, expect, it, vi } from "vitest"; import { describe, expect, it, vi, type Mock } from "vitest";
import { AcpxProviderProcessLostError } from "./codex-runtime-adapter.js"; import { AcpxProviderProcessLostError } from "./codex-runtime-adapter.js";
import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js"; import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
@@ -31,7 +31,7 @@ describe("Codex ACPX harness driver", () => {
it("captures the direct driver's normalized input only when the exact proposal is retained", async () => { it("captures the direct driver's normalized input only when the exact proposal is retained", async () => {
const fixture = driverFixture({ agent: "copilot", model: "explicit-test-model", providerPolicy: { readOnly: true } }, { runtimeEvents: [] }); const fixture = driverFixture({ agent: "copilot", model: "explicit-test-model", providerPolicy: { readOnly: true } }, { runtimeEvents: [] });
const session = await fixture.driver.openSession({ runId: "run-normalized-receipt", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-normalized-receipt", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
await session.startTurn({ message: { text: "Complete" } }); await session.startTurn({ message: { role: "user", text: "Complete" } });
const { schema: _, attentionRequests: __, artifacts: ___, ...raw } = completedResult(); const { schema: _, attentionRequests: __, artifacts: ___, ...raw } = completedResult();
const commit = vi.fn(), capture = vi.fn((value: unknown) => { expect(commit).not.toHaveBeenCalled(); return commit; }); const commit = vi.fn(), capture = vi.fn((value: unknown) => { expect(commit).not.toHaveBeenCalled(); return commit; });
const handler = fixture.hostOptions!.semanticTools!.handler; const handler = fixture.hostOptions!.semanticTools!.handler;
@@ -58,7 +58,7 @@ describe("Codex ACPX harness driver", () => {
runtimeEvents: Array.from({ length: 8 }, (_, n) => ({ type: "text_delta" as const, stream: "output" as const, text: `bounded-${n}` })), runtimeEvents: Array.from({ length: 8 }, (_, n) => ({ type: "text_delta" as const, stream: "output" as const, text: `bounded-${n}` })),
}); });
const session = await fixture.driver.openSession({ runId: "run-normalized-pressure", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-normalized-pressure", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
await session.startTurn({ message: { text: "Complete" } }); await session.startTurn({ message: { role: "user", text: "Complete" } });
await vi.waitFor(async () => expect((await session.transcript!()).eventCount).toBeGreaterThanOrEqual(4)); await vi.waitFor(async () => expect((await session.transcript!()).eventCount).toBeGreaterThanOrEqual(4));
const commit = vi.fn(), capture = vi.fn(() => commit); const commit = vi.fn(), capture = vi.fn(() => commit);
await expect(fixture.hostOptions!.semanticTools!.handler({ tool: PRP_COMPLETION_TOOL_NAME, callId: "finish", arguments: completedResult(), await expect(fixture.hostOptions!.semanticTools!.handler({ tool: PRP_COMPLETION_TOOL_NAME, callId: "finish", arguments: completedResult(),
@@ -107,23 +107,23 @@ describe("Codex ACPX harness driver", () => {
}, },
} }; } };
fixture.host.status.mockResolvedValueOnce(before as never).mockResolvedValue(after as never); fixture.host.status.mockResolvedValueOnce(before as never).mockResolvedValue(after as never);
await session.startTurn({ message: { text: "fixture" } }); await session.startTurn({ message: { role: "user", text: "fixture" } });
fixture.finishTurn({ status: "completed", stopReason: "end_turn" }); fixture.finishTurn({ status: "completed", stopReason: "end_turn" });
const events = await collectUntil(session.events(), "turn.completed"); const events = await collectUntil(session.events(), "turn.completed");
const notices = events.filter(e => e.eventType === "provider.notice.recorded" && e.payload.category === "cursor_native_usage_observed"); const notices = events.filter(e => e.eventType === "provider.notice.recorded" && e.payload.category === "cursor_native_usage_observed");
expect(notices).toHaveLength(agent === "cursor" ? 1 : 0); expect(notices).toHaveLength(agent === "cursor" ? 1 : 0);
expect(events.filter(e => e.eventType === "usage.updated")).toEqual([]); expect(events.filter(e => e.eventType === "usage.reported")).toEqual([]);
expect(events.at(-1)?.eventType).toBe("turn.completed"); expect(events.at(-1)?.eventType).toBe("turn.completed");
await session.close({ reason: "partial diagnostics verified" }); await session.close({ reason: "partial diagnostics verified" });
}); });
it.each(["tool-1", "tool with spaces", "🧭/plan", "x".repeat(300)])("binds a native Cursor plan request to its actual projected tool identity: %s", async toolCallId => { it.each(["tool-1", "tool with spaces", "🧭/plan", "x".repeat(300)])("binds a native Cursor plan request to its actual projected tool identity: %s", async toolCallId => {
const fixture = driverFixture({ agent: "cursor", model: "explicit-test-model", mode: "plan", providerPolicy: { readOnly: false } }, { const fixture = driverFixture({ agent: "cursor", model: "explicit-test-model", mode: "plan", providerPolicy: { readOnly: false } }, {
runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "arbitrary tool display", kind: "execute", status: "pending" }], runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "arbitrary tool display", kind: "execute", status: "pending", text: "arbitrary tool display" }],
}); });
const session = await fixture.driver.openSession({ runId: "run-plan-identity", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-plan-identity", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const toolEvents = collectUntil(session.events(), "tool.execution.started"); const toolEvents = collectUntil(session.events(), "tool.execution.started");
const { turnId } = await session.startTurn({ message: { text: "Plan" } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Plan" } });
const tool = (await toolEvents).find(e => e.eventType === "tool.execution.started")!; const tool = (await toolEvents).find(e => e.eventType === "tool.execution.started")!;
const created = collectUntil(session.events(), "runtime_request.created"); const created = collectUntil(session.events(), "runtime_request.created");
const callback = fixture.host.startTurn.mock.calls[0]![0].onExtensionRequest!; const callback = fixture.host.startTurn.mock.calls[0]![0].onExtensionRequest!;
@@ -145,7 +145,7 @@ describe("Codex ACPX harness driver", () => {
]; ];
const fixture = driverFixture({ agent, model: "explicit-model", providerPolicy: { readOnly: true } }, { runtimeEvents }); const fixture = driverFixture({ agent, model: "explicit-model", providerPolicy: { readOnly: true } }, { runtimeEvents });
const session = await fixture.driver.openSession({ runId: "other-provider-identity", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "other-provider-identity", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const { turnId } = await session.startTurn({ message: { text: "fixture" } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "fixture" } });
fixture.finishTurn({ status: "completed" }); fixture.finishTurn({ status: "completed" });
const events = await collectUntil(session.events(), "turn.completed"); const events = await collectUntil(session.events(), "turn.completed");
const messages = events.filter(event => event.payload.kind === "agentMessage"); const messages = events.filter(event => event.payload.kind === "agentMessage");
@@ -162,7 +162,7 @@ describe("Codex ACPX harness driver", () => {
runtimeEvents.push(chunk(undefined, "Info: native session activity")); runtimeEvents.push(chunk(undefined, "Info: native session activity"));
const fixture = driverFixture({ agent: "copilot", model: "explicit-model", providerPolicy: { readOnly: true } }, { runtimeEvents }); const fixture = driverFixture({ agent: "copilot", model: "explicit-model", providerPolicy: { readOnly: true } }, { runtimeEvents });
const session = await fixture.driver.openSession({ runId: "run-copilot-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-copilot-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
await session.startTurn({ message: { text: "Answer exactly" } }); await session.startTurn({ message: { role: "user", text: "Answer exactly" } });
fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" }); fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" });
const events = await collectUntil(session.events(), scenario === "cancelled" ? "turn.interrupted" : "turn.completed"); const events = await collectUntil(session.events(), scenario === "cancelled" ? "turn.interrupted" : "turn.completed");
const interim = events.find(event => event.eventType === "item.delta" && event.payload.text === "EXACT_MARKER"); const interim = events.find(event => event.eventType === "item.delta" && event.payload.text === "EXACT_MARKER");
@@ -179,7 +179,7 @@ describe("Codex ACPX harness driver", () => {
it("keeps Pi retry-only activity as a rich notice without inventing a final answer", async () => { it("keeps Pi retry-only activity as a rich notice without inventing a final answer", async () => {
const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] }); const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] });
const session = await fixture.driver.openSession({ runId: "run-pi-notice", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-pi-notice", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
await session.startTurn({ message: { text: "Work" } }); await session.startTurn({ message: { role: "user", text: "Work" } });
const input = fixture.host.startTurn.mock.calls[0]![0]; const input = fixture.host.startTurn.mock.calls[0]![0];
await input.onExtensionNotification!("paperclip/pi_notice", { sessionId: "backend-1", category: "auto_retry_end", summary: "Retry did not recover.", severity: "warning", details: { attempt: 2, success: false } }); await input.onExtensionNotification!("paperclip/pi_notice", { sessionId: "backend-1", category: "auto_retry_end", summary: "Retry did not recover.", severity: "warning", details: { attempt: 2, success: false } });
fixture.finishTurn({ status: "completed" }); fixture.finishTurn({ status: "completed" });
@@ -201,7 +201,7 @@ describe("Codex ACPX harness driver", () => {
} }
const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents }); const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents });
const session = await fixture.driver.openSession({ runId: "run-pi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-pi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
await session.startTurn({ message: { text: "Answer exactly" } }); await session.startTurn({ message: { role: "user", text: "Answer exactly" } });
fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" }); fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" });
const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed"); const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed");
expect(events.some(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")).toBe(true); expect(events.some(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")).toBe(true);
@@ -233,7 +233,7 @@ describe("Codex ACPX harness driver", () => {
} }
const fixture = driverFixture({ agent: "kimi", model: "kimi-for-coding", providerPolicy: { readOnly: true } }, { runtimeEvents }); const fixture = driverFixture({ agent: "kimi", model: "kimi-for-coding", providerPolicy: { readOnly: true } }, { runtimeEvents });
const session = await fixture.driver.openSession({ runId: "run-kimi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-kimi-boundaries", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const { turnId } = await session.startTurn({ message: { text: "Answer exactly" } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Answer exactly" } });
fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" }); fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "completed" });
const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed"); const events = await collectUntil(session.events(), scenario === "missing-end" ? "turn.failed" : scenario === "cancelled" ? "turn.interrupted" : "turn.completed");
const progress = events.find(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")!; const progress = events.find(event => event.eventType === "item.delta" && event.payload.text === "Calling finish.")!;
@@ -256,34 +256,34 @@ describe("Codex ACPX harness driver", () => {
const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
expect(fixture.hostOptions?.providerPolicy).toEqual({ readOnly: true }); expect(fixture.hostOptions?.providerPolicy).toEqual({ readOnly: true });
expect(session.turnControlCapabilities?.()).toEqual({ steering: true, queuedFollowUp: true }); expect(session.turnControlCapabilities?.()).toEqual({ steering: true, queuedFollowUp: true });
const { turnId } = await session.startTurn({ message: { text: "Work" } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Work" } });
const turnInput = fixture.host.startTurn.mock.calls[0]![0]; const turnInput = fixture.host.startTurn.mock.calls[0]![0];
const context = { requestId: 0, signal: new AbortController().signal }; const context = { requestId: 0, signal: new AbortController().signal };
await expect(turnInput.onExtensionRequest!("provider/request", { sessionId: "agent-1" }, context)).rejects.toThrow(/session mismatch/); await expect(turnInput.onExtensionRequest!("provider/request", { sessionId: "agent-1" }, context)).rejects.toThrow(/session mismatch/);
await session.steer!({ turnId, correlationId: "control-1", message: { text: "Change focus" } }); await session.steer!({ turnId, correlationId: "control-1", message: { role: "user", text: "Change focus" } });
await session.steer!({ turnId, correlationId: "control-2", mode: "follow_up", message: { text: "Then validate" } }); await session.steer!({ turnId, correlationId: "control-2", mode: "follow_up", message: { role: "user", text: "Then validate" } });
expect(fixture.host.steerActiveTurn).toHaveBeenCalledExactlyOnceWith("Change focus", `run-controls:${turnId}`); expect(fixture.host.steerActiveTurn).toHaveBeenCalledExactlyOnceWith("Change focus", `run-controls:${turnId}`);
expect(fixture.host.queueFollowUp).toHaveBeenCalledExactlyOnceWith("Then validate", `run-controls:${turnId}`); expect(fixture.host.queueFollowUp).toHaveBeenCalledExactlyOnceWith("Then validate", `run-controls:${turnId}`);
expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled(); expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled();
await expect(session.steer!({ turnId, correlationId: "control-2", message: { text: "Duplicate" } })).rejects.toThrow(/already attempted/); await expect(session.steer!({ turnId, correlationId: "control-2", message: { role: "user", text: "Duplicate" } })).rejects.toThrow(/already attempted/);
await expect(session.steer!({ turnId: "wrong-turn", message: { text: "Stale" } })).rejects.toThrow(); await expect(session.steer!({ turnId: "wrong-turn", message: { role: "user", text: "Stale" } })).rejects.toThrow();
fixture.finishTurn({ status: "completed" }); fixture.finishTurn({ status: "completed" });
const events = await collectUntil(session.events(), "turn.completed"); const events = await collectUntil(session.events(), "turn.completed");
expect(events.filter(event => event.payload.kind === "steering_acknowledgement").map(event => event.payload.mode)).toEqual(["steer", "follow_up"]); expect(events.filter(event => event.payload.kind === "steering_acknowledgement").map(event => event.payload.mode)).toEqual(["steer", "follow_up"]);
for (const event of events) expect(validatePrpEvent(event).ok).toBe(true); for (const event of events) expect(validatePrpEvent(event).ok).toBe(true);
await expect(session.steer!({ turnId, message: { text: "Already settled" } })).rejects.toThrow(); await expect(session.steer!({ turnId, message: { role: "user", text: "Already settled" } })).rejects.toThrow();
await session.close({ reason: "controls verified" }); await session.close({ reason: "controls verified" });
}); });
it("rejects unnegotiated controls and retains ambiguous delivery attempts", async () => { it("rejects unnegotiated controls and retains ambiguous delivery attempts", async () => {
const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] }); const fixture = driverFixture({ agent: "pi", model: "openrouter/deepseek/deepseek-v4-flash-0731", providerPolicy: { readOnly: true } }, { runtimeEvents: [] });
const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const { turnId } = await session.startTurn({ message: { text: "Work" } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Work" } });
await expect(session.steer!({ turnId, message: { text: "No handshake" } })).rejects.toThrow(/did not negotiate/); await expect(session.steer!({ turnId, message: { role: "user", text: "No handshake" } })).rejects.toThrow(/did not negotiate/);
expect(fixture.host.steerActiveTurn).not.toHaveBeenCalled(); expect(fixture.host.steerActiveTurn).not.toHaveBeenCalled();
fixture.host.steeringCapability.mockReturnValue({ steering: true, queuedFollowUp: false }); fixture.host.steeringCapability.mockReturnValue({ steering: true, queuedFollowUp: false });
fixture.host.steerActiveTurn.mockRejectedValueOnce(new Error("delivery acknowledgement lost")); fixture.host.steerActiveTurn.mockRejectedValueOnce(new Error("delivery acknowledgement lost"));
const input = { turnId, correlationId: "ambiguous", message: { text: "Maybe delivered" } }; const input = { turnId, correlationId: "ambiguous", message: { role: "user" as const, text: "Maybe delivered" } };
await expect(session.steer!(input)).rejects.toThrow(/acknowledgement lost/); await expect(session.steer!(input)).rejects.toThrow(/acknowledgement lost/);
await expect(session.steer!(input)).rejects.toThrow(/already attempted/); await expect(session.steer!(input)).rejects.toThrow(/already attempted/);
expect(fixture.host.steerActiveTurn).toHaveBeenCalledOnce(); expect(fixture.host.steerActiveTurn).toHaveBeenCalledOnce();
@@ -453,7 +453,7 @@ describe("Codex ACPX harness driver", () => {
], ],
}); });
const session = await fixture.driver.openSession({ runId: "run-claude-banners", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-claude-banners", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
await session.startTurn({ message: { text: "Fix it" } }); await session.startTurn({ message: { role: "user", text: "Fix it" } });
fixture.finishTurn({ status: "completed" }); fixture.finishTurn({ status: "completed" });
const events = await collectUntil(session.events(), "turn.completed"); const events = await collectUntil(session.events(), "turn.completed");
for (const event of events) validatePrpEvent(event); for (const event of events) validatePrpEvent(event);
@@ -713,11 +713,11 @@ describe("Codex ACPX harness driver", () => {
signal: new AbortController().signal, signal: new AbortController().signal,
}), }),
).rejects.toThrow("does not match"); ).rejects.toThrow("does not match");
await session.interrupt({ turnId, reason: "user cancelled" }); await session.interrupt!({ turnId, reason: "user cancelled" });
expect(fixture.host.interruptActiveTurn).toHaveBeenCalledWith( expect(fixture.host.interruptActiveTurn).toHaveBeenCalledWith(
"user cancelled", "user cancelled",
); );
await expect(session.interrupt({ turnId: "stale-turn" })).rejects.toThrow( await expect(session.interrupt!({ turnId: "stale-turn" })).rejects.toThrow(
"is not the active turn", "is not the active turn",
); );
await session.close({ reason: "cancelled" }); await session.close({ reason: "cancelled" });
@@ -1291,13 +1291,13 @@ describe("Codex ACPX harness driver", () => {
const fixture = driverFixture({}, { closeSettlementTimeoutMs: 1 }); const fixture = driverFixture({}, { closeSettlementTimeoutMs: 1 });
const hostA = { const hostA = {
...fixture.host, ...fixture.host,
close: vi.fn(async () => { close: vi.fn(async (_input: { reason: string }): Promise<void> => {
throw new Error("host A cleanup failed"); throw new Error("host A cleanup failed");
}), }),
}; };
const hostB = { const hostB = {
...fixture.host, ...fixture.host,
close: vi.fn(async () => { close: vi.fn(async (_input: { reason: string }): Promise<void> => {
throw new Error("host B cleanup failed"); throw new Error("host B cleanup failed");
}), }),
}; };
@@ -1606,7 +1606,7 @@ describe("Codex ACPX harness driver", () => {
).rejects.toThrow("awaiting canonical terminal-event retention"); ).rejects.toThrow("awaiting canonical terminal-event retention");
expect(dynamicToolHandler).not.toHaveBeenCalled(); expect(dynamicToolHandler).not.toHaveBeenCalled();
await expect( await expect(
session.interrupt({ turnId, reason: "too late" }), session.interrupt!({ turnId, reason: "too late" }),
).rejects.toThrow("awaiting canonical terminal-event retention"); ).rejects.toThrow("awaiting canonical terminal-event retention");
expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled(); expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled();
await expect(session.snapshot()).resolves.toMatchObject({ await expect(session.snapshot()).resolves.toMatchObject({
@@ -1761,7 +1761,7 @@ describe("Codex ACPX harness driver", () => {
const fixture = driverFixture({ agent: "copilot", model: "gpt-5.6-luna", providerPolicy: { readOnly: false } }, { eventStreamFailure: failure.promise }); const fixture = driverFixture({ agent: "copilot", model: "gpt-5.6-luna", providerPolicy: { readOnly: false } }, { eventStreamFailure: failure.promise });
const session = await fixture.driver.openSession({ runId: "run-provider-death", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-provider-death", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const created = collectUntil(session.events(), "runtime_request.created"); const created = collectUntil(session.events(), "runtime_request.created");
const { turnId } = await session.startTurn({ message: { text: "Edit the file" } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Edit the file" } });
const callback = fixture.host.startTurn.mock.calls[0]![0].onPermissionRequest!; const callback = fixture.host.startTurn.mock.calls[0]![0].onPermissionRequest!;
const controller = new AbortController(); const controller = new AbortController();
const response = callback({ inferredKind: "edit", raw: { const response = callback({ inferredKind: "edit", raw: {
@@ -1895,11 +1895,11 @@ describe("Codex ACPX harness driver", () => {
it.each(["cursor-tool", "native\u0000tool", "native\u007ftool"].flatMap(toolCallId => ["written", "failed"].map(outcome => ({ toolCallId, outcome }))))("binds Cursor denial evidence to its original tool and response write: $outcome/$toolCallId", async ({ outcome, toolCallId }) => { it.each(["cursor-tool", "native\u0000tool", "native\u007ftool"].flatMap(toolCallId => ["written", "failed"].map(outcome => ({ toolCallId, outcome }))))("binds Cursor denial evidence to its original tool and response write: $outcome/$toolCallId", async ({ outcome, toolCallId }) => {
const command = "printf 'sensitive-value' > /workspace/denied.txt"; const command = "printf 'sensitive-value' > /workspace/denied.txt";
const fixture = driverFixture({ agent: "cursor", model: "explicit-test-model", providerPolicy: { readOnly: false } }, { const fixture = driverFixture({ agent: "cursor", model: "explicit-test-model", providerPolicy: { readOnly: false } }, {
runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "Run command", kind: "execute", status: "pending", rawInput: { command } }], runtimeEvents: [{ type: "tool_call", tag: "tool_call", toolCallId, title: "Run command", kind: "execute", status: "pending", text: "Run command", rawInput: { command } }],
}); });
const session = await fixture.driver.openSession({ runId: "run-cursor-receipt", normalizedSessionId: "session-1", workingDirectory: "/workspace" }); const session = await fixture.driver.openSession({ runId: "run-cursor-receipt", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const origin = collectUntil(session.events(), "tool.execution.started"); const origin = collectUntil(session.events(), "tool.execution.started");
const { turnId } = await session.startTurn({ message: { text: "Request the command." } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Request the command." } });
const originEvents = await origin; const originEvents = await origin;
const created = collectUntil(session.events(), "runtime_request.created"); const created = collectUntil(session.events(), "runtime_request.created");
const callback = fixture.host.startTurn.mock.calls[0]![0].onPermissionRequest!; const callback = fixture.host.startTurn.mock.calls[0]![0].onPermissionRequest!;
@@ -1942,7 +1942,7 @@ describe("Codex ACPX harness driver", () => {
const fixture = driverFixture({ agent, providerPolicy: { readOnly: false } }, { runtimeEvents }); const fixture = driverFixture({ agent, providerPolicy: { readOnly: false } }, { runtimeEvents });
const session = await fixture.driver.openSession({ runId: "run-attached", normalizedSessionId: "session-1", workingDirectory: "/fixture/workspace" }); const session = await fixture.driver.openSession({ runId: "run-attached", normalizedSessionId: "session-1", workingDirectory: "/fixture/workspace" });
const completed = collectUntil(session.events(), "turn.completed"); const completed = collectUntil(session.events(), "turn.completed");
const { turnId } = await session.startTurn({ message: { text: "Start attached work." } }); const { turnId } = await session.startTurn({ message: { role: "user", text: "Start attached work." } });
fixture.finishTurn({ status: "completed", stopReason: "end_turn" }); fixture.finishTurn({ status: "completed", stopReason: "end_turn" });
const events = await completed; const events = await completed;
const evidence = events.filter(event => event.eventType === "provider.notice.recorded" && event.payload.category === "copilot_tool_evidence_v1"); const evidence = events.filter(event => event.eventType === "provider.notice.recorded" && event.payload.category === "copilot_tool_evidence_v1");
@@ -1976,6 +1976,7 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation( const providerResponse = onElicitation(
{ {
mode: "form", mode: "form",
sessionId: "backend-1",
message: "Choose deployment settings.", message: "Choose deployment settings.",
requestedSchema: { requestedSchema: {
type: "object", type: "object",
@@ -2065,6 +2066,8 @@ describe("Codex ACPX harness driver", () => {
onElicitation( onElicitation(
{ {
mode: "form", mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: { requestedSchema: {
type: "object", type: "object",
properties: { value: { type: "string" } }, properties: { value: { type: "string" } },
@@ -2101,6 +2104,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation( const providerResponse = onElicitation(
{ {
mode: "form", mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: { requestedSchema: {
type: "object", type: "object",
properties: { value: { type: "string" } }, properties: { value: { type: "string" } },
@@ -2148,6 +2153,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation( const providerResponse = onElicitation(
{ {
mode: "form", mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: { requestedSchema: {
type: "object", type: "object",
properties: { value: { type: "string" } }, properties: { value: { type: "string" } },
@@ -2194,6 +2201,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation( const providerResponse = onElicitation(
{ {
mode: "form", mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: { requestedSchema: {
type: "object", type: "object",
properties: { value: { type: "string" } }, properties: { value: { type: "string" } },
@@ -2262,6 +2271,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation( const providerResponse = onElicitation(
{ {
mode: "form", mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: { requestedSchema: {
type: "object", type: "object",
properties: { value: { type: "string" } }, properties: { value: { type: "string" } },
@@ -3177,12 +3188,12 @@ function driverFixture(
function recoveryWorkspaceLease( function recoveryWorkspaceLease(
path = "/workspace", path = "/workspace",
): AcpxRecoveryWorkspaceLease & { ): AcpxRecoveryWorkspaceLease & {
assertHeld: ReturnType<typeof vi.fn>; assertHeld: Mock<() => void>;
close: ReturnType<typeof vi.fn>; close: Mock<() => Promise<void>>;
} { } {
return { return {
path, path,
assertHeld: vi.fn(), assertHeld: vi.fn<() => void>(),
close: vi.fn(async () => undefined), close: vi.fn(async () => undefined),
}; };
} }
@@ -3207,6 +3218,7 @@ function fakeHost(createTurn: () => AcpxRuntimeTurn, onClose: () => void) {
workspacePath: "/workspace", workspacePath: "/workspace",
workspaceDigest: `sha256:${"b".repeat(64)}`, workspaceDigest: `sha256:${"b".repeat(64)}`,
runtimeRoot: "/runtime/acpx/session-1", runtimeRoot: "/runtime/acpx/session-1",
commandDigest: `sha256:${"c".repeat(64)}`,
profileDigest: `sha256:${"a".repeat(64)}`, profileDigest: `sha256:${"a".repeat(64)}`,
requestedModel: "gpt-5.6-sol", requestedModel: "gpt-5.6-sol",
effectiveModel: "gpt-5.6-sol", effectiveModel: "gpt-5.6-sol",
@@ -3225,7 +3237,7 @@ function fakeHost(createTurn: () => AcpxRuntimeTurn, onClose: () => void) {
steerActiveTurn: vi.fn(async (_text: string, _requestId?: string) => undefined), steerActiveTurn: vi.fn(async (_text: string, _requestId?: string) => undefined),
queueFollowUp: vi.fn(async (_text: string, _requestId?: string) => undefined), queueFollowUp: vi.fn(async (_text: string, _requestId?: string) => undefined),
interruptActiveTurn: vi.fn(async () => undefined), interruptActiveTurn: vi.fn(async () => undefined),
close: vi.fn(async () => { close: vi.fn(async (_input: { reason: string }) => {
onClose(); onClose();
}), }),
}; };
@@ -7,6 +7,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import type { import type {
AcpPermissionRequest,
AcpAgentRegistry, AcpAgentRegistry,
AcpRuntime, AcpRuntime,
AcpRuntimeHandle, AcpRuntimeHandle,
@@ -114,7 +115,7 @@ describe("Codex ACPX runtime adapter", () => {
if (!restoring) spawnProvider(); if (!restoring) spawnProvider();
return HANDLE; return HANDLE;
}); });
vi.mocked(runtime.setConfigOption).mockImplementation(async input => { vi.mocked(runtime.setConfigOption!).mockImplementation(async input => {
if (restoring) spawnProvider(); if (restoring) spawnProvider();
const guard = created.protocolGuardFactory!(); const guard = created.protocolGuardFactory!();
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
@@ -546,7 +547,7 @@ describe("Codex ACPX runtime adapter", () => {
const options = openOptions(fakeCommand()); options.profile = { ...options.profile, agent: "cursor" }; const options = openOptions(fakeCommand()); options.profile = { ...options.profile, agent: "cursor" };
options.refreshConsumedCommand = vi.fn(async () => undefined); options.refreshConsumedCommand = vi.fn(async () => undefined);
let created!: AcpRuntimeOptions; let created!: AcpRuntimeOptions;
vi.mocked(runtime.setConfigOption).mockImplementation(async () => { vi.mocked(runtime.setConfigOption!).mockImplementation(async () => {
const guard = created.protocolGuardFactory!(); const guard = created.protocolGuardFactory!();
const binding = cursorInstructionBinding(options.systemInstructions); const binding = cursorInstructionBinding(options.systemInstructions);
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
@@ -574,7 +575,7 @@ describe("Codex ACPX runtime adapter", () => {
let created!: AcpRuntimeOptions; let created!: AcpRuntimeOptions;
const binding = cursorInstructionBinding(options.systemInstructions); const binding = cursorInstructionBinding(options.systemInstructions);
const configs = (mode: string) => [{ id: "mode", currentValue: mode }]; const configs = (mode: string) => [{ id: "mode", currentValue: mode }];
vi.mocked(runtime.setConfigOption).mockImplementation(async input => { vi.mocked(runtime.setConfigOption!).mockImplementation(async input => {
const guard = created.protocolGuardFactory!(); const guard = created.protocolGuardFactory!();
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
guard("inbound", { id: 0, result: { modes: { currentModeId: "agent" }, configOptions: configs("agent"), _meta: { paperclipCursorInstructions: { guard("inbound", { id: 0, result: { modes: { currentModeId: "agent" }, configOptions: configs("agent"), _meta: { paperclipCursorInstructions: {
@@ -586,7 +587,7 @@ describe("Codex ACPX runtime adapter", () => {
const port = await openCodexAcpxRuntime(options, { const port = await openCodexAcpxRuntime(options, {
createRegistry: () => registry(), createStore: () => store(), createRuntime: value => { created = value; return runtime; }, createRegistry: () => registry(), createStore: () => store(), createRuntime: value => { created = value; return runtime; },
}); });
expect(vi.mocked(runtime.setConfigOption).mock.calls.map(([call]) => [call.key, call.value])).toEqual([["model", options.profile.reportedModelId], ["mode", selected]]); expect(vi.mocked(runtime.setConfigOption!).mock.calls.map(([call]) => [call.key, call.value])).toEqual([["model", options.profile.reportedModelId], ["mode", selected]]);
expect(options.refreshConsumedCommand).toHaveBeenCalledTimes(2); expect(options.refreshConsumedCommand).toHaveBeenCalledTimes(2);
expect(await port.identity()).toMatchObject({ mode: selected }); expect(await port.identity()).toMatchObject({ mode: selected });
expect(runtime.startTurn).not.toHaveBeenCalled(); expect(runtime.startTurn).not.toHaveBeenCalled();
@@ -652,13 +653,13 @@ describe("Codex ACPX runtime adapter", () => {
it("requires the Pi capability handshake and acknowledgments, preserves follow-up order, and never starts a second prompt", async () => { it("requires the Pi capability handshake and acknowledgments, preserves follow-up order, and never starts a second prompt", async () => {
const pending = pendingExtensionTurn("turn-1"); const pending = pendingExtensionTurn("turn-1");
const runtime = Object.assign(fakeRuntime(), { requestExtension: vi.fn(async () => ({ accepted: true })) }); const runtime = Object.assign(fakeRuntime(), { requestExtension: vi.fn(async (_input: unknown) => ({ accepted: true })) });
vi.mocked(runtime.startTurn).mockReturnValue(pending.turn); vi.mocked(runtime.startTurn).mockReturnValue(pending.turn);
let created!: AcpRuntimeOptions & { onAgentInitialize?: (result: unknown) => void }; let created!: AcpRuntimeOptions & { onAgentInitialize?: (result: unknown) => void };
const options = openOptions(fakeCommand()); const options = openOptions(fakeCommand());
options.profile = { ...options.profile, agent: "pi" }; options.profile = { ...options.profile, agent: "pi" };
options.piThinkingLevel = "low"; options.piThinkingLevel = "low";
vi.mocked(runtime.setConfigOption).mockImplementation(async input => { vi.mocked(runtime.setConfigOption!).mockImplementation(async input => {
const guard = created.protocolGuardFactory!(); const guard = created.protocolGuardFactory!();
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } }); guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
guard("inbound", { id: 0, result: { modes: { currentModeId: "high" }, configOptions: [{ id: "thought_level", currentValue: "high" }] } }); guard("inbound", { id: 0, result: { modes: { currentModeId: "high" }, configOptions: [{ id: "thought_level", currentValue: "high" }] } });
@@ -734,9 +735,7 @@ describe("Codex ACPX runtime adapter", () => {
expect(runtimeOptions!.permissionPolicy).toMatchObject({ defaultAction: "escalate" }); expect(runtimeOptions!.permissionPolicy).toMatchObject({ defaultAction: "escalate" });
const turn = port.startTurn({ text: "Attempt a write.", requestId: "permission-test" }); const turn = port.startTurn({ text: "Attempt a write.", requestId: "permission-test" });
try { try {
await runtimeOptions!.onPermissionRequest!({ await runtimeOptions!.onPermissionRequest!(permissionRequest("backend-1", "edit"), { signal: new AbortController().signal });
sessionId: "backend-1", inferredKind: "write", raw: {},
}, { signal: new AbortController().signal });
expect(signal?.aborted).toBe(true); expect(signal?.aborted).toBe(true);
await expect(turn.result).rejects.toThrow("Approval required"); await expect(turn.result).rejects.toThrow("Approval required");
await expect((async () => { for await (const _event of turn.events) { /* drain */ } })()) await expect((async () => { for await (const _event of turn.events) { /* drain */ } })())
@@ -2361,21 +2360,13 @@ describe("Codex ACPX runtime adapter", () => {
]); ]);
await expect( await expect(
runtimeOptions?.onPermissionRequest?.( runtimeOptions?.onPermissionRequest?.(
{ permissionRequest("session-1", "execute", { _meta: { is_mcp_tool_approval: true } }),
sessionId: "session-1",
inferredKind: "execute",
raw: { _meta: { is_mcp_tool_approval: true } },
},
{ signal: new AbortController().signal }, { signal: new AbortController().signal },
), ),
).resolves.toEqual({ outcome: "reject_once" }); ).resolves.toEqual({ outcome: "reject_once" });
await expect( await expect(
runtimeOptions?.onPermissionRequest?.( runtimeOptions?.onPermissionRequest?.(
{ permissionRequest("session-1", "execute"),
sessionId: "session-1",
inferredKind: "execute",
raw: {},
},
{ signal: new AbortController().signal }, { signal: new AbortController().signal },
), ),
).resolves.toEqual({ outcome: "reject_once" }); ).resolves.toEqual({ outcome: "reject_once" });
@@ -2399,11 +2390,7 @@ describe("Codex ACPX runtime adapter", () => {
await expect( await expect(
runtimeOptions?.onPermissionRequest?.( runtimeOptions?.onPermissionRequest?.(
{ permissionRequest("session-1", "edit"),
sessionId: "session-1",
inferredKind: "write",
raw: {},
},
{ signal: new AbortController().signal }, { signal: new AbortController().signal },
), ),
).resolves.toEqual({ outcome: "reject_once" }); ).resolves.toEqual({ outcome: "reject_once" });
@@ -2438,11 +2425,7 @@ describe("Codex ACPX runtime adapter", () => {
expect(runtimeOptions?.nonInteractivePermissions).toBe("fail"); expect(runtimeOptions?.nonInteractivePermissions).toBe("fail");
await expect( await expect(
runtimeOptions?.onPermissionRequest?.( runtimeOptions?.onPermissionRequest?.(
{ permissionRequest("backend-1", "edit"),
sessionId: "backend-1",
inferredKind: "write",
raw: {},
},
{ signal: new AbortController().signal }, { signal: new AbortController().signal },
), ),
).resolves.toEqual(expected); ).resolves.toEqual(expected);
@@ -3618,8 +3601,18 @@ function fakeCommand(): VerifiedAcpxCommandLease {
return { spawn: vi.fn(), close: vi.fn() }; return { spawn: vi.fn(), close: vi.fn() };
} }
function fakeChild(): ChildProcess { type FakeChildProcess = ChildProcess & { exitCode: number | null; signalCode: NodeJS.Signals | null };
const child = new EventEmitter() as ChildProcess;
function permissionRequest(
sessionId: string,
inferredKind: AcpPermissionRequest["inferredKind"],
raw: Record<string, unknown> = {},
): AcpPermissionRequest {
return { sessionId, inferredKind, raw: raw as unknown as AcpPermissionRequest["raw"] };
}
function fakeChild(): FakeChildProcess {
const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, { Object.defineProperties(child, {
exitCode: { value: null, writable: true }, exitCode: { value: null, writable: true },
signalCode: { value: null, writable: true }, signalCode: { value: null, writable: true },
@@ -3633,10 +3626,10 @@ function fakeChild(): ChildProcess {
} }
function failingSignalChild(): { function failingSignalChild(): {
child: ChildProcess; child: FakeChildProcess;
errors: [Error, Error]; errors: [Error, Error];
} { } {
const child = new EventEmitter() as ChildProcess; const child = new EventEmitter() as FakeChildProcess;
const errors: [Error, Error] = [ const errors: [Error, Error] = [
new Error("SIGTERM delivery failed"), new Error("SIGTERM delivery failed"),
new Error("SIGKILL delivery failed"), new Error("SIGKILL delivery failed"),
@@ -3654,8 +3647,8 @@ function failingSignalChild(): {
return { child, errors }; return { child, errors };
} }
function childThatExitsAfterSignalCalls(exitAfter: number): ChildProcess { function childThatExitsAfterSignalCalls(exitAfter: number): FakeChildProcess {
const child = new EventEmitter() as ChildProcess; const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, { Object.defineProperties(child, {
exitCode: { value: null, writable: true }, exitCode: { value: null, writable: true },
signalCode: { value: null, writable: true }, signalCode: { value: null, writable: true },
@@ -3676,8 +3669,8 @@ function childThatExitsAfterSignalCalls(exitAfter: number): ChildProcess {
return child; return child;
} }
function stubbornChild(): ChildProcess { function stubbornChild(): FakeChildProcess {
const child = new EventEmitter() as ChildProcess; const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, { Object.defineProperties(child, {
pid: { value: 71_001 }, pid: { value: 71_001 },
exitCode: { value: null, writable: true }, exitCode: { value: null, writable: true },
@@ -3691,8 +3684,8 @@ function stubbornChild(): ChildProcess {
return child; return child;
} }
function childThatExitsOnKill(): ChildProcess { function childThatExitsOnKill(): FakeChildProcess {
const child = new EventEmitter() as ChildProcess; const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, { Object.defineProperties(child, {
pid: { value: 71_002 }, pid: { value: 71_002 },
exitCode: { value: null, writable: true }, exitCode: { value: null, writable: true },
@@ -20,7 +20,7 @@ it("reports missing Copilot authentication before spawn and releases admission o
agentServerPackageJsonPath: join(root, "provider/package.json"), agentRuntimePackageJsonPath: null, openCommand }), agentServerPackageJsonPath: join(root, "provider/package.json"), agentRuntimePackageJsonPath: null, openCommand }),
openRuntime, reportRetainedCleanupFailure: () => {}, openRuntime, reportRetainedCleanupFailure: () => {},
}; };
const options = { agent: "copilot" as const, model: "explicit-model", normalizedSessionId: "copilot-auth-test", const options = { agent: "copilot" as const, model: "explicit-model", permissionMode: "approve-reads" as const, normalizedSessionId: "copilot-auth-test",
runtimeDirectory, workingDirectory, providerPolicy: { readOnly: false } }; runtimeDirectory, workingDirectory, providerPolicy: { readOnly: false } };
try { try {
await expect(AcpxRuntimeHost.open({ ...options, environment: { GH_TOKEN: "ambient-forbidden", GITHUB_TOKEN: "ambient-forbidden" } }, dependencies)) await expect(AcpxRuntimeHost.open({ ...options, environment: { GH_TOKEN: "ambient-forbidden", GITHUB_TOKEN: "ambient-forbidden" } }, dependencies))
@@ -1,15 +1,16 @@
/// <reference lib="es2024.string" />
import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises"; import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest"; import { afterEach, describe, expect, it } from "vitest";
import { validateAcpxRichEvent } from "./profile-extensions.js"; import { validateAcpxRichEvent } from "./profile-extensions.js";
import { PAPERCLIP_QUESTION_RESPONSE_SCHEMA } from "../../contracts/question-set.js"; import { PAPERCLIP_QUESTION_RESPONSE_SCHEMA, type PaperclipQuestionAnswer } from "../../contracts/question-set.js";
import { import {
createCursorNotificationNormalizer, createCursorProfileExtensionAdapter, createCursorSubagentNormalizer, cursorWorkspaceArtifactReference, createCursorNotificationNormalizer, createCursorProfileExtensionAdapter, createCursorSubagentNormalizer, cursorWorkspaceArtifactReference,
normalizeCursorPlanRequest, normalizeCursorQuestionRequest, normalizeCursorSubagentUpdate, normalizeCursorPlanRequest, normalizeCursorQuestionRequest, normalizeCursorSubagentUpdate,
} from "./cursor-extensions.js"; } from "./cursor-extensions.js";
const response = (answers: Record<string, unknown>) => ({ schema: PAPERCLIP_QUESTION_RESPONSE_SCHEMA, answers }); const response = (answers: Record<string, PaperclipQuestionAnswer>) => ({ schema: PAPERCLIP_QUESTION_RESPONSE_SCHEMA, answers });
const roots: string[] = []; const roots: string[] = [];
afterEach(async () => { await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true }))); }); afterEach(async () => { await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true }))); });
async function workspace() { const root = await mkdtemp(join(tmpdir(), "cursor-extension-")); roots.push(root); return root; } async function workspace() { const root = await mkdtemp(join(tmpdir(), "cursor-extension-")); roots.push(root); return root; }
@@ -1,3 +1,4 @@
/// <reference lib="es2024.promise" />
import { spawn, type ChildProcess } from "node:child_process"; import { spawn, type ChildProcess } from "node:child_process";
import { mkdtemp, rm } from "node:fs/promises"; import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
@@ -43,7 +44,7 @@ it.each(["tool-first", "permission-first"])("real ACPX streams Cursor tool origi
const evidence = createCursorToolEvidence({ sessionId: "native", turnId: "turn", workingDirectory: cwd, active: () => true, const evidence = createCursorToolEvidence({ sessionId: "native", turnId: "turn", workingDirectory: cwd, active: () => true,
emit: event => { validateAcpxRichEvent(event); fields.push(Object.fromEntries((event.payload.details as Array<{ name: string; value: string }>).map(field => [field.name, field.value]))); }, emit: event => { validateAcpxRichEvent(event); fields.push(Object.fromEntries((event.payload.details as Array<{ name: string; value: string }>).map(field => [field.name, field.value]))); },
}); });
const runtime = createAcpRuntime({ cwd, agentRegistry: createAgentRegistry({ overrides: { cursor: "fixture" } }), sessionStore: createRuntimeStore({ stateDir: join(cwd, "state") }), permissionMode: "approve-reads", authPolicy: "skip", const runtime = createAcpRuntime({ cwd, agentRegistry: createAgentRegistry({ overrides: { cursor: "fixture" } }), sessionStore: createRuntimeStore({ stateDir: join(cwd, "state") }), permissionMode: "approve-reads",
spawnAgent: () => { const child = spawn(process.execPath, ["-e", peer], { cwd, env: { ORDER: order }, stdio: ["pipe", "pipe", "pipe"] }); children.push(child); return child; }, spawnAgent: () => { const child = spawn(process.execPath, ["-e", peer], { cwd, env: { ORDER: order }, stdio: ["pipe", "pipe", "pipe"] }); children.push(child); return child; },
onAcpMessage: (direction, value) => { onAcpMessage: (direction, value) => {
const message = value as { id?: number; result?: { outcome?: unknown } }; const message = value as { id?: number; result?: { outcome?: unknown } };
@@ -1,3 +1,4 @@
/// <reference lib="es2024.promise" />
import { spawn } from "node:child_process"; import { spawn } from "node:child_process";
import { createRequire } from "node:module"; import { createRequire } from "node:module";
import { mkdtemp, rm } from "node:fs/promises"; import { mkdtemp, rm } from "node:fs/promises";
@@ -34,7 +34,7 @@ it.each(["tool-first", "permission-first"])("correlates opaque C0/C1/DEL native
expect(expectedIds.at(-1)).toBe("safe-tool-1"); expect(expectedIds.at(-1)).toBe("safe-tool-1");
for (const [index, toolCallId] of rawIds.entries()) { for (const [index, toolCallId] of rawIds.entries()) {
const native = { sessionId: "session", inferredKind: "execute" as const, raw: { const native = { sessionId: "session", inferredKind: "execute" as const, raw: {
sessionId: "session", toolCall: { toolCallId, kind: "execute" }, sessionId: "session", toolCall: { toolCallId, kind: "execute" as const },
options: [{ kind: "reject_once" as const, optionId: `native-denial-${index}`, name: "Deny" }], options: [{ kind: "reject_once" as const, optionId: `native-denial-${index}`, name: "Deny" }],
} }; } };
const before = structuredClone(native); const before = structuredClone(native);
@@ -99,7 +99,7 @@ it("ignores inactive turns and never lets observation errors undo a delivered de
}); });
it("bounds retained tool origins", () => { it("bounds retained tool origins", () => {
const s = setup(); for (let i = 0; i < 257; i++) s.p.tool({ ...initial, toolCallId: `tool-${i}` }); const s = setup(); for (let i = 0; i < 257; i++) s.p.tool({ ...initial, toolCallId: `tool-${i}` });
expect(s.fields().at(-1).stage).toBe("evidence_incomplete"); expect(s.events).toHaveLength(257); expect(s.fields().at(-1)!.stage).toBe("evidence_incomplete"); expect(s.events).toHaveLength(257);
}); });
it("rejects commands missing from their original frame and bounded oversized input", () => { it("rejects commands missing from their original frame and bounded oversized input", () => {
@@ -159,5 +159,5 @@ it("preserves execute denial evidence after valid edit and read permission input
it.each(["edit", "read", "unknown-native-kind"])("still rejects a contradictory permission kind %s", kind => { it.each(["edit", "read", "unknown-native-kind"])("still rejects a contradictory permission kind %s", kind => {
const s = setup(); s.p.tool(initial); const s = setup(); s.p.tool(initial);
s.p.permission({ raw: { sessionId: "session", toolCall: { toolCallId: "tool", kind, rawInput: { path: "/fixture/file" } } } }, "request", ["decline"]); s.p.permission({ raw: { sessionId: "session", toolCall: { toolCallId: "tool", kind, rawInput: { path: "/fixture/file" } } } }, "request", ["decline"]);
expect(s.fields().at(-1).stage).toBe("evidence_incomplete"); expect(s.unavailable()).toBe(true); expect(s.fields().at(-1)!.stage).toBe("evidence_incomplete"); expect(s.unavailable()).toBe(true);
}); });
@@ -7,6 +7,11 @@ import { expect, it } from "vitest";
import { createGrokEffortAdmission } from "./grok-effort.js"; import { createGrokEffortAdmission } from "./grok-effort.js";
import { acpxSidecarErrorCode } from "../../cli/acpx-sidecar-input.js"; import { acpxSidecarErrorCode } from "../../cli/acpx-sidecar-input.js";
// The patched acpx runtime implements this method without declaring it.
interface ExtensionRuntime {
requestExtension(input: { handle: AcpRuntimeHandle; method: string; params: Record<string, unknown>; sessionMode?: "persistent" | "oneshot" }): Promise<Record<string, unknown>>;
}
it("retains a native Grok acknowledgement failure through the real ACPX connection", async () => { it("retains a native Grok acknowledgement failure through the real ACPX connection", async () => {
const root = await mkdtemp(join(tmpdir(), "grok-rejected-admission-")); const root = await mkdtemp(join(tmpdir(), "grok-rejected-admission-"));
const fixture = join(root, "grok.cjs"); const fixture = join(root, "grok.cjs");
@@ -94,7 +99,7 @@ it.each(["low", "high"] as const)("transports and restores native Grok %s effort
handle = await runtime.ensureSession(input); handle = await runtime.ensureSession(input);
expect(handle.backendSessionId).toBe(originalSession); expect(handle.backendSessionId).toBe(originalSession);
expect(admission.isReady()).toBe(false); expect(admission.isReady()).toBe(false);
await runtime.requestExtension({ handle, method: "_x.ai/session/info", params: { sessionId: handle.backendSessionId }, sessionMode: "persistent" }); await (runtime as typeof runtime & ExtensionRuntime).requestExtension({ handle, method: "_x.ai/session/info", params: { sessionId: handle.backendSessionId }, sessionMode: "persistent" });
admission.assertReady(); admission.assertReady();
const turn = runtime.startTurn({ handle, text: "Verify the restored fixture", mode: "prompt", requestId: "grok-cold-turn", timeoutMs: 5_000 }); const turn = runtime.startTurn({ handle, text: "Verify the restored fixture", mode: "prompt", requestId: "grok-cold-turn", timeoutMs: 5_000 });
const output: string[] = []; const output: string[] = [];
@@ -686,7 +686,7 @@ describe("native ACPX execution closure", () => {
const fences = await Promise.all([listen(), listen()]); const fences = await Promise.all([listen(), listen()]);
const fds = fences.map(server => (server as Server & { _handle?: { fd?: number } })._handle!.fd!); const fds = fences.map(server => (server as Server & { _handle?: { fd?: number } })._handle!.fd!);
const installation = await verifyNativeAcpxInstallation(declaration); const installation = await verifyNativeAcpxInstallation(declaration);
const owner = createAcpxCommandLeaseOwner(await installation.openCommand(), installation.openCommand); const owner = createAcpxCommandLeaseOwner(await installation.openCommand(), signal => installation.openCommand({ signal }));
const pidFile = join(declaration.distributionRoot, "native-pid"); const pidFile = join(declaration.distributionRoot, "native-pid");
let port: Awaited<ReturnType<typeof openCodexAcpxRuntime>> | undefined; let port: Awaited<ReturnType<typeof openCodexAcpxRuntime>> | undefined;
let deadline: ReturnType<typeof setTimeout> | undefined; let deadline: ReturnType<typeof setTimeout> | undefined;
@@ -4,7 +4,7 @@ import { mkdtemp, mkdir, realpath, rename, rm, symlink, writeFile } from "node:f
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import Ajv from "ajv"; import { Ajv } from "ajv";
import { import {
checkPiNativeTool, installPiRuntimeExtension, piMcpRequest, readPiRuntimeConfiguration, checkPiNativeTool, installPiRuntimeExtension, piMcpRequest, readPiRuntimeConfiguration,
PI_NATIVE_QUESTION_TOOL, PI_PERMISSION_TITLE_PREFIX, type PiExtensionApi, type PiRuntimeConfiguration, type PiToolDefinition, PI_NATIVE_QUESTION_TOOL, PI_PERMISSION_TITLE_PREFIX, type PiExtensionApi, type PiRuntimeConfiguration, type PiToolDefinition,
@@ -12,7 +12,7 @@ import {
const directories: string[] = []; const directories: string[] = [];
afterEach(async () => { for (const path of directories.splice(0)) await rm(path, { recursive: true, force: true }); }); afterEach(async () => { for (const path of directories.splice(0)) await rm(path, { recursive: true, force: true }); });
async function workspace() { async function workspace(): Promise<{ root: string; config: PiRuntimeConfiguration }> {
const root = await mkdtemp(join(tmpdir(), "paperclip-pi-extension-")); directories.push(root); const root = await mkdtemp(join(tmpdir(), "paperclip-pi-extension-")); directories.push(root);
await mkdir(join(root, "workspace")); await mkdir(join(root, "skills")); await mkdir(join(root, "private")); await mkdir(join(root, "workspace")); await mkdir(join(root, "skills")); await mkdir(join(root, "private"));
return { root, config: { return { root, config: {
@@ -20,7 +20,7 @@ async function workspace() {
workspace: join(root, "workspace"), readOnly: false, workspace: join(root, "workspace"), readOnly: false,
readRoots: [join(root, "skills")], protectedRoots: [join(root, "private")], readRoots: [join(root, "skills")], protectedRoots: [join(root, "private")],
instructions: "Use the assigned Paperclip tools", servers: [], instructions: "Use the assigned Paperclip tools", servers: [],
} satisfies PiRuntimeConfiguration }; } };
} }
function harness() { function harness() {
const handlers = new Map<string, (...args: any[]) => any>(); const handlers = new Map<string, (...args: any[]) => any>();
@@ -241,7 +241,7 @@ describe("owned Pi runtime extension", () => {
it("revalidates paths after a human wait and never treats cancellation as approval", async () => { it("revalidates paths after a human wait and never treats cancellation as approval", async () => {
const { root, config } = await workspace(); const h = harness(); const { root, config } = await workspace(); const h = harness();
await installPiRuntimeExtension(h.api, config); h.handlers.get("turn_start")!(); await installPiRuntimeExtension(h.api, config); h.handlers.get("turn_start")!();
const context = { cwd: config.workspace, ui: { select: vi.fn(async () => { const context = { cwd: config.workspace, ui: { select: vi.fn(async (_title: string) => {
await symlink(join(root, "private"), join(config.workspace, "target")); await symlink(join(root, "private"), join(config.workspace, "target"));
return "Allow once"; return "Allow once";
}) } }; }) } };
@@ -68,7 +68,7 @@ describe("historical ACPX profile decoding", () => {
["copilot", "copilot-profile-v85-cursor-cloud-qualification-historical-identity.json", "977d68b327f3964e8337ef8ba00230eb1b1697017db21f8010f664fee3d3c70a", "private Cloud qualification"], ["copilot", "copilot-profile-v85-cursor-cloud-qualification-historical-identity.json", "977d68b327f3964e8337ef8ba00230eb1b1697017db21f8010f664fee3d3c70a", "private Cloud qualification"],
["grok", "grok-profile-v14-identity.json", "08294ec6bdcbdbe46c8c8b711d8b368558a05f0d84d25c9f5fd0c3b67b79cd17", "published ed8"], ["grok", "grok-profile-v14-identity.json", "08294ec6bdcbdbe46c8c8b711d8b368558a05f0d84d25c9f5fd0c3b67b79cd17", "published ed8"],
["grok", "grok-profile-v14-cursor-cloud-qualification-historical-identity.json", "fa34ba395ff51affed7e7195dc44f4e33a3c6718ada1c80e8e36852cf7af40ac", "private Cloud qualification"], ["grok", "grok-profile-v14-cursor-cloud-qualification-historical-identity.json", "fa34ba395ff51affed7e7195dc44f4e33a3c6718ada1c80e8e36852cf7af40ac", "private Cloud qualification"],
] as const)("keeps %s identity %s immutable and decode-only", (agent, filename, checksum) => { ] as const)("keeps %s identity %s immutable and decode-only", (agent, filename, checksum, _source) => {
const bytes = readFileSync(new URL(`../../../test/fixtures/${filename}`, import.meta.url)); const bytes = readFileSync(new URL(`../../../test/fixtures/${filename}`, import.meta.url));
expect(createHash("sha256").update(bytes).digest("hex")).toBe(checksum); expect(createHash("sha256").update(bytes).digest("hex")).toBe(checksum);
const identity = JSON.parse(bytes.toString("utf8")); const identity = JSON.parse(bytes.toString("utf8"));
@@ -1,3 +1,4 @@
/// <reference lib="es2024.promise" />
import { describe, expect, it, vi } from "vitest"; import { describe, expect, it, vi } from "vitest";
import { deliverAcpxResponse, requireAcpxResponseDelivery } from "./response-delivery.js"; import { deliverAcpxResponse, requireAcpxResponseDelivery } from "./response-delivery.js";
@@ -272,7 +272,7 @@ describe("ACPX runtime host", () => {
it("rejects preconfigured Codex before admission resources or runtime launch", async () => { it("rejects preconfigured Codex before admission resources or runtime launch", async () => {
const fixture = await hostFixture(); const fixture = await hostFixture();
const openRuntime = vi.fn(), prepareSandbox = vi.fn(); const openRuntime = vi.fn(), prepareSandbox = vi.fn();
await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "codex", permissionMode: "approve-all", await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "codex", model: "gpt-5.6-sol", permissionMode: "approve-all",
environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" }, environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" },
}, fixture.dependencies({ openRuntime, prepareSandbox }))).rejects.toThrow("cannot protect Runner state"); }, fixture.dependencies({ openRuntime, prepareSandbox }))).rejects.toThrow("cannot protect Runner state");
expect(prepareSandbox).not.toHaveBeenCalled(); expect(prepareSandbox).not.toHaveBeenCalled();
@@ -282,7 +282,7 @@ describe("ACPX runtime host", () => {
it.each(["claude", "codex"] as const)("rejects preconfigured read-only %s before provider admission", async agent => { it.each(["claude", "codex"] as const)("rejects preconfigured read-only %s before provider admission", async agent => {
const fixture = await hostFixture(); const fixture = await hostFixture();
const openRuntime = vi.fn(); const openRuntime = vi.fn();
await expect(AcpxRuntimeHost.open({ ...fixture.options, agent, permissionMode: "approve-all", await expect(AcpxRuntimeHost.open({ ...fixture.options, agent, model: "explicit-test-model", permissionMode: "approve-all",
providerPolicy: { readOnly: true }, environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" }, providerPolicy: { readOnly: true }, environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" },
}, fixture.dependencies({ openRuntime }))).rejects.toThrow("preconfigured ACPX read-only execution is not supported"); }, fixture.dependencies({ openRuntime }))).rejects.toThrow("preconfigured ACPX read-only execution is not supported");
expect(openRuntime).not.toHaveBeenCalled(); expect(openRuntime).not.toHaveBeenCalled();
@@ -291,7 +291,7 @@ describe("ACPX runtime host", () => {
it.each(["approve-reads", "approve-paperclip", "deny-all"] as const)("rejects unqualified preconfigured permission mode %s before admission", async permissionMode => { it.each(["approve-reads", "approve-paperclip", "deny-all"] as const)("rejects unqualified preconfigured permission mode %s before admission", async permissionMode => {
const fixture = await hostFixture(); const fixture = await hostFixture();
const openRuntime = vi.fn(); const openRuntime = vi.fn();
await expect(AcpxRuntimeHost.open({ ...fixture.options, permissionMode, await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", permissionMode,
environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" }, environment: { HOME: fixture.root, PAPERCLIP_RUNNER_HOME_MODE: "preconfigured" },
}, fixture.dependencies({ openRuntime }))).rejects.toThrow("requires ACPX approve-all"); }, fixture.dependencies({ openRuntime }))).rejects.toThrow("requires ACPX approve-all");
expect(openRuntime).not.toHaveBeenCalled(); expect(openRuntime).not.toHaveBeenCalled();
@@ -379,7 +379,7 @@ describe("ACPX runtime host", () => {
])("rejects invalid assigned connection bindings before runtime launch", async (environment) => { ])("rejects invalid assigned connection bindings before runtime launch", async (environment) => {
const fixture = await hostFixture(); const fixture = await hostFixture();
const openRuntime = vi.fn(); const openRuntime = vi.fn();
await expect(AcpxRuntimeHost.open({ ...fixture.options, environment }, fixture.dependencies({ openRuntime }))).rejects.toThrow(/assigned native MCP/); await expect(AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", permissionMode: "approve-reads", environment }, fixture.dependencies({ openRuntime }))).rejects.toThrow(/assigned native MCP/);
expect(openRuntime).not.toHaveBeenCalled(); expect(openRuntime).not.toHaveBeenCalled();
}); });
@@ -491,7 +491,8 @@ describe("ACPX runtime host", () => {
const host = await AcpxRuntimeHost.open({ const host = await AcpxRuntimeHost.open({
...fixture.options, ...fixture.options,
agent: "claude", model: "claude-sonnet-5", agent: "claude", model: "claude-sonnet-5",
environment: { ...fixture.options.environment, permissionMode: "approve-reads",
environment: {
PAPERCLIP_NATIVE_MCP_NAME: "paperclip-assigned", PAPERCLIP_NATIVE_MCP_NAME: "paperclip-assigned",
PAPERCLIP_NATIVE_MCP_URL: "http://127.0.0.1:3211/mcp/gateway", PAPERCLIP_NATIVE_MCP_URL: "http://127.0.0.1:3211/mcp/gateway",
PAPERCLIP_NATIVE_MCP_TOKEN: "fixture-gateway-token-".repeat(3), PAPERCLIP_NATIVE_MCP_TOKEN: "fixture-gateway-token-".repeat(3),
@@ -518,7 +519,7 @@ describe("ACPX runtime host", () => {
let servers: AcpxRuntimePortOpenOptions["mcpServers"] = []; let servers: AcpxRuntimePortOpenOptions["mcpServers"] = [];
const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5", const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5",
permissionMode: "approve-reads", providerPolicy: { readOnly }, permissionMode: "approve-reads", providerPolicy: { readOnly },
environment: { ...fixture.options.environment, PAPERCLIP_NATIVE_MCP_STDIO: JSON.stringify([binding]), PAPERCLIP_NATIVE_MCP_PROXY: "/verified/runnerd" }, environment: { PAPERCLIP_NATIVE_MCP_STDIO: JSON.stringify([binding]), PAPERCLIP_NATIVE_MCP_PROXY: "/verified/runnerd" },
}, fixture.dependencies({ openRuntime: async options => { }, fixture.dependencies({ openRuntime: async options => {
servers = options.mcpServers; servers = options.mcpServers;
return runtimePort({ getStatus: async () => ({ models: { currentModelId: "claude-sonnet-5" } }) }); return runtimePort({ getStatus: async () => ({ models: { currentModelId: "claude-sonnet-5" } }) });
@@ -1042,12 +1043,12 @@ describe("ACPX runtime host", () => {
const events: CanonicalProviderEvent[] = []; const events: CanonicalProviderEvent[] = [];
const evidence = createCopilotToolEvidence({ sessionId: "backend-1", turnId: "turn-1", workingDirectory: fixture.options.workingDirectory, const evidence = createCopilotToolEvidence({ sessionId: "backend-1", turnId: "turn-1", workingDirectory: fixture.options.workingDirectory,
active: () => true, emit: event => { validateAcpxRichEvent(event); events.push(JSON.parse(JSON.stringify(event))); } }); active: () => true, emit: event => { validateAcpxRichEvent(event); events.push(JSON.parse(JSON.stringify(event))); } });
let bridge: AcpxRuntimePortOpenOptions["mcpServers"][number] | undefined; let bridge: Extract<AcpxRuntimePortOpenOptions["mcpServers"][number], { url: string }> | undefined;
const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "copilot", model: "gpt-5.6-sol", const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "copilot", model: "gpt-5.6-sol",
permissionMode: "deny-all", environment: { COPILOT_GITHUB_TOKEN: "fixture-not-a-real-credential" }, permissionMode: "deny-all", environment: { COPILOT_GITHUB_TOKEN: "fixture-not-a-real-credential" },
semanticTools: { tools: [{ name: "get_task_context", inputSchema: { type: "object", properties: {}, additionalProperties: false } }], semanticTools: { tools: [{ name: "get_task_context", inputSchema: { type: "object", properties: {}, additionalProperties: false } }],
handler: async () => ({ accepted: false }), captureSemanticReceipt: () => evidence.captureSemanticReceipt() }, handler: async () => ({ accepted: false }), captureSemanticReceipt: () => evidence.captureSemanticReceipt() },
}, fixture.dependencies({ openRuntime: async options => { bridge = options.mcpServers[0]; return runtimePort(); } })); }, fixture.dependencies({ openRuntime: async options => { bridge = urlBridge(options.mcpServers[0]); return runtimePort(); } }));
try { try {
const response = await fetch(bridge!.url, { method: "POST", headers: { Authorization: `Bearer ${bridge!.bearerToken}`, "Content-Type": "application/json" }, const response = await fetch(bridge!.url, { method: "POST", headers: { Authorization: `Bearer ${bridge!.bearerToken}`, "Content-Type": "application/json" },
body: JSON.stringify({ jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "get_task_context", arguments: {} } }) }); body: JSON.stringify({ jsonrpc: "2.0", id: 3, method: "tools/call", params: { name: "get_task_context", arguments: {} } }) });
@@ -1092,7 +1093,7 @@ describe("ACPX runtime host", () => {
}, },
fixture.dependencies({ fixture.dependencies({
openRuntime: async (options) => { openRuntime: async (options) => {
bridge = options.mcpServers[0]; bridge = urlBridge(options.mcpServers[0]);
return runtimePort(); return runtimePort();
}, },
}), }),
@@ -1990,10 +1991,10 @@ describe("ACPX runtime host", () => {
it("retains a settled turn when shutdown cleanup must be retried", async () => { it("retains a settled turn when shutdown cleanup must be retried", async () => {
const fixture = await hostFixture(); const fixture = await hostFixture();
const credential = credentialLeaseFixture(fixture.root); const credential = credentialLeaseFixture(fixture.root);
let settleTurn!: (value: { stopReason: string }) => void; let settleTurn!: (value: Awaited<AcpxRuntimeTurn["result"]>) => void;
const turn = { const turn = {
...runtimeTurn(), ...runtimeTurn(),
result: new Promise<{ stopReason: string }>((resolve) => { result: new Promise<Awaited<AcpxRuntimeTurn["result"]>>((resolve) => {
settleTurn = resolve; settleTurn = resolve;
}), }),
}; };
@@ -2019,7 +2020,7 @@ describe("ACPX runtime host", () => {
host.startTurn({ text: "Complete the task.", requestId: "turn-1" }); host.startTurn({ text: "Complete the task.", requestId: "turn-1" });
const firstClose = host.close({ reason: "first shutdown" }); const firstClose = host.close({ reason: "first shutdown" });
settleTurn({ stopReason: "end_turn" }); settleTurn({ status: "completed", stopReason: "end_turn" });
await expect(firstClose).rejects.toThrow(/cleanup failed/); await expect(firstClose).rejects.toThrow(/cleanup failed/);
expect(turn.cancel).toHaveBeenCalledOnce(); expect(turn.cancel).toHaveBeenCalledOnce();
expect(credential.close).not.toHaveBeenCalled(); expect(credential.close).not.toHaveBeenCalled();
@@ -2332,6 +2333,11 @@ function credentialLeaseFixture(root: string) {
}; };
} }
function urlBridge(server: AcpxRuntimePortOpenOptions["mcpServers"][number] | undefined) {
if (!server || !("url" in server)) throw new Error("missing tool bridge");
return server;
}
function runtimePort( function runtimePort(
input: { input: {
identity?: AcpxRuntimePort["identity"]; identity?: AcpxRuntimePort["identity"];
@@ -2402,7 +2408,7 @@ async function hostFixture() {
dependencies( dependencies(
input: Pick<AcpxRuntimeHostDependencies, "openRuntime"> & input: Pick<AcpxRuntimeHostDependencies, "openRuntime"> &
Partial< Partial<
Pick<AcpxRuntimeHostDependencies, "reportRetainedCleanupFailure"> Pick<AcpxRuntimeHostDependencies, "reportRetainedCleanupFailure" | "prepareSandbox" | "verifyInstallation">
> & { > & {
// Opt-in only. When true, `prepareSandbox` runs the real // Opt-in only. When true, `prepareSandbox` runs the real
// preparation once, then returns that same sandbox for every // preparation once, then returns that same sandbox for every
@@ -2413,19 +2419,19 @@ async function hostFixture() {
): AcpxRuntimeHostDependencies { ): AcpxRuntimeHostDependencies {
let reusedSandbox: AcpxRuntimeSandbox | null = null; let reusedSandbox: AcpxRuntimeSandbox | null = null;
return { return {
verifyInstallation: async (profile) => verifyInstallation: input.verifyInstallation ?? (async (profile) =>
({ ({
commandDigest: profile.commandDigest, commandDigest: profile.commandDigest,
agentServerPackageJsonPath: join(root, "package.json"), agentServerPackageJsonPath: join(root, "package.json"),
agentRuntimePackageJsonPath: null, agentRuntimePackageJsonPath: null,
openCommand: async () => command, openCommand: async () => command,
}) satisfies VerifiedAcpxInstallation, }) satisfies VerifiedAcpxInstallation),
openRuntime: input.openRuntime, openRuntime: input.openRuntime,
// Record the skills home the instant the sandbox exists, ahead of // Record the skills home the instant the sandbox exists, ahead of
// the materialize call that seals it. A test that overrides // the materialize call that seals it. A test that overrides
// `prepareSandbox` for a non-Claude agent replaces this wrapper, but // `prepareSandbox` for a non-Claude agent replaces this wrapper, but
// those agents never materialize skills, so nothing is lost. // those agents never materialize skills, so nothing is lost.
prepareSandbox: async (sandboxInput) => { prepareSandbox: input.prepareSandbox ?? (async (sandboxInput) => {
if (input.reuseSandbox && reusedSandbox) return reusedSandbox; if (input.reuseSandbox && reusedSandbox) return reusedSandbox;
const sandbox = await prepareAcpxRuntimeSandbox(sandboxInput); const sandbox = await prepareAcpxRuntimeSandbox(sandboxInput);
if (sandboxInput.agent === "claude") { if (sandboxInput.agent === "claude") {
@@ -2433,7 +2439,7 @@ async function hostFixture() {
} }
if (input.reuseSandbox) reusedSandbox = sandbox; if (input.reuseSandbox) reusedSandbox = sandbox;
return sandbox; return sandbox;
}, }),
retainAdmissionCleanup: trackAdmissionCleanup, retainAdmissionCleanup: trackAdmissionCleanup,
reportRetainedCleanupFailure: reportRetainedCleanupFailure:
input.reportRetainedCleanupFailure ?? vi.fn(), input.reportRetainedCleanupFailure ?? vi.fn(),
@@ -128,10 +128,11 @@ describe("Codex app-server transport limits", () => {
}); });
it("installs cleanup handlers before synchronously reporting process ownership", async () => { it("installs cleanup handlers before synchronously reporting process ownership", async () => {
const originalSpawn = ChildProcess.prototype.spawn; const childProcessPrototype = ChildProcess.prototype as ChildProcess & { spawn(...args: unknown[]): unknown };
const originalSpawn = childProcessPrototype.spawn;
let spawnedProcess: ChildProcess | undefined; let spawnedProcess: ChildProcess | undefined;
const spawnSpy = vi const spawnSpy = vi
.spyOn(ChildProcess.prototype, "spawn") .spyOn(childProcessPrototype, "spawn")
.mockImplementation(function (this: ChildProcess, ...args) { .mockImplementation(function (this: ChildProcess, ...args) {
spawnedProcess = this; spawnedProcess = this;
return originalSpawn.apply(this, args); return originalSpawn.apply(this, args);
@@ -660,7 +660,7 @@ describe("Codex app-server Codex driver", () => {
).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError); ).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError);
for (const action of ["set", "pause", "resume", "clear"] as const) { for (const action of ["set", "pause", "resume", "clear"] as const) {
await expect( await expect(
unsupported.goal?.({ action, ...(action === "set" ? { objective: "Must not reach the provider" } : {}) }), unsupported.goal?.(action === "set" ? { action, objective: "Must not reach the provider" } : { action }),
).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError); ).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError);
} }
expect(unsupportedTransport.calls.filter(({ method }) => expect(unsupportedTransport.calls.filter(({ method }) =>
@@ -401,7 +401,7 @@ describe("Codex app-server Codex driver", () => {
{ runDelta: first, runDeltaComplete: true }, { runDelta: first, runDeltaComplete: true },
{ runDelta: { ...first, ...second }, runDeltaComplete: false }, { runDelta: { ...first, ...second }, runDeltaComplete: false },
]); ]);
expect(await session.usage()).toMatchObject({ runDeltaComplete: false }); expect(await session.usage!()).toMatchObject({ runDeltaComplete: false });
}); });
it("keeps usage reported between runs in the next run's delta so totals stay exact", async () => { it("keeps usage reported between runs in the next run's delta so totals stay exact", async () => {
@@ -478,7 +478,7 @@ describe("Codex app-server Codex driver", () => {
expect(events.some((event) => event.eventType === "session.failed")).toBe( expect(events.some((event) => event.eventType === "session.failed")).toBe(
false, false,
); );
expect(await session.usage()).toMatchObject({ expect(await session.usage!()).toMatchObject({
total: { inputTokens: 10, outputTokens: 4 }, total: { inputTokens: 10, outputTokens: 4 },
}); });
}); });
@@ -785,6 +785,7 @@ describe("Codex app-server Codex driver", () => {
runId: "run-1", runId: "run-1",
environmentLeaseId: "lease-1", environmentLeaseId: "lease-1",
runnerInstanceId: "runner-codex", runnerInstanceId: "runner-codex",
driverSessionId: "thread-1",
normalizedSessionId: "normalized-1", normalizedSessionId: "normalized-1",
}, },
capabilities, capabilities,
@@ -819,7 +820,7 @@ it("sends explicit skill input on every requested turn", async () => {
await collectUntilTerminal(session.events()); await collectUntilTerminal(session.events());
transport.turnStartResponse = Promise.resolve({ turn: { id: "turn-2", status: "inProgress", items: [] } }); transport.turnStartResponse = Promise.resolve({ turn: { id: "turn-2", status: "inProgress", items: [] } });
} }
await session.close(); await session.close({ reason: "fixture complete" });
}); });
@@ -830,7 +831,7 @@ it("reapplies explicit skills after recovery without persisting them into ordina
runId: "initial", normalizedSessionId: "skill-recovery", workingDirectory: WORKSPACE, runId: "initial", normalizedSessionId: "skill-recovery", workingDirectory: WORKSPACE,
}); });
const snapshot = await original.snapshot(); const snapshot = await original.snapshot();
await original.close(); await original.close({ reason: "fixture complete" });
for (const selected of [skillInputs, []]) { for (const selected of [skillInputs, []]) {
const transport = new FakeCodexTransport(); const transport = new FakeCodexTransport();
const recovery = await makeDriver([transport], { skillInputs: selected }).recoverSession(snapshot); const recovery = await makeDriver([transport], { skillInputs: selected }).recoverSession(snapshot);
@@ -840,6 +841,6 @@ it("reapplies explicit skills after recovery without persisting them into ordina
await session.startTurn({ message: { role: "user", text: "Continue" } }); await session.startTurn({ message: { role: "user", text: "Continue" } });
const items = transport.calls.find((call) => call.method === "turn/start")?.params.input as Array<{type:string}>; const items = transport.calls.find((call) => call.method === "turn/start")?.params.input as Array<{type:string}>;
expect(items.filter((item) => item.type === "skill")).toEqual(selected); expect(items.filter((item) => item.type === "skill")).toEqual(selected);
} finally { await session.close(); } } finally { await session.close({ reason: "fixture complete" }); }
} }
}); });
@@ -76,7 +76,7 @@ it("preserves runnerd permission expiry before a failed provider terminal and re
expect(resolve).not.toHaveBeenCalled(); expect(resolve).not.toHaveBeenCalled();
transport.push("turn/completed", { threadId: "thread-1", turn: { id: turnId, status: "failed", error: { code: "ACPX_PROVIDER_PROCESS_LOST", message: "Provider died" } } }); transport.push("turn/completed", { threadId: "thread-1", turn: { id: turnId, status: "failed", error: { code: "ACPX_PROVIDER_PROCESS_LOST", message: "Provider died" } } });
await vi.waitFor(() => expect(events.some(event => event.eventType === "turn.failed")).toBe(true)); await vi.waitFor(() => expect(events.some(event => event.eventType === "turn.failed")).toBe(true));
await session.close(); await consume; await session.close({ reason: "fixture complete" }); await consume;
const settlements = events.filter(event => ["runtime_request.expired", "runtime_request.cancelled", "runtime_request.resolved"].includes(event.eventType)); const settlements = events.filter(event => ["runtime_request.expired", "runtime_request.cancelled", "runtime_request.resolved"].includes(event.eventType));
expect(settlements).toHaveLength(1); expect(settlements).toHaveLength(1);
expect(settlements[0]).toMatchObject({ eventType: "runtime_request.expired", turnId, itemId: "native-edit", payload: { ...payload, turnId, itemId: "native-edit" } }); expect(settlements[0]).toMatchObject({ eventType: "runtime_request.expired", turnId, itemId: "native-edit", payload: { ...payload, turnId, itemId: "native-edit" } });
@@ -105,7 +105,7 @@ it("preserves canonical question expiry after provider loss and refuses late ans
await expect(session.resolveRuntimeRequest?.({ requestId: "native-question", turnId, resolution: { action: "accept" } })).rejects.toThrow(); await expect(session.resolveRuntimeRequest?.({ requestId: "native-question", turnId, resolution: { action: "accept" } })).rejects.toThrow();
transport.push("turn/completed", { threadId: "thread-1", turn: { id: turnId, status: "failed", error: { code: "ACPX_PROVIDER_PROCESS_LOST", message: "Provider died" } } }); transport.push("turn/completed", { threadId: "thread-1", turn: { id: turnId, status: "failed", error: { code: "ACPX_PROVIDER_PROCESS_LOST", message: "Provider died" } } });
await vi.waitFor(() => expect(events.some(event => event.eventType === "turn.failed" || event.eventType === "session.failed")).toBe(true)); await vi.waitFor(() => expect(events.some(event => event.eventType === "turn.failed" || event.eventType === "session.failed")).toBe(true));
await session.close(); await consume; await session.close({ reason: "fixture complete" }); await consume;
expect(events.filter(event => event.eventType === "runtime_request.expired")).toHaveLength(1); expect(events.filter(event => event.eventType === "runtime_request.expired")).toHaveLength(1);
expect(events.find(event => event.eventType === "runtime_request.expired")).toMatchObject({ turnId, itemId: "question-item", payload }); expect(events.find(event => event.eventType === "runtime_request.expired")).toMatchObject({ turnId, itemId: "question-item", payload });
expect(events.some(event => event.eventType === "session.failed" && event.payload.code === "turn_binding_mismatch")).toBe(false); expect(events.some(event => event.eventType === "session.failed" && event.payload.code === "turn_binding_mismatch")).toBe(false);
@@ -127,7 +127,7 @@ it.each(["turn", "kind", "type", "replay"])("refuses canonical permission expiry
requestType: kind === "type" ? "input" : "permission", replayAllowed: kind === "replay", reason: "provider_process_lost" }; requestType: kind === "type" ? "input" : "permission", replayAllowed: kind === "replay", reason: "provider_process_lost" };
const notification = runnerdRuntimeRequestExpiryNotification("runtime_request.expired", payload, "thread-1", kind === "turn" ? "foreign-turn" : turnId)!; const notification = runnerdRuntimeRequestExpiryNotification("runtime_request.expired", payload, "thread-1", kind === "turn" ? "foreign-turn" : turnId)!;
transport.push(notification.method, notification.params); transport.push(notification.method, notification.params);
await pending; await consume; await session.close(); await pending; await consume; await session.close({ reason: "fixture complete" });
expect(events.some(e => e.eventType === "runtime_request.expired")).toBe(false); expect(events.some(e => e.eventType === "runtime_request.expired")).toBe(false);
expect(events.some(e => e.eventType === "session.failed" && e.payload.code === "turn_binding_mismatch")).toBe(true); expect(events.some(e => e.eventType === "session.failed" && e.payload.code === "turn_binding_mismatch")).toBe(true);
}); });
@@ -159,7 +159,7 @@ describe("Codex app-server Codex driver", () => {
}); });
expect(await response).toEqual({ action: "accept", content: null, _meta: null }); expect(await response).toEqual({ action: "accept", content: null, _meta: null });
expect(session.pendingRuntimeRequests?.()).toHaveLength(0); expect(session.pendingRuntimeRequests?.()).toHaveLength(0);
await session.close(); await session.close({ reason: "fixture complete" });
}); });
it("validates runtime request resolutions against the kind of request they answer", async () => { it("validates runtime request resolutions against the kind of request they answer", async () => {
@@ -68,7 +68,7 @@ describe("Codex app-server Codex driver", () => {
threadId: "thread-1", turnId: "turn-1", arguments: {} }); threadId: "thread-1", turnId: "turn-1", arguments: {} });
expect(feedback).toHaveBeenCalledExactlyOnceWith({ ...report, continuation }); expect(feedback).toHaveBeenCalledExactlyOnceWith({ ...report, continuation });
expect((await session.snapshot()).semanticResult?.result).toEqual({ ...report, continuation }); expect((await session.snapshot()).semanticResult?.result).toEqual({ ...report, continuation });
} finally { await session.close(); } } finally { await session.close({ reason: "fixture complete" }); }
}); });
it.each([ it.each([
@@ -92,7 +92,7 @@ describe("Codex app-server Codex driver", () => {
expect(response).toMatchObject({ success: false }); expect(response).toMatchObject({ success: false });
expect(feedback).not.toHaveBeenCalled(); expect(feedback).not.toHaveBeenCalled();
expect((await session.snapshot()).semanticResult).toBeNull(); expect((await session.snapshot()).semanticResult).toBeNull();
} finally { await session.close(); } } finally { await session.close({ reason: "fixture complete" }); }
}); });
it("returns current approval feedback and permits correcting a rejected completion report", async () => { it("returns current approval feedback and permits correcting a rejected completion report", async () => {
@@ -111,7 +111,7 @@ describe("Codex app-server Codex driver", () => {
expect(await call("corrected")).toMatchObject({ success: true, expect(await call("corrected")).toMatchObject({ success: true,
contentItems: [{ type: "inputText", text: expect.stringContaining("/approvals/approval-1") }] }); contentItems: [{ type: "inputText", text: expect.stringContaining("/approvals/approval-1") }] });
expect((await session.snapshot()).semanticResult?.result).toEqual(result); expect((await session.snapshot()).semanticResult?.result).toEqual(result);
await session.close(); await session.close({ reason: "fixture complete" });
}); });
it("accepts an explicit response-wake yield through paperclip_finish", async () => { it("accepts an explicit response-wake yield through paperclip_finish", async () => {
@@ -194,7 +194,7 @@ describe("Codex app-server Codex driver", () => {
}, },
}, },
})).toMatchObject({ success: false }); })).toMatchObject({ success: false });
await otherSession.close(); await otherSession.close({ reason: "fixture complete" });
}); });
it("makes duplicate semantic completion idempotent and rejects changed payloads", async () => { it("makes duplicate semantic completion idempotent and rejects changed payloads", async () => {
@@ -16,8 +16,8 @@ import { NativeSessionProtocolIntegrityError } from "../../contracts/native-sess
import { import {
DurablePrpControlPlane, DurablePrpControlPlane,
durableRecoveryInternals, durableRecoveryInternals,
type DurableRecoveryIdentity,
} from "../../control-plane/durable-prp-control-plane.js"; } from "../../control-plane/durable-prp-control-plane.js";
import type { DurableRecoveryIdentity } from "../../control-plane/prp-transport-types.js";
import { createCapabilityRunnerdCodexTransport } from "../../live/runnerd-codex-transport.js"; import { createCapabilityRunnerdCodexTransport } from "../../live/runnerd-codex-transport.js";
import { executeNativeSession } from "../../native-session-runtime.js"; import { executeNativeSession } from "../../native-session-runtime.js";
import { CodexAppServerDriver } from "./codex-app-server-driver.js"; import { CodexAppServerDriver } from "./codex-app-server-driver.js";
@@ -44,7 +44,7 @@ async function authenticatedRunner(
runnerBinary: string, runnerBinary: string,
) { ) {
const framed = (domain: string, parts: Buffer[]) => { const framed = (domain: string, parts: Buffer[]) => {
const values = [Buffer.from(domain), Buffer.from([0])]; const values: Buffer[] = [Buffer.from(domain), Buffer.from([0])];
for (const part of parts) { for (const part of parts) {
const length = Buffer.alloc(8); const length = Buffer.alloc(8);
length.writeBigUInt64BE(BigInt(part.length)); length.writeBigUInt64BE(BigInt(part.length));
@@ -476,6 +476,7 @@ describe("Codex protocol integrity propagation", () => {
normalizedSessionId: identity.normalizedSessionId, normalizedSessionId: identity.normalizedSessionId,
driverKind: "codex_app_server", driverKind: "codex_app_server",
protocolVersion: 1, protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
}, },
provider: { kind: "codex", model: null }, provider: { kind: "codex", model: null },
completionContract: { completionContract: {
@@ -335,7 +335,7 @@ describe("Codex structured question adapter", () => {
questions: [{ id: "form", question: "Choose one" }], questions: [{ id: "form", question: "Choose one" }],
}); });
expect(safeForm?.description).toContain("Bearer [REDACTED]"); expect(safeForm?.description).toContain("Bearer [REDACTED]");
expect(safeForm?.description.endsWith(preservedTail)).toBe(true); expect(safeForm?.description?.endsWith(preservedTail)).toBe(true);
expect(() => normalizeCodexQuestionSet("mcpServer/elicitation/request", { expect(() => normalizeCodexQuestionSet("mcpServer/elicitation/request", {
requestedSchema: { requestedSchema: {
@@ -35,7 +35,7 @@ describe("Codex resume accounting through the production driver", () => {
turnId: string, turnId: string,
) => { ) => {
const saved = JSON.parse(JSON.stringify(await session.snapshot!())); const saved = JSON.parse(JSON.stringify(await session.snapshot!()));
await session.close(); await session.close({ reason: "test complete" });
transport.readResponse = { transport.readResponse = {
thread: { id: "thread-1", sessionId: "provider-session-1", cwd: WORKSPACE, turns: [] }, thread: { id: "thread-1", sessionId: "provider-session-1", cwd: WORKSPACE, turns: [] },
}; };
@@ -60,7 +60,7 @@ describe("Codex resume accounting through the production driver", () => {
session = await coldRestart(session, transports[2]!, "third", "turn-3"); session = await coldRestart(session, transports[2]!, "third", "turn-3");
await expect(runTurn(session, transports[2]!, "turn-3", { inputTokens: 170, outputTokens: 20 })) await expect(runTurn(session, transports[2]!, "turn-3", { inputTokens: 170, outputTokens: 20 }))
.resolves.toEqual({ inputTokens: 30, outputTokens: 4 }); .resolves.toEqual({ inputTokens: 30, outputTokens: 4 });
await session.close(); await session.close({ reason: "test complete" });
}); });
it.each(["thread", "snapshot"] as const)( it.each(["thread", "snapshot"] as const)(
@@ -137,7 +137,7 @@ describe("Codex resume accounting through the production driver", () => {
expect(await session.usage!()).toMatchObject({ expect(await session.usage!()).toMatchObject({
runDelta: { inputTokens: 40, outputTokens: 6 }, runDelta: { inputTokens: 40, outputTokens: 6 },
}); });
await session.close(); await session.close({ reason: "fixture complete" });
second.readResponse = readResponse(140, 16); second.readResponse = readResponse(140, 16);
const recovery = await driver.recoverSession(persisted); const recovery = await driver.recoverSession(persisted);
expect(recovery.recovered).toBe(true); expect(recovery.recovered).toBe(true);
@@ -187,7 +187,7 @@ describe("Codex resume accounting through the production driver", () => {
inputTokens: 140, inputTokens: 140,
outputTokens: 16, outputTokens: 16,
}); });
await recoveredSession.close(); await recoveredSession.close({ reason: "fixture complete" });
third.readResponse = readResponse(150, 19); third.readResponse = readResponse(150, 19);
const secondRecovery = await driver.recoverSession(advanced); const secondRecovery = await driver.recoverSession(advanced);
expect(secondRecovery.recovered).toBe(true); expect(secondRecovery.recovered).toBe(true);
@@ -205,8 +205,8 @@ describe("Codex resume accounting through the production driver", () => {
), ),
).toEqual([]); ).toEqual([]);
} finally { } finally {
await recoveredSession?.close(); await recoveredSession?.close({ reason: "fixture complete" });
await session.close(); await session.close({ reason: "fixture complete" });
} }
}, },
); );
@@ -245,7 +245,7 @@ describe("Codex resume accounting through the production driver", () => {
baseline: { inputTokens: 100 }, baseline: { inputTokens: 100 },
latest: { inputTokens: 140 }, latest: { inputTokens: 140 },
}; };
await session.close(); await session.close({ reason: "fixture complete" });
const tokenUsage = { const tokenUsage = {
total: { inputTokens: 150 }, total: { inputTokens: 150 },
last: { inputTokens: 10 }, last: { inputTokens: 10 },
@@ -270,8 +270,8 @@ describe("Codex resume accounting through the production driver", () => {
second.calls.filter((call) => call.method === "turn/start"), second.calls.filter((call) => call.method === "turn/start"),
).toEqual([]); ).toEqual([]);
} finally { } finally {
await recoveredSession?.close(); await recoveredSession?.close({ reason: "fixture complete" });
await session.close(); await session.close({ reason: "fixture complete" });
} }
}, },
); );
@@ -300,7 +300,7 @@ describe("Codex resume accounting through the production driver", () => {
}); });
await collectUntilTerminal(session.events()); await collectUntilTerminal(session.events());
const persisted = JSON.parse(JSON.stringify(await session.snapshot!())); const persisted = JSON.parse(JSON.stringify(await session.snapshot!()));
await session.close(); await session.close({ reason: "fixture complete" });
second.readResponse = { second.readResponse = {
thread: { thread: {
id: "thread-1", id: "thread-1",
@@ -384,7 +384,7 @@ describe("Codex resume accounting through the production driver", () => {
.filter((call) => call.method === "thread/read") .filter((call) => call.method === "thread/read")
.every((call) => call.params.includeTurns === false), .every((call) => call.params.includeTurns === false),
).toBe(true); ).toBe(true);
await session.close(); await session.close({ reason: "fixture complete" });
}); });
it("keeps usage reported between runs in the next run's delta after a restore", async () => { it("keeps usage reported between runs in the next run's delta after a restore", async () => {
@@ -401,7 +401,7 @@ describe("Codex resume accounting through the production driver", () => {
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
const persisted = JSON.parse(JSON.stringify(await session.snapshot!())); const persisted = JSON.parse(JSON.stringify(await session.snapshot!()));
expect(persisted.codexUsageBaseline).toMatchObject({ latest: { inputTokens: 130 }, accounted: { inputTokens: 100 } }); expect(persisted.codexUsageBaseline).toMatchObject({ latest: { inputTokens: 130 }, accounted: { inputTokens: 100 } });
await session.close(); await session.close({ reason: "test complete" });
second.readResponse = { thread: { id: "thread-1", sessionId: "provider-session-1", cwd: WORKSPACE, second.readResponse = { thread: { id: "thread-1", sessionId: "provider-session-1", cwd: WORKSPACE,
turns: [{ id: "turn-1", status: "completed", items: [] }], tokenUsage: { total: { inputTokens: 130, outputTokens: 10 } } } }; turns: [{ id: "turn-1", status: "completed", items: [] }], tokenUsage: { total: { inputTokens: 130, outputTokens: 10 } } } };
const recovery = await driver.recoverSession(persisted); const recovery = await driver.recoverSession(persisted);
@@ -413,7 +413,7 @@ describe("Codex resume accounting through the production driver", () => {
second.push("thread/tokenUsage/updated", { threadId: "thread-1", turnId: "turn-2", tokenUsage: { total: { inputTokens: 150, outputTokens: 16 } } }); second.push("thread/tokenUsage/updated", { threadId: "thread-1", turnId: "turn-2", tokenUsage: { total: { inputTokens: 150, outputTokens: 16 } } });
for await (const event of recovered.events()) if (event.payload.kind === "usage") break; for await (const event of recovered.events()) if (event.payload.kind === "usage") break;
expect(await recovered.usage!()).toMatchObject({ runDelta: { inputTokens: 50, outputTokens: 6 } }); expect(await recovered.usage!()).toMatchObject({ runDelta: { inputTokens: 50, outputTokens: 6 } });
await recovered.close(); await recovered.close({ reason: "test complete" });
}); });
it("keeps the startup external sandbox profile on subsequent turns", async () => { it("keeps the startup external sandbox profile on subsequent turns", async () => {
@@ -439,6 +439,6 @@ describe("Codex resume accounting through the production driver", () => {
transport.calls.find((call) => call.method === "turn/start")?.params transport.calls.find((call) => call.method === "turn/start")?.params
.permissions, .permissions,
).toBe(start.params.permissions); ).toBe(start.params.permissions);
await session.close(); await session.close({ reason: "fixture complete" });
}); });
}); });
@@ -1,17 +1,19 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { parseCodexTurnDiff, summarizeCodexTurnDiff } from "./codex-turn-diff.js"; import { parseCodexTurnDiff, summarizeCodexTurnDiff, type ParsedCodexTurnDiffFile } from "./codex-turn-diff.js";
describe("Codex turn diff parser", () => { describe("Codex turn diff parser", () => {
it("summarizes parsed files for work-product metadata", () => { it("summarizes parsed files for work-product metadata", () => {
expect(summarizeCodexTurnDiff([ const textFiles: ParsedCodexTurnDiffFile[] = [
{ path: "a.ts", operation: "modify", previousPath: null, additions: 4, deletions: 2, binary: false, diff: "patch" }, { path: "a.ts", operation: "modify", previousPath: null, additions: 4, deletions: 2, binary: false, diff: "patch" },
{ path: "b.ts", operation: "create", previousPath: null, additions: 3, deletions: 0, binary: false, diff: "patch" }, { path: "b.ts", operation: "create", previousPath: null, additions: 3, deletions: 0, binary: false, diff: "patch" },
])).toEqual({ files: 2, additions: 7, deletions: 2 }); ];
expect(summarizeCodexTurnDiff(textFiles)).toEqual({ files: 2, additions: 7, deletions: 2 });
expect(summarizeCodexTurnDiff([ const binaryFiles: ParsedCodexTurnDiffFile[] = [
{ path: "logo.png", operation: "modify", previousPath: null, additions: null, deletions: null, binary: true, diff: null }, { path: "logo.png", operation: "modify", previousPath: null, additions: null, deletions: null, binary: true, diff: null },
])).toEqual({ files: 1, additions: null, deletions: null }); ];
expect(summarizeCodexTurnDiff(binaryFiles)).toEqual({ files: 1, additions: null, deletions: null });
}); });
it("parses a complete snapshot with bounded file statistics", () => { it("parses a complete snapshot with bounded file statistics", () => {
@@ -91,7 +91,7 @@ describe("prepared native context transport", () => {
text: expect.stringContaining('"task"'), text: expect.stringContaining('"task"'),
}), }),
]); ]);
expect((input?.[0] as { text: string }).text).toContain( expect((input as Array<{ text: string }>)[0]!.text).toContain(
'"message":"ordinary standalone task"', '"message":"ordinary standalone task"',
); );
}); });
@@ -87,7 +87,7 @@ describe("Cursor Cloud bounded stdio proxy", () => {
const terminalGate = new Promise<void>(resolve => { releaseTerminalWrites = resolve; }); const terminalGate = new Promise<void>(resolve => { releaseTerminalWrites = resolve; });
let pausedTerminalWrites = 0; let pausedTerminalWrites = 0;
const write = CursorCloudCheckpointStore.prototype.write; const write = CursorCloudCheckpointStore.prototype.write;
vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(value) { vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(this: CursorCloudCheckpointStore, value) {
if (value.phase === "terminal" && value.pendingTerminalDelivery) { if (value.phase === "terminal" && value.pendingTerminalDelivery) {
pausedTerminalWrites += 1; pausedTerminalWrites += 1;
await terminalGate; await terminalGate;
@@ -151,7 +151,7 @@ describe("Cursor Cloud bounded stdio proxy", () => {
}); });
let openedSession!: CursorCloudSessionHandle; let openedSession!: CursorCloudSessionHandle;
const openSession = CursorCloudDriver.prototype.openSession; const openSession = CursorCloudDriver.prototype.openSession;
vi.spyOn(CursorCloudDriver.prototype, "openSession").mockImplementation(async function(input) { vi.spyOn(CursorCloudDriver.prototype, "openSession").mockImplementation(async function(this: CursorCloudDriver, input) {
openedSession = await openSession.call(this, input); openedSession = await openSession.call(this, input);
return openedSession; return openedSession;
}); });
@@ -139,7 +139,7 @@ describe("Cursor Cloud v1 HTTP client", () => {
if (request.url?.includes("/artifacts/download?")) return json(response, { url: `https://${host}/proof?private-signature`, expiresAt: TIME }); if (request.url?.includes("/artifacts/download?")) return json(response, { url: `https://${host}/proof?private-signature`, expiresAt: TIME });
response.end(bytes); response.end(bytes);
}); });
const externalHeaders: HeadersInit[] = []; const externalHeaders: Array<NonNullable<RequestInit["headers"]>> = [];
let unexpectedTargets = 0; let unexpectedTargets = 0;
const transport: typeof fetch = (input, init) => { const transport: typeof fetch = (input, init) => {
const url = String(input); const url = String(input);
@@ -71,7 +71,7 @@ function maintainedMutations(peer: Peer, maxResponseBytes?: number) {
async function fixture(peer = new Peer(), extra: Partial<CursorCloudDriverOptions> = {}) { async function fixture(peer = new Peer(), extra: Partial<CursorCloudDriverOptions> = {}) {
const root = await mkdtemp("/tmp/cc-"); roots.push(root); const root = await mkdtemp("/tmp/cc-"); roots.push(root);
const handler = vi.fn(async () => ({ success: true, contentItems: [{ type: "inputText", text: "accepted" }] })); const handler = vi.fn(async (_call: Parameters<CursorCloudDriverOptions["dynamicToolHandler"]>[0]) => ({ success: true, contentItems: [{ type: "inputText", text: "accepted" }] }));
const options: CursorCloudDriverOptions = { model: "explicit-model", config: { schema: "paperclip.cursor-cloud.config.v1", repos: [{ url: "https://github.com/example/repo", startingRef: "main" }] }, const options: CursorCloudDriverOptions = { model: "explicit-model", config: { schema: "paperclip.cursor-cloud.config.v1", repos: [{ url: "https://github.com/example/repo", startingRef: "main" }] },
binding: { companyId: "company", agentId: "agent", issueId: "issue", normalizedSessionId: "session" }, runtimeDirectory: root, binding: { companyId: "company", agentId: "agent", issueId: "issue", normalizedSessionId: "session" }, runtimeDirectory: root,
relay: { url: "https://relay.example.test/mcp", token: "private-relay-token", socketPath: join(root, "mcp.sock"), expiresAt: Date.now() + 600_000 }, instructions: "Follow these exact instructions.", relay: { url: "https://relay.example.test/mcp", token: "private-relay-token", socketPath: join(root, "mcp.sock"), expiresAt: Date.now() + 600_000 }, instructions: "Follow these exact instructions.",
@@ -111,7 +111,11 @@ async function nextAuthority(f: Awaited<ReturnType<typeof fixture>>) {
return { next, checkpoint: await savedCheckpoint(f.root, f.session), eventCount: f.events.length }; return { next, checkpoint: await savedCheckpoint(f.root, f.session), eventCount: f.events.length };
} }
function relayHeaders(peer: Peer): Record<string, string> { return structuredClone(peer.request!.mcpServers[0]!.headers!); } function relayHeaders(peer: Peer): Record<string, string> {
const server = peer.request!.mcpServers![0]!;
if (!("headers" in server)) throw new Error("Expected an HTTP relay MCP server");
return structuredClone(server.headers!);
}
async function rpc(path: string, body: unknown, token: string | Record<string, string> = "private-relay-token", session?: string): Promise<{ status: number; value: any; session?: string }> { async function rpc(path: string, body: unknown, token: string | Record<string, string> = "private-relay-token", session?: string): Promise<{ status: number; value: any; session?: string }> {
return new Promise((accept, reject) => { return new Promise((accept, reject) => {
@@ -206,7 +210,7 @@ describe("Cursor Cloud native driver", () => {
await f.session.startTurn({ message: { role: "user", text: "Stop an observation in flight" } }); await f.session.startTurn({ message: { role: "user", text: "Stop an observation in flight" } });
await reading.promise; await reading.promise;
try { try {
await f.session.interrupt!(); await f.session.interrupt!({});
await vi.waitFor(() => expect(readAborted).toBe(true), { timeout: 100 }); await vi.waitFor(() => expect(readAborted).toBe(true), { timeout: 100 });
expect((await f.session.read()).phase).toBe("terminal"); expect((await f.session.read()).phase).toBe("terminal");
} finally { blocked.resolve(await original()); } } finally { blocked.resolve(await original()); }
@@ -257,7 +261,7 @@ describe("Cursor Cloud native driver", () => {
await f.session.startTurn({ message: { role: "user", text: "Stop during a provider retry delay" } }); await f.session.startTurn({ message: { role: "user", text: "Stop during a provider retry delay" } });
await waitFor(() => f.events.some(event => (event.payload as { code?: string }).code === "cursor_cloud_observation_retry")); await waitFor(() => f.events.some(event => (event.payload as { code?: string }).code === "cursor_cloud_observation_retry"));
const signal = ((peer.getRun.mock.calls[0] as unknown[])[2] as { signal: AbortSignal }).signal; const signal = ((peer.getRun.mock.calls[0] as unknown[])[2] as { signal: AbortSignal }).signal;
await f.session.interrupt!(); await f.session.interrupt!({});
expect(signal.aborted).toBe(true); expect(signal.aborted).toBe(true);
expect((await f.session.read()).phase).toBe("terminal"); expect((await f.session.read()).phase).toBe("terminal");
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled(); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled();
@@ -354,7 +358,7 @@ describe("Cursor Cloud native driver", () => {
} }
} }
await reading.promise; await reading.promise;
await session.interrupt!(); await session.interrupt!({});
await acknowledgeTerminal(session); await acknowledgeTerminal(session);
await session.attachRun!({ runId: "paperclip-followup" }); await session.attachRun!({ runId: "paperclip-followup" });
const next = await session.startTurn({ message: { role: "user", text: "Second task" } }); const next = await session.startTurn({ message: { role: "user", text: "Second task" } });
@@ -370,7 +374,7 @@ describe("Cursor Cloud native driver", () => {
expect(JSON.parse(await readFile(checkpointPath, "utf8"))).toEqual(before); expect(JSON.parse(await readFile(checkpointPath, "utf8"))).toEqual(before);
expect(await session.read()).toMatchObject({ agentId: previous.agentId, runId: followupRunId, turnId: next.turnId, phase: "running", pendingTerminalDelivery: null }); expect(await session.read()).toMatchObject({ agentId: previous.agentId, runId: followupRunId, turnId: next.turnId, phase: "running", pendingTerminalDelivery: null });
expect(events.slice(eventCount)).toEqual([]); expect(events.slice(eventCount)).toEqual([]);
await session.interrupt!(); await session.interrupt!({});
expect(await session.read()).toMatchObject({ runId: followupRunId, phase: "terminal", pendingTerminalDelivery: { providerRunId: followupRunId, turnId: next.turnId, status: "CANCELLED" } }); expect(await session.read()).toMatchObject({ runId: followupRunId, phase: "terminal", pendingTerminalDelivery: { providerRunId: followupRunId, turnId: next.turnId, status: "CANCELLED" } });
expect(events.filter(event => event.turnId === next.turnId && event.eventType === "turn.interrupted")).toHaveLength(1); expect(events.filter(event => event.turnId === next.turnId && event.eventType === "turn.interrupted")).toHaveLength(1);
}); });
@@ -393,14 +397,14 @@ describe("Cursor Cloud native driver", () => {
}; };
const f = await fixture(peer); const f = await fixture(peer);
await f.session.startTurn({ message: { role: "user", text: "First task" } }); await waiting.promise; await f.session.startTurn({ message: { role: "user", text: "First task" } }); await waiting.promise;
await f.session.interrupt!(); await f.session.interrupt!({});
const next = await nextAuthority(f); const next = await nextAuthority(f);
release.resolve(); await new Promise<void>(accept => setImmediate(accept)); release.resolve(); await new Promise<void>(accept => setImmediate(accept));
expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint); expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint);
expect(f.events.slice(next.eventCount)).toEqual([]); expect(f.events.slice(next.eventCount)).toEqual([]);
expect(streams.map(stream => stream.runId)).toEqual([runId, peer.native!.id]); expect(streams.map(stream => stream.runId)).toEqual([runId, peer.native!.id]);
expect(streams[1]!.signal!.aborted).toBe(false); expect(streams[1]!.signal!.aborted).toBe(false);
await f.session.interrupt!(); await f.session.interrupt!({});
expect((await f.session.read()).pendingTerminalDelivery).toMatchObject({ providerRunId: peer.native!.id, turnId: next.next.turnId }); expect((await f.session.read()).pendingTerminalDelivery).toMatchObject({ providerRunId: peer.native!.id, turnId: next.next.turnId });
}); });
@@ -421,7 +425,7 @@ describe("Cursor Cloud native driver", () => {
released.resolve(); await new Promise<void>(accept => setImmediate(accept)); released.resolve(); await new Promise<void>(accept => setImmediate(accept));
expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint); expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint);
expect(f.events.slice(next.eventCount)).toEqual([]); expect(f.events.slice(next.eventCount)).toEqual([]);
await f.session.interrupt!(); await f.session.interrupt!({});
expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: peer.native!.id, turnId: next.next.turnId } }); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: peer.native!.id, turnId: next.next.turnId } });
}); });
@@ -441,7 +445,7 @@ describe("Cursor Cloud native driver", () => {
released.resolve(oldAgent); await oldReconcile; released.resolve(oldAgent); await oldReconcile;
expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint); expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint);
expect(f.events.slice(next.eventCount)).toEqual([]); expect(f.events.slice(next.eventCount)).toEqual([]);
await f.session.interrupt!(); await f.session.interrupt!({});
expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: peer.native!.id, turnId: next.next.turnId } }); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: peer.native!.id, turnId: next.next.turnId } });
}); });
@@ -453,19 +457,19 @@ describe("Cursor Cloud native driver", () => {
const previous = { ...peer.native! }; const previous = { ...peer.native! };
const oldRead = deferred<CursorCloudRun>(), reading = deferred(); const oldRead = deferred<CursorCloudRun>(), reading = deferred();
peer.getRun.mockImplementationOnce(async () => { reading.resolve(); return oldRead.promise; }); peer.getRun.mockImplementationOnce(async () => { reading.resolve(); return oldRead.promise; });
const oldStop = f.session.interrupt!(); await reading.promise; const oldStop = f.session.interrupt!({}); await reading.promise;
peer.getRun.mockResolvedValueOnce({ ...previous, status: "FINISHED" }); peer.getRun.mockResolvedValueOnce({ ...previous, status: "FINISHED" });
await f.session.reconcile!(); await f.session.reconcile!();
const next = await nextAuthority(f); const next = await nextAuthority(f);
const currentRead = deferred<CursorCloudRun>(), currentReading = deferred(); const currentRead = deferred<CursorCloudRun>(), currentReading = deferred();
peer.getRun.mockImplementationOnce(async () => { currentReading.resolve(); return currentRead.promise; }); peer.getRun.mockImplementationOnce(async () => { currentReading.resolve(); return currentRead.promise; });
const currentStop = f.session.interrupt!(); await currentReading.promise; const currentStop = f.session.interrupt!({}); await currentReading.promise;
const before = await savedCheckpoint(f.root, f.session); const before = await savedCheckpoint(f.root, f.session);
oldRead.resolve(previous); await oldStop; oldRead.resolve(previous); await oldStop;
expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(await savedCheckpoint(f.root, f.session)).toEqual(before);
expect(peer.cancelRun).not.toHaveBeenCalled(); expect(peer.cancelRun).not.toHaveBeenCalled();
const readCount = peer.getRun.mock.calls.length; const readCount = peer.getRun.mock.calls.length;
const duplicateStop = f.session.interrupt!(); const duplicateStop = f.session.interrupt!({});
await new Promise<void>(accept => setImmediate(accept)); await new Promise<void>(accept => setImmediate(accept));
expect(peer.getRun).toHaveBeenCalledTimes(readCount); expect(peer.getRun).toHaveBeenCalledTimes(readCount);
currentRead.resolve({ ...peer.native! }); await Promise.all([currentStop, duplicateStop]); currentRead.resolve({ ...peer.native! }); await Promise.all([currentStop, duplicateStop]);
@@ -480,7 +484,7 @@ describe("Cursor Cloud native driver", () => {
if (phase === "follow-up") { if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "First task" } }); await f.session.startTurn({ message: { role: "user", text: "First task" } });
await waitFor(() => f.events.some(event => event.eventType === "item.delta")); await waitFor(() => f.events.some(event => event.eventType === "item.delta"));
await f.session.interrupt!(); await acknowledgeTerminal(f.session); await f.session.interrupt!({}); await acknowledgeTerminal(f.session);
await f.session.attachRun!({ runId: "paperclip-followup" }); await f.session.attachRun!({ runId: "paperclip-followup" });
} }
const before = await savedCheckpoint(f.root, f.session); const before = await savedCheckpoint(f.root, f.session);
@@ -498,7 +502,7 @@ describe("Cursor Cloud native driver", () => {
expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(await savedCheckpoint(f.root, f.session)).toEqual(before);
expect(f.events.slice(eventCount)).toEqual([]); expect(f.events.slice(eventCount)).toEqual([]);
const reconciliations = peer.getAgent.mock.calls.length, cancellations = peer.cancelRun.mock.calls.length; const reconciliations = peer.getAgent.mock.calls.length, cancellations = peer.cancelRun.mock.calls.length;
await f.session.interrupt!(); await f.session.interrupt!({});
expect(peer.getAgent).toHaveBeenCalledTimes(reconciliations); expect(peer.cancelRun).toHaveBeenCalledTimes(cancellations); expect(peer.getAgent).toHaveBeenCalledTimes(reconciliations); expect(peer.cancelRun).toHaveBeenCalledTimes(cancellations);
const snapshot = await f.session.snapshot(); await f.session.close({ reason: "storage_recovered" }); await f.pump; const snapshot = await f.session.snapshot(); await f.session.close({ reason: "storage_recovered" }); await f.pump;
expect(peer.getAgent).toHaveBeenCalledTimes(reconciliations); expect(peer.cancelRun).toHaveBeenCalledTimes(cancellations); expect(peer.getAgent).toHaveBeenCalledTimes(reconciliations); expect(peer.cancelRun).toHaveBeenCalledTimes(cancellations);
@@ -507,7 +511,7 @@ describe("Cursor Cloud native driver", () => {
expect(await savedCheckpoint(f.root, recovered)).toEqual({ ...before, relayTurnToken: before.relayTurnToken ?? null }); expect(await savedCheckpoint(f.root, recovered)).toEqual({ ...before, relayTurnToken: before.relayTurnToken ?? null });
await recovered.startTurn({ message: { role: "user", text: "Retry after storage recovery" } }); await recovered.startTurn({ message: { role: "user", text: "Retry after storage recovery" } });
await waitFor(() => peer.createAgent.mock.calls.length === 1 && peer.createRun.mock.calls.length === (phase === "fresh" ? 0 : 1)); await waitFor(() => peer.createAgent.mock.calls.length === 1 && peer.createRun.mock.calls.length === (phase === "fresh" ? 0 : 1));
await recovered.interrupt!(); await recovered.interrupt!({});
}); });
it("retains accepted provider work when its confirmed-identity checkpoint write fails", async () => { it("retains accepted provider work when its confirmed-identity checkpoint write fails", async () => {
@@ -531,7 +535,7 @@ describe("Cursor Cloud native driver", () => {
await rm(checkpointPath, { recursive: true }); await rename(savedPath, checkpointPath); await rm(checkpointPath, { recursive: true }); await rename(savedPath, checkpointPath);
} }
expect(await savedCheckpoint(f.root, f.session)).toMatchObject({ agentId: peer.native!.agentId, runId, phase: "running", pendingOperation: null }); expect(await savedCheckpoint(f.root, f.session)).toMatchObject({ agentId: peer.native!.agentId, runId, phase: "running", pendingOperation: null });
await f.session.interrupt!(); await f.session.interrupt!({});
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.cancelRun).toHaveBeenCalledTimes(1); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.cancelRun).toHaveBeenCalledTimes(1);
expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { status: "CANCELLED", providerRunId: runId } }); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { status: "CANCELLED", providerRunId: runId } });
}); });
@@ -543,7 +547,7 @@ describe("Cursor Cloud native driver", () => {
const failure = new Error("controlled post-publication persistence failure"); const failure = new Error("controlled post-publication persistence failure");
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
let intercepted = false; let intercepted = false;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
const persisted = originalWrite.call(this, value); const persisted = originalWrite.call(this, value);
if (value.phase === "creating" && !intercepted) { if (value.phase === "creating" && !intercepted) {
intercepted = true; intercepted = true;
@@ -556,13 +560,13 @@ describe("Cursor Cloud native driver", () => {
const started = f.session.startTurn({ message: { role: "user", text: "Stop before unsent launch" } }); const started = f.session.startTurn({ message: { role: "user", text: "Stop before unsent launch" } });
const rejected = expect(started).rejects.toBe(failure); const rejected = expect(started).rejects.toBe(failure);
await held.promise; await held.promise;
const stopped = f.session.interrupt!(); await stoppingWritten.promise; const stopped = f.session.interrupt!({}); await stoppingWritten.promise;
expect(f.peer.createAgent).not.toHaveBeenCalled(); expect(f.peer.createAgent).not.toHaveBeenCalled();
release.resolve(); await rejected; await stopped; release.resolve(); await rejected; await stopped;
expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(await savedCheckpoint(f.root, f.session)).toEqual(before);
expect(f.peer.createAgent).not.toHaveBeenCalled(); expect(f.peer.getAgent).not.toHaveBeenCalled(); expect(f.peer.cancelRun).not.toHaveBeenCalled(); expect(f.peer.createAgent).not.toHaveBeenCalled(); expect(f.peer.getAgent).not.toHaveBeenCalled(); expect(f.peer.cancelRun).not.toHaveBeenCalled();
await f.session.startTurn({ message: { role: "user", text: "Next admitted task" } }); await f.peer.created.promise; await f.session.startTurn({ message: { role: "user", text: "Next admitted task" } }); await f.peer.created.promise;
await f.session.interrupt!(); await f.session.interrupt!({});
} finally { release.resolve(); write.mockRestore(); } } finally { release.resolve(); write.mockRestore(); }
}); });
@@ -571,10 +575,10 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer); const f = await fixture(peer);
await f.session.startTurn({ message: { role: "user", text: "First task" } }); await peer.created.promise; await f.session.startTurn({ message: { role: "user", text: "First task" } }); await peer.created.promise;
await waitFor(() => f.events.some(event => event.eventType === "item.delta")); await waitFor(() => f.events.some(event => event.eventType === "item.delta"));
await f.session.interrupt!(); await acknowledgeTerminal(f.session); await f.session.interrupt!({}); await acknowledgeTerminal(f.session);
const held = deferred(), release = deferred(), stoppingWritten = deferred(); const held = deferred(), release = deferred(), stoppingWritten = deferred();
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
const persisted = originalWrite.call(this, value); const persisted = originalWrite.call(this, value);
if (value.phase === "creating") return persisted.then(async () => { held.resolve(); await release.promise; }); if (value.phase === "creating") return persisted.then(async () => { held.resolve(); await release.promise; });
if (value.phase === "stopping") void persisted.then(() => stoppingWritten.resolve()); if (value.phase === "stopping") void persisted.then(() => stoppingWritten.resolve());
@@ -582,7 +586,7 @@ describe("Cursor Cloud native driver", () => {
}); });
try { try {
const started = f.session.startTurn({ message: { role: "user", text: "Second task" } }); await held.promise; const started = f.session.startTurn({ message: { role: "user", text: "Second task" } }); await held.promise;
const stopped = f.session.interrupt!(); await stoppingWritten.promise; const stopped = f.session.interrupt!({}); await stoppingWritten.promise;
expect(peer.createRun).not.toHaveBeenCalled(); expect(peer.createRun).not.toHaveBeenCalled();
release.resolve(); release.resolve();
const next = await started; await stopped; const next = await started; await stopped;
@@ -605,7 +609,7 @@ describe("Cursor Cloud native driver", () => {
const after = await savedCheckpoint(f.root, f.session); const after = await savedCheckpoint(f.root, f.session);
expect(after).toEqual({ ...before, sourceSequence: before.sourceSequence + 1 }); expect(after).toEqual({ ...before, sourceSequence: before.sourceSequence + 1 });
expect(f.events.some(event => event.eventType === "turn.failed")).toBe(false); expect(f.events.some(event => event.eventType === "turn.failed")).toBe(false);
await f.session.interrupt!(); await f.session.interrupt!({});
expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: runId } }); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: runId } });
}); });
@@ -617,7 +621,7 @@ describe("Cursor Cloud native driver", () => {
f.peer.getRun.mockResolvedValueOnce({ ...f.peer.native!, status: "FINISHED", ...(identity === "agent" ? { agentId: "bc-different" } : { id: "run-22222222-2222-4222-8222-222222222222" }) }); f.peer.getRun.mockResolvedValueOnce({ ...f.peer.native!, status: "FINISHED", ...(identity === "agent" ? { agentId: "bc-different" } : { id: "run-22222222-2222-4222-8222-222222222222" }) });
await expect(f.session.reconcile!()).rejects.toThrow("confirmed Agent/Run identity"); await expect(f.session.reconcile!()).rejects.toThrow("confirmed Agent/Run identity");
expect(await savedCheckpoint(f.root, f.session)).toEqual(before); expect(await savedCheckpoint(f.root, f.session)).toEqual(before);
await f.session.interrupt!(); await f.session.interrupt!({});
expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: runId } }); expect(await f.session.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { providerRunId: runId } });
}); });
@@ -660,7 +664,7 @@ describe("Cursor Cloud native driver", () => {
const activeSnapshot = await restored.snapshot(); await restored.detachControllerForRestart!(); const activeSnapshot = await restored.snapshot(); await restored.detachControllerForRestart!();
await expect(new CursorCloudDriver(f.options).recoverSession(activeSnapshot)).rejects.toThrow("instructions changed"); await expect(new CursorCloudDriver(f.options).recoverSession(activeSnapshot)).rejects.toThrow("instructions changed");
const final = (await new CursorCloudDriver(revised).recoverSession(activeSnapshot)).session!; sessions.push(final); const final = (await new CursorCloudDriver(revised).recoverSession(activeSnapshot)).session!; sessions.push(final);
await final.interrupt!(); await final.interrupt!({});
await acknowledgeTerminal(final); await acknowledgeTerminal(final);
await final.startTurn({ message: { role: "user", text: "One more task" } }); await final.startTurn({ message: { role: "user", text: "One more task" } });
await waitFor(() => f.peer.createRun.mock.calls.length === 2); await waitFor(() => f.peer.createRun.mock.calls.length === 2);
@@ -702,7 +706,7 @@ describe("Cursor Cloud native driver", () => {
expect(f.handler).toHaveBeenCalledWith(expect.objectContaining({ tool: "write_note", arguments: { note: "first" }, turnId: expect.stringMatching(/^turn-/) })); expect(f.handler).toHaveBeenCalledWith(expect.objectContaining({ tool: "write_note", arguments: { note: "first" }, turnId: expect.stringMatching(/^turn-/) }));
await rpc(path, { id: "missing", method: "tools/call", params: { name: "not_admitted", arguments: {} } }, relayHeaders(f.peer), initialized.session); await rpc(path, { id: "missing", method: "tools/call", params: { name: "not_admitted", arguments: {} } }, relayHeaders(f.peer), initialized.session);
expect(f.handler).toHaveBeenCalledTimes(1); expect(f.handler).toHaveBeenCalledTimes(1);
await f.session.interrupt!(); await f.session.interrupt!({});
expect(f.peer.cancelRun).toHaveBeenCalledTimes(1); expect(f.peer.cancelRun).toHaveBeenCalledTimes(1);
expect(f.peer.getRun.mock.calls.length).toBeGreaterThanOrEqual(2); expect(f.peer.getRun.mock.calls.length).toBeGreaterThanOrEqual(2);
const late = await rpc(path, { id: "late", method: "tools/call", params: { name: "write_note", arguments: { note: "late" } } }, relayHeaders(f.peer), initialized.session); const late = await rpc(path, { id: "late", method: "tools/call", params: { name: "write_note", arguments: { note: "late" } } }, relayHeaders(f.peer), initialized.session);
@@ -849,7 +853,7 @@ describe("Cursor Cloud native driver", () => {
await waitFor(() => peer.getAgent.mock.calls.length > 0); await waitFor(() => peer.getAgent.mock.calls.length > 0);
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createAgent).toHaveBeenCalledTimes(1);
expect((await restored.read?.())?.effectiveModelAcknowledged).toBe(false); expect((await restored.read?.())?.effectiveModelAcknowledged).toBe(false);
await restored.interrupt!(); await restored.interrupt!({});
const restoredSnapshot = await restored.snapshot(); await restored.close({ reason: "done" }); const restoredSnapshot = await restored.snapshot(); await restored.close({ reason: "done" });
await expect(new CursorCloudDriver({ ...f.options, model: "different-model" }).recoverSession(restoredSnapshot)).rejects.toThrow("changed"); await expect(new CursorCloudDriver({ ...f.options, model: "different-model" }).recoverSession(restoredSnapshot)).rejects.toThrow("changed");
}); });
@@ -873,7 +877,7 @@ describe("Cursor Cloud native driver", () => {
expect(after.requestedAgentId).toMatch(/^bc-/); expect(after.requestedAgentId).toMatch(/^bc-/);
await expect(restored.startTurn({ message: { role: "user", text: "Must not replace uncertain work" } })).rejects.toThrow("unresolved remote work"); await expect(restored.startTurn({ message: { role: "user", text: "Must not replace uncertain work" } })).rejects.toThrow("unresolved remote work");
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled(); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled();
peer.getAgent.mockImplementation(getAgent); await restored.interrupt!(); peer.getAgent.mockImplementation(getAgent); await restored.interrupt!({});
}); });
it("allows a definitive direct mutation rejection to settle without pretending work was accepted", async () => { it("allows a definitive direct mutation rejection to settle without pretending work was accepted", async () => {
@@ -893,7 +897,7 @@ describe("Cursor Cloud native driver", () => {
if (phase === "follow-up") { if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } });
await peer.created.promise; await peer.created.promise;
await f.session.interrupt!(); await acknowledgeTerminal(f.session); await f.session.interrupt!({}); await acknowledgeTerminal(f.session);
} }
const before = { fetch: fetch.mock.calls.length, getAgent: peer.getAgent.mock.calls.length, getRun: peer.getRun.mock.calls.length, cancel: peer.cancelRun.mock.calls.length }; const before = { fetch: fetch.mock.calls.length, getAgent: peer.getAgent.mock.calls.length, getRun: peer.getRun.mock.calls.length, cancel: peer.cancelRun.mock.calls.length };
const text = "\u0001".repeat(400_000); const text = "\u0001".repeat(400_000);
@@ -905,11 +909,11 @@ describe("Cursor Cloud native driver", () => {
expect(fetch).toHaveBeenCalledTimes(before.fetch); expect(fetch).toHaveBeenCalledTimes(before.fetch);
expect(await f.session.read()).toMatchObject({ phase: "idle", providerStatus: "REJECTED" }); expect(await f.session.read()).toMatchObject({ phase: "idle", providerStatus: "REJECTED" });
expect(f.events.some(event => event.turnId === turn.turnId && event.eventType === "turn.failed" && event.payload.remoteWorkAccepted === false)).toBe(true); expect(f.events.some(event => event.turnId === turn.turnId && event.eventType === "turn.failed" && event.payload.remoteWorkAccepted === false)).toBe(true);
await f.session.interrupt!(); await f.session.interrupt!({});
expect(peer.getAgent).toHaveBeenCalledTimes(before.getAgent); expect(peer.getAgent).toHaveBeenCalledTimes(before.getAgent);
expect(peer.getRun).toHaveBeenCalledTimes(before.getRun); expect(peer.getRun).toHaveBeenCalledTimes(before.getRun);
expect(peer.cancelRun).toHaveBeenCalledTimes(before.cancel); expect(peer.cancelRun).toHaveBeenCalledTimes(before.cancel);
const snapshot = await f.session.snapshot(); await f.session.detachControllerForRestart(); await f.pump; const snapshot = await f.session.snapshot(); await f.session.detachControllerForRestart!(); await f.pump;
const recovered = await new CursorCloudDriver(f.options).recoverSession(snapshot); const recovered = await new CursorCloudDriver(f.options).recoverSession(snapshot);
expect(recovered.recovered).toBe(true); expect(recovered.recovered).toBe(true);
const restored = recovered.session!; sessions.push(restored); const restored = recovered.session!; sessions.push(restored);
@@ -919,7 +923,7 @@ describe("Cursor Cloud native driver", () => {
const next = await restored.startTurn({ message: { role: "user", text: "A smaller admitted task" } }); const next = await restored.startTurn({ message: { role: "user", text: "A smaller admitted task" } });
await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId })); await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId }));
expect(fetch).toHaveBeenCalledTimes(before.fetch + 1); expect(fetch).toHaveBeenCalledTimes(before.fetch + 1);
await restored.interrupt!(); await restored.interrupt!({});
}); });
it.each(["fresh", "follow-up"] as const)("keeps a %s oversized response fenced after the mutation was sent", async phase => { it.each(["fresh", "follow-up"] as const)("keeps a %s oversized response fenced after the mutation was sent", async phase => {
@@ -928,7 +932,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer, { client: mutations.client }); const f = await fixture(peer, { client: mutations.client });
if (phase === "follow-up") { if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } });
await peer.created.promise; await f.session.interrupt!(); await acknowledgeTerminal(f.session); await peer.created.promise; await f.session.interrupt!({}); await acknowledgeTerminal(f.session);
} }
const before = mutations.fetch.mock.calls.length; const before = mutations.fetch.mock.calls.length;
mutations.fetch.mockImplementationOnce(async (url, init) => { mutations.fetch.mockImplementationOnce(async (url, init) => {
@@ -954,7 +958,7 @@ describe("Cursor Cloud native driver", () => {
if (phase === "follow-up") { if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } });
await peer.created.promise; await peer.created.promise;
await f.session.interrupt!(); await f.session.interrupt!({});
await acknowledgeTerminal(f.session); await acknowledgeTerminal(f.session);
} }
const entered = deferred(), rejectCreate = deferred(), stopHeld = deferred(), releaseStop = deferred(); const entered = deferred(), rejectCreate = deferred(), stopHeld = deferred(), releaseStop = deferred();
@@ -975,7 +979,7 @@ describe("Cursor Cloud native driver", () => {
const before = { getAgent: peer.getAgent.mock.calls.length, getRun: peer.getRun.mock.calls.length, cancel: peer.cancelRun.mock.calls.length }; const before = { getAgent: peer.getAgent.mock.calls.length, getRun: peer.getRun.mock.calls.length, cancel: peer.cancelRun.mock.calls.length };
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
let pausedStop = false, pausedRejection = false; let pausedStop = false, pausedRejection = false;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
if (value.phase === "idle" && value.terminalStatus === "REJECTED" && !pausedRejection) { if (value.phase === "idle" && value.terminalStatus === "REJECTED" && !pausedRejection) {
pausedRejection = true; rejectionHeld.resolve(); pausedRejection = true; rejectionHeld.resolve();
return releaseRejection.promise.then(() => originalWrite.call(this, value)); return releaseRejection.promise.then(() => originalWrite.call(this, value));
@@ -989,7 +993,7 @@ describe("Cursor Cloud native driver", () => {
}); });
let stopDone = false; let stopDone = false;
let stopped: Promise<{ error: unknown | null }> | undefined; let stopped: Promise<{ error: unknown | null }> | undefined;
const stop = () => stopped = f.session.interrupt!().then(() => ({ error: null }), error => ({ error })) const stop = () => stopped = f.session.interrupt!({}).then(() => ({ error: null }), error => ({ error }))
.finally(() => { stopDone = true; }); .finally(() => { stopDone = true; });
try { try {
const turn = await f.session.startTurn({ message: { role: "user", text: rejection === "unsent_payload" ? "\u0001".repeat(400_000) : "Rejected task" } }); const turn = await f.session.startTurn({ message: { role: "user", text: rejection === "unsent_payload" ? "\u0001".repeat(400_000) : "Rejected task" } });
@@ -1029,7 +1033,7 @@ describe("Cursor Cloud native driver", () => {
expect(peer.cancelRun).toHaveBeenCalledTimes(before.cancel); expect(peer.cancelRun).toHaveBeenCalledTimes(before.cancel);
if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(fetchesBefore!); if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(fetchesBefore!);
const snapshot = await f.session.snapshot(); const snapshot = await f.session.snapshot();
await f.session.detachControllerForRestart(); await f.pump; await f.session.detachControllerForRestart!(); await f.pump;
const recovered = await new CursorCloudDriver(f.options).recoverSession(snapshot); const recovered = await new CursorCloudDriver(f.options).recoverSession(snapshot);
expect(recovered.recovered).toBe(true); expect(recovered.recovered).toBe(true);
const restored = recovered.session!; sessions.push(restored); const restored = recovered.session!; sessions.push(restored);
@@ -1037,7 +1041,7 @@ describe("Cursor Cloud native driver", () => {
distinctFollowup(peer); peer.release = deferred(); distinctFollowup(peer); peer.release = deferred();
const next = await restored.startTurn({ message: { role: "user", text: "Next admitted task" } }); const next = await restored.startTurn({ message: { role: "user", text: "Next admitted task" } });
await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId })); await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId }));
await restored.interrupt!(); await restored.interrupt!({});
expect(await restored.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { turnId: next.turnId } }); expect(await restored.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { turnId: next.turnId } });
} finally { } finally {
rejectCreate.resolve(); releaseStop.resolve(); releaseRejection.resolve(); rejectCreate.resolve(); releaseStop.resolve(); releaseRejection.resolve();
@@ -1051,7 +1055,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer), held = deferred(), release = deferred(); const f = await fixture(peer), held = deferred(), release = deferred();
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
let paused = false; let paused = false;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
if (value.phase === "idle" && value.terminalStatus === "REJECTED" && !paused) { if (value.phase === "idle" && value.terminalStatus === "REJECTED" && !paused) {
paused = true; held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); paused = true; held.resolve(); return release.promise.then(() => originalWrite.call(this, value));
} }
@@ -1082,7 +1086,7 @@ describe("Cursor Cloud native driver", () => {
await f.session.reconcile!(); await acknowledgeTerminal(f.session); await f.session.attachRun!({ runId: "current-owner" }); await f.session.reconcile!(); await acknowledgeTerminal(f.session); await f.session.attachRun!({ runId: "current-owner" });
peer.createRun.mockRejectedValue(new CursorCloudClientError("http_error", { status: 422 })); peer.createRun.mockRejectedValue(new CursorCloudClientError("http_error", { status: 422 }));
const held = deferred(), release = deferred(), originalWrite = CursorCloudCheckpointStore.prototype.write; const held = deferred(), release = deferred(), originalWrite = CursorCloudCheckpointStore.prototype.write;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
if (value.phase === "idle" && value.terminalStatus === "REJECTED") { held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); } if (value.phase === "idle" && value.terminalStatus === "REJECTED") { held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); }
return originalWrite.call(this, value); return originalWrite.call(this, value);
}); });
@@ -1105,7 +1109,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer, mutations ? { client: mutations.client } : {}); const f = await fixture(peer, mutations ? { client: mutations.client } : {});
if (phase === "follow-up") { if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await peer.created.promise; await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } }); await peer.created.promise;
await f.session.interrupt!(); await acknowledgeTerminal(f.session); await f.session.interrupt!({}); await acknowledgeTerminal(f.session);
} }
const entered = deferred(), rejected = deferred(); const entered = deferred(), rejected = deferred();
if (mutations) { if (mutations) {
@@ -1119,14 +1123,14 @@ describe("Cursor Cloud native driver", () => {
}); });
peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 })); peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 }));
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
return value.phase === "idle" && value.terminalStatus === "REJECTED" return value.phase === "idle" && value.terminalStatus === "REJECTED"
? Promise.reject(new Error("injected rejection checkpoint failure")) : originalWrite.call(this, value); ? Promise.reject(new Error("injected rejection checkpoint failure")) : originalWrite.call(this, value);
}); });
try { try {
const before = mutations?.fetch.mock.calls.length; const before = mutations?.fetch.mock.calls.length;
const turn = await f.session.startTurn({ message: { role: "user", text: mutations ? "\u0001".repeat(400_000) : "Rejected task" } }); await entered.promise; const turn = await f.session.startTurn({ message: { role: "user", text: mutations ? "\u0001".repeat(400_000) : "Rejected task" } }); await entered.promise;
const stopped = f.session.interrupt!().then(() => null, error => error); const stopped = f.session.interrupt!({}).then(() => null, error => error);
rejected.resolve(); rejected.resolve();
expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") }); expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") });
if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(before!); if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(before!);
@@ -1147,7 +1151,7 @@ describe("Cursor Cloud native driver", () => {
peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 })); peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 }));
const f = await fixture(peer), turn = await f.session.startTurn({ message: { role: "user", text: "Unknown create" } }); const f = await fixture(peer), turn = await f.session.startTurn({ message: { role: "user", text: "Unknown create" } });
await entered.promise; await entered.promise;
const stopped = f.session.interrupt!().then(() => null, error => error); released.resolve(); const stopped = f.session.interrupt!({}).then(() => null, error => error); released.resolve();
expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") }); expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") });
expect(await savedCheckpoint(f.root, f.session)).toMatchObject({ phase: "stopping", pendingOperation: "create_agent", terminalStatus: null }); expect(await savedCheckpoint(f.root, f.session)).toMatchObject({ phase: "stopping", pendingOperation: "create_agent", terminalStatus: null });
expect(f.events.some(event => event.turnId === turn.turnId && event.payload.remoteWorkAccepted === false)).toBe(false); expect(f.events.some(event => event.turnId === turn.turnId && event.payload.remoteWorkAccepted === false)).toBe(false);
@@ -1159,7 +1163,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer, { cancelTimeoutMs: 120 }); const f = await fixture(peer, { cancelTimeoutMs: 120 });
await f.session.startTurn({ message: { role: "user", text: "Keep running" } }); await peer.created.promise; await f.session.startTurn({ message: { role: "user", text: "Keep running" } }); await peer.created.promise;
await waitFor(() => f.events.some(event => event.eventType === "item.delta")); await waitFor(() => f.events.some(event => event.eventType === "item.delta"));
await expect(f.session.interrupt!()).rejects.toThrow("did not confirm"); await expect(f.session.interrupt!({})).rejects.toThrow("did not confirm");
expect(f.events.some(event => event.eventType === "turn.interrupted" || event.eventType === "turn.completed")).toBe(false); expect(f.events.some(event => event.eventType === "turn.interrupted" || event.eventType === "turn.completed")).toBe(false);
expect((await f.session.read?.())?.phase).toBe("stopping"); expect((await f.session.read?.())?.phase).toBe("stopping");
await expect(f.session.startTurn({ message: { role: "user", text: "New work" } })).rejects.toThrow("unresolved"); await expect(f.session.startTurn({ message: { role: "user", text: "New work" } })).rejects.toThrow("unresolved");
@@ -1204,7 +1208,7 @@ describe("Cursor Cloud native driver", () => {
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
const attempts: Array<NonNullable<typeof before.pendingTerminalDelivery>> = []; const attempts: Array<NonNullable<typeof before.pendingTerminalDelivery>> = [];
let injected = false; let injected = false;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(async function(this: CursorCloudCheckpointStore, value) {
if (this.path !== checkpointPath || value.phase !== "terminal") return originalWrite.call(this, value); if (this.path !== checkpointPath || value.phase !== "terminal") return originalWrite.call(this, value);
attempts.push(structuredClone(value.pendingTerminalDelivery)); attempts.push(structuredClone(value.pendingTerminalDelivery));
if (injected) { await releaseRetry.promise; return originalWrite.call(this, value); } if (injected) { await releaseRetry.promise; return originalWrite.call(this, value); }
@@ -1248,7 +1252,7 @@ describe("Cursor Cloud native driver", () => {
const held = deferred(), release = deferred(); const held = deferred(), release = deferred();
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
let paused = false; let paused = false;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
if (this.path === checkpointPath && value.phase === "terminal" && !paused) { if (this.path === checkpointPath && value.phase === "terminal" && !paused) {
paused = true; held.resolve(); return release.promise.then(() => originalWrite.call(this, value)); paused = true; held.resolve(); return release.promise.then(() => originalWrite.call(this, value));
} }
@@ -1261,7 +1265,7 @@ describe("Cursor Cloud native driver", () => {
const late = await rpc(f.options.relay.socketPath, { id: "late", method: "tools/call", params: { name: "write_note", arguments: { note: "No authority after provider termination" } } }, relayHeaders(f.peer), initialized.session); const late = await rpc(f.options.relay.socketPath, { id: "late", method: "tools/call", params: { name: "write_note", arguments: { note: "No authority after provider termination" } } }, relayHeaders(f.peer), initialized.session);
expect(late.status).toBe(403); expect(f.handler).not.toHaveBeenCalled(); expect(late.status).toBe(403); expect(f.handler).not.toHaveBeenCalled();
let stopped = false, reconciled = false, snapshotted = false; let stopped = false, reconciled = false, snapshotted = false;
const stop = f.session.interrupt!().finally(() => { stopped = true; }); const stop = f.session.interrupt!({}).finally(() => { stopped = true; });
const reconcile = f.session.reconcile!().finally(() => { reconciled = true; }); const reconcile = f.session.reconcile!().finally(() => { reconciled = true; });
const snapshot = f.session.snapshot().finally(() => { snapshotted = true; }); const snapshot = f.session.snapshot().finally(() => { snapshotted = true; });
await new Promise<void>(accept => setImmediate(accept)); await new Promise<void>(accept => setImmediate(accept));
@@ -1290,7 +1294,7 @@ describe("Cursor Cloud native driver", () => {
const originalWrite = CursorCloudCheckpointStore.prototype.write; const originalWrite = CursorCloudCheckpointStore.prototype.write;
const deliveries: unknown[] = []; const deliveries: unknown[] = [];
let unavailable = true; let unavailable = true;
const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(value) { const write = vi.spyOn(CursorCloudCheckpointStore.prototype, "write").mockImplementation(function(this: CursorCloudCheckpointStore, value) {
if (this.path === checkpointPath && value.phase === "terminal") { if (this.path === checkpointPath && value.phase === "terminal") {
deliveries.push(structuredClone(value.pendingTerminalDelivery)); deliveries.push(structuredClone(value.pendingTerminalDelivery));
if (unavailable) return Promise.reject(Object.assign(new Error("controlled unavailable checkpoint storage"), { code: "ENOSPC" })); if (unavailable) return Promise.reject(Object.assign(new Error("controlled unavailable checkpoint storage"), { code: "ENOSPC" }));
@@ -1363,7 +1367,7 @@ describe("Cursor Cloud native driver", () => {
await waitFor(() => stream.mock.calls.length > 0); await waitFor(() => stream.mock.calls.length > 0);
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled(); expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled();
expect(JSON.parse(await readFile(checkpointPath, "utf8")).deadlineAt).toBe(deadlineAt); expect(JSON.parse(await readFile(checkpointPath, "utf8")).deadlineAt).toBe(deadlineAt);
await restored.interrupt!(); await restored.interrupt!({});
}); });
it("enforces the saved deadline while the first recovery read remains pending", async () => { it("enforces the saved deadline while the first recovery read remains pending", async () => {
@@ -11,7 +11,7 @@ const result = { schema: "paperclip.run_result.v1", reportedWorkDisposition: "do
async function fixture(overrides: Partial<DotDriverOptions> = {}) { async function fixture(overrides: Partial<DotDriverOptions> = {}) {
const identity = { companyId: randomUUID(), agentId: randomUUID(), issueId: randomUUID(), runId: randomUUID(), sessionId: randomUUID() }; const identity = { companyId: randomUUID(), agentId: randomUUID(), issueId: randomUUID(), runId: randomUUID(), sessionId: randomUUID() };
const principal = { companyId: identity.companyId, grantId: randomUUID() }; const principal = { companyId: identity.companyId, grantId: randomUUID() };
const publish = vi.fn(async () => {}); const publish = vi.fn<DotDriverOptions["publish"]>(async () => {});
const executeTool = vi.fn(async () => ({ saved: true })); const executeTool = vi.fn(async () => ({ saved: true }));
const assertAuthority = vi.fn(async () => {}); const assertAuthority = vi.fn(async () => {});
const driver = new DotHarnessDriver({ identity, principal, expiresAt: Date.now() + 60_000, const driver = new DotHarnessDriver({ identity, principal, expiresAt: Date.now() + 60_000,
@@ -96,7 +96,7 @@ it.each([
return handle; return handle;
}); });
const getCommand = controlPlane.DurablePrpControlPlane.prototype.getCommand; const getCommand = controlPlane.DurablePrpControlPlane.prototype.getCommand;
const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "getCommand").mockImplementation(function (id) { const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "getCommand").mockImplementation(function (this: controlPlane.DurablePrpControlPlane, id) {
const command = getCommand.call(this, id); const command = getCommand.call(this, id);
// Hold the SDK poll until the real process has exited. The authenticated // Hold the SDK poll until the real process has exited. The authenticated
// shutdown receipt and its ACK remain committed in the transport journal. // shutdown receipt and its ACK remain committed in the transport journal.
@@ -112,7 +112,7 @@ it.each([
}); });
let session: Awaited<ReturnType<typeof driver.openSession>> | undefined; let session: Awaited<ReturnType<typeof driver.openSession>> | undefined;
try { try {
session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root });
if (kind === "shutdown") { if (kind === "shutdown") {
await expect(session.close({ reason: "Test complete" })).resolves.toBeUndefined(); await expect(session.close({ reason: "Test complete" })).resolves.toBeUndefined();
expect(exited).toBe(true); expect(exited).toBe(true);
@@ -152,7 +152,7 @@ it.each(["local", "local pending finish", "custom local directory", "external la
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId,
normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId },
runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"),
onSpawn: process => { spawned = process; }, onSpawn: async process => { spawned = process; },
controlPlaneRegistration: async authority => { controlPlaneRegistration: async authority => {
if (emptyController) { if (emptyController) {
authority.setCheckpointPersistence(async saved => { checkpoint = controlPlane.decodeDurablePrpCheckpoint(new Map(saved.records)); }); authority.setCheckpointPersistence(async saved => { checkpoint = controlPlane.decodeDurablePrpCheckpoint(new Map(saved.records)); });
@@ -190,7 +190,7 @@ it.each(["local", "local pending finish", "custom local directory", "external la
}; };
} }
const driver = new RunnerdDotDriver(options); const driver = new RunnerdDotDriver(options);
let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root });
const operation = (action: ExternalProviderOperation["action"], args: Record<string, unknown>): ExternalProviderOperation => ({ const operation = (action: ExternalProviderOperation["action"], args: Record<string, unknown>): ExternalProviderOperation => ({
requestId: randomUUID(), bindingId: input.provider.binding.bindingId, bindingGeneration: 1, runId: input.binding.runId, requestId: randomUUID(), bindingId: input.provider.binding.bindingId, bindingGeneration: 1, runId: input.binding.runId,
normalizedSessionId: input.session.normalizedSessionId!, turnId: options.identity.turnId, assignmentRevision: 1, normalizedSessionId: input.session.normalizedSessionId!, turnId: options.identity.turnId, assignmentRevision: 1,
@@ -82,7 +82,7 @@ it("uses the native request, exact question command ID and durable answer receip
execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"),
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId,
normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId },
onSpawn: process => { spawned = process; }, onSpawn: async process => { spawned = process; },
controlPlaneRegistration: async authority => { await authority.start(controllerPort); controllerPort = Number(new URL(authority.connectUrl).port); controlPlaneRegistration: async authority => { await authority.start(controllerPort); controllerPort = Number(new URL(authority.connectUrl).port);
return { connectUrl: authority.connectUrl, release: () => authority.stop() }; }, return { connectUrl: authority.connectUrl, release: () => authority.stop() }; },
port: { dispatch: async () => {}, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome); }, port: { dispatch: async () => {}, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome); },
@@ -95,7 +95,7 @@ it("uses the native request, exact question command ID and durable answer receip
}); });
const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand"); const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand");
let driver = new RunnerdMuseDriver(options); let driver = new RunnerdMuseDriver(options);
let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root });
try { try {
const prepared = commandSpy.mock.calls.find(call => call[0] === "run.prepare"); const prepared = commandSpy.mock.calls.find(call => call[0] === "run.prepare");
expect(JSON.stringify(prepared?.[1])).not.toContain("connections_search"); expect(JSON.stringify(prepared?.[1])).not.toContain("connections_search");
@@ -166,7 +166,7 @@ it("bounds default Muse question answer command IDs at the native queue boundary
execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"),
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId,
normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId },
onSpawn: process => { spawned = process; }, onSpawn: async process => { spawned = process; },
controlPlaneRegistration: async authority => { await authority.start(controllerPort); controllerPort = Number(new URL(authority.connectUrl).port); controlPlaneRegistration: async authority => { await authority.start(controllerPort); controllerPort = Number(new URL(authority.connectUrl).port);
return { connectUrl: authority.connectUrl, release: () => authority.stop() }; }, return { connectUrl: authority.connectUrl, release: () => authority.stop() }; },
port: { dispatch: async () => {}, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome); }, port: { dispatch: async () => {}, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome); },
@@ -182,7 +182,7 @@ it("bounds default Muse question answer command IDs at the native queue boundary
const response = { schema: "paperclip.question_response.v1" as const, answers: { environment: { selectedOptionIds: ["test"] } } }; const response = { schema: "paperclip.question_response.v1" as const, answers: { environment: { selectedOptionIds: ["test"] } } };
const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand"); const commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand");
let driver = new RunnerdMuseDriver(options); let driver = new RunnerdMuseDriver(options);
let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); let session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root });
try { try {
await session.startTurn({ message: { role: "user", text: "Answer boundary questions." } }); await session.startTurn({ message: { role: "user", text: "Answer boundary questions." } });
await send!(operation("accept", {})); await send!(operation("accept", {}));
@@ -253,7 +253,7 @@ it("rejects bridge-unsupported questions at native admission without a stored re
execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), execution: input, stateDirectory: join(root, "state"), runnerBinary: resolve("runner/target/debug/paperclip-runnerd"),
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId, identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: input.binding.runId,
normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }, normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId },
onSpawn: process => { spawned = process; }, onSpawn: async process => { spawned = process; },
port: { dispatch: async () => {}, settle: async () => {}, inputAvailable: async () => {}, port: { dispatch: async () => {}, settle: async () => {}, inputAvailable: async () => {},
attach: async callback => { send = callback; return async () => { send = undefined; }; } }, attach: async callback => { send = callback; return async () => { send = undefined; }; } },
}; };
@@ -263,7 +263,7 @@ it("rejects bridge-unsupported questions at native admission without a stored re
action: "request_user_input", input: args, digest: externalOperationDigest("request_user_input", args), action: "request_user_input", input: args, digest: externalOperationDigest("request_user_input", args),
}); });
const driver = new RunnerdMuseDriver(options); const driver = new RunnerdMuseDriver(options);
const session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId! }); const session = await driver.openSession({ runId: input.binding.runId, normalizedSessionId: input.session.normalizedSessionId!, workingDirectory: root });
const events: PrpEvent[] = []; const events: PrpEvent[] = [];
let eventReadFailure: unknown; let eventReadFailure: unknown;
const reading = (async () => { for await (const event of session.events()) events.push(event); })() const reading = (async () => { for await (const event of session.events()) events.push(event); })()
@@ -315,8 +315,8 @@ it("keeps the exact Muse question and assignment alive past the native live wind
await writeFile(join(root, "AGENTS.md"), "Use only first-party tools."); await writeFile(join(root, "AGENTS.md"), "Use only first-party tools.");
const input = execution(root); const input = execution(root);
const events: PrpEvent[] = []; const events: PrpEvent[] = [];
const dispatches: PrpEvent[] = []; const dispatches: Array<{ sourceEventId: string; payload: Record<string, unknown> }> = [];
const deliveries: PrpEvent[] = []; const deliveries: Array<{ sourceEventId: string; payload: Record<string, unknown> }> = [];
const settlements = new Map<string, Record<string, unknown>>(); const settlements = new Map<string, Record<string, unknown>>();
let send: ((operation: ExternalProviderOperation) => Promise<void>) | undefined; let send: ((operation: ExternalProviderOperation) => Promise<void>) | undefined;
let spawned: { pid: number; processGroupId: number | null; startedAt: string } | undefined; let spawned: { pid: number; processGroupId: number | null; startedAt: string } | undefined;
@@ -327,7 +327,7 @@ it("keeps the exact Muse question and assignment alive past the native live wind
normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId }; normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId };
const driver = new RunnerdMuseDriver({ const driver = new RunnerdMuseDriver({
execution: input, stateDirectory: join(root, "state"), identity, execution: input, stateDirectory: join(root, "state"), identity,
runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), onSpawn: process => { spawned = process; }, runnerBinary: resolve("runner/target/debug/paperclip-runnerd"), onSpawn: async process => { spawned = process; },
port: { dispatch: async event => { dispatches.push(event); }, inputAvailable: async event => { deliveries.push(event); }, port: { dispatch: async event => { dispatches.push(event); }, inputAvailable: async event => { deliveries.push(event); },
settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome as Record<string, unknown>); }, settle: async event => { settlements.set(String(event.payload.requestId), event.payload.outcome as Record<string, unknown>); },
attach: async callback => { send = callback; return async () => { send = undefined; }; } }, attach: async callback => { send = callback; return async () => { send = undefined; }; } },
@@ -12,10 +12,14 @@ const report = {
evidence: [], verification: [], evidence: [], verification: [],
}; };
type RpcResponse = {
result: { isError?: boolean; tools: Array<{ name: string; inputSchema: unknown }> };
};
async function rpc(bridge: OpenCodeMcpBridge, id: number, method: string, params?: unknown) { async function rpc(bridge: OpenCodeMcpBridge, id: number, method: string, params?: unknown) {
return (await fetch(bridge.url, { method: "POST", headers: { return (await fetch(bridge.url, { method: "POST", headers: {
Authorization: `Bearer ${bridge.secret}`, "Content-Type": "application/json", Authorization: `Bearer ${bridge.secret}`, "Content-Type": "application/json",
}, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }) })).json(); }, body: JSON.stringify({ jsonrpc: "2.0", id, method, params }) })).json() as Promise<RpcResponse>;
} }
it("binds closed rejection shapes to the actual offered finish schema without recording values", async () => { it("binds closed rejection shapes to the actual offered finish schema without recording values", async () => {
@@ -23,7 +27,7 @@ it("binds closed rejection shapes to the actual offered finish schema without re
const bridge = await startOpenCodeMcpBridge({ handler, onDiagnostic: message => messages.push(message) }); const bridge = await startOpenCodeMcpBridge({ handler, onDiagnostic: message => messages.push(message) });
bridges.push(bridge); bridges.push(bridge);
const listed = await rpc(bridge, 1, "tools/list"); const listed = await rpc(bridge, 1, "tools/list");
const schema = listed.result.tools.find((tool: { name: string }) => tool.name === "paperclip_finish").inputSchema; const schema = listed.result.tools.find((tool: { name: string }) => tool.name === "paperclip_finish")!.inputSchema;
const digest = createHash("sha256").update(JSON.stringify(schema)).digest("hex"); const digest = createHash("sha256").update(JSON.stringify(schema)).digest("hex");
for (const [index, input] of [report, { ...report, continuation: null }].entries()) { for (const [index, input] of [report, { ...report, continuation: null }].entries()) {
const result = await rpc(bridge, index + 2, "tools/call", { name: "paperclip_finish", arguments: input }); const result = await rpc(bridge, index + 2, "tools/call", { name: "paperclip_finish", arguments: input });
@@ -241,7 +241,7 @@ describe("OpenCodeServerDriver", () => {
}); });
const session = await driver.openSession({ runId: "empty", normalizedSessionId: "empty", workingDirectory: root }); const session = await driver.openSession({ runId: "empty", normalizedSessionId: "empty", workingDirectory: root });
try { try {
expect(await session.read()).toMatchObject({ messages: [] }); expect(await session.read!()).toMatchObject({ messages: [] });
} finally { } finally {
await session.close({ reason: "test" }); await session.close({ reason: "test" });
} }
@@ -338,7 +338,7 @@ describe("OpenCodeServerDriver", () => {
}); });
const descriptor = await driver.descriptor(); const descriptor = await driver.descriptor();
expect( expect(
descriptor.capabilities.typedEventFamilies.find( descriptor.capabilities.typedEventFamilies?.find(
(family) => family.family === "plan", (family) => family.family === "plan",
), ),
).toMatchObject({ availability: "unsupported" }); ).toMatchObject({ availability: "unsupported" });
@@ -751,7 +751,7 @@ describe("OpenCodeServerDriver", () => {
// run.terminal is owned by the native-session layer above this driver. // run.terminal is owned by the native-session layer above this driver.
// Keep both absences explicit instead of synthesizing Codex-shaped input. // Keep both absences explicit instead of synthesizing Codex-shaped input.
expect( expect(
(await driver.descriptor()).capabilities.typedEventFamilies.find( (await driver.descriptor()).capabilities.typedEventFamilies?.find(
(family) => family.family === "plan", (family) => family.family === "plan",
), ),
).toMatchObject({ availability: "unsupported" }); ).toMatchObject({ availability: "unsupported" });
@@ -1105,7 +1105,7 @@ describe("OpenCodeServerDriver", () => {
expect(JSON.stringify(events)).not.toContain( expect(JSON.stringify(events)).not.toContain(
"submitted prompt must not become assistant text", "submitted prompt must not become assistant text",
); );
expect(await session.usage()).toMatchObject({ expect(await session.usage!()).toMatchObject({
input: 3, input: 3,
output: 2, output: 2,
costUsd: 0.001, costUsd: 0.001,
@@ -1772,7 +1772,7 @@ describe("OpenCodeServerDriver", () => {
// A prompt tools map replaces OpenCode's session permissions. Question is // A prompt tools map replaces OpenCode's session permissions. Question is
// enabled in config without overwriting the allow/ask/deny or path policy. // enabled in config without overwriting the allow/ask/deny or path policy.
expect(submittedPrompt).not.toHaveProperty("tools"); expect(submittedPrompt).not.toHaveProperty("tools");
expect(JSON.stringify(submittedPrompt?.parts ?? null)).not.toContain( expect(JSON.stringify((submittedPrompt as Record<string, unknown> | null)?.parts ?? null)).not.toContain(
PAPERCLIP_EXECUTION_PROMPT, PAPERCLIP_EXECUTION_PROMPT,
); );
const sessionRoot = join(root, "context"); const sessionRoot = join(root, "context");
@@ -2871,13 +2871,13 @@ describe("OpenCodeServerDriver", () => {
await session.startTurn({ message: { role: "user", text: "finish" } }); await session.startTurn({ message: { role: "user", text: "finish" } });
await started; await started;
let stopped = false; let stopped = false;
const stop = session[operation]({ reason: "fixture stop" }).then(() => { stopped = true; }); const stop = session[operation]!({ reason: "fixture stop" }).then(() => { stopped = true; });
await new Promise<void>(resolve => setImmediate(resolve)); await new Promise<void>(resolve => setImmediate(resolve));
expect(stopped).toBe(false); expect(stopped).toBe(false);
releaseFeedback(); releaseFeedback();
await stop; await stop;
expect((await session.snapshot()).semanticResult).not.toBeNull(); expect((await session.snapshot()).semanticResult).not.toBeNull();
const transcript = await session.transcript(); const transcript = await session.transcript!();
expect(transcript.events.filter(event => event.eventType === "run.result.proposed")).toHaveLength(1); expect(transcript.events.filter(event => event.eventType === "run.result.proposed")).toHaveLength(1);
expect(transcript.events.some(event => event.eventType === "item.completed" && (event.payload.item as { is_error?: boolean })?.is_error)).toBe(false); expect(transcript.events.some(event => event.eventType === "item.completed" && (event.payload.item as { is_error?: boolean })?.is_error)).toBe(false);
} finally { } finally {
@@ -57,7 +57,7 @@ describe("per-turn OpenCode reasoning", () => {
}); });
expect(config.provider.openrouter.models["deepseek/deepseek-v4-flash-0731"].options).toBeUndefined(); expect(config.provider.openrouter.models["deepseek/deepseek-v4-flash-0731"].options).toBeUndefined();
} finally { } finally {
try { await session?.close(); } try { await session?.close({ reason: "fixture complete" }); }
finally { await rm(root, { recursive: true, force: true }); } finally { await rm(root, { recursive: true, force: true }); }
} }
}); });
@@ -23,6 +23,8 @@ afterEach(async () => {
await Promise.all(bridges.splice(0).map((bridge) => bridge.close())); await Promise.all(bridges.splice(0).map((bridge) => bridge.close()));
}); });
type ToolCallBody = { result: { content: unknown } };
async function rpc( async function rpc(
bridge: RunnerToolBridge, bridge: RunnerToolBridge,
body: Record<string, unknown>, body: Record<string, unknown>,
@@ -563,7 +565,7 @@ describe("Copilot semantic receipt opt-in", () => {
} }); } });
bridges.push(bridge); bridges.push(bridge);
const request = { id: 3, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } }; const request = { id: 3, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } };
const body = await (await rpc(bridge, request)).json(); const body = await (await rpc(bridge, request)).json() as ToolCallBody;
expect(receipts).toHaveLength(1); expect(receipts).toHaveLength(1);
expect(readNativeSemanticReceipt({ contents: body.result.content })).toEqual(receipts[0]); expect(readNativeSemanticReceipt({ contents: body.result.content })).toEqual(receipts[0]);
expect(receipts[0]).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(rawFinish), expect(receipts[0]).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(rawFinish),
@@ -587,11 +589,11 @@ describe("Copilot semantic receipt opt-in", () => {
const normalizedFirst = validatePrpStructuredRunResult(rawFinish); const normalizedSecond = validatePrpStructuredRunResult(secondInput); const normalizedFirst = validatePrpStructuredRunResult(rawFinish); const normalizedSecond = validatePrpStructuredRunResult(secondInput);
if (!normalizedFirst.ok || !normalizedSecond.ok) throw new Error("Invalid fixture"); if (!normalizedFirst.ok || !normalizedSecond.ok) throw new Error("Invalid fixture");
pending.get("second")!.capture(normalizedSecond.result)(); pending.get("second")!.settle(); pending.get("second")!.capture(normalizedSecond.result)(); pending.get("second")!.settle();
const secondReceipt = readNativeSemanticReceipt({ contents: (await (await second).json()).result.content }); const secondReceipt = readNativeSemanticReceipt({ contents: (await (await second).json() as ToolCallBody).result.content });
expect(secondReceipt).toMatchObject({ inputSha256: semanticInputSha256(secondInput), normalizedInputSha256: semanticInputSha256(normalizedSecond.result) }); expect(secondReceipt).toMatchObject({ inputSha256: semanticInputSha256(secondInput), normalizedInputSha256: semanticInputSha256(normalizedSecond.result) });
pending.get("second")!.capture(normalizedFirst.result)(); pending.get("second")!.capture(normalizedFirst.result)();
pending.get("first")!.capture(normalizedFirst.result)(); pending.get("first")!.settle(); pending.get("first")!.capture(normalizedFirst.result)(); pending.get("first")!.settle();
const firstReceipt = readNativeSemanticReceipt({ contents: (await (await first).json()).result.content }); const firstReceipt = readNativeSemanticReceipt({ contents: (await (await first).json() as ToolCallBody).result.content });
expect(firstReceipt).toMatchObject({ inputSha256: semanticInputSha256(rawFinish), normalizedInputSha256: semanticInputSha256(normalizedFirst.result) }); expect(firstReceipt).toMatchObject({ inputSha256: semanticInputSha256(rawFinish), normalizedInputSha256: semanticInputSha256(normalizedFirst.result) });
expect(receipts).toEqual([secondReceipt, firstReceipt]); expect(receipts).toEqual([secondReceipt, firstReceipt]);
}); });
@@ -600,10 +602,10 @@ describe("Copilot semantic receipt opt-in", () => {
const withEvidence = await startRunnerToolBridge({ tools: [tool("documents.read")], captureSemanticReceipt: () => () => {}, handler: async call => { const withEvidence = await startRunnerToolBridge({ tools: [tool("documents.read")], captureSemanticReceipt: () => () => {}, handler: async call => {
expect(call.captureNormalizedInput).toBeUndefined(); return modelMetadata; expect(call.captureNormalizedInput).toBeUndefined(); return modelMetadata;
} }); bridges.push(withEvidence); } }); bridges.push(withEvidence);
const body = await (await rpc(withEvidence, { id: 1, method: "tools/call", params: { name: "documents.read", arguments: modelMetadata } })).json(); const body = await (await rpc(withEvidence, { id: 1, method: "tools/call", params: { name: "documents.read", arguments: modelMetadata } })).json() as ToolCallBody;
expect(readNativeSemanticReceipt({ contents: body.result.content })).toHaveProperty("normalizedInputSha256", null); expect(readNativeSemanticReceipt({ contents: body.result.content })).toHaveProperty("normalizedInputSha256", null);
const plain = await startRunnerToolBridge({ handler: async call => { expect(call.captureNormalizedInput).toBeUndefined(); return { accepted: true }; } }); bridges.push(plain); const plain = await startRunnerToolBridge({ handler: async call => { expect(call.captureNormalizedInput).toBeUndefined(); return { accepted: true }; } }); bridges.push(plain);
const plainBody = await (await rpc(plain, { id: 2, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } })).json(); const plainBody = await (await rpc(plain, { id: 2, method: "tools/call", params: { name: "paperclip_finish", arguments: rawFinish } })).json() as ToolCallBody;
expect(readNativeSemanticReceipt({ contents: plainBody.result.content })).toBeNull(); expect(readNativeSemanticReceipt({ contents: plainBody.result.content })).toBeNull();
}); });
it.each(["small", "chunked", "tagged", "error"])("preserves %s result encoding and captures scope before dispatch", async encoding => { it.each(["small", "chunked", "tagged", "error"])("preserves %s result encoding and captures scope before dispatch", async encoding => {
@@ -1,7 +1,7 @@
import { readFile, stat } from "node:fs/promises"; import { readFile, stat } from "node:fs/promises";
import { resolve } from "node:path"; import { resolve } from "node:path";
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { import {
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { promisify } from "node:util"; import { promisify } from "node:util";
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { prpSchemaBundle } from "../protocol/generated/schema-bundle.js"; import { prpSchemaBundle } from "../protocol/generated/schema-bundle.js";
@@ -76,7 +76,7 @@ describe("live ACP request recovery", () => {
recovered = await original.snapshot(); recovered = await original.snapshot();
expect(recovered.pendingRuntimeRequests).toHaveLength(2); expect(recovered.pendingRuntimeRequests).toHaveLength(2);
} finally { } finally {
await original.close(); await original.close({ reason: "test complete" });
} }
class AdoptedTransport extends FakeCodexTransport { class AdoptedTransport extends FakeCodexTransport {
takeRestoredRuntimeRequests(): CodexRpcServerRequest[] { return requests; } takeRestoredRuntimeRequests(): CodexRpcServerRequest[] { return requests; }
@@ -99,7 +99,7 @@ describe("live ACP request recovery", () => {
await expect(session.resolveRuntimeRequest?.({ requestId, turnId: "turn-1", resolution })).rejects.toThrow("no longer pending"); await expect(session.resolveRuntimeRequest?.({ requestId, turnId: "turn-1", resolution })).rejects.toThrow("no longer pending");
expect(deliver).toHaveBeenCalledTimes(1); expect(deliver).toHaveBeenCalledTimes(1);
} finally { } finally {
await session.close(); await drained; await session.close({ reason: "test complete" }); await drained;
expect(recoveredEvents.map(event => event.eventType)).not.toContain("runtime_request.created"); expect(recoveredEvents.map(event => event.eventType)).not.toContain("runtime_request.created");
expect(recoveredEvents.filter(event => event.eventType === "runtime_request.expired").map(event => event.payload.requestId)).not.toContain(requestId); expect(recoveredEvents.filter(event => event.eventType === "runtime_request.expired").map(event => event.payload.requestId)).not.toContain(requestId);
} }
@@ -63,7 +63,9 @@ describe("Antigravity candidate trusted startup", () => {
const root = await mkdtemp(join(tmpdir(), "antigravity-candidate-continuation-")); const root = await mkdtemp(join(tmpdir(), "antigravity-candidate-continuation-"));
const admission = candidate(); const admission = candidate();
const retained = { driverSessionId: "driver-retained", providerSessionId: "native-retained", providerIdentity: { const retained = { driverSessionId: "driver-retained", providerSessionId: "native-retained", providerIdentity: {
kind: "acpx", normalizedSessionId: identity.normalizedSessionId, kind: "acpx" as const, normalizedSessionId: identity.normalizedSessionId,
acpxRecordId: "acpx-retained", backendSessionId: "backend-retained", agentSessionId: "native-retained",
workspaceDigest: `sha256:${"e".repeat(64)}`, providerLifetimeFenceCandidates: [1, 2, 3] as const,
// The recovery binding digest is distinct from the release command digest. // The recovery binding digest is distinct from the release command digest.
profileDigest: `sha256:${"a".repeat(64)}`, requestedModel: admission.model, effectiveModel: admission.model, profileDigest: `sha256:${"a".repeat(64)}`, requestedModel: admission.model, effectiveModel: admission.model,
} }; } };
@@ -15,6 +15,7 @@ import type {
} from "../drivers/codex/app-server-transport.js"; } from "../drivers/codex/app-server-transport.js";
import type { CapabilityIssueThreadSnapshot, CapabilityThreadItem } from "../issue-thread/types.js"; import type { CapabilityIssueThreadSnapshot, CapabilityThreadItem } from "../issue-thread/types.js";
import type { CapabilityLiveTransportFactory } from "./live-session.js"; import type { CapabilityLiveTransportFactory } from "./live-session.js";
import type { CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js";
import { import {
createCapabilityTurnStreamDecoder, createCapabilityTurnStreamDecoder,
readCapabilityTurnStream, readCapabilityTurnStream,
@@ -343,10 +344,25 @@ class FakeCleanRoomTransport implements CodexAppServerTransport {
function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory { function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory {
return (options) => { return (options) => {
const evidence = { const evidence: CapabilityRunnerdProcessEvidence = {
runnerPid: 7100, runnerPid: 7100,
runnerProcessGroupId: 7100, runnerProcessGroupId: 7100,
providerPid: null,
providerProcessStartedAt: null,
codexPid: 7200, codexPid: 7200,
codexProcessStartedAt: null,
sidecarPid: null,
sidecarProcessStartedAt: null,
agentPid: null,
agentProcessStartedAt: null,
providerDriver: null,
providerVersion: null,
acpxAgent: null,
agentServerVersion: null,
agentRuntimeVersion: null,
acpProtocolVersion: null,
providerExecutionKind: null,
providerService: null,
runnerExited: false, runnerExited: false,
runnerExitCode: null, runnerExitCode: null,
runnerSignal: null, runnerSignal: null,
@@ -359,7 +375,7 @@ function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransport
options.onEvidence?.(evidence); options.onEvidence?.(evidence);
}); });
options.onEvidence?.(evidence); options.onEvidence?.(evidence);
return { transport, evidence: () => ({ ...evidence }) }; return { transport, evidence: () => ({ ...evidence }), detachControllerForRestart: async () => {} };
}; };
} }
@@ -369,6 +385,8 @@ interface CleanRoomPayload {
identity: { token: string; companyId: string; actorId: string; taskId: string; identifier: string }; identity: { token: string; companyId: string; actorId: string; taskId: string; identifier: string };
limits: { maxTurns: number; maxMessageBytes: number }; limits: { maxTurns: number; maxMessageBytes: number };
view: CapabilityIssueThreadSnapshot; view: CapabilityIssueThreadSnapshot;
toolResult?: unknown;
toolTurnId?: string | null;
configuration: { configuration: {
provider: "codex" | "opencode"; provider: "codex" | "opencode";
model: string | null; model: string | null;
@@ -801,9 +819,9 @@ describe("Capability clean-room chat server", () => {
expect(after.toolTurnId).toMatch(/^turn-/); expect(after.toolTurnId).toMatch(/^turn-/);
const turn = after.view.turns.find((candidate: { id: string }) => candidate.id === after.toolTurnId); const turn = after.view.turns.find((candidate: { id: string }) => candidate.id === after.toolTurnId);
expect(turn).toBeDefined(); expect(turn).toBeDefined();
expect(turn.items.some((item: CapabilityThreadItem) => item.kind === "user_message")).toBe(true); expect(turn!.items.some((item: CapabilityThreadItem) => item.kind === "user_message")).toBe(true);
expect(turn.items.some((item: CapabilityThreadItem) => item.kind === "tool_activity" && item.operationId === "report_progress")).toBe(true); expect(turn!.items.some((item: CapabilityThreadItem) => item.kind === "tool_activity" && item.operationId === "report_progress")).toBe(true);
expect(turn.items.some((item: CapabilityThreadItem) => item.kind === "durable_comment" && item.body === "Progress entered manually from DevTools.")).toBe(true); expect(turn!.items.some((item: CapabilityThreadItem) => item.kind === "durable_comment" && item.body === "Progress entered manually from DevTools.")).toBe(true);
expect(after.view.evidence.state.at(-1)?.toRevision).toBeGreaterThan(0); expect(after.view.evidence.state.at(-1)?.toRevision).toBeGreaterThan(0);
}); });
@@ -24,7 +24,7 @@ import {
type CapabilityLiveTurnEvent, type CapabilityLiveTurnEvent,
} from "./live-session.js"; } from "./live-session.js";
import { DurableCapabilityLiveSessionStore } from "./durable-live-session-store.js"; import { DurableCapabilityLiveSessionStore } from "./durable-live-session-store.js";
import { defaultCapabilityRunnerdBinary } from "./runnerd-codex-transport.js"; import { defaultCapabilityRunnerdBinary, type CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js";
import { persistedCursorUsageNotice } from "../drivers/acpx/usage-accounting.js"; import { persistedCursorUsageNotice } from "../drivers/acpx/usage-accounting.js";
import { captureTurnRejection } from "../../test/capture-turn-rejection.js"; import { captureTurnRejection } from "../../test/capture-turn-rejection.js";
import * as workspaceDiff from "./workspace-diff.js"; import * as workspaceDiff from "./workspace-diff.js";
@@ -45,7 +45,7 @@ it("admits Pi live sessions without candidate opt-in while preserving the exact
const session = await service.create({ provider: "acpx", acpxAgent: "pi", piThinkingLevel: "low", requestedModel: "openrouter/deepseek/deepseek-v4-flash-0731" }); const session = await service.create({ provider: "acpx", acpxAgent: "pi", piThinkingLevel: "low", requestedModel: "openrouter/deepseek/deepseek-v4-flash-0731" });
try { try {
expect(session.snapshot().config.acpxProfile).toMatchObject({ expect(session.snapshot().config.acpxProfile).toMatchObject({
agent: "pi", ...resolveQualifiedAcpxProfile("pi", "openrouter/deepseek/deepseek-v4-flash-0731"), ...resolveQualifiedAcpxProfile("pi", "openrouter/deepseek/deepseek-v4-flash-0731"), agent: "pi",
}); });
const custom = await service.create({ provider: "acpx", acpxAgent: "pi", piThinkingLevel: "low", requestedModel: "another-model" }); const custom = await service.create({ provider: "acpx", acpxAgent: "pi", piThinkingLevel: "low", requestedModel: "another-model" });
expect(custom.snapshot().config.acpxProfile).toMatchObject({ qualificationModel: "another-model", reportedModelId: "another-model" }); expect(custom.snapshot().config.acpxProfile).toMatchObject({ qualificationModel: "another-model", reportedModelId: "another-model" });
@@ -322,10 +322,25 @@ class FakeCapabilityCodexTransport implements CodexAppServerTransport {
function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory { function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory {
return (options) => { return (options) => {
const evidence = { const evidence: CapabilityRunnerdProcessEvidence = {
runnerPid: 4100 + state.transports.length, runnerPid: 4100 + state.transports.length,
runnerProcessGroupId: 4100 + state.transports.length, runnerProcessGroupId: 4100 + state.transports.length,
providerPid: null,
providerProcessStartedAt: null,
codexPid: 5100 + state.transports.length, codexPid: 5100 + state.transports.length,
codexProcessStartedAt: null,
sidecarPid: null,
sidecarProcessStartedAt: null,
agentPid: null,
agentProcessStartedAt: null,
providerDriver: null,
providerVersion: null,
acpxAgent: null,
agentServerVersion: null,
agentRuntimeVersion: null,
acpProtocolVersion: null,
providerExecutionKind: null,
providerService: null,
runnerExited: false, runnerExited: false,
runnerExitCode: null, runnerExitCode: null,
runnerSignal: null, runnerSignal: null,
@@ -337,7 +352,7 @@ function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransport
options.onEvidence?.(evidence); options.onEvidence?.(evidence);
}); });
options.onEvidence?.(evidence); options.onEvidence?.(evidence);
return { transport, evidence: () => ({ ...evidence, runnerExited: transport.processInfo().exited }) }; return { transport, evidence: () => ({ ...evidence, runnerExited: transport.processInfo().exited }), detachControllerForRestart: async () => {} };
}; };
} }
@@ -394,7 +409,7 @@ class TransientFailureLiveSessionStore implements CapabilityLiveSessionStore {
throw new Error("transient live-session store failure"); throw new Error("transient live-session store failure");
} }
if ( if (
snapshot.terminalTurns.length > 0 && snapshot.terminalTurns!.length > 0 &&
(this.#rejectTerminalSaves || this.#terminalSaveFailuresRemaining > 0) (this.#rejectTerminalSaves || this.#terminalSaveFailuresRemaining > 0)
) { ) {
if (this.#terminalSaveFailuresRemaining > 0) { if (this.#terminalSaveFailuresRemaining > 0) {
@@ -647,7 +662,7 @@ describe("Capability live runnerd and Codex session", () => {
outputTokens: 4, outputTokens: 4,
})).resolves.toBe("committed"); })).resolves.toBe("committed");
expect((await store.load(session.id))?.usageLedger.map((receipt) => receipt.receiptId)).toEqual([ expect((await store.load(session.id))?.usageLedger!.map((receipt) => receipt.receiptId)).toEqual([
"usage-before-recovery", "usage-before-recovery",
"usage-after-recovery", "usage-after-recovery",
]); ]);
@@ -1159,7 +1174,7 @@ describe("Capability live runnerd and Codex session", () => {
expect(first.snapshot.providerThreadId).toBe(second.snapshot.providerThreadId); expect(first.snapshot.providerThreadId).toBe(second.snapshot.providerThreadId);
expect(state.attachments).toHaveLength(1); expect(state.attachments).toHaveLength(1);
expect(second.snapshot.providerRunBinding).toEqual(state.attachments[0]); expect(second.snapshot.providerRunBinding).toEqual(state.attachments[0]);
expect(second.snapshot.usageLedger.slice(-2)).toMatchObject([ expect(second.snapshot.usageLedger!.slice(-2)).toMatchObject([
{ providerRequests: 1, inputTokens: 100, outputTokens: 10 }, { providerRequests: 1, inputTokens: 100, outputTokens: 10 },
{ providerRequests: 1, inputTokens: 100, outputTokens: 10 }, { providerRequests: 1, inputTokens: 100, outputTokens: 10 },
]); ]);
@@ -1497,7 +1512,7 @@ describe("Capability live runnerd and Codex session", () => {
const result = await session.sendMessage("report overlapping usage"); const result = await session.sendMessage("report overlapping usage");
expect(result.snapshot.usageLedger).toHaveLength(1); expect(result.snapshot.usageLedger).toHaveLength(1);
expect(result.snapshot.usageLedger[0]).toMatchObject({ expect(result.snapshot.usageLedger![0]).toMatchObject({
providerRequests: 1, providerRequests: 1,
inputTokens: 100, inputTokens: 100,
outputTokens: 10, outputTokens: 10,
@@ -1596,7 +1611,7 @@ describe("Capability live runnerd and Codex session", () => {
const result = await session.sendMessage("report late usage"); const result = await session.sendMessage("report late usage");
expect(result.snapshot.usageLedger).toHaveLength(1); expect(result.snapshot.usageLedger).toHaveLength(1);
expect(result.snapshot.usageLedger[0]).toMatchObject({ expect(result.snapshot.usageLedger![0]).toMatchObject({
providerRequests: 2, providerRequests: 2,
inputTokens: 100, inputTokens: 100,
outputTokens: 10, outputTokens: 10,
@@ -106,10 +106,10 @@ it.each([
const recovered = await restarted.backend.recoverSession!(snapshot, { signal: new AbortController().signal }); const recovered = await restarted.backend.recoverSession!(snapshot, { signal: new AbortController().signal });
expect(recovered).toMatchObject({ recovered: true }); expect(recovered).toMatchObject({ recovered: true });
if (!recovered.recovered) throw new Error(recovered.reason); if (!recovered.recovered) throw new Error(recovered.reason);
expect((await recovered.session.snapshot()).workingDirectory).toBe(cwd); expect((await recovered.session!.snapshot()).workingDirectory).toBe(cwd);
// Recovery reuses the saved preparation instead of resolving a new directory. // Recovery reuses the saved preparation instead of resolving a new directory.
expect(restarted.prepareLaunch.mock.calls[0]![1]).toMatchObject({ configurationId: "plugin-run" }); expect(restarted.prepareLaunch.mock.calls[0]![1]).toMatchObject({ configurationId: "plugin-run" });
await recovered.session.close({ reason: "test complete" }); await recovered.session!.close({ reason: "test complete" });
const moved = await pluginBackend(root, join(root, "unused"), "runner_prepared_launch"); const moved = await pluginBackend(root, join(root, "unused"), "runner_prepared_launch");
await expect(moved.backend.recoverSession!({ ...snapshot, workingDirectory: join(root, "elsewhere") }, await expect(moved.backend.recoverSession!({ ...snapshot, workingDirectory: join(root, "elsewhere") },
@@ -24,7 +24,7 @@ describe("ACPX questions through the shared runner transport", () => {
[timing!.id]: { selectedOptionIds: [timing!.options![1]!.id] }, [timing!.id]: { selectedOptionIds: [timing!.options![1]!.id] },
} }; } };
expect(runtimeRequestResponse({ requestId: "question-1", requestKind: "elicitation", method: origin.method, expect(runtimeRequestResponse({ requestId: "question-1", requestKind: "elicitation", method: origin.method,
turnId: "turn", itemId: "item", status: "pending", prompt: "Interview", input: shown }, { action: "submit", response }, context)).toEqual({ action: "submit", response }); turnId: "turn", itemId: "item", status: "pending", prompt: "Interview", details: {}, input: shown }, { action: "submit", response }, context)).toEqual({ action: "submit", response });
expect(form.accept(response)).toEqual({ action: "accept", content: { organization: "Garden club", goal: "Welcome note", timing: "later" } }); expect(form.accept(response)).toEqual({ action: "accept", content: { organization: "Garden club", goal: "Welcome note", timing: "later" } });
}); });
it("does not admit an invalid canonical question set", () => { it("does not admit an invalid canonical question set", () => {
@@ -47,7 +47,7 @@ async function fixture(agent: "codex" | "claude" = "codex", nativeSource = false
binding: { companyId: nativeIdentity.companyId, agentId: nativeIdentity.agentId, issueId: nativeIdentity.issueId, runId: identity.runId, executionWorkspaceId: "workspace" }, binding: { companyId: nativeIdentity.companyId, agentId: nativeIdentity.agentId, issueId: nativeIdentity.issueId, runId: identity.runId, executionWorkspaceId: "workspace" },
task: { identifier: "TASK-1", title: "Complete fake work", description: null, prompt: "Complete fake work", workMode: "standard" }, task: { identifier: "TASK-1", title: "Complete fake work", description: null, prompt: "Complete fake work", workMode: "standard" },
workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null }, workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null },
session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "acpx_runtime", protocolVersion: 1 }, session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "acpx_runtime", protocolVersion: 1, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } },
provider: { kind: "acpx", agent, model: "explicit-source-model", permissionPolicy: "interactive", profile: resolveQualifiedAcpxProfile(agent, "explicit-source-model") }, provider: { kind: "acpx", agent, model: "explicit-source-model", permissionPolicy: "interactive", profile: resolveQualifiedAcpxProfile(agent, "explicit-source-model") },
completionContract: { id: "contract", sha256: sha("contract"), schemaVersion: "paperclip.completion-contract.v1", completionContract: { id: "contract", sha256: sha("contract"), schemaVersion: "paperclip.completion-contract.v1",
contract: { revision: "1", objective: "Complete fake work", criteria: [{ id: "objective", requirement: "Complete fake work" }] } }, interactionResponses: [], credentialBindings: [] }; contract: { revision: "1", objective: "Complete fake work", criteria: [{ id: "objective", requirement: "Complete fake work" }] } }, interactionResponses: [], credentialBindings: [] };
@@ -147,7 +147,7 @@ it.skipIf(process.platform === "win32")("recovers and adopts ACPX through the re
environment: { PAPERCLIP_WORKSPACE_CWD: f.root }, dynamicTools: [] }); environment: { PAPERCLIP_WORKSPACE_CWD: f.root }, dynamicTools: [] });
let recovered = await backend().recoverSession!(f.nativeSnapshot!, { signal: new AbortController().signal }); let recovered = await backend().recoverSession!(f.nativeSnapshot!, { signal: new AbortController().signal });
expect(recovered.recovered).toBe(true); expect(recovered.recovered).toBe(true);
const pid = source.evidence().runnerPid, startedAt = source.transport.processInfo().startedAt; const pid = source.evidence().runnerPid, startedAt = source.transport.processInfo!().startedAt;
expect(await f.count("turn.start")).toBe(1); expect(await f.count("processStart")).toBe(2); expect(await f.count("turn.start")).toBe(1); expect(await f.count("processStart")).toBe(2);
await recovered.session!.detachControllerForRestart!(); await recovered.session!.detachControllerForRestart!();
const launch = vi.fn(() => { throw new Error("duplicate source launch"); }); const launch = vi.fn(() => { throw new Error("duplicate source launch"); });
@@ -197,7 +197,7 @@ it.skipIf(process.platform === "win32").each(["codex", "claude", "adopt", "pause
await expect(source.transport.request("turn/start", {})).rejects.toThrow("source_attachment_pending"); await expect(source.transport.request("turn/start", {})).rejects.toThrow("source_attachment_pending");
const pid = source.evidence().runnerPid; const pid = source.evidence().runnerPid;
if (mode === "adopt") { if (mode === "adopt") {
const startedAt = source.transport.processInfo().startedAt; const startedAt = source.transport.processInfo!().startedAt;
await source.detachControllerForRestart(); await source.detachControllerForRestart();
const launch = vi.fn(() => { throw new Error("duplicate source launch"); }); const launch = vi.fn(() => { throw new Error("duplicate source launch"); });
source = f.recover({ runnerProcessLauncher: launch, authorizeRunnerAdoption: async () => { throw new Error("old source configuration"); }, source = f.recover({ runnerProcessLauncher: launch, authorizeRunnerAdoption: async () => { throw new Error("old source configuration"); },
@@ -38,7 +38,7 @@ async function verifyLiveRunnerAdoption(
evidence: [], verification: [], attentionRequests: [], artifacts: [], evidence: [], verification: [], attentionRequests: [], artifacts: [],
})); }));
const server = createServer(); const server = createServer();
let authority: DurablePrpControlPlane | null = null; let authority = null as DurablePrpControlPlane | null;
const checkpoint = vi.fn(async () => undefined); const checkpoint = vi.fn(async () => undefined);
server.on("upgrade", (request, socket, head) => { server.on("upgrade", (request, socket, head) => {
if (!authority) { if (!authority) {
@@ -136,7 +136,7 @@ async function verifyLiveRunnerAdoption(
); );
} }
runnerPid = first.evidence().runnerPid; runnerPid = first.evidence().runnerPid;
runnerStartedAt = first.transport.processInfo().startedAt; runnerStartedAt = first.transport.processInfo!().startedAt;
expect(runnerPid).toEqual(expect.any(Number)); expect(runnerPid).toEqual(expect.any(Number));
if (goalMidTurn) { if (goalMidTurn) {
@@ -404,7 +404,7 @@ async function verifyLiveRunnerAdoption(
} else expect(redispatchedTool).not.toHaveBeenCalled(); } else expect(redispatchedTool).not.toHaveBeenCalled();
} }
expect(adopted.evidence().runnerPid).toBe(runnerPid); expect(adopted.evidence().runnerPid).toBe(runnerPid);
expect(adopted.transport.processInfo().startedAt).toBe(runnerStartedAt); expect(adopted.transport.processInfo!().startedAt).toBe(runnerStartedAt);
if (startWithoutCheckpoint) { if (startWithoutCheckpoint) {
const retained = JSON.parse(await readFile(controlPlaneStatePath, "utf8")); const retained = JSON.parse(await readFile(controlPlaneStatePath, "utf8"));
for (const type of ["run.prepare", "session.open"]) { for (const type of ["run.prepare", "session.open"]) {
@@ -448,7 +448,7 @@ async function verifyLiveRunnerAdoption(
expect(await Promise.race([notificationsClosed, new Promise<boolean>(resolve => { queueDeadline = setTimeout(() => resolve(false), 250); })])).toBe(true); expect(await Promise.race([notificationsClosed, new Promise<boolean>(resolve => { queueDeadline = setTimeout(() => resolve(false), 250); })])).toBe(true);
} finally { clearTimeout(queueDeadline); } } finally { clearTimeout(queueDeadline); }
await expect(adopted.transport.close()).rejects.toBe(probeError); await expect(adopted.transport.close()).rejects.toBe(probeError);
expect(adopted.transport.processInfo()).toMatchObject({ exited: false }); expect(adopted.transport.processInfo!()).toMatchObject({ exited: false });
expect(() => process.kill(runnerPid!, 0)).not.toThrow(); expect(() => process.kill(runnerPid!, 0)).not.toThrow();
expect(signal).not.toHaveBeenCalled(); expect(signal).not.toHaveBeenCalled();
expect(checkpoint).not.toHaveBeenCalled(); expect(checkpoint).not.toHaveBeenCalled();
@@ -14,9 +14,18 @@ import { expect, it, vi } from "vitest";
import { DurablePrpControlPlane } from "../control-plane/durable-prp-control-plane.js"; import { DurablePrpControlPlane } from "../control-plane/durable-prp-control-plane.js";
import * as durableControlPlane from "../control-plane/durable-prp-control-plane.js"; import * as durableControlPlane from "../control-plane/durable-prp-control-plane.js";
import { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js"; import { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js";
import { defaultCapabilityRunnerdBinary, fakeCodex, fakeCodexArgs } from "../../test/support/runnerd-transport.js"; import { committableStore, defaultCapabilityRunnerdBinary, fakeCodex, fakeCodexArgs } from "../../test/support/runnerd-transport.js";
it.each([ type WarmAttachRecoveryCase = {
lossPoint: string;
routed: boolean;
recoveryFault: string;
executionPrepared?: boolean;
externalArtifact?: boolean;
ordinaryFollowup?: boolean | string;
};
it.each<WarmAttachRecoveryCase>([
...[ ...[
"before-result", "before-result",
"after-result", "after-result",
@@ -478,14 +487,15 @@ it.each([
).toBe(false); ).toBe(false);
return; return;
} }
const commit = core.store.commit.bind(core.store); const store = committableStore(core);
const commit = store.commit.bind(store);
let lossObserved = false; let lossObserved = false;
let signalLoss!: () => void; let signalLoss!: () => void;
const loss = new Promise<void>((resolveLoss) => { const loss = new Promise<void>((resolveLoss) => {
signalLoss = resolveLoss; signalLoss = resolveLoss;
}); });
commitSpy = vi commitSpy = vi
.spyOn(core.store, "commit") .spyOn(store, "commit")
.mockImplementation((candidate) => { .mockImplementation((candidate) => {
// The selected process-crash boundary stays unavailable until the // The selected process-crash boundary stays unavailable until the
// exact owned runner is joined. A later replay must not silently // exact owned runner is joined. A later replay must not silently
@@ -67,7 +67,7 @@ it("keeps a quiet active Codex turn in the same process across connection lease
expect(calls).not.toContain("turn/interrupt"); expect(calls).not.toContain("turn/interrupt");
expect(calls).not.toContain("thread/resume"); expect(calls).not.toContain("thread/resume");
const state = JSON.parse(await readFile(join(stateDirectory, "fake-codex-state.json"), "utf8")); const state = JSON.parse(await readFile(join(stateDirectory, "fake-codex-state.json"), "utf8"));
expect(state.threadId).toBe(opened.thread.id); expect(state.threadId).toBe((opened.thread as { id: string }).id);
expect(state.activeTurnId).toBe("provider-turn-1"); expect(state.activeTurnId).toBe("provider-turn-1");
} finally { } finally {
await bundle.transport.close(); await bundle.transport.close();
@@ -286,7 +286,7 @@ it.each([
// output plus another full provider-owned prefix behind the old suspend. // output plus another full provider-owned prefix behind the old suspend.
const runnerFile = join(original, "runner/runner-state.json"); const runnerFile = join(original, "runner/runner-state.json");
const runnerBefore = JSON.parse(await readFile(runnerFile, "utf8")); const runnerBefore = JSON.parse(await readFile(runnerFile, "utf8"));
const template = builder.store.state.committedEvents[0]!.envelope.payload; const template = builder.store.state.committedEvents[0]!.envelope.payload as Record<string, unknown>;
for (let index = 0; index < 90; index++) { for (let index = 0; index < 90; index++) {
const sourceSeq = runnerBefore.nextSourceSeq++; const sourceSeq = runnerBefore.nextSourceSeq++;
const event = { const event = {
@@ -388,7 +388,7 @@ it.each([
sessionId: identity.normalizedSessionId, sessionId: identity.normalizedSessionId,
}, },
backend: { backend: {
kind: "codex", kind: "runner" as const,
name: maintenanceFixtureBackendName(fixtureId), name: maintenanceFixtureBackendName(fixtureId),
}, },
identity, identity,
@@ -406,7 +406,7 @@ it.each([
recordEpoch, recordEpoch,
signal: maintenanceAbort.signal, signal: maintenanceAbort.signal,
}; };
const close = vi.fn(async () => { const close = vi.fn(async (): Promise<void> => {
throw new NativeSessionCloseUnrecoverableError(); throw new NativeSessionCloseUnrecoverableError();
}); });
const start = vi.fn(async () => { const start = vi.fn(async () => {
@@ -424,9 +424,10 @@ it.each([
capabilities: async () => capabilities, capabilities: async () => capabilities,
events: async function* () {}, events: async function* () {},
startTurn: start, startTurn: start,
result: async () => { throw new Error("fixture never produces a result"); },
close, close,
snapshot: async () => ({ snapshot: async () => ({
backendKind: "codex", backendKind: "runner",
sessionId: input.binding.sessionId, sessionId: input.binding.sessionId,
identity: input.binding, identity: input.binding,
providerSessionId: thread.thread.id, providerSessionId: thread.thread.id,
@@ -471,6 +472,7 @@ it.each([
normalizedSessionId: input.binding.sessionId, normalizedSessionId: input.binding.sessionId,
driverKind: "codex_app_server", driverKind: "codex_app_server",
protocolVersion: 1, protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
}, },
completionContract: { completionContract: {
id: "contract-maintenance", id: "contract-maintenance",
@@ -211,6 +211,7 @@ it("does not retry a real memoized transport close whose suspension proof is una
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
driverKind: "codex_app_server", driverKind: "codex_app_server",
protocolVersion: 1, protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
}, },
provider: { kind: "codex", model: null }, provider: { kind: "codex", model: null },
completionContract: { completionContract: {
@@ -499,7 +500,7 @@ it.each([
message: { role: "user", text: "Keep working until interrupted." }, message: { role: "user", text: "Keep working until interrupted." },
}); });
if (label === "interrupted") { if (label === "interrupted") {
await session.interrupt({ await session.interrupt!({
turnId: turn.turnId, turnId: turn.turnId,
reason: "Stopped by the user", reason: "Stopped by the user",
}); });
@@ -525,7 +526,7 @@ it.each([
}, },
}); });
} finally { } finally {
await session.close().catch(() => undefined); await session.close({ reason: "test complete" }).catch(() => undefined);
await rm(stateDirectory, { await rm(stateDirectory, {
recursive: true, recursive: true,
force: true, force: true,
@@ -13,6 +13,7 @@ import { codexSemanticToolSpecs } from "../drivers/codex/codex-app-server-driver
import { releaseMaterializedNativeRuntimeSkills } from "../drivers/runtime-context-materializer.js"; import { releaseMaterializedNativeRuntimeSkills } from "../drivers/runtime-context-materializer.js";
import { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js"; import { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js";
import { import {
committableStore,
defaultCapabilityRunnerdBinary, defaultCapabilityRunnerdBinary,
fakeCodex, fakeCodex,
fakeCodexArgs, fakeCodexArgs,
@@ -163,8 +164,9 @@ it.each([
providerPid = bundle.evidence().codexPid; providerPid = bundle.evidence().codexPid;
const rotations: (typeof core.store.state)[] = []; const rotations: (typeof core.store.state)[] = [];
const preparedStates: (typeof core.store.state)[] = []; const preparedStates: (typeof core.store.state)[] = [];
const commit = core.store.commit.bind(core.store); const store = committableStore(core);
vi.spyOn(core.store, "commit").mockImplementation((candidate) => { const commit = store.commit.bind(store);
vi.spyOn(store, "commit").mockImplementation((candidate) => {
commit(candidate); commit(candidate);
// Capture the durable old epoch at publication. The authenticated // Capture the durable old epoch at publication. The authenticated
// successor may activate before attachRun observes the completed command. // successor may activate before attachRun observes the completed command.
@@ -716,7 +718,7 @@ it("recovers an active second warm business run after deleting its controller co
await writeFile(join(skillRoot, "SKILL.md"), "# Pinned skill\n"); await writeFile(join(skillRoot, "SKILL.md"), "# Pinned skill\n");
await writeFile(join(instructionRoot, "AGENTS.md"), "Pinned instructions\n"); await writeFile(join(instructionRoot, "AGENTS.md"), "Pinned instructions\n");
const server = createServer(); const server = createServer();
let authority: DurablePrpControlPlane | null = null, generation = 0; let authority = null as DurablePrpControlPlane | null, generation = 0;
let checkpoint: durableControlPlane.DurablePrpCheckpoint | undefined; let checkpoint: durableControlPlane.DurablePrpCheckpoint | undefined;
const registered = new WeakSet<DurablePrpControlPlane>(); const registered = new WeakSet<DurablePrpControlPlane>();
server.on("upgrade", (request, socket, head) => authority server.on("upgrade", (request, socket, head) => authority
@@ -760,7 +762,7 @@ it("recovers an active second warm business run after deleting its controller co
providerPid = first.evidence().codexPid; providerPid = first.evidence().codexPid;
expect(providerPid).toEqual(expect.any(Number)); expect(providerPid).toEqual(expect.any(Number));
const providerStart = execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim(); const providerStart = execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim();
const startedAt = first.transport.processInfo().startedAt; const startedAt = first.transport.processInfo!().startedAt;
const firstCompleted = waitComplete(first.transport); const firstCompleted = waitComplete(first.transport);
await within("first turn/start", first.transport.request("turn/start", { input: [{ type: "text", text: "first healthy business run" }] })); await within("first turn/start", first.transport.request("turn/start", { input: [{ type: "text", text: "first healthy business run" }] }));
await within("healthy first turn completion", firstCompleted); await within("healthy first turn completion", firstCompleted);
@@ -805,7 +807,7 @@ it("recovers an active second warm business run after deleting its controller co
expect(execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim()).toBe(providerStart); expect(execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim()).toBe(providerStart);
expect(calls).toBe(2); expect(calls).toBe(2);
expect(authority!.store.state.commands.filter(command => command.type === "semantic_tool.result")).toHaveLength(0); expect(authority!.store.state.commands.filter(command => command.type === "semantic_tool.result")).toHaveLength(0);
expect(recovered.transport.processInfo().startedAt).toBe(startedAt); expect(recovered.transport.processInfo!().startedAt).toBe(startedAt);
expect(authority!.store.state.identity).toEqual(savedIdentity); expect(authority!.store.state.identity).toEqual(savedIdentity);
expect(authority!.store.state.commands.filter(command => command.type === "turn.start")).toHaveLength(1); expect(authority!.store.state.commands.filter(command => command.type === "turn.start")).toHaveLength(1);
expect(noSpawn).not.toHaveBeenCalled(); expect(noSpawn).not.toHaveBeenCalled();
@@ -283,7 +283,7 @@ it.each(["resolved", "rejected"] as const)(
expect(error).toMatchObject({ message: expect.stringContaining("runner_process_termination_unconfirmed") }); expect(error).toMatchObject({ message: expect.stringContaining("runner_process_termination_unconfirmed") });
if (outcome === "rejected") expect(error).toHaveProperty("cause", probeError); if (outcome === "rejected") expect(error).toHaveProperty("cause", probeError);
expect(children).toHaveLength(1); expect(children).toHaveLength(1);
expect(bundle.transport.processInfo()).toMatchObject({ exited: false }); expect(bundle.transport.processInfo!()).toMatchObject({ exited: false });
expect(() => process.kill(children[0]!.pid!, 0)).not.toThrow(); expect(() => process.kill(children[0]!.pid!, 0)).not.toThrow();
} finally { } finally {
const providerPid = bundle.evidence().codexPid; const providerPid = bundle.evidence().codexPid;
@@ -358,7 +358,7 @@ it("preserves the cause when a replacement runner's health cannot be verified",
if (child) return { if (child) return {
child: { pid: 42, exitCode: null, kill: () => false }, child: { pid: 42, exitCode: null, kill: () => false },
completion: Promise.reject(probeError), completion: Promise.reject(probeError),
isExitConfirmed: () => false, isExitConfirmed: (): boolean => false,
}; };
child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" }); child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" });
const ownedChild = child; const ownedChild = child;
@@ -390,7 +390,7 @@ it("preserves the cause when a replacement runner's health cannot be verified",
}); });
expect(error).toHaveProperty("message", expect.stringContaining("probe_timeout")); expect(error).toHaveProperty("message", expect.stringContaining("probe_timeout"));
expect(launch).toHaveBeenCalledTimes(2); expect(launch).toHaveBeenCalledTimes(2);
expect(bundle.transport.processInfo()).toMatchObject({ exited: false }); expect(bundle.transport.processInfo!()).toMatchObject({ exited: false });
} finally { } finally {
const providerPid = bundle.evidence().codexPid; const providerPid = bundle.evidence().codexPid;
if (providerPid) { if (providerPid) {
@@ -415,7 +415,7 @@ it("retries a replacement whose staging failed before any process launched", asy
if (launch.mock.calls.length === 2) return { if (launch.mock.calls.length === 2) return {
child: { pid: undefined, exitCode: null, kill: () => false }, child: { pid: undefined, exitCode: null, kill: () => false },
completion: Promise.reject(stagingError), completion: Promise.reject(stagingError),
isExitConfirmed: () => true, isExitConfirmed: (): boolean => true,
}; };
const child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" }); const child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" });
children.push(child); children.push(child);
@@ -2659,7 +2659,7 @@ it.each(["reuse", "large-instructions", "rotated-recovery", "removed-context", "
resolved.skills[0]!.bundle.rootPath = "/guest/.agents/skills/assigned"; resolved.skills[0]!.bundle.rootPath = "/guest/.agents/skills/assigned";
const materialize = vi.spyOn(runtimeMaterializer, "prepareIsolatedCodexHome").mockRejectedValue(new Error("unexpected Core materialization")); const materialize = vi.spyOn(runtimeMaterializer, "prepareIsolatedCodexHome").mockRejectedValue(new Error("unexpected Core materialization"));
const discover = vi.spyOn(codexCommandRuntime, "resolveCodexCommand").mockImplementation(() => { throw new Error("unexpected Core discovery"); }); const discover = vi.spyOn(codexCommandRuntime, "resolveCodexCommand").mockImplementation(() => { throw new Error("unexpected Core discovery"); });
let configuration: Record<string, unknown> | undefined; let configuration: Record<string, unknown> | null | undefined;
let recovered = false; let recovered = false;
let requestedRun = ""; let requestedRun = "";
let requestedInstructions = ""; let requestedInstructions = "";
@@ -3574,7 +3574,7 @@ it.each(["stuck", "late-release", "late-release-error", "stuck-release", "cooper
let template: Record<string, unknown> | null = null; let template: Record<string, unknown> | null = null;
let registrationStarted!: () => void; let registrationStarted!: () => void;
const started = new Promise<void>(resolve => { registrationStarted = resolve; }); const started = new Promise<void>(resolve => { registrationStarted = resolve; });
let returnRegistration!: (registration: { connection: { mode: "listen"; listenAddress: string; listenPort: number; listenPath: string }; release: () => Promise<void> }) => void; let returnRegistration!: (registration: { connection: { mode: "listen"; listenAddress: "0.0.0.0"; listenPort: number; listenPath: string }; release: () => Promise<void> }) => void;
const registration = new Promise<Parameters<typeof returnRegistration>[0]>(resolve => { returnRegistration = resolve; }); const registration = new Promise<Parameters<typeof returnRegistration>[0]>(resolve => { returnRegistration = resolve; });
let finishRelease!: () => void; let finishRelease!: () => void;
const releasing = new Promise<void>(resolve => { finishRelease = resolve; }); const releasing = new Promise<void>(resolve => { finishRelease = resolve; });
@@ -3585,7 +3585,7 @@ it.each(["stuck", "late-release", "late-release-error", "stuck-release", "cooper
if (releaseFails) throw releaseError; if (releaseFails) throw releaseError;
}); });
const registered = { const registered = {
connection: { mode: "listen" as const, listenAddress: "127.0.0.1", listenPort: 43127, listenPath: "/setup-probe" }, connection: { mode: "listen" as const, listenAddress: "0.0.0.0" as const, listenPort: 43127, listenPath: "/setup-probe" },
release, release,
}; };
let registrationSignal: AbortSignal | undefined; let registrationSignal: AbortSignal | undefined;
@@ -4037,7 +4037,7 @@ it("runs the lab provider boundary through authenticated durable PRP", async ()
} }
expect(methods).toContain("turn/completed"); expect(methods).toContain("turn/completed");
expect(terminalParams).toMatchObject({ expect(terminalParams).toMatchObject({
threadId: opened.thread.id, threadId: (opened.thread as { id: string }).id,
turnId: "provider-turn-1", turnId: "provider-turn-1",
}); });
expect(bundle.evidence().diagnostics).toContain( expect(bundle.evidence().diagnostics).toContain(
@@ -4081,7 +4081,7 @@ it("controls a Codex session goal end to end through durable PRP v2", async () =
}, },
], ],
}); });
const threadId = opened.thread.id; const threadId = (opened.thread as { id: string }).id;
await expect( await expect(
bundle.transport.request("thread/goal/set", { bundle.transport.request("thread/goal/set", {
@@ -4236,7 +4236,7 @@ it.each([false, true])("binds goal turns through the full Codex harness (autonom
}); });
expect(observed.some((event) => event.eventType === "session.failed")).toBe(false); expect(observed.some((event) => event.eventType === "session.failed")).toBe(false);
} finally { } finally {
await session?.close(); await session?.close({ reason: "test complete" });
await bundle.transport.close(); await bundle.transport.close();
await rm(stateDirectory, { recursive: true, force: true }); await rm(stateDirectory, { recursive: true, force: true });
} }
@@ -4882,7 +4882,7 @@ it("binds an immediately failed durable turn before exposing its terminal", asyn
], ],
}); });
} finally { } finally {
await session.close(); await session.close({ reason: "test complete" });
await rm(stateDirectory, { await rm(stateDirectory, {
recursive: true, recursive: true,
force: true, force: true,
@@ -5089,9 +5089,10 @@ it("captures exact provider frames and correlates Rust and TypeScript interpreta
if (event.eventType === "turn.completed") break; if (event.eventType === "turn.completed") break;
} }
} finally { } finally {
await session.close(); await session.close({ reason: "test complete" });
} }
const snapshot = await session.snapshot(); const snapshot = await session.snapshot();
if (snapshot.lastSourceSequence === undefined) throw new Error("Expected a source sequence in the session snapshot");
for ( for (
let sourceSeq = 1; let sourceSeq = 1;
sourceSeq <= snapshot.lastSourceSequence; sourceSeq <= snapshot.lastSourceSequence;
@@ -5791,7 +5792,7 @@ it.each([
expect(outcomes[1].result).toMatchObject({ content: [{ text: expect.stringContaining(feedback) }] }); expect(outcomes[1].result).toMatchObject({ content: [{ text: expect.stringContaining(feedback) }] });
if (adoptAfterTurn) { if (adoptAfterTurn) {
const snapshot = await session.snapshot(); const snapshot = await session.snapshot();
const runnerStartedAt = bundle.transport.processInfo().startedAt; const runnerStartedAt = bundle.transport.processInfo!().startedAt;
survivingPid = bundle.evidence().runnerPid; survivingPid = bundle.evidence().runnerPid;
expect(survivingPid).toEqual(expect.any(Number)); expect(survivingPid).toEqual(expect.any(Number));
const artifact = readRunnerdArtifactBinding(defaultCapabilityRunnerdBinary()); const artifact = readRunnerdArtifactBinding(defaultCapabilityRunnerdBinary());
@@ -5833,7 +5834,7 @@ it.each([
expect(recovery.session).toBeDefined(); expect(recovery.session).toBeDefined();
session = recovery.session!; session = recovery.session!;
expect(adopted.evidence().runnerPid).toBe(survivingPid); expect(adopted.evidence().runnerPid).toBe(survivingPid);
expect(adopted.transport.processInfo().startedAt).toBe(runnerStartedAt); expect(adopted.transport.processInfo!().startedAt).toBe(runnerStartedAt);
expect((await session.snapshot()).providerSessionId).toBe(snapshot.providerSessionId); expect((await session.snapshot()).providerSessionId).toBe(snapshot.providerSessionId);
expect(duplicateLauncher).not.toHaveBeenCalled(); expect(duplicateLauncher).not.toHaveBeenCalled();
} }
@@ -5843,7 +5844,7 @@ it.each([
expect(allRequests.map(request => request.variant)).toEqual(supportsReasoning expect(allRequests.map(request => request.variant)).toEqual(supportsReasoning
? ["paperclip-no-reasoning", "paperclip-default"] : [undefined, undefined]); ? ["paperclip-no-reasoning", "paperclip-default"] : [undefined, undefined]);
}, },
closeSession: async () => { await session?.close(); }, closeSession: async () => { await session?.close({ reason: "test complete" }); },
closeTransport: async () => { closeTransport: async () => {
try { await adopted?.transport.close(); } try { await adopted?.transport.close(); }
finally { finally {
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join, resolve } from "node:path"; import { join, resolve } from "node:path";
import { expect, it, vi } from "vitest"; import { expect, it, vi } from "vitest";
import { DurablePrpControlPlane } from "../control-plane/durable-prp-control-plane.js"; import { DurablePrpControlPlane, type RunnerProcessResult } from "../control-plane/durable-prp-control-plane.js";
import { createCapabilityRunnerdCodexTransport, defaultCapabilityRunnerdBinary, readRunnerdArtifactBinding, runnerdRecoveryInternals } from "./runnerd-codex-transport.js"; import { createCapabilityRunnerdCodexTransport, defaultCapabilityRunnerdBinary, readRunnerdArtifactBinding, runnerdRecoveryInternals } from "./runnerd-codex-transport.js";
it.each(["resolved", "rejected"] as const)("keeps the checkpoint behind controller retirement (%s)", async outcome => { it.each(["resolved", "rejected"] as const)("keeps the checkpoint behind controller retirement (%s)", async outcome => {
@@ -39,7 +39,7 @@ it.each(["drain", "timeout", "external_close"] as const)("joins an output flush
const artifact = readRunnerdArtifactBinding(binary); const artifact = readRunnerdArtifactBinding(binary);
let core!: DurablePrpControlPlane; let core!: DurablePrpControlPlane;
const start = DurablePrpControlPlane.prototype.start; const start = DurablePrpControlPlane.prototype.start;
const startSpy = vi.spyOn(DurablePrpControlPlane.prototype, "start").mockImplementation(function (port) { const startSpy = vi.spyOn(DurablePrpControlPlane.prototype, "start").mockImplementation(function (this: DurablePrpControlPlane, port) {
core = this; core = this;
return start.call(this, port); return start.call(this, port);
}); });
@@ -81,7 +81,7 @@ it.each(["drain", "timeout", "external_close"] as const)("joins an output flush
let stderr = ""; let stderr = "";
owned.stderr?.on("data", chunk => { stderr += String(chunk); }); owned.stderr?.on("data", chunk => { stderr += String(chunk); });
exit = new Promise(resolveExit => owned.once("exit", () => resolveExit())); exit = new Promise(resolveExit => owned.once("exit", () => resolveExit()));
const completion = new Promise<{ code: number | null; signal: string | null; stdout: string; stderr: string }>((resolveExit, reject) => { const completion = new Promise<RunnerProcessResult>((resolveExit, reject) => {
owned.once("error", reject); owned.once("error", reject);
owned.once("exit", (code, signal) => resolveExit({ code, signal, stdout: "", stderr })); owned.once("exit", (code, signal) => resolveExit({ code, signal, stdout: "", stderr }));
}); });
@@ -76,14 +76,14 @@ it.skipIf(!compilerAvailable || !existsSync(runner)).each(["discovery", "complet
expect(seen).toEqual(tool !== "completion" ? ["paperclip_search_assigned_tools", "paperclip_finish"] : ["paperclip_finish"]); expect(seen).toEqual(tool !== "completion" ? ["paperclip_search_assigned_tools", "paperclip_finish"] : ["paperclip_finish"]);
expect(text.join("")).toBe("Streaming progress. ".repeat(800)); expect(text.join("")).toBe("Streaming progress. ".repeat(800));
if (tool === "durable") { if (tool === "durable") {
const runnerPid = transport.processInfo().pid; const runnerPid = transport.processInfo!().pid;
await transport.attachRun!({ runId: "next-settled-run", turnId: "next-turn", itemId: "next-item" }); await transport.attachRun!({ runId: "next-settled-run", turnId: "next-turn", itemId: "next-item" });
completed = (async () => { completed = (async () => {
for await (const event of transport.notifications()) if (event.method === "turn/completed") return; for await (const event of transport.notifications()) if (event.method === "turn/completed") return;
})(); })();
await transport.request("turn/start", { input: [{ type: "text", text: "stream-backlog-discovery" }] }); await transport.request("turn/start", { input: [{ type: "text", text: "stream-backlog-discovery" }] });
await completed; await completed;
expect(transport.processInfo().pid).toBe(runnerPid); expect(transport.processInfo!().pid).toBe(runnerPid);
expect(seen).toEqual(["paperclip_search_assigned_tools", "paperclip_finish", "paperclip_search_assigned_tools", "paperclip_finish"]); expect(seen).toEqual(["paperclip_search_assigned_tools", "paperclip_finish", "paperclip_search_assigned_tools", "paperclip_finish"]);
const provider = JSON.parse(await readFile(join(root, "state", "runner", "codex-provider-state.json"), "utf8")); const provider = JSON.parse(await readFile(join(root, "state", "runner", "codex-provider-state.json"), "utf8"));
expect(provider.toolBridge.pending).toEqual({}); expect(provider.toolBridge.pending).toEqual({});
@@ -15,7 +15,7 @@ it.each(["none", "before-start", "after-start"] as const)("completes after the r
const execution: NativeExecutionInputV1 = { const execution: NativeExecutionInputV1 = {
schema: "paperclip.native-execution-input.v1", schema: "paperclip.native-execution-input.v1",
binding: { runId: identity.runId, companyId: identity.companyId, issueId: identity.issueId, agentId: identity.agentId, executionWorkspaceId: "workspace-restart" }, binding: { runId: identity.runId, companyId: identity.companyId, issueId: identity.issueId, agentId: identity.agentId, executionWorkspaceId: "workspace-restart" },
session: { normalizedSessionId: identity.sessionId, driverKind: "codex_app_server", protocolVersion: 1 }, session: { normalizedSessionId: identity.sessionId, driverKind: "codex_app_server", protocolVersion: 1, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } },
task: { identifier: "TEST-1", title: "Finish checks", description: null, prompt: "Watch the already running checks and finish the request.", workMode: "standard" }, task: { identifier: "TEST-1", title: "Finish checks", description: null, prompt: "Watch the already running checks and finish the request.", workMode: "standard" },
workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null }, workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null },
provider: { kind: "codex", model: null }, provider: { kind: "codex", model: null },
@@ -88,7 +88,7 @@ it.each(["none", "before-start", "after-start"] as const)("completes after the r
throw interruptedController; throw interruptedController;
} }
}, },
async appendEvent(event) { events.push(event); durableEvents.push(event); return { cursor: durableEvents.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event) { if (!("sourceSeq" in event)) throw new Error("Expected a durable PRP event"); events.push(event); durableEvents.push(event); return { cursor: durableEvents.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents(request) { async replayEvents(request) {
const source = durableEvents.filter(event => event.sourceInstanceId === request.sourceInstanceId); const source = durableEvents.filter(event => event.sourceInstanceId === request.sourceInstanceId);
return { events: source.filter(event => event.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: source.at(-1)?.sourceSeq ?? 0 }; return { events: source.filter(event => event.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: source.at(-1)?.sourceSeq ?? 0 };
@@ -63,7 +63,7 @@ it("delivers authenticated runtime activity through the real Runner bridge acros
const firstContext = (await helper(["notification-context"])).stdout.trim(); const firstContext = (await helper(["notification-context"])).stdout.trim();
for (const runId of ["activity-first", "activity-next"]) { for (const runId of ["activity-first", "activity-next"]) {
if (runId === "activity-next") { if (runId === "activity-next") {
attaching = session.attachRun!({ runId }); attaching = Promise.resolve(session.attachRun!({ runId }));
await releasing; await releasing;
await vi.waitFor(async () => { await vi.waitFor(async () => {
expect((await helper(["notification-context"])).stdout.trim()).not.toBe(firstContext); expect((await helper(["notification-context"])).stdout.trim()).not.toBe(firstContext);
@@ -93,7 +93,7 @@ it("delivers authenticated runtime activity through the real Runner bridge acros
} finally { } finally {
releasePrevious(); releasePrevious();
await attaching?.catch(() => undefined); await attaching?.catch(() => undefined);
await session?.close(); await session?.close({ reason: "test complete" });
await bundle.transport.close(); await bundle.transport.close();
await rm(root, { recursive: true, force: true }); await rm(root, { recursive: true, force: true });
} }
@@ -49,7 +49,7 @@ it.each(["omitted", "null", "resolved"] as const)("retains a server-owned worksp
expect(terminals).toHaveLength(1); expect(terminals).toHaveLength(1);
expect(terminals[0].envelope.payload).toMatchObject({ runId, eventType: "run.terminal", payload: { runTerminalState: "succeeded", reportedWorkDisposition: "yielded" } }); expect(terminals[0].envelope.payload).toMatchObject({ runId, eventType: "run.terminal", payload: { runTerminalState: "succeeded", reportedWorkDisposition: "yielded" } });
} finally { } finally {
await session?.close(); await session?.close({ reason: "test complete" });
await bundle.transport.close(); await bundle.transport.close();
await rm(root, { recursive: true, force: true }); await rm(root, { recursive: true, force: true });
} }
@@ -61,7 +61,7 @@ async function sourceFixture(registration?: CapabilityRunnerdCodexTransportOptio
runId: identity.runId, executionWorkspaceId: "workspace" }, runId: identity.runId, executionWorkspaceId: "workspace" },
task: { identifier: "TASK-1", title: "Complete fake work", description: null, prompt: "Complete fake work", workMode: "standard" }, task: { identifier: "TASK-1", title: "Complete fake work", description: null, prompt: "Complete fake work", workMode: "standard" },
workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null }, workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: null },
session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "codex_app_server", protocolVersion: 1 }, session: { normalizedSessionId: identity.normalizedSessionId, driverKind: "codex_app_server", protocolVersion: 1, lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null } },
provider: { kind: "codex", model: null, approvalPolicy: "never" }, provider: { kind: "codex", model: null, approvalPolicy: "never" },
completionContract: { id: "contract", sha256: `sha256:${"a".repeat(64)}`, schemaVersion: "paperclip.completion-contract.v1", completionContract: { id: "contract", sha256: `sha256:${"a".repeat(64)}`, schemaVersion: "paperclip.completion-contract.v1",
contract: { revision: "1", objective: "Complete fake work", criteria: [{ id: "objective", requirement: "Complete fake work" }] } }, interactionResponses: [], credentialBindings: [] }; contract: { revision: "1", objective: "Complete fake work", criteria: [{ id: "objective", requirement: "Complete fake work" }] } }, interactionResponses: [], credentialBindings: [] };
@@ -231,7 +231,7 @@ it.each([["launch", "default"], ["adopt", "default"], ["adopt-denied", "default"
expect((await read(fixture.coreFile)).preparedLaunchConfigurations).toEqual(before.preparedLaunchConfigurations); expect((await read(fixture.coreFile)).preparedLaunchConfigurations).toEqual(before.preparedLaunchConfigurations);
await expect(readFile(join(fixture.root, "launch-preparation.json"))).rejects.toHaveProperty("code", "ENOENT"); await expect(readFile(join(fixture.root, "launch-preparation.json"))).rejects.toHaveProperty("code", "ENOENT");
if (mode !== "launch") { if (mode !== "launch") {
const evidence = resumed.evidence(), startedAt = resumed.transport.processInfo().startedAt; const evidence = resumed.evidence(), startedAt = resumed.transport.processInfo!().startedAt;
await resumed.detachControllerForRestart(); await resumed.detachControllerForRestart();
prepare.mockClear(); prepare.mockClear();
if (mode === "adopt-denied") prepare.mockRejectedValueOnce(new Error("source preparation authorization revoked")); if (mode === "adopt-denied") prepare.mockRejectedValueOnce(new Error("source preparation authorization revoked"));
@@ -205,7 +205,7 @@ it.each([["connect", "owned"], ["listen", "owned"], ["external", "owned"], ["con
try { try {
const opened = await within(first.transport.request("thread/start", { cwd: root, dynamicTools: [] }), "source open"); const opened = await within(first.transport.request("thread/start", { cwd: root, dynamicTools: [] }), "source open");
runnerPid = first.evidence().runnerPid; providerPid = first.evidence().providerPid; runnerPid = first.evidence().runnerPid; providerPid = first.evidence().providerPid;
const startedAt = first.transport.processInfo().startedAt; const startedAt = first.transport.processInfo!().startedAt;
const providerStart = execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim(); const providerStart = execFileSync("ps", ["-p", String(providerPid), "-o", "lstart="], { encoding: "utf8" }).trim();
expect(runnerPid).toBeGreaterThan(0); expect(providerPid).toBeGreaterThan(0); expect(runnerPid).toBeGreaterThan(0); expect(providerPid).toBeGreaterThan(0);
const attach = first.transport.attachRun!({ runId: target.runId, turnId: target.turnId, itemId: target.itemId }); const attach = first.transport.attachRun!({ runId: target.runId, turnId: target.turnId, itemId: target.itemId });
@@ -9,6 +9,9 @@ vi.mock("node:fs", async (importOriginal) => {
return { ...fs, readSync: vi.fn(fs.readSync) }; return { ...fs, readSync: vi.fn(fs.readSync) };
}); });
type PositionalRead = [fd: number, buffer: NodeJS.ArrayBufferView, offset: number, length: number, position: number | null];
const readPosition = (call: number) => (vi.mocked(readSync).mock.calls[call] as unknown as PositionalRead)[4];
const interpretation = (frameId: number, ...emittedEventIds: string[]) => const interpretation = (frameId: number, ...emittedEventIds: string[]) =>
`${JSON.stringify({ kind: "interpretation", frameId, emittedEventIds })}\n`; `${JSON.stringify({ kind: "interpretation", frameId, emittedEventIds })}\n`;
const settled = `${JSON.stringify({ kind: "trace_status", debugChannel: "rust_native", status: "complete" })}\n`; const settled = `${JSON.stringify({ kind: "trace_status", debugChannel: "rust_native", status: "complete" })}\n`;
@@ -41,7 +44,7 @@ describe("runnerd trace frame index", () => {
expect(index.locate(path, "pending-1")).toEqual({ frameId: 2, nativeChannelSettled: true }); expect(index.locate(path, "pending-1")).toEqual({ frameId: 2, nativeChannelSettled: true });
expect(index.locate(path, "missing")).toEqual({ frameId: null, nativeChannelSettled: true }); expect(index.locate(path, "missing")).toEqual({ frameId: null, nativeChannelSettled: true });
expect(readSync).toHaveBeenCalledTimes(2); expect(readSync).toHaveBeenCalledTimes(2);
expect(vi.mocked(readSync).mock.calls[1]![4]).toBe(Buffer.byteLength(first)); expect(readPosition(1)).toBe(Buffer.byteLength(first));
}); });
it("retains partial lines and split UTF-8 until the append is complete", () => { it("retains partial lines and split UTF-8 until the append is complete", () => {
@@ -87,7 +90,7 @@ describe("runnerd trace frame index", () => {
expect(index.locate(path, "last").frameId).toBeNull(); expect(index.locate(path, "last").frameId).toBeNull();
expect(index.locate(path, "last").frameId).toBeNull(); expect(index.locate(path, "last").frameId).toBeNull();
expect(index.locate(path, "last").frameId).toBe(4); expect(index.locate(path, "last").frameId).toBe(4);
for (const call of vi.mocked(readSync).mock.calls) expect(call[3]).toBeLessThanOrEqual(1024 * 1024); for (const call of vi.mocked(readSync).mock.calls as unknown as PositionalRead[]) expect(call[3]).toBeLessThanOrEqual(1024 * 1024);
const calls = vi.mocked(readSync).mock.calls.length; const calls = vi.mocked(readSync).mock.calls.length;
for (let i = 0; i < 1000; i++) index.locate(path, "missing"); for (let i = 0; i < 1000; i++) index.locate(path, "missing");
expect(readSync).toHaveBeenCalledTimes(calls); expect(readSync).toHaveBeenCalledTimes(calls);
@@ -124,8 +127,8 @@ describe("runnerd trace frame index", () => {
} }
expect(readSync).toHaveBeenCalledTimes(72); expect(readSync).toHaveBeenCalledTimes(72);
for (let i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
expect(vi.mocked(readSync).mock.calls[24 + i]![4]).toBe(1024 * 1024); expect(readPosition(24 + i)).toBe(1024 * 1024);
expect(vi.mocked(readSync).mock.calls[48 + i]![4]).toBe(2 * 1024 * 1024); expect(readPosition(48 + i)).toBe(2 * 1024 * 1024);
} }
}); });
@@ -39,7 +39,7 @@ it("starts one provider after delayed bootstrap admission and retains it for a s
await bundle.transport.request("turn/start", { input: [{ type: "text", text }] }); await bundle.transport.request("turn/start", { input: [{ type: "text", text }] });
for await (const event of bundle.transport.notifications()) { for await (const event of bundle.transport.notifications()) {
if (event.method === "turn/completed") { if (event.method === "turn/completed") {
expect(event.params).toMatchObject({ threadId: opened.thread.id }); expect(event.params).toMatchObject({ threadId: (opened.thread as { id: string }).id });
terminalTurns.push(event.params); terminalTurns.push(event.params);
break; break;
} }
@@ -136,7 +136,7 @@ describe("Scenario chat deployment boundary", () => {
it("requires exact Origin and Fetch Metadata on every mutation", async () => { it("requires exact Origin and Fetch Metadata on every mutation", async () => {
await withHttpRuntime(async ({ request }) => { await withHttpRuntime(async ({ request }) => {
const body = JSON.stringify({ scenarioId: SCENARIO_ID }); const body = JSON.stringify({ scenarioId: SCENARIO_ID });
const invalidHeaders = [ const invalidHeaders: Record<string, string>[] = [
{ origin: `${PUBLIC_ORIGIN}/` }, { origin: `${PUBLIC_ORIGIN}/` },
{ "sec-fetch-site": "none" }, { "sec-fetch-site": "none" },
{ "sec-fetch-site": "cross-site" }, { "sec-fetch-site": "cross-site" },
@@ -14,6 +14,7 @@ import {
type CapabilityLiveTransportFactory, type CapabilityLiveTransportFactory,
type CapabilityLiveTurnEvent, type CapabilityLiveTurnEvent,
} from "./live-session.js"; } from "./live-session.js";
import type { CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js";
import { import {
createCapabilityTurnStreamDecoder, createCapabilityTurnStreamDecoder,
encodeCapabilityTurnStreamFrame, encodeCapabilityTurnStreamFrame,
@@ -161,10 +162,25 @@ class GatedTransport implements CodexAppServerTransport {
function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory { function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory {
return (options) => { return (options) => {
const evidence = { const evidence: CapabilityRunnerdProcessEvidence = {
runnerPid: 7100, runnerPid: 7100,
runnerProcessGroupId: 7100, runnerProcessGroupId: 7100,
providerPid: null,
providerProcessStartedAt: null,
codexPid: 7200, codexPid: 7200,
codexProcessStartedAt: null,
sidecarPid: null,
sidecarProcessStartedAt: null,
agentPid: null,
agentProcessStartedAt: null,
providerDriver: null,
providerVersion: null,
acpxAgent: null,
agentServerVersion: null,
agentRuntimeVersion: null,
acpProtocolVersion: null,
providerExecutionKind: null,
providerService: null,
runnerExited: false, runnerExited: false,
runnerExitCode: null, runnerExitCode: null,
runnerSignal: null, runnerSignal: null,
@@ -172,7 +188,7 @@ function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory {
diagnostics: [], diagnostics: [],
}; };
options.onEvidence?.(evidence); options.onEvidence?.(evidence);
return { transport: new GatedTransport(gate), evidence: () => ({ ...evidence }) }; return { transport: new GatedTransport(gate), evidence: () => ({ ...evidence }), detachControllerForRestart: async () => {} };
}; };
} }
@@ -584,6 +584,7 @@ describe("CapabilityMockControlPlaneAdapter", () => {
commandKind: "record_budget_usage", commandKind: "record_budget_usage",
effect: "lost_ack", effect: "lost_ack",
remaining: 1, remaining: 1,
code: "budget_ack_lost",
}], }],
}); });
await adapter.start(); await adapter.start();
@@ -333,7 +333,7 @@ class StubDriver implements HarnessDriver {
const servers: LiveConsoleDemoServer[] = []; const servers: LiveConsoleDemoServer[] = [];
function browserHeaders(url: string, headers: HeadersInit = {}): Headers { function browserHeaders(url: string, headers: ConstructorParameters<typeof Headers>[0] = {}): Headers {
const result = new Headers(headers); const result = new Headers(headers);
result.set("origin", new URL(url).origin); result.set("origin", new URL(url).origin);
result.set("sec-fetch-site", "same-origin"); result.set("sec-fetch-site", "same-origin");
@@ -82,6 +82,7 @@ function reduce(events: readonly PrpEvent[]): SessionSnapshot {
environmentLeaseId: "package-local", environmentLeaseId: "package-local",
runnerInstanceId: "live-console-demo-server", runnerInstanceId: "live-console-demo-server",
normalizedSessionId: events[0]?.normalizedSessionId ?? "session", normalizedSessionId: events[0]?.normalizedSessionId ?? "session",
driverSessionId: "live-console-demo",
}, },
capabilities: { capabilities: {
schema: "paperclip.prp.capabilities.v1", schema: "paperclip.prp.capabilities.v1",
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import type { LocalRunnerRunMetadata, LocalRunnerRunTrace } from "../contracts/local-runner.js";
import { import {
applyLocalRunnerLiveEvent, applyLocalRunnerLiveEvent,
createLocalRunnerLiveSnapshot, createLocalRunnerLiveSnapshot,
@@ -13,6 +14,16 @@ import {
startLocalRunnerScenario, startLocalRunnerScenario,
} from "./local-runner.js"; } from "./local-runner.js";
function traceMetadata(trace: LocalRunnerRunTrace): LocalRunnerRunMetadata {
return {
schema: "paperclip.runner.local-runner.metadata.v1",
scenario: trace.scenario,
fixtureName: trace.fixtureName,
identity: trace.identity,
capabilities: trace.capabilities,
};
}
describe("Local runner and fake harness", () => { describe("Local runner and fake harness", () => {
it("runs lifecycle, tool, file, structured-result, and process-exit events", async () => { it("runs lifecycle, tool, file, structured-result, and process-exit events", async () => {
const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 }); const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 });
@@ -108,7 +119,7 @@ describe("Local runner and fake harness", () => {
), ),
).toBe(true); ).toBe(true);
expect( expect(
replayLocalRunnerEvents(trace, trace.events).timeline.find( replayLocalRunnerEvents(traceMetadata(trace), trace.events).timeline.find(
(event) => event.eventType === "harness.diagnostic", (event) => event.eventType === "harness.diagnostic",
)?.summary, )?.summary,
).toBe( ).toBe(
@@ -160,7 +171,7 @@ describe("Local runner and fake harness", () => {
it("produces the same snapshot for validated live events and replay", async () => { it("produces the same snapshot for validated live events and replay", async () => {
const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 }); const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 });
let live = createLocalRunnerLiveSnapshot(trace); let live = createLocalRunnerLiveSnapshot(traceMetadata(trace));
for (const event of trace.events) { for (const event of trace.events) {
const next = applyLocalRunnerLiveEvent(live, event); const next = applyLocalRunnerLiveEvent(live, event);
expect(next.ok).toBe(true); expect(next.ok).toBe(true);
@@ -168,7 +179,7 @@ describe("Local runner and fake harness", () => {
live = next.snapshot; live = next.snapshot;
} }
} }
const replay = replayLocalRunnerEvents(trace, trace.events); const replay = replayLocalRunnerEvents(traceMetadata(trace), trace.events);
expect(localRunnerSnapshotsMatch(live, replay)).toBe(true); expect(localRunnerSnapshotsMatch(live, replay)).toBe(true);
}); });
@@ -33,6 +33,7 @@ import {
PAPERCLIP_EXECUTION_PROMPT_REVISION, PAPERCLIP_EXECUTION_PROMPT_REVISION,
canonicalNativeRuntimeContextDigest, canonicalNativeRuntimeContextDigest,
nativeRuntimePromptDigest, nativeRuntimePromptDigest,
type NativeRuntimeContextSnapshot,
} from "./contracts/runtime-context.js"; } from "./contracts/runtime-context.js";
import { import {
executeNativeSession, executeNativeSession,
@@ -129,6 +130,7 @@ const input: NativeExecutionInputV1 = {
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
driverKind: "codex_app_server", driverKind: "codex_app_server",
protocolVersion: 1, protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
}, },
provider: { kind: "codex", model: null }, provider: { kind: "codex", model: null },
completionContract: { completionContract: {
@@ -145,13 +147,15 @@ const input: NativeExecutionInputV1 = {
credentialBindings: [], credentialBindings: [],
}; };
type RuntimeContextFields = Omit<NativeRuntimeContextSnapshot, "aggregateDigest">;
function preparedInput(): NativeExecutionInputV5 { function preparedInput(): NativeExecutionInputV5 {
const digest = "0".repeat(64); const digest = "0".repeat(64);
const context = { const context = {
prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() }, prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() },
instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } }, instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } },
skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null }, skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const; } satisfies RuntimeContextFields;
const requirement = input.completionContract.contract.criteria[0]!.requirement; const requirement = input.completionContract.contract.criteria[0]!.requirement;
const prompt = `${input.task.prompt}\n\n${requirement}`; const prompt = `${input.task.prompt}\n\n${requirement}`;
return { return {
@@ -256,7 +260,7 @@ describe("executeNativeSession recovery", () => {
const completeRun = vi.fn(async () => undefined); const completeRun = vi.fn(async () => undefined);
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async checkpointSession() {}, async openRun() {}, async checkpointSession() {},
async appendEvent(event) { committed.push(structuredClone(event)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { committed.push(structuredClone(event)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents(query) { const events = committed.filter(event => event.sourceInstanceId === query.sourceInstanceId); return { events: events.filter(event => event.sourceSeq > query.afterSourceSeq).slice(0, query.limit), highestContiguousSourceSeq: highestContiguous(events) }; }, async replayEvents(query) { const events = committed.filter(event => event.sourceInstanceId === query.sourceInstanceId); return { events: events.filter(event => event.sourceSeq > query.afterSourceSeq).slice(0, query.limit), highestContiguousSourceSeq: highestContiguous(events) }; },
completeRun, completeRun,
}; };
@@ -318,7 +322,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async checkpointSession() {}, async openRun() {}, async checkpointSession() {},
async loadSessionCheckpoint() { return recovered ? structuredClone(snapshot) : null; }, async loadSessionCheckpoint() { return recovered ? structuredClone(snapshot) : null; },
async appendEvent(event) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; },
async completeRun() {}, async completeRun() {},
}; };
@@ -390,7 +394,7 @@ describe("executeNativeSession recovery", () => {
async openRun() {}, async openRun() {},
async loadSessionCheckpoint() { return structuredClone(checkpoint); }, async loadSessionCheckpoint() { return structuredClone(checkpoint); },
async checkpointSession(value) { checkpoints.push(structuredClone(value)); }, async checkpointSession(value) { checkpoints.push(structuredClone(value)); },
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const existing = journal.find(value => value.sourceEventId === event.sourceEventId); const existing = journal.find(value => value.sourceEventId === event.sourceEventId);
if (existing) expect(event).toEqual(existing); if (existing) expect(event).toEqual(existing);
else journal.push(structuredClone(event as PrpEvent)); else journal.push(structuredClone(event as PrpEvent));
@@ -479,7 +483,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession(snapshot) { checkpoints.push(structuredClone(snapshot)); }, async checkpointSession(snapshot) { checkpoints.push(structuredClone(snapshot)); },
async appendEvent(event) { events.push(event); return { cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { events.push(event); return { cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; },
async completeRun() {}, async completeRun() {},
}; };
@@ -533,7 +537,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; },
async completeRun() {}, async completeRun() {},
}; };
@@ -555,7 +559,7 @@ describe("executeNativeSession recovery", () => {
resume: true, typedEvents: true, steering: false, resume: true, typedEvents: true, steering: false,
interruption: true, structuredResult: true, interruption: true, structuredResult: true,
}; };
const cancel = vi.fn(async () => { finish(); }); const cancel = vi.fn(() => { finish(); return { cleanup: Promise.resolve() }; });
const close = vi.fn(async () => { finish(); }); const close = vi.fn(async () => { finish(); });
const session: NativeSession = { const session: NativeSession = {
identity: () => identity, identity: () => identity,
@@ -574,7 +578,7 @@ describe("executeNativeSession recovery", () => {
}, },
cancel, close, cancel, close,
}; };
const appendEvent = vi.fn<ControlPlanePort["appendEvent"]>(async event => ({ const appendEvent = vi.fn<ControlPlanePort["appendEvent"]>(async (event: PrpEvent) => ({
cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq,
disposition: "committed", disposition: "committed",
})); }));
@@ -728,7 +732,7 @@ describe("executeNativeSession recovery", () => {
prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() }, prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() },
instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } }, instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } },
skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null }, skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const; } satisfies RuntimeContextFields;
const completed = await executeNativeSession({ const completed = await executeNativeSession({
input: snapshotBeforeUpdate ? { input: snapshotBeforeUpdate ? {
...input, schema: "paperclip.native-execution-input.v4", executionMode: "default", planningContext: null, ...input, schema: "paperclip.native-execution-input.v4", executionMode: "default", planningContext: null,
@@ -748,7 +752,7 @@ describe("executeNativeSession recovery", () => {
controlPlane: { controlPlane: {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
appended.push(event); appended.push(event);
return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" };
}, },
@@ -885,7 +889,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
appended.push(event); appended.push(event);
return { return {
cursor: event.sourceSeq, cursor: event.sourceSeq,
@@ -1034,7 +1038,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
appended.push(event); appended.push(event);
return { return {
cursor: event.sourceSeq, cursor: event.sourceSeq,
@@ -1185,7 +1189,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
return { return {
cursor: event.sourceSeq, cursor: event.sourceSeq,
highestContiguousSourceSeq: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq,
@@ -1241,7 +1245,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" };
}, },
async replayEvents() { async replayEvents() {
@@ -1574,7 +1578,7 @@ describe("executeNativeSession recovery", () => {
}, },
controlPlane: { controlPlane: {
async openRun() {}, async checkpointSession() {}, async openRun() {}, async checkpointSession() {},
async appendEvent(event) { appended.push(event); return { async appendEvent(event: PrpEvent) { appended.push(event); return {
cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed", cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed",
}; }, }; },
async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; },
@@ -2568,12 +2572,12 @@ describe("executeNativeSession recovery", () => {
}, },
skills: [], skills: [],
mcp: { assignmentSetId: "none", digest, bindingId: null }, mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const; } satisfies RuntimeContextFields;
const openSession = vi.fn(); const openSession = vi.fn();
const backend: NativeSessionBackend = { const backend: NativeSessionBackend = {
async descriptor() { async descriptor() {
return { return {
kind: "future-provider", kind: "mock",
name: "future-provider", name: "future-provider",
version: "1", version: "1",
capabilities: { capabilities: {
@@ -2741,6 +2745,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true, typedEvents: true,
steering: false, steering: false,
interruption: true, interruption: true,
structuredResult: true,
}; };
}, },
async *events() {}, async *events() {},
@@ -2854,7 +2859,7 @@ describe("executeNativeSession recovery", () => {
const snapshot = vi.fn(async () => { throw snapshotFailure; }); const snapshot = vi.fn(async () => { throw snapshotFailure; });
const session: NativeSession = { const session: NativeSession = {
identity: () => identity, identity: () => identity,
async capabilities() { return { resume: false, typedEvents: true, steering: false, interruption: true }; }, async capabilities() { return { resume: false, typedEvents: true, steering: false, interruption: true, structuredResult: true }; },
async *events() {}, async *events() {},
async startTurn() { throw new Error("unexpected turn"); }, async startTurn() { throw new Error("unexpected turn"); },
async result() { return null; }, async result() { return null; },
@@ -2886,6 +2891,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true, typedEvents: true,
steering: false, steering: false,
interruption: true, interruption: true,
structuredResult: true,
}; };
}, },
async *events() {}, async *events() {},
@@ -3509,7 +3515,7 @@ describe("executeNativeSession recovery", () => {
} }
}, },
); );
const checkpointSession = vi.fn<ControlPlanePort["checkpointSession"]>( const checkpointSession = vi.fn<NonNullable<ControlPlanePort["checkpointSession"]>>(
async () => undefined, async () => undefined,
); );
const port: ControlPlanePort = { const port: ControlPlanePort = {
@@ -3744,8 +3750,9 @@ describe("executeNativeSession recovery", () => {
timeoutMs: 10, timeoutMs: 10,
keepSessionOpen: true, keepSessionOpen: true,
onSession: (current) => retainedSessions.push(current), onSession: (current) => retainedSessions.push(current),
onPostCompletionEnrichmentFailure: ({ stage }) => onPostCompletionEnrichmentFailure: ({ stage }) => {
enrichmentFailures.push(stage), enrichmentFailures.push(stage);
},
}); });
await enrichmentStalled; await enrichmentStalled;
if (stalledSignal !== undefined) if (stalledSignal !== undefined)
@@ -4650,12 +4657,12 @@ describe("executeNativeSession recovery", () => {
async *events() { throw new Error("provider failed after editing instructions"); }, async *events() { throw new Error("provider failed after editing instructions"); },
startTurn: async () => ({ turnId: "instruction-turn" }), startTurn: async () => ({ turnId: "instruction-turn" }),
result: async () => null, result: async () => null,
snapshot: async () => ({ backendKind: "local", sessionId: "instruction-session", identity: scopedIdentity, snapshot: async () => ({ backendKind: "runner", sessionId: "instruction-session", identity: scopedIdentity,
providerSessionId: "instruction-provider", cursor: null, activeTurnId: null, pendingRuntimeRequests: [], lineage: [] }), providerSessionId: "instruction-provider", cursor: null, activeTurnId: null, pendingRuntimeRequests: [], lineage: [] }),
close: async () => { order.push("closing"); await closed; order.push("stopped"); }, close: async () => { order.push("closing"); await closed; order.push("stopped"); },
}; };
const backend: NativeSessionBackend = { const backend: NativeSessionBackend = {
descriptor: async () => ({ kind: "local", name: "instruction-close-test", version: "1", capabilities }), descriptor: async () => ({ kind: "runner", name: "instruction-close-test", version: "1", capabilities }),
openSession: async () => session, openSession: async () => session,
}; };
const controlPlane: ControlPlanePort = { const controlPlane: ControlPlanePort = {
@@ -4747,7 +4754,7 @@ describe("executeNativeSession recovery", () => {
close: vi.fn(async () => { throw failure; }), close: vi.fn(async () => { throw failure; }),
}; };
const backend: NativeSessionBackend = { const backend: NativeSessionBackend = {
descriptor: async () => ({ kind: "local", name: "local-stop-test", version: "1", capabilities }), descriptor: async () => ({ kind: "runner", name: "local-stop-test", version: "1", capabilities }),
openSession: vi.fn(async () => session), openSession: vi.fn(async () => session),
}; };
const controlPlane: ControlPlanePort = { const controlPlane: ControlPlanePort = {
@@ -4982,7 +4989,7 @@ describe("executeNativeSession recovery", () => {
runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 5_000, runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 5_000,
controlPlane: { controlPlane: {
async openRun() {}, async checkpointSession() {}, completeRun, async openRun() {}, async checkpointSession() {}, completeRun,
async appendEvent(event, options) { async appendEvent(event: PrpEvent, options) {
if (options?.deferPostCommitObservers) deferredEventTypes.push(event.eventType); if (options?.deferPostCommitObservers) deferredEventTypes.push(event.eventType);
events.push(structuredClone(event)); return { events.push(structuredClone(event)); return {
cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed", cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed",
@@ -5080,7 +5087,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
events.push(structuredClone(event as PrpEvent)); events.push(structuredClone(event as PrpEvent));
return { return {
cursor: events.length, cursor: events.length,
@@ -5199,7 +5206,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
events.push(structuredClone(event as PrpEvent)); events.push(structuredClone(event as PrpEvent));
const sourceEvents = events.filter( const sourceEvents = events.filter(
(candidate) => candidate.sourceInstanceId === event.sourceInstanceId, (candidate) => candidate.sourceInstanceId === event.sourceInstanceId,
@@ -5417,7 +5424,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
if (event.eventType === "item.completed") { if (event.eventType === "item.completed") {
persistingAnswer = true; persistingAnswer = true;
await persistence; await persistence;
@@ -5925,7 +5932,7 @@ describe("executeNativeSession recovery", () => {
const openReplacementSession = vi.fn(); const openReplacementSession = vi.fn();
const backend: NativeSessionBackend = { const backend: NativeSessionBackend = {
descriptor: async () => ({ kind: "mock", name: "incompatible", version: "1", descriptor: async () => ({ kind: "mock", name: "incompatible", version: "1",
capabilities: { resume: true, typedEvents: true, steering: false, interruption: true } }), capabilities: { resume: true, typedEvents: true, steering: false, interruption: true, structuredResult: true } }),
openSession, openReplacementSession, openSession, openReplacementSession,
recoverSession: async () => ({ recovered: false, reason: "saved launch profile changed", recoverSession: async () => ({ recovered: false, reason: "saved launch profile changed",
failureClass: PROVIDER_STATE_INCOMPATIBLE }), failureClass: PROVIDER_STATE_INCOMPATIBLE }),
@@ -6093,6 +6100,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true, typedEvents: true,
steering: false, steering: false,
interruption: true, interruption: true,
structuredResult: true,
}; };
}, },
async *events() {}, async *events() {},
@@ -6136,6 +6144,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true, typedEvents: true,
steering: false, steering: false,
interruption: true, interruption: true,
structuredResult: true,
}, },
}; };
}, },
@@ -6224,6 +6233,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true, typedEvents: true,
steering: false, steering: false,
interruption: true, interruption: true,
structuredResult: true,
}; };
}, },
async *events() {}, async *events() {},
@@ -6268,6 +6278,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true, typedEvents: true,
steering: false, steering: false,
interruption: true, interruption: true,
structuredResult: true,
}, },
}; };
}, },
@@ -6375,7 +6386,7 @@ describe("executeNativeSession recovery", () => {
}, },
async replayEvents(replay) { async replayEvents(replay) {
return { return {
events: replay.afterSourceSeq === 0 ? [runnerEvent(1)] : [], events: replay.afterSourceSeq === 0 ? [runnerEvent(1, "turn.started")] : [],
highestContiguousSourceSeq: 1, highestContiguousSourceSeq: 1,
}; };
}, },
@@ -6479,7 +6490,7 @@ describe("executeNativeSession recovery", () => {
}, },
async replayEvents(replay) { async replayEvents(replay) {
return { return {
events: replay.afterSourceSeq === 0 ? [runnerEvent(1)] : [], events: replay.afterSourceSeq === 0 ? [runnerEvent(1, "turn.started")] : [],
highestContiguousSourceSeq: 1, highestContiguousSourceSeq: 1,
}; };
}, },
@@ -6530,7 +6541,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:1", sourceEventId: "provider-recovery:1",
sourceSeq: 1, sourceSeq: 1,
sourceInstanceId: "provider-recovery", sourceInstanceId: "provider-recovery",
sourceKind: "provider", sourceKind: "runner",
runId: identity.runId, runId: identity.runId,
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
turnId: "turn-recovery", turnId: "turn-recovery",
@@ -6594,7 +6605,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint); return structuredClone(checkpoint);
}, },
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? []; const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event)); list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list); bySource.set(event.sourceInstanceId, list);
@@ -6718,7 +6729,7 @@ describe("executeNativeSession recovery", () => {
async checkpointSession(snapshot) { async checkpointSession(snapshot) {
checkpoints.push(structuredClone(snapshot)); checkpoints.push(structuredClone(snapshot));
}, },
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const target = const target =
event.sourceInstanceId === "runner-recovery" event.sourceInstanceId === "runner-recovery"
? runnerEvents ? runnerEvents
@@ -6852,7 +6863,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint); return structuredClone(checkpoint);
}, },
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
events.push(structuredClone(event)); events.push(structuredClone(event));
return { return {
cursor: events.length, cursor: events.length,
@@ -6989,8 +7000,8 @@ describe("executeNativeSession recovery", () => {
instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } }, instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest, manifestDigest: digest, rootPath: "/runtime/instructions", fileCount: 1, totalBytes: 1 } },
skills: [skill], skills: [skill],
mcp: { assignmentSetId: "none", digest, bindingId: null }, mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const; } satisfies RuntimeContextFields;
const legacyContext = { ...context, skills: [] as const }; const legacyContext = { ...context, skills: [] } satisfies RuntimeContextFields;
const prepared = preparedInput(); const prepared = preparedInput();
const executionInput = preparedMode const executionInput = preparedMode
? { ? {
@@ -7049,7 +7060,7 @@ describe("executeNativeSession recovery", () => {
terminal: null, terminal: null,
terminalTurns: [], terminalTurns: [],
}; };
const startTurn = vi.fn(async () => ({ turnId: "turn-replacement" })); const startTurn = vi.fn<NativeSession["startTurn"]>(async () => ({ turnId: "turn-replacement" }));
const replacementSession: NativeSession = { const replacementSession: NativeSession = {
identity: () => identity, identity: () => identity,
async capabilities() { async capabilities() {
@@ -7108,7 +7119,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint); return structuredClone(checkpoint);
}, },
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
events.push(structuredClone(event)); events.push(structuredClone(event));
return { return {
cursor: events.length, cursor: events.length,
@@ -7194,7 +7205,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:2", sourceEventId: "provider-recovery:2",
sourceSeq: 2, sourceSeq: 2,
sourceInstanceId: "provider-recovery", sourceInstanceId: "provider-recovery",
sourceKind: "provider", sourceKind: "runner",
runId: identity.runId, runId: identity.runId,
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
turnId: "turn-continuation", turnId: "turn-continuation",
@@ -7204,7 +7215,7 @@ describe("executeNativeSession recovery", () => {
emittedAt: "2026-08-09T00:00:01.000Z", emittedAt: "2026-08-09T00:00:01.000Z",
payload: {}, payload: {},
}; };
const startTurn = vi.fn(async () => ({ turnId: "turn-continuation" })); const startTurn = vi.fn<NativeSession["startTurn"]>(async () => ({ turnId: "turn-continuation" }));
const session: NativeSession = { const session: NativeSession = {
identity: () => identity, identity: () => identity,
async capabilities() { async capabilities() {
@@ -7273,7 +7284,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint); return structuredClone(checkpoint);
}, },
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? []; const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event)); list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list); bySource.set(event.sourceInstanceId, list);
@@ -7490,7 +7501,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:2", sourceEventId: "provider-recovery:2",
sourceSeq: 2, sourceSeq: 2,
sourceInstanceId: "provider-recovery", sourceInstanceId: "provider-recovery",
sourceKind: "provider", sourceKind: "runner",
runId: identity.runId, runId: identity.runId,
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
turnId: "turn-disposition", turnId: "turn-disposition",
@@ -7571,7 +7582,7 @@ describe("executeNativeSession recovery", () => {
prematureDispositionCheckpoint = true; prematureDispositionCheckpoint = true;
durableCheckpoint = structuredClone(snapshot); durableCheckpoint = structuredClone(snapshot);
}, },
async appendEvent(event) { async appendEvent(event: PrpEvent) {
if (event.eventType === terminalType && failNextAppend) { if (event.eventType === terminalType && failNextAppend) {
failNextAppend = false; failNextAppend = false;
throw appendFailure; throw appendFailure;
@@ -7674,7 +7685,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "runner-recovery:run-native:4", sourceEventId: "runner-recovery:run-native:4",
sourceSeq: 4, sourceSeq: 4,
sourceInstanceId: "runner-recovery", sourceInstanceId: "runner-recovery",
sourceKind: "provider", sourceKind: "runner",
runId: identity.runId, runId: identity.runId,
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
turnId: "turn-disposition", turnId: "turn-disposition",
@@ -7784,7 +7795,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint); return structuredClone(checkpoint);
}, },
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? []; const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event)); list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list); bySource.set(event.sourceInstanceId, list);
@@ -7935,7 +7946,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint); return structuredClone(checkpoint);
}, },
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? []; const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event)); list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list); bySource.set(event.sourceInstanceId, list);
@@ -8085,7 +8096,7 @@ describe("executeNativeSession recovery", () => {
pendingRuntimeRequests: [], pendingRuntimeRequests: [],
lineage: [], lineage: [],
}; };
const events = [ const events: PrpEvent[] = [
{ {
...controlEvent(1, "run.result.accepted", { result }), ...controlEvent(1, "run.result.accepted", { result }),
turnId: "turn-with-result", turnId: "turn-with-result",
@@ -8223,7 +8234,7 @@ describe("executeNativeSession recovery", () => {
backend: { async descriptor() { return { kind: "mock", name: "missing-current-completion", version: "1", capabilities }; }, async openSession() { return session; } }, backend: { async descriptor() { return { kind: "mock", name: "missing-current-completion", version: "1", capabilities }; }, async openSession() { return session; } },
controlPlane: { controlPlane: {
async openRun() {}, async checkpointSession() {}, async openRun() {}, async checkpointSession() {},
async appendEvent(event) { events.push(structuredClone(event as PrpEvent)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { events.push(structuredClone(event as PrpEvent)); return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, completeRun, async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; }, completeRun,
}, runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 1000, }, runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 1000,
resolveMissingResult: async () => null, resolveMissingResult: async () => null,
@@ -8239,7 +8250,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:1", sourceEventId: "provider-recovery:1",
sourceSeq: 1, sourceSeq: 1,
sourceInstanceId: "provider-recovery", sourceInstanceId: "provider-recovery",
sourceKind: "provider", sourceKind: "runner",
runId: identity.runId, runId: identity.runId,
normalizedSessionId: identity.sessionId, normalizedSessionId: identity.sessionId,
turnId: "turn-waiting", turnId: "turn-waiting",
@@ -8420,7 +8431,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, completeRun: complete, async openRun() {}, completeRun: complete,
async checkpointSession(value) { checkpoint = structuredClone(value); }, async checkpointSession(value) { checkpoint = structuredClone(value); },
async appendEvent(event) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents(request) { return { events: history.filter(row => row.sourceInstanceId === request.sourceInstanceId && row.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: 0 }; }, async replayEvents(request) { return { events: history.filter(row => row.sourceInstanceId === request.sourceInstanceId && row.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: 0 }; },
}; };
const outcome = expect(executeNativeSession({ input, backend, controlPlane: port, timeoutMs: 1_000, const outcome = expect(executeNativeSession({ input, backend, controlPlane: port, timeoutMs: 1_000,
@@ -8475,7 +8486,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, completeRun: complete, async openRun() {}, completeRun: complete,
async checkpointSession(value) { checkpoint = structuredClone(value); }, async checkpointSession(value) { checkpoint = structuredClone(value); },
async appendEvent(event) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; }, async appendEvent(event: PrpEvent) { history.push(event); return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; },
async replayEvents(request) { return { events: history.filter(row => row.sourceInstanceId === request.sourceInstanceId && row.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: 0 }; }, async replayEvents(request) { return { events: history.filter(row => row.sourceInstanceId === request.sourceInstanceId && row.sourceSeq > request.afterSourceSeq), highestContiguousSourceSeq: 0 }; },
}; };
const execution = executeNativeSession({ input, backend, controlPlane: port, timeoutMs: operationMs ?? 120_000, const execution = executeNativeSession({ input, backend, controlPlane: port, timeoutMs: operationMs ?? 120_000,
@@ -8523,7 +8534,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, completeRun: complete, async openRun() {}, completeRun: complete,
async checkpointSession(value) { checkpoint = structuredClone(value); }, async checkpointSession(value) { checkpoint = structuredClone(value); },
async appendEvent(event) { async appendEvent(event: PrpEvent) {
const duplicate = history.some(row => row.sourceEventId === event.sourceEventId); const duplicate = history.some(row => row.sourceEventId === event.sourceEventId);
if (!duplicate) history.push(event); if (!duplicate) history.push(event);
return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: duplicate ? "duplicate" : "committed" }; return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: duplicate ? "duplicate" : "committed" };
@@ -8585,7 +8596,7 @@ describe("executeNativeSession recovery", () => {
throw new Error("controller lost after governed checkpoint"); throw new Error("controller lost after governed checkpoint");
} }
}, },
async appendEvent(event) { async appendEvent(event: PrpEvent) {
if (!history.some(row => row.sourceEventId === event.sourceEventId)) history.push(event); if (!history.some(row => row.sourceEventId === event.sourceEventId)) history.push(event);
return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" }; return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" };
}, },
@@ -8673,14 +8684,14 @@ describe("executeNativeSession recovery", () => {
}), }),
sourceEventId: "provider-recovery:1", sourceEventId: "provider-recovery:1",
sourceInstanceId: "provider-recovery", sourceInstanceId: "provider-recovery",
sourceKind: "provider", sourceKind: "runner",
turnId: "turn-waiting", turnId: "turn-waiting",
}; };
const turnInterrupted: PrpEvent = { const turnInterrupted: PrpEvent = {
...controlEvent(3, "turn.interrupted", { reason: "governed_wait" }), ...controlEvent(3, "turn.interrupted", { reason: "governed_wait" }),
sourceEventId: "provider-recovery:3", sourceEventId: "provider-recovery:3",
sourceInstanceId: "provider-recovery", sourceInstanceId: "provider-recovery",
sourceKind: "provider", sourceKind: "runner",
turnId: "turn-waiting", turnId: "turn-waiting",
}; };
let releaseCancelled!: () => void; let releaseCancelled!: () => void;
@@ -8945,7 +8956,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
events.push(structuredClone(event as PrpEvent)); events.push(structuredClone(event as PrpEvent));
if (event.eventType === "runtime_request.created") releaseCreated(); if (event.eventType === "runtime_request.created") releaseCreated();
const sourceEvents = events.filter( const sourceEvents = events.filter(
@@ -9491,7 +9502,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = { const port: ControlPlanePort = {
async openRun() {}, async openRun() {},
async checkpointSession() {}, async checkpointSession() {},
async appendEvent(event) { async appendEvent(event: PrpEvent) {
if (event.eventType === "runtime_request.resolved") { if (event.eventType === "runtime_request.resolved") {
markSettlementAppendStarted(); markSettlementAppendStarted();
await settlementAppendReleased; await settlementAppendReleased;
@@ -9521,11 +9532,11 @@ describe("executeNativeSession recovery", () => {
}; };
const originalSetTimeout = globalThis.setTimeout; const originalSetTimeout = globalThis.setTimeout;
let queuedHandoffCallback: (() => void) | null = null; let queuedHandoffCallback = null as (() => void) | null;
const timeoutSpy = vi.spyOn(globalThis, "setTimeout").mockImplementation((( const timeoutSpy = vi.spyOn(globalThis, "setTimeout").mockImplementation(((
callback, callback: (...args: unknown[]) => void,
delay, delay?: number,
...args ...args: unknown[]
) => { ) => {
if (delay === 123_456) { if (delay === 123_456) {
queuedHandoffCallback = () => callback(...args); queuedHandoffCallback = () => callback(...args);
@@ -9576,7 +9587,7 @@ it("fails preconfigured recovery without replacing a missing provider session",
const backend: NativeSessionBackend = { const backend: NativeSessionBackend = {
descriptor: async () => ({ kind: "mock", name: "preconfigured", version: "1", descriptor: async () => ({ kind: "mock", name: "preconfigured", version: "1",
runtimeContextCapabilities: { instructions: "native", skills: "native", mcp: "native" }, runtimeContextCapabilities: { instructions: "native", skills: "native", mcp: "native" },
capabilities: { resume: true, typedEvents: true, steering: false, interruption: true } }), capabilities: { resume: true, typedEvents: true, steering: false, interruption: true, structuredResult: true } }),
openSession, openReplacementSession, openSession, openReplacementSession,
recoverSession: async () => ({ recovered: false, reason: "provider history is missing" }), recoverSession: async () => ({ recovered: false, reason: "provider history is missing" }),
}; };
@@ -9586,7 +9597,7 @@ it("fails preconfigured recovery without replacing a missing provider session",
}; };
await expect(executeNativeSession({ await expect(executeNativeSession({
input: { ...preparedInput(), schema: "paperclip.native-execution-input.v7", homeMode: "preconfigured" }, input: { ...preparedInput(), schema: "paperclip.native-execution-input.v7", homeMode: "preconfigured" },
backend, controlPlane: port, runnerInstanceId: "runner-preconfigured", persistedSession: checkpoint, backend, controlPlane: port, runnerInstanceId: "runner-preconfigured", controlPlaneInstanceId: "control-preconfigured", persistedSession: checkpoint,
})).rejects.toThrow("native_session_recovery_failed: provider history is missing"); })).rejects.toThrow("native_session_recovery_failed: provider history is missing");
expect(openSession).not.toHaveBeenCalled(); expect(openSession).not.toHaveBeenCalled();
expect(openReplacementSession).not.toHaveBeenCalled(); expect(openReplacementSession).not.toHaveBeenCalled();
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { paperclipSemanticAction } from "../catalog/semantic-action-catalog.js"; import { paperclipSemanticAction } from "../catalog/semantic-action-catalog.js";
import { paperclipSemanticActionNamespace } from "../semantic-tools/paperclip-discovery.js"; import { paperclipSemanticActionNamespace } from "../semantic-tools/paperclip-discovery.js";
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { PAPERCLIP_COLLABORATION_PROTOCOL_ACTIONS } from "./collaboration.js"; import { PAPERCLIP_COLLABORATION_PROTOCOL_ACTIONS } from "./collaboration.js";
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { PAPERCLIP_CORE_PROTOCOL_ACTIONS } from "./core.js"; import { PAPERCLIP_CORE_PROTOCOL_ACTIONS } from "./core.js";
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { questionSetSchema, requestSchema } from "./generated/schema-bundle.js"; import { questionSetSchema, requestSchema } from "./generated/schema-bundle.js";
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { providerDescriptorSchema } from "./generated/schema-bundle.js"; import { providerDescriptorSchema } from "./generated/schema-bundle.js";
@@ -19,7 +19,7 @@ describe("provider runtime descriptor", () => {
} }
const event: PrpEvent = { const event: PrpEvent = {
schema: "paperclip.prp.event.v2", schemaVersion: 2, sourceEventId: "controls", sourceSeq: 1, schema: "paperclip.prp.event.v2", schemaVersion: 2, sourceEventId: "controls", sourceSeq: 1,
sourceInstanceId: "runner", sourceKind: "runner", runId: "run", eventType: "session.capabilities.updated", sourceInstanceId: "runner", sourceKind: "runner", runId: "run", normalizedSessionId: "session", eventType: "session.capabilities.updated",
priority: 0, emittedAt: "2026-09-28T12:00:00.000Z", payload: { priority: 0, emittedAt: "2026-09-28T12:00:00.000Z", payload: {
sessionGoals: { availability: "unsupported", reason: "provider has no native goals", actions: [], sessionGoals: { availability: "unsupported", reason: "provider has no native goals", actions: [],
autonomousUpdates: false, persistentAcrossResume: false, maxObjectiveChars: 4000, autonomousUpdates: false, persistentAcrossResume: false, maxObjectiveChars: 4000,
@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path"; import { dirname, resolve } from "node:path";
import Ajv2020 from "ajv/dist/2020"; import { Ajv2020 } from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
type Classification = type Classification =
@@ -1,4 +1,4 @@
import Ajv2020 from "ajv/dist/2020.js"; import { Ajv2020 } from "ajv/dist/2020.js";
import { usageSchema } from "./generated/schema-bundle.js"; import { usageSchema } from "./generated/schema-bundle.js";
import { readFile } from "node:fs/promises"; import { readFile } from "node:fs/promises";
@@ -77,7 +77,7 @@ describe("PRP v1 JSON Schema contract", () => {
expect(result.fixture.futureFixtureHint).toEqual({ expect(result.fixture.futureFixtureHint).toEqual({
producerVersion: "1.1-preview", producerVersion: "1.1-preview",
}); });
expect(result.fixture.events[0]?.futureEnvelopeField).toBe(42); expect((result.fixture.events[0] as unknown as Record<string, unknown>).futureEnvelopeField).toBe(42);
} }
}); });
@@ -269,7 +269,7 @@ describe("PRP v1 JSON Schema contract", () => {
expect(result.ok).toBe(true); expect(result.ok).toBe(true);
if (!result.ok) return; if (!result.ok) return;
expect( expect(
result.fixture.events[0]?.payload.semantic_tool?.futureEnvelopeHint, (result.fixture.events[0]?.payload.semantic_tool as { futureEnvelopeHint?: unknown } | undefined)?.futureEnvelopeHint,
).toEqual({ version: "1.1-preview" }); ).toEqual({ version: "1.1-preview" });
const withoutEnvelope = structuredClone(result.fixture); const withoutEnvelope = structuredClone(result.fixture);
@@ -10,6 +10,7 @@ function workspaceEvent(eventType: "workspace.change.updated" | "workspace.diff.
sourceInstanceId: "runner-1", sourceInstanceId: "runner-1",
sourceKind: "runner", sourceKind: "runner",
runId: "run-1", runId: "run-1",
normalizedSessionId: "session-1",
turnId: "turn-1", turnId: "turn-1",
eventType, eventType,
schemaVersion: 1, schemaVersion: 1,
@@ -44,7 +45,7 @@ describe("PRP workspace change events", () => {
it("rejects traversal paths and incomplete recorded diffs", () => { it("rejects traversal paths and incomplete recorded diffs", () => {
const traversal = workspaceEvent("workspace.diff.recorded"); const traversal = workspaceEvent("workspace.diff.recorded");
traversal.payload.files[0]!.path = "../secret"; (traversal.payload.files as Array<{ path: string }>)[0]!.path = "../secret";
expect(validatePrpEvent(traversal).ok).toBe(false); expect(validatePrpEvent(traversal).ok).toBe(false);
const incomplete = workspaceEvent("workspace.diff.recorded"); const incomplete = workspaceEvent("workspace.diff.recorded");
incomplete.payload.complete = false; incomplete.payload.complete = false;
@@ -59,6 +60,7 @@ describe("PRP workspace change events", () => {
sourceInstanceId: "runner-1", sourceInstanceId: "runner-1",
sourceKind: "runner", sourceKind: "runner",
runId: "run-1", runId: "run-1",
normalizedSessionId: "session-1",
turnId: "turn-1", turnId: "turn-1",
eventType: "workspace.file.referenced", eventType: "workspace.file.referenced",
schemaVersion: 1, schemaVersion: 1,
Loaded 100 of 200 files, more files were not shown because too many files have changed in this diff. Show more