Merge current master before PR validation

Co-Authored-By: Paperclip <[email protected]>
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
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
env:
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
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
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
preparation before native execution was selected, recovery verifies that no
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
leases before the task can continue. Once these checks pass, the server retires
its own unchanged bootstrap-failure block and schedules one bounded retry.
Startup also discovers this obligation when file recovery completed before the
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,
pauses, approvals, budgets and the existing retry limit remain effective.
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:posthog-live": "node scripts/smoke/posthog-live.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",
"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",
+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 { PROVIDER_STATE_INCOMPATIBLE, runnerRecoveryFailureClass } from "../contracts/harness-driver.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";
const result: PrpStructuredRunResult = {
@@ -55,8 +55,13 @@ function prpEvent(sourceSeq: number, eventType: PrpEvent["eventType"], payload:
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 {
ids() { return { driverSessionId: "driver-1", providerSessionId: "provider-1" }; }
ids(): ReturnType<HarnessSession["ids"]> { return { driverSessionId: "driver-1", providerSessionId: "provider-1" }; }
async *events() {
yield prpEvent(1, "run.result.proposed", result);
yield prpEvent(2, "turn.completed", { status: "completed" });
@@ -942,7 +947,7 @@ describe("HarnessDriverBackend", () => {
);
class TerminalThenIntegrityFailure extends FakeHarnessSession {
goal = vi.fn(async () => null);
steer = vi.fn(async () => ({ correlationId: "blocked-steer" }));
steer = vi.fn(async () => {});
override async *events() {
yield* super.events();
throw fault;
@@ -1068,8 +1073,8 @@ describe("HarnessDriverBackend", () => {
const session = await backend.openSession({
identity: { runId: "run-1", sessionId: "session-1", companyId: "company-1", issueId: "issue-1", agentId: "agent-1" }, workingDirectory: "/workspace",
});
if (journalOnly) session.revokeTurnPublication();
else await session.cancel({ reason: "durable governed wait", signal: new AbortController().signal }).cleanup;
if (journalOnly) session.revokeTurnPublication!();
else await cancelSession(session, { reason: "durable governed wait", signal: new AbortController().signal }).cleanup;
const events = [];
for await (const event of session.events()) events.push(event);
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" },
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" } }))
.rejects.toThrow("native_session_cancelled");
expect(start).not.toHaveBeenCalled();
@@ -1108,7 +1113,7 @@ describe("HarnessDriverBackend", () => {
});
const iterator = session.events()[Symbol.asyncIterator]();
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: "turn.failed", sourceSeq: 4 } });
await expect(iterator.next()).resolves.toMatchObject({ done: true });
@@ -1133,7 +1138,7 @@ describe("HarnessDriverBackend", () => {
});
const iterator = session.events()[Symbol.asyncIterator]();
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,
payload: { status: "cancelled", providerTerminalState: "completed", reason: "cancelled_after_provider_completed" } } });
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "run.terminal", sourceSeq: 3,
@@ -1166,7 +1171,7 @@ describe("HarnessDriverBackend", () => {
});
const iterator = session.events()[Symbol.asyncIterator]();
await expect(iterator.next()).resolves.toMatchObject({ value: { eventType: "runtime_request.created" } });
await session.cancel({
await cancelSession(session, {
reason: "operator cancelled the run",
signal: new AbortController().signal,
}).cleanup;
@@ -5,7 +5,7 @@ import { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
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";
const contextRoots: string[] = [];
@@ -36,12 +36,12 @@ import { createCodexNativeSessionBackend, createRunnerdNativeSessionBackend } fr
import { createOpenCodeNativeSessionBackend } from "./opencode-native-backend.js";
function execution(
provider: NativeExecutionInput["provider"] = {
provider: NativeExecutionInputV1["provider"] = {
kind: "codex",
model: null,
approvalPolicy: "never",
},
): NativeExecutionInput {
): NativeExecutionInputV1 {
return {
schema: "paperclip.native-execution-input.v1",
binding: {
@@ -88,7 +88,7 @@ function execution(
function acpxExecution(
agent: "codex" | "pi" | "claude" = "codex",
): NativeExecutionInput {
): NativeExecutionInputV1 {
return {
...execution(),
session: {
@@ -107,7 +107,7 @@ function acpxExecution(
};
}
function opencodeExecution(): NativeExecutionInput {
function opencodeExecution(): NativeExecutionInputV1 {
return {
...execution(),
session: {
@@ -124,7 +124,7 @@ function opencodeExecution(): NativeExecutionInput {
};
}
function planningExecution(input: NativeExecutionInput): NativeExecutionInput {
function planningExecution(input: NativeExecutionInputV1): NativeExecutionInputV2 {
return {
...input,
schema: "paperclip.native-execution-input.v2",
@@ -143,7 +143,7 @@ function planningExecution(input: NativeExecutionInput): NativeExecutionInput {
function managedExecution(
kind: "claude_managed" | "aws_agentcore",
): NativeExecutionInput {
): NativeExecutionInputV1 {
if (kind === "claude_managed") {
return {
...execution(),
@@ -223,7 +223,7 @@ describe("native backend factory", () => {
});
try {
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");
expect(generated).toBeDefined();
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(factory).toHaveBeenCalledWith(expect.objectContaining({ baseInstructions: retainedSystemInstructions }));
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", () => {
@@ -420,7 +420,7 @@ describe("native backend factory", () => {
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 = [
["Codex", execution()],
["OpenCode", opencodeExecution()],
@@ -452,8 +452,8 @@ describe("native backend factory", () => {
}).recoverSession!({ ...snapshot, activeTurnId: null }, { signal: new AbortController().signal });
expect(recovered).toMatchObject({ recovered: true });
if (recovered.recovered) {
expect((await recovered.session.snapshot()).workingDirectory).toBe(cwd);
await recovered.session.close({ reason: "test complete" });
expect((await recovered.session!.snapshot()).workingDirectory).toBe(cwd);
await recovered.session!.close({ reason: "test complete" });
}
});
@@ -769,6 +769,7 @@ describe("native backend factory", () => {
await backend.openSession({
identity: {
companyId: "company",
issueId: "issue",
agentId: "agent",
runId: "run",
sessionId: "session",
@@ -807,7 +808,7 @@ describe("native backend factory", () => {
environment: { PAPERCLIP_WORKSPACE_CWD: WORKSPACE },
});
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,
});
const first = JSON.stringify(buildNativeModelEnvelope(input));
@@ -21,7 +21,7 @@ import { inspectNativeCompletionSourceMetadata } from "../../../../tests/runner-
// It measures complete Paperclip-supplied instruction/tool/message projections,
// not provider-owned stock prompts, tokenization, billing or model behavior.
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 }));
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");
@@ -182,7 +182,7 @@ describe("direct OpenCode HTTP instruction boundary", () => {
try {
const checkpoint = await session.snapshot();
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];
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}`); }
@@ -54,16 +54,20 @@ describe("public runner browser client", () => {
fetchImpl: vi.fn() as unknown as typeof fetch,
eventSourceFactory: (nextUrl) => { url = nextUrl; return source; },
});
const event = {
const event: PrpEvent = {
schema: "paperclip.prp.event.v1",
sourceEventId: "event-9",
sourceKind: "harness",
sourceKind: "runner",
sourceInstanceId: "driver",
sourceSeq: 9,
runId: "run-1",
normalizedSessionId: "session/id",
schemaVersion: 1,
priority: 2,
emittedAt: "2026-08-08T00:00:00.000Z",
eventType: "item.delta",
payload: { text: "exact" },
} as PrpEvent;
};
const onEvent = vi.fn();
const onError = vi.fn();
const handle = client.openEventStream("session/id", 8, { onEvent, onError });
@@ -1,17 +1,17 @@
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 {
parseHarnessRuntimeRequestResolution,
type HarnessSession,
} from "../contracts/harness-driver";
import type { PrpEvent } from "../protocol/replay-contract";
} from "../contracts/harness-driver.js";
import type { PrpEvent } from "../protocol/replay-contract.js";
import {
applyPrpEvent,
createSessionSnapshotFromMetadata,
reduceSessionEvents,
type SessionSnapshot,
} from "../reducer/session-reducer";
} from "../reducer/session-reducer.js";
import type { RunnerCapabilities } from "./protocol.js";
import {
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 { PAPERCLIP_PROTOCOL_ACTIONS } from "../protocol-actions/index.js";
@@ -50,8 +50,9 @@ describe("canonical Paperclip protocol action contracts", () => {
).toEqual([]);
if (action.scenario.descriptor.idempotency === "required") {
expect(action.examples).toHaveProperty("scenarioCall");
expect(scenarioCall.idempotencyKey).toEqual(expect.any(String));
expect(scenarioCall.idempotencyKey).not.toHaveLength(0);
const { idempotencyKey } = scenarioCall as { idempotencyKey?: string };
expect(idempotencyKey).toEqual(expect.any(String));
expect(idempotencyKey).not.toHaveLength(0);
} else {
expect(scenarioCall).not.toHaveProperty("idempotencyKey");
}
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
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 { createTaskAction } from "../protocol-actions/create-task.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.
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 });
expect(receipt).toMatchObject({ schema: "paperclip.semantic_tool_receipt.v2", inputSha256: semanticInputSha256(raw),
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);
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 });
expect(receipt).toMatchObject({ inputSha256: semanticInputSha256(raw),
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 expect(handler(call)).rejects.toThrow("duplicated");
} finally { controller.abort(); }
expect((await result).message).toContain("cancelled");
expect((await result as Error).message).toContain("cancelled");
expect(capture).not.toHaveBeenCalled();
expect(tools.size).toBe(0);
expect(emitted[0].operationId).toBe("get_task_context");
@@ -547,7 +547,7 @@ describe("qualified ACPX runtime sidecar", () => {
const bridge = await startRunnerToolBridge({ handler, captureSemanticReceipt: () => () => {} });
try {
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.content[0].text).toBe("ACPX semantic tool call exceeds the sidecar frame limit");
expect(readNativeSemanticReceipt({ contents: body.result.content })).toMatchObject({ outcome: "error", normalizedInputSha256: null });
@@ -561,7 +561,7 @@ describe("qualified ACPX runtime sidecar", () => {
} 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 tools = new Map<string, any>(), emitted: any[] = [], commit = vi.fn(), capture = vi.fn((_input: unknown) => commit);
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(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") {
expect(JSON.stringify(body)).not.toContain(credentialCanary);
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;}`)(
(value: string) => value, (value: string) => `stable-${value}`,
);
const projection = createPiMessageProjection<{ 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)}`,
type PiTextFrame = { type: string; text: string; stream: string; messageId: string; meta: Record<string, string> };
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" } });
const startFrame = sanitize(projection.normalize(event("start")));
const endFrame = sanitize(projection.normalize(event("end:toolUse")));
@@ -830,7 +831,7 @@ describe("qualified ACPX runtime sidecar", () => {
expect(startFrame.messageId).toBe(endFrame.messageId);
expect(JSON.stringify([startFrame, endFrame])).not.toContain("DROP_ME");
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)}`,
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();
@@ -1744,6 +1745,8 @@ class SidecarProcess {
}
}
type ToolCallResponseBody = { result: { content: Array<{ text: string }>; isError?: boolean } };
function loadWaitForTool(input: {
tools: Map<string, unknown>;
emitted: unknown[];
@@ -54,7 +54,9 @@ describe("ACPX sidecar input sequencing", () => {
async () => {
events.push(command);
},
() => events.push(`${command}:error`),
() => {
events.push(`${command}:error`);
},
);
}
const shutdown = pending.then(() => events.push("shutdown"));
@@ -87,7 +89,9 @@ describe("ACPX sidecar input sequencing", () => {
async () => {
events.push("recovered");
},
() => events.push("unexpected"),
() => {
events.push("unexpected");
},
);
await recovered;
@@ -113,7 +117,9 @@ describe("ACPX sidecar input sequencing", () => {
async () => {
events.push("recovered");
},
() => events.push("unexpected"),
() => {
events.push("unexpected");
},
);
await expect(
@@ -123,7 +123,7 @@ describe("eval-session request contract", () => {
const value = request({ provider: "acpx", acpxAgent: agent, model: "explicit-provider-model" });
expect(() => parseEvalSessionRequest(value)).toThrow("--candidate-profile");
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: "codex", session: { acpxAgent: agent } }))).toThrow("session.acpxAgent must match");
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 },
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());
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, {
turnId: "codex-turn", status: "completed", assistantText: "Completed", snapshot: costlySnapshot,
})).toThrow("legacy ledger cost limit exceeded");
@@ -609,7 +609,7 @@ describe("eval-session budget settlement", () => {
});
const input = request({ provider: "acpx", acpxAgent: agent, model,
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 outputPath = join(workspace, "output.json");
@@ -17,14 +17,18 @@ describe("OpenCode proxy input sequencing", () => {
await firstBlocked;
events.push("initialize:complete");
},
() => events.push("initialize:error"),
() => {
events.push("initialize:error");
},
);
pending = enqueueOpenCodeProxyInput(
pending,
async () => {
events.push("thread:start");
},
() => events.push("thread:error"),
() => {
events.push("thread:error");
},
);
const shutdown = pending.then(() => {
events.push("shutdown");
@@ -50,7 +54,9 @@ describe("OpenCode proxy input sequencing", () => {
async () => {
throw new Error("initialize failed");
},
(error) => events.push((error as Error).message),
(error) => {
events.push((error as Error).message);
},
);
await pending;
@@ -1,6 +1,6 @@
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 {
NativeRunIdentity,
NativeSessionCapabilities,
@@ -29,8 +29,8 @@ type ProfileKey = "codex" | "opencode" | "acpx-claude" | "acpx-codex";
interface LocalNativeProfile {
key: ProfileKey;
driverKind: NativeExecutionInput["session"]["driverKind"];
provider: NativeExecutionInput["provider"];
driverKind: NativeExecutionInputV1["session"]["driverKind"];
provider: NativeExecutionInputV1["provider"];
}
/**
@@ -1,5 +1,5 @@
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 {
PRP_BLOCK_RESULT_OUTPUT_SCHEMA,
@@ -76,5 +76,5 @@ describe("ProviderTraceFileSink", () => {
});
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 { 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) };
const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand,
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];
let stop: Promise<unknown> | undefined;
try {
@@ -169,7 +169,7 @@ it("opens and closes the real Cloud facade without a provider prompt", async ()
runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand,
cursorCloudProxyPath: proxyPath,
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",
runId: "00000000-0000-4000-8000-000000000761", normalizedSessionId, turnId: "turn-cloud-launch", itemId: "item-cloud-launch" },
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"),
runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath,
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",
runId: "00000000-0000-4000-8000-000000000762", normalizedSessionId, turnId: "turn-cloud-tool", itemId: "item-cloud-tool" },
environment: {
@@ -343,7 +343,7 @@ async function cloudQuestionHandoff(terminalDelayMs: number, governed: boolean,
provider: "cursor_cloud", cursorCloud, stateDirectory: join(root, "runner-session"),
runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand, cursorCloudProxyPath: proxyPath,
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",
runId: "00000000-0000-4000-8000-000000000763", normalizedSessionId, turnId: "turn-cloud-question", itemId: "item-cloud-question" },
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,
controlPlane: {
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 }; },
},
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) });
const options = { provider: "cursor_cloud" as const, cursorCloud, stateDirectory, runnerBinary: defaultCapabilityRunnerdBinary(), providerNodeCommand,
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 scopeHeader = priorScopeHeader ?? await currentCloudScopeHeader(runtime, normalizedSessionId);
return new Promise((accept, reject) => {
@@ -39,6 +39,8 @@ import {
inspectRetainedRunInstructions,
inspectStagedRunAttachmentHeader,
type DurablePrpCheckpoint,
type DurablePrpControlPlaneStore,
type RunnerProcessHandle,
durableRecoveryInternals,
SemanticToolNotDispatchedError,
inspectWarmRunTransition,
@@ -48,6 +50,16 @@ import {
} from "./durable-prp-control-plane.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 = {
runnerInstanceId: "runner-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, expected)).toBe(template.provider.instructions);
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();
} 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.envelope.payload as Record<string, unknown>).eventType = "turn.started";
core.store.state.ackedSourceSeq = 4096;
core.store.save();
(core.store as WritableCoreStore).save();
const snapshots: DurablePrpCheckpoint[] = [];
core.setCheckpointPersistence(async saved => { snapshots.push(saved); });
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; }
});
const pending: Promise<void>[] = [];
let client: Awaited<ReturnType<typeof authenticate>>;
let client: AuthenticatedClient | null = null;
try {
await core.start();
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");
}
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");
expect(JSON.parse(readFileSync(core.store.path, "utf8")).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);
for (const launch of launches) {
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);
for (const launch of launches) {
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", () => {
const process = { child: { exitCode: null, signalCode: null, kill: () => true },
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,
ticket: "synthetic-ticket", maxOutboxBytes: 262144, p0ReserveBytes: 65536,
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, processLauncher: launcher })).toBe(process);
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", () => {
@@ -2147,7 +2159,7 @@ it.each([
completion: Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }) };
},
});
handle.restart("replacement-ticket");
restartRunner(handle, "replacement-ticket");
for (const launch of launches.splice(0)) {
expect(launch.environment[key]).toBe(explicit ? "explicit-fixture-credential" : undefined);
const receipt = launch.environment[ACPX_CREDENTIAL_BINDING_ENV];
@@ -19,37 +19,41 @@ function snapshot(): CapabilityLiveSessionSnapshot {
current.lifecycle = "running";
current.artifacts.push({
id: "artifact-1",
companyId: "company-1",
taskId: "task-1",
runId: "run-1",
kind: "text",
name: "result.txt",
filename: "result.txt",
contentType: "text/plain",
byteSize: 6,
sha256: "a".repeat(64),
contentRef: "file:///private/result.txt",
metadata: {},
createdAt: "2026-08-09T00:00:01.000Z",
});
return {
id: "session-1",
schema: "paperclip.capability.live-session.v1",
sessionId: "session-1",
revision: 1,
status: "running",
activeTurnId: null,
providerThreadId: "thread-1",
providerSessionId: "provider-session-1",
providerModel: "gpt-test",
providerModel: { id: "gpt-test", provider: "openai" },
config: {
seedState: JSON.stringify(seed),
workingDirectory: "/private/worktree",
scenario: "hb-baseline",
scenario: { id: "hb-baseline" },
capabilities: ["task:read"],
explicitClaims: [],
turnTimeoutMs: 1_000,
},
authority: {
active: true,
companyId: "company-1",
actorId: "actor-1",
taskId: "task-1",
runId: "run-1",
sessionId: "session-1",
scenarioId: "hb-baseline",
capabilities: ["task:read"],
explicitClaims: [],
},
transcript: [],
evidence: [{
@@ -64,8 +68,9 @@ function snapshot(): CapabilityLiveSessionSnapshot {
},
}],
mockState: JSON.stringify(current),
authorizationRecords: [],
process: null,
networkEvidence: [],
networkEvidence: { realPaperclipRequests: 0, childPaperclipEnvironmentKeys: [] },
createdAt: "2026-08-09T00:00:00.000Z",
updatedAt: "2026-08-09T00:00:01.000Z",
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 result = await f.turn(owner);
expect(result.status).toBe("failed");
if (result.status !== "failed") throw new Error("turn did not fail");
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" },
_meta: { paperclipAntigravityAdmission: {
...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" }] } });
@@ -2,7 +2,7 @@ import { appendSemanticToolReceipt } from "../semantic-tool-receipt.js";
import { readFileSync } from "node:fs";
import { createHash } from "node:crypto";
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 { 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 () => {
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" });
await session.startTurn({ message: { text: "Complete" } });
await session.startTurn({ message: { role: "user", text: "Complete" } });
const { schema: _, attentionRequests: __, artifacts: ___, ...raw } = completedResult();
const commit = vi.fn(), capture = vi.fn((value: unknown) => { expect(commit).not.toHaveBeenCalled(); return commit; });
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}` })),
});
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));
const commit = vi.fn(), capture = vi.fn(() => commit);
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);
await session.startTurn({ message: { text: "fixture" } });
await session.startTurn({ message: { role: "user", text: "fixture" } });
fixture.finishTurn({ status: "completed", stopReason: "end_turn" });
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");
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");
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 => {
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 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 created = collectUntil(session.events(), "runtime_request.created");
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 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" });
const events = await collectUntil(session.events(), "turn.completed");
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"));
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" });
await session.startTurn({ message: { text: "Answer exactly" } });
await session.startTurn({ message: { role: "user", text: "Answer exactly" } });
fixture.finishTurn({ status: scenario === "cancelled" ? "cancelled" : "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");
@@ -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 () => {
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" });
await session.startTurn({ message: { text: "Work" } });
await session.startTurn({ message: { role: "user", text: "Work" } });
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 } });
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 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" });
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);
@@ -233,7 +233,7 @@ describe("Codex ACPX harness driver", () => {
}
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 { 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" });
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.")!;
@@ -256,34 +256,34 @@ describe("Codex ACPX harness driver", () => {
const session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
expect(fixture.hostOptions?.providerPolicy).toEqual({ readOnly: 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 context = { requestId: 0, signal: new AbortController().signal };
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-2", mode: "follow_up", message: { text: "Then validate" } });
await session.steer!({ turnId, correlationId: "control-1", message: { role: "user", text: "Change focus" } });
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.queueFollowUp).toHaveBeenCalledExactlyOnceWith("Then validate", `run-controls:${turnId}`);
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: "wrong-turn", message: { text: "Stale" } })).rejects.toThrow();
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: { role: "user", text: "Stale" } })).rejects.toThrow();
fixture.finishTurn({ status: "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"]);
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" });
});
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 session = await fixture.driver.openSession({ runId: "run-controls", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
const { turnId } = await session.startTurn({ message: { text: "Work" } });
await expect(session.steer!({ turnId, message: { text: "No handshake" } })).rejects.toThrow(/did not negotiate/);
const { turnId } = await session.startTurn({ message: { role: "user", text: "Work" } });
await expect(session.steer!({ turnId, message: { role: "user", text: "No handshake" } })).rejects.toThrow(/did not negotiate/);
expect(fixture.host.steerActiveTurn).not.toHaveBeenCalled();
fixture.host.steeringCapability.mockReturnValue({ steering: true, queuedFollowUp: false });
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(/already attempted/);
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" });
await session.startTurn({ message: { text: "Fix it" } });
await session.startTurn({ message: { role: "user", text: "Fix it" } });
fixture.finishTurn({ status: "completed" });
const events = await collectUntil(session.events(), "turn.completed");
for (const event of events) validatePrpEvent(event);
@@ -713,11 +713,11 @@ describe("Codex ACPX harness driver", () => {
signal: new AbortController().signal,
}),
).rejects.toThrow("does not match");
await session.interrupt({ turnId, reason: "user cancelled" });
await session.interrupt!({ turnId, reason: "user cancelled" });
expect(fixture.host.interruptActiveTurn).toHaveBeenCalledWith(
"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",
);
await session.close({ reason: "cancelled" });
@@ -1291,13 +1291,13 @@ describe("Codex ACPX harness driver", () => {
const fixture = driverFixture({}, { closeSettlementTimeoutMs: 1 });
const hostA = {
...fixture.host,
close: vi.fn(async () => {
close: vi.fn(async (_input: { reason: string }): Promise<void> => {
throw new Error("host A cleanup failed");
}),
};
const hostB = {
...fixture.host,
close: vi.fn(async () => {
close: vi.fn(async (_input: { reason: string }): Promise<void> => {
throw new Error("host B cleanup failed");
}),
};
@@ -1606,7 +1606,7 @@ describe("Codex ACPX harness driver", () => {
).rejects.toThrow("awaiting canonical terminal-event retention");
expect(dynamicToolHandler).not.toHaveBeenCalled();
await expect(
session.interrupt({ turnId, reason: "too late" }),
session.interrupt!({ turnId, reason: "too late" }),
).rejects.toThrow("awaiting canonical terminal-event retention");
expect(fixture.host.interruptActiveTurn).not.toHaveBeenCalled();
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 session = await fixture.driver.openSession({ runId: "run-provider-death", normalizedSessionId: "session-1", workingDirectory: "/workspace" });
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 controller = new AbortController();
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 }) => {
const command = "printf 'sensitive-value' > /workspace/denied.txt";
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 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 created = collectUntil(session.events(), "runtime_request.created");
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 session = await fixture.driver.openSession({ runId: "run-attached", normalizedSessionId: "session-1", workingDirectory: "/fixture/workspace" });
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" });
const events = await completed;
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(
{
mode: "form",
sessionId: "backend-1",
message: "Choose deployment settings.",
requestedSchema: {
type: "object",
@@ -2065,6 +2066,8 @@ describe("Codex ACPX harness driver", () => {
onElicitation(
{
mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: {
type: "object",
properties: { value: { type: "string" } },
@@ -2101,6 +2104,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation(
{
mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: {
type: "object",
properties: { value: { type: "string" } },
@@ -2148,6 +2153,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation(
{
mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: {
type: "object",
properties: { value: { type: "string" } },
@@ -2194,6 +2201,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation(
{
mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: {
type: "object",
properties: { value: { type: "string" } },
@@ -2262,6 +2271,8 @@ describe("Codex ACPX harness driver", () => {
const providerResponse = onElicitation(
{
mode: "form",
sessionId: "backend-1",
message: "Provide a value.",
requestedSchema: {
type: "object",
properties: { value: { type: "string" } },
@@ -3177,12 +3188,12 @@ function driverFixture(
function recoveryWorkspaceLease(
path = "/workspace",
): AcpxRecoveryWorkspaceLease & {
assertHeld: ReturnType<typeof vi.fn>;
close: ReturnType<typeof vi.fn>;
assertHeld: Mock<() => void>;
close: Mock<() => Promise<void>>;
} {
return {
path,
assertHeld: vi.fn(),
assertHeld: vi.fn<() => void>(),
close: vi.fn(async () => undefined),
};
}
@@ -3207,6 +3218,7 @@ function fakeHost(createTurn: () => AcpxRuntimeTurn, onClose: () => void) {
workspacePath: "/workspace",
workspaceDigest: `sha256:${"b".repeat(64)}`,
runtimeRoot: "/runtime/acpx/session-1",
commandDigest: `sha256:${"c".repeat(64)}`,
profileDigest: `sha256:${"a".repeat(64)}`,
requestedModel: "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),
queueFollowUp: vi.fn(async (_text: string, _requestId?: string) => undefined),
interruptActiveTurn: vi.fn(async () => undefined),
close: vi.fn(async () => {
close: vi.fn(async (_input: { reason: string }) => {
onClose();
}),
};
@@ -7,6 +7,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import type {
AcpPermissionRequest,
AcpAgentRegistry,
AcpRuntime,
AcpRuntimeHandle,
@@ -114,7 +115,7 @@ describe("Codex ACPX runtime adapter", () => {
if (!restoring) spawnProvider();
return HANDLE;
});
vi.mocked(runtime.setConfigOption).mockImplementation(async input => {
vi.mocked(runtime.setConfigOption!).mockImplementation(async input => {
if (restoring) spawnProvider();
const guard = created.protocolGuardFactory!();
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" };
options.refreshConsumedCommand = vi.fn(async () => undefined);
let created!: AcpRuntimeOptions;
vi.mocked(runtime.setConfigOption).mockImplementation(async () => {
vi.mocked(runtime.setConfigOption!).mockImplementation(async () => {
const guard = created.protocolGuardFactory!();
const binding = cursorInstructionBinding(options.systemInstructions);
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
@@ -574,7 +575,7 @@ describe("Codex ACPX runtime adapter", () => {
let created!: AcpRuntimeOptions;
const binding = cursorInstructionBinding(options.systemInstructions);
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!();
guard("outbound", { id: 0, method: "session/load", params: { sessionId: "backend-1" } });
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, {
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(await port.identity()).toMatchObject({ mode: selected });
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 () => {
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);
let created!: AcpRuntimeOptions & { onAgentInitialize?: (result: unknown) => void };
const options = openOptions(fakeCommand());
options.profile = { ...options.profile, agent: "pi" };
options.piThinkingLevel = "low";
vi.mocked(runtime.setConfigOption).mockImplementation(async input => {
vi.mocked(runtime.setConfigOption!).mockImplementation(async input => {
const guard = created.protocolGuardFactory!();
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" }] } });
@@ -734,9 +735,7 @@ describe("Codex ACPX runtime adapter", () => {
expect(runtimeOptions!.permissionPolicy).toMatchObject({ defaultAction: "escalate" });
const turn = port.startTurn({ text: "Attempt a write.", requestId: "permission-test" });
try {
await runtimeOptions!.onPermissionRequest!({
sessionId: "backend-1", inferredKind: "write", raw: {},
}, { signal: new AbortController().signal });
await runtimeOptions!.onPermissionRequest!(permissionRequest("backend-1", "edit"), { signal: new AbortController().signal });
expect(signal?.aborted).toBe(true);
await expect(turn.result).rejects.toThrow("Approval required");
await expect((async () => { for await (const _event of turn.events) { /* drain */ } })())
@@ -2361,21 +2360,13 @@ describe("Codex ACPX runtime adapter", () => {
]);
await expect(
runtimeOptions?.onPermissionRequest?.(
{
sessionId: "session-1",
inferredKind: "execute",
raw: { _meta: { is_mcp_tool_approval: true } },
},
permissionRequest("session-1", "execute", { _meta: { is_mcp_tool_approval: true } }),
{ signal: new AbortController().signal },
),
).resolves.toEqual({ outcome: "reject_once" });
await expect(
runtimeOptions?.onPermissionRequest?.(
{
sessionId: "session-1",
inferredKind: "execute",
raw: {},
},
permissionRequest("session-1", "execute"),
{ signal: new AbortController().signal },
),
).resolves.toEqual({ outcome: "reject_once" });
@@ -2399,11 +2390,7 @@ describe("Codex ACPX runtime adapter", () => {
await expect(
runtimeOptions?.onPermissionRequest?.(
{
sessionId: "session-1",
inferredKind: "write",
raw: {},
},
permissionRequest("session-1", "edit"),
{ signal: new AbortController().signal },
),
).resolves.toEqual({ outcome: "reject_once" });
@@ -2438,11 +2425,7 @@ describe("Codex ACPX runtime adapter", () => {
expect(runtimeOptions?.nonInteractivePermissions).toBe("fail");
await expect(
runtimeOptions?.onPermissionRequest?.(
{
sessionId: "backend-1",
inferredKind: "write",
raw: {},
},
permissionRequest("backend-1", "edit"),
{ signal: new AbortController().signal },
),
).resolves.toEqual(expected);
@@ -3618,8 +3601,18 @@ function fakeCommand(): VerifiedAcpxCommandLease {
return { spawn: vi.fn(), close: vi.fn() };
}
function fakeChild(): ChildProcess {
const child = new EventEmitter() as ChildProcess;
type FakeChildProcess = ChildProcess & { exitCode: number | null; signalCode: NodeJS.Signals | null };
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, {
exitCode: { value: null, writable: true },
signalCode: { value: null, writable: true },
@@ -3633,10 +3626,10 @@ function fakeChild(): ChildProcess {
}
function failingSignalChild(): {
child: ChildProcess;
child: FakeChildProcess;
errors: [Error, Error];
} {
const child = new EventEmitter() as ChildProcess;
const child = new EventEmitter() as FakeChildProcess;
const errors: [Error, Error] = [
new Error("SIGTERM delivery failed"),
new Error("SIGKILL delivery failed"),
@@ -3654,8 +3647,8 @@ function failingSignalChild(): {
return { child, errors };
}
function childThatExitsAfterSignalCalls(exitAfter: number): ChildProcess {
const child = new EventEmitter() as ChildProcess;
function childThatExitsAfterSignalCalls(exitAfter: number): FakeChildProcess {
const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, {
exitCode: { value: null, writable: true },
signalCode: { value: null, writable: true },
@@ -3676,8 +3669,8 @@ function childThatExitsAfterSignalCalls(exitAfter: number): ChildProcess {
return child;
}
function stubbornChild(): ChildProcess {
const child = new EventEmitter() as ChildProcess;
function stubbornChild(): FakeChildProcess {
const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, {
pid: { value: 71_001 },
exitCode: { value: null, writable: true },
@@ -3691,8 +3684,8 @@ function stubbornChild(): ChildProcess {
return child;
}
function childThatExitsOnKill(): ChildProcess {
const child = new EventEmitter() as ChildProcess;
function childThatExitsOnKill(): FakeChildProcess {
const child = new EventEmitter() as FakeChildProcess;
Object.defineProperties(child, {
pid: { value: 71_002 },
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 }),
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 } };
try {
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 { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
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 {
createCursorNotificationNormalizer, createCursorProfileExtensionAdapter, createCursorSubagentNormalizer, cursorWorkspaceArtifactReference,
normalizeCursorPlanRequest, normalizeCursorQuestionRequest, normalizeCursorSubagentUpdate,
} 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[] = [];
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; }
@@ -1,3 +1,4 @@
/// <reference lib="es2024.promise" />
import { spawn, type ChildProcess } from "node:child_process";
import { mkdtemp, rm } from "node:fs/promises";
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,
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; },
onAcpMessage: (direction, value) => {
const message = value as { id?: number; result?: { outcome?: unknown } };
@@ -1,3 +1,4 @@
/// <reference lib="es2024.promise" />
import { spawn } from "node:child_process";
import { createRequire } from "node:module";
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");
for (const [index, toolCallId] of rawIds.entries()) {
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" }],
} };
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", () => {
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", () => {
@@ -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 => {
const s = setup(); s.p.tool(initial);
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 { 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 () => {
const root = await mkdtemp(join(tmpdir(), "grok-rejected-admission-"));
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);
expect(handle.backendSessionId).toBe(originalSession);
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();
const turn = runtime.startTurn({ handle, text: "Verify the restored fixture", mode: "prompt", requestId: "grok-cold-turn", timeoutMs: 5_000 });
const output: string[] = [];
@@ -686,7 +686,7 @@ describe("native ACPX execution closure", () => {
const fences = await Promise.all([listen(), listen()]);
const fds = fences.map(server => (server as Server & { _handle?: { fd?: number } })._handle!.fd!);
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");
let port: Awaited<ReturnType<typeof openCodexAcpxRuntime>> | 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 { join } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import Ajv from "ajv";
import { Ajv } from "ajv";
import {
checkPiNativeTool, installPiRuntimeExtension, piMcpRequest, readPiRuntimeConfiguration,
PI_NATIVE_QUESTION_TOOL, PI_PERMISSION_TITLE_PREFIX, type PiExtensionApi, type PiRuntimeConfiguration, type PiToolDefinition,
@@ -12,7 +12,7 @@ import {
const directories: string[] = [];
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);
await mkdir(join(root, "workspace")); await mkdir(join(root, "skills")); await mkdir(join(root, "private"));
return { root, config: {
@@ -20,7 +20,7 @@ async function workspace() {
workspace: join(root, "workspace"), readOnly: false,
readRoots: [join(root, "skills")], protectedRoots: [join(root, "private")],
instructions: "Use the assigned Paperclip tools", servers: [],
} satisfies PiRuntimeConfiguration };
} };
}
function harness() {
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 () => {
const { root, config } = await workspace(); const h = harness();
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"));
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"],
["grok", "grok-profile-v14-identity.json", "08294ec6bdcbdbe46c8c8b711d8b368558a05f0d84d25c9f5fd0c3b67b79cd17", "published ed8"],
["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));
expect(createHash("sha256").update(bytes).digest("hex")).toBe(checksum);
const identity = JSON.parse(bytes.toString("utf8"));
@@ -1,3 +1,4 @@
/// <reference lib="es2024.promise" />
import { describe, expect, it, vi } from "vitest";
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 () => {
const fixture = await hostFixture();
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" },
}, fixture.dependencies({ openRuntime, prepareSandbox }))).rejects.toThrow("cannot protect Runner state");
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 => {
const fixture = await hostFixture();
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" },
}, fixture.dependencies({ openRuntime }))).rejects.toThrow("preconfigured ACPX read-only execution is not supported");
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 => {
const fixture = await hostFixture();
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" },
}, fixture.dependencies({ openRuntime }))).rejects.toThrow("requires ACPX approve-all");
expect(openRuntime).not.toHaveBeenCalled();
@@ -379,7 +379,7 @@ describe("ACPX runtime host", () => {
])("rejects invalid assigned connection bindings before runtime launch", async (environment) => {
const fixture = await hostFixture();
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();
});
@@ -491,7 +491,8 @@ describe("ACPX runtime host", () => {
const host = await AcpxRuntimeHost.open({
...fixture.options,
agent: "claude", model: "claude-sonnet-5",
environment: { ...fixture.options.environment,
permissionMode: "approve-reads",
environment: {
PAPERCLIP_NATIVE_MCP_NAME: "paperclip-assigned",
PAPERCLIP_NATIVE_MCP_URL: "http://127.0.0.1:3211/mcp/gateway",
PAPERCLIP_NATIVE_MCP_TOKEN: "fixture-gateway-token-".repeat(3),
@@ -518,7 +519,7 @@ describe("ACPX runtime host", () => {
let servers: AcpxRuntimePortOpenOptions["mcpServers"] = [];
const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5",
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 => {
servers = options.mcpServers;
return runtimePort({ getStatus: async () => ({ models: { currentModelId: "claude-sonnet-5" } }) });
@@ -1042,12 +1043,12 @@ describe("ACPX runtime host", () => {
const events: CanonicalProviderEvent[] = [];
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))); } });
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",
permissionMode: "deny-all", environment: { COPILOT_GITHUB_TOKEN: "fixture-not-a-real-credential" },
semanticTools: { tools: [{ name: "get_task_context", inputSchema: { type: "object", properties: {}, additionalProperties: false } }],
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 {
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: {} } }) });
@@ -1092,7 +1093,7 @@ describe("ACPX runtime host", () => {
},
fixture.dependencies({
openRuntime: async (options) => {
bridge = options.mcpServers[0];
bridge = urlBridge(options.mcpServers[0]);
return runtimePort();
},
}),
@@ -1990,10 +1991,10 @@ describe("ACPX runtime host", () => {
it("retains a settled turn when shutdown cleanup must be retried", async () => {
const fixture = await hostFixture();
const credential = credentialLeaseFixture(fixture.root);
let settleTurn!: (value: { stopReason: string }) => void;
let settleTurn!: (value: Awaited<AcpxRuntimeTurn["result"]>) => void;
const turn = {
...runtimeTurn(),
result: new Promise<{ stopReason: string }>((resolve) => {
result: new Promise<Awaited<AcpxRuntimeTurn["result"]>>((resolve) => {
settleTurn = resolve;
}),
};
@@ -2019,7 +2020,7 @@ describe("ACPX runtime host", () => {
host.startTurn({ text: "Complete the task.", requestId: "turn-1" });
const firstClose = host.close({ reason: "first shutdown" });
settleTurn({ stopReason: "end_turn" });
settleTurn({ status: "completed", stopReason: "end_turn" });
await expect(firstClose).rejects.toThrow(/cleanup failed/);
expect(turn.cancel).toHaveBeenCalledOnce();
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(
input: {
identity?: AcpxRuntimePort["identity"];
@@ -2402,7 +2408,7 @@ async function hostFixture() {
dependencies(
input: Pick<AcpxRuntimeHostDependencies, "openRuntime"> &
Partial<
Pick<AcpxRuntimeHostDependencies, "reportRetainedCleanupFailure">
Pick<AcpxRuntimeHostDependencies, "reportRetainedCleanupFailure" | "prepareSandbox" | "verifyInstallation">
> & {
// Opt-in only. When true, `prepareSandbox` runs the real
// preparation once, then returns that same sandbox for every
@@ -2413,19 +2419,19 @@ async function hostFixture() {
): AcpxRuntimeHostDependencies {
let reusedSandbox: AcpxRuntimeSandbox | null = null;
return {
verifyInstallation: async (profile) =>
verifyInstallation: input.verifyInstallation ?? (async (profile) =>
({
commandDigest: profile.commandDigest,
agentServerPackageJsonPath: join(root, "package.json"),
agentRuntimePackageJsonPath: null,
openCommand: async () => command,
}) satisfies VerifiedAcpxInstallation,
}) satisfies VerifiedAcpxInstallation),
openRuntime: input.openRuntime,
// Record the skills home the instant the sandbox exists, ahead of
// the materialize call that seals it. A test that overrides
// `prepareSandbox` for a non-Claude agent replaces this wrapper, but
// those agents never materialize skills, so nothing is lost.
prepareSandbox: async (sandboxInput) => {
prepareSandbox: input.prepareSandbox ?? (async (sandboxInput) => {
if (input.reuseSandbox && reusedSandbox) return reusedSandbox;
const sandbox = await prepareAcpxRuntimeSandbox(sandboxInput);
if (sandboxInput.agent === "claude") {
@@ -2433,7 +2439,7 @@ async function hostFixture() {
}
if (input.reuseSandbox) reusedSandbox = sandbox;
return sandbox;
},
}),
retainAdmissionCleanup: trackAdmissionCleanup,
reportRetainedCleanupFailure:
input.reportRetainedCleanupFailure ?? vi.fn(),
@@ -128,10 +128,11 @@ describe("Codex app-server transport limits", () => {
});
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;
const spawnSpy = vi
.spyOn(ChildProcess.prototype, "spawn")
.spyOn(childProcessPrototype, "spawn")
.mockImplementation(function (this: ChildProcess, ...args) {
spawnedProcess = this;
return originalSpawn.apply(this, args);
@@ -660,7 +660,7 @@ describe("Codex app-server Codex driver", () => {
).rejects.toBeInstanceOf(HarnessCapabilityUnavailableError);
for (const action of ["set", "pause", "resume", "clear"] as const) {
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);
}
expect(unsupportedTransport.calls.filter(({ method }) =>
@@ -401,7 +401,7 @@ describe("Codex app-server Codex driver", () => {
{ runDelta: first, runDeltaComplete: true },
{ 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 () => {
@@ -478,7 +478,7 @@ describe("Codex app-server Codex driver", () => {
expect(events.some((event) => event.eventType === "session.failed")).toBe(
false,
);
expect(await session.usage()).toMatchObject({
expect(await session.usage!()).toMatchObject({
total: { inputTokens: 10, outputTokens: 4 },
});
});
@@ -785,6 +785,7 @@ describe("Codex app-server Codex driver", () => {
runId: "run-1",
environmentLeaseId: "lease-1",
runnerInstanceId: "runner-codex",
driverSessionId: "thread-1",
normalizedSessionId: "normalized-1",
},
capabilities,
@@ -819,7 +820,7 @@ it("sends explicit skill input on every requested turn", async () => {
await collectUntilTerminal(session.events());
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,
});
const snapshot = await original.snapshot();
await original.close();
await original.close({ reason: "fixture complete" });
for (const selected of [skillInputs, []]) {
const transport = new FakeCodexTransport();
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" } });
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);
} 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();
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 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));
expect(settlements).toHaveLength(1);
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();
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 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.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);
@@ -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" };
const notification = runnerdRuntimeRequestExpiryNotification("runtime_request.expired", payload, "thread-1", kind === "turn" ? "foreign-turn" : turnId)!;
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 === "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(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 () => {
@@ -68,7 +68,7 @@ describe("Codex app-server Codex driver", () => {
threadId: "thread-1", turnId: "turn-1", arguments: {} });
expect(feedback).toHaveBeenCalledExactlyOnceWith({ ...report, continuation });
expect((await session.snapshot()).semanticResult?.result).toEqual({ ...report, continuation });
} finally { await session.close(); }
} finally { await session.close({ reason: "fixture complete" }); }
});
it.each([
@@ -92,7 +92,7 @@ describe("Codex app-server Codex driver", () => {
expect(response).toMatchObject({ success: false });
expect(feedback).not.toHaveBeenCalled();
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 () => {
@@ -111,7 +111,7 @@ describe("Codex app-server Codex driver", () => {
expect(await call("corrected")).toMatchObject({ success: true,
contentItems: [{ type: "inputText", text: expect.stringContaining("/approvals/approval-1") }] });
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 () => {
@@ -194,7 +194,7 @@ describe("Codex app-server Codex driver", () => {
},
},
})).toMatchObject({ success: false });
await otherSession.close();
await otherSession.close({ reason: "fixture complete" });
});
it("makes duplicate semantic completion idempotent and rejects changed payloads", async () => {
@@ -16,8 +16,8 @@ import { NativeSessionProtocolIntegrityError } from "../../contracts/native-sess
import {
DurablePrpControlPlane,
durableRecoveryInternals,
type DurableRecoveryIdentity,
} 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 { executeNativeSession } from "../../native-session-runtime.js";
import { CodexAppServerDriver } from "./codex-app-server-driver.js";
@@ -44,7 +44,7 @@ async function authenticatedRunner(
runnerBinary: string,
) {
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) {
const length = Buffer.alloc(8);
length.writeBigUInt64BE(BigInt(part.length));
@@ -476,6 +476,7 @@ describe("Codex protocol integrity propagation", () => {
normalizedSessionId: identity.normalizedSessionId,
driverKind: "codex_app_server",
protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
},
provider: { kind: "codex", model: null },
completionContract: {
@@ -335,7 +335,7 @@ describe("Codex structured question adapter", () => {
questions: [{ id: "form", question: "Choose one" }],
});
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", {
requestedSchema: {
@@ -35,7 +35,7 @@ describe("Codex resume accounting through the production driver", () => {
turnId: string,
) => {
const saved = JSON.parse(JSON.stringify(await session.snapshot!()));
await session.close();
await session.close({ reason: "test complete" });
transport.readResponse = {
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");
await expect(runTurn(session, transports[2]!, "turn-3", { inputTokens: 170, outputTokens: 20 }))
.resolves.toEqual({ inputTokens: 30, outputTokens: 4 });
await session.close();
await session.close({ reason: "test complete" });
});
it.each(["thread", "snapshot"] as const)(
@@ -137,7 +137,7 @@ describe("Codex resume accounting through the production driver", () => {
expect(await session.usage!()).toMatchObject({
runDelta: { inputTokens: 40, outputTokens: 6 },
});
await session.close();
await session.close({ reason: "fixture complete" });
second.readResponse = readResponse(140, 16);
const recovery = await driver.recoverSession(persisted);
expect(recovery.recovered).toBe(true);
@@ -187,7 +187,7 @@ describe("Codex resume accounting through the production driver", () => {
inputTokens: 140,
outputTokens: 16,
});
await recoveredSession.close();
await recoveredSession.close({ reason: "fixture complete" });
third.readResponse = readResponse(150, 19);
const secondRecovery = await driver.recoverSession(advanced);
expect(secondRecovery.recovered).toBe(true);
@@ -205,8 +205,8 @@ describe("Codex resume accounting through the production driver", () => {
),
).toEqual([]);
} finally {
await recoveredSession?.close();
await session.close();
await recoveredSession?.close({ reason: "fixture complete" });
await session.close({ reason: "fixture complete" });
}
},
);
@@ -245,7 +245,7 @@ describe("Codex resume accounting through the production driver", () => {
baseline: { inputTokens: 100 },
latest: { inputTokens: 140 },
};
await session.close();
await session.close({ reason: "fixture complete" });
const tokenUsage = {
total: { inputTokens: 150 },
last: { inputTokens: 10 },
@@ -270,8 +270,8 @@ describe("Codex resume accounting through the production driver", () => {
second.calls.filter((call) => call.method === "turn/start"),
).toEqual([]);
} finally {
await recoveredSession?.close();
await session.close();
await recoveredSession?.close({ reason: "fixture complete" });
await session.close({ reason: "fixture complete" });
}
},
);
@@ -300,7 +300,7 @@ describe("Codex resume accounting through the production driver", () => {
});
await collectUntilTerminal(session.events());
const persisted = JSON.parse(JSON.stringify(await session.snapshot!()));
await session.close();
await session.close({ reason: "fixture complete" });
second.readResponse = {
thread: {
id: "thread-1",
@@ -384,7 +384,7 @@ describe("Codex resume accounting through the production driver", () => {
.filter((call) => call.method === "thread/read")
.every((call) => call.params.includeTurns === false),
).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 () => {
@@ -401,7 +401,7 @@ describe("Codex resume accounting through the production driver", () => {
await new Promise(resolve => setImmediate(resolve));
const persisted = JSON.parse(JSON.stringify(await session.snapshot!()));
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,
turns: [{ id: "turn-1", status: "completed", items: [] }], tokenUsage: { total: { inputTokens: 130, outputTokens: 10 } } } };
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 } } });
for await (const event of recovered.events()) if (event.payload.kind === "usage") break;
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 () => {
@@ -439,6 +439,6 @@ describe("Codex resume accounting through the production driver", () => {
transport.calls.find((call) => call.method === "turn/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 { parseCodexTurnDiff, summarizeCodexTurnDiff } from "./codex-turn-diff.js";
import { parseCodexTurnDiff, summarizeCodexTurnDiff, type ParsedCodexTurnDiffFile } from "./codex-turn-diff.js";
describe("Codex turn diff parser", () => {
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: "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 },
])).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", () => {
@@ -91,7 +91,7 @@ describe("prepared native context transport", () => {
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"',
);
});
@@ -87,7 +87,7 @@ describe("Cursor Cloud bounded stdio proxy", () => {
const terminalGate = new Promise<void>(resolve => { releaseTerminalWrites = resolve; });
let pausedTerminalWrites = 0;
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) {
pausedTerminalWrites += 1;
await terminalGate;
@@ -151,7 +151,7 @@ describe("Cursor Cloud bounded stdio proxy", () => {
});
let openedSession!: CursorCloudSessionHandle;
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);
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 });
response.end(bytes);
});
const externalHeaders: HeadersInit[] = [];
const externalHeaders: Array<NonNullable<RequestInit["headers"]>> = [];
let unexpectedTargets = 0;
const transport: typeof fetch = (input, init) => {
const url = String(input);
@@ -71,7 +71,7 @@ function maintainedMutations(peer: Peer, maxResponseBytes?: number) {
async function fixture(peer = new Peer(), extra: Partial<CursorCloudDriverOptions> = {}) {
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" }] },
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.",
@@ -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 };
}
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 }> {
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 reading.promise;
try {
await f.session.interrupt!();
await f.session.interrupt!({});
await vi.waitFor(() => expect(readAborted).toBe(true), { timeout: 100 });
expect((await f.session.read()).phase).toBe("terminal");
} 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 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;
await f.session.interrupt!();
await f.session.interrupt!({});
expect(signal.aborted).toBe(true);
expect((await f.session.read()).phase).toBe("terminal");
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled();
@@ -354,7 +358,7 @@ describe("Cursor Cloud native driver", () => {
}
}
await reading.promise;
await session.interrupt!();
await session.interrupt!({});
await acknowledgeTerminal(session);
await session.attachRun!({ runId: "paperclip-followup" });
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(await session.read()).toMatchObject({ agentId: previous.agentId, runId: followupRunId, turnId: next.turnId, phase: "running", pendingTerminalDelivery: null });
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(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);
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);
release.resolve(); await new Promise<void>(accept => setImmediate(accept));
expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint);
expect(f.events.slice(next.eventCount)).toEqual([]);
expect(streams.map(stream => stream.runId)).toEqual([runId, peer.native!.id]);
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 });
});
@@ -421,7 +425,7 @@ describe("Cursor Cloud native driver", () => {
released.resolve(); await new Promise<void>(accept => setImmediate(accept));
expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint);
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 } });
});
@@ -441,7 +445,7 @@ describe("Cursor Cloud native driver", () => {
released.resolve(oldAgent); await oldReconcile;
expect(await savedCheckpoint(f.root, f.session)).toEqual(next.checkpoint);
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 } });
});
@@ -453,19 +457,19 @@ describe("Cursor Cloud native driver", () => {
const previous = { ...peer.native! };
const oldRead = deferred<CursorCloudRun>(), reading = deferred();
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" });
await f.session.reconcile!();
const next = await nextAuthority(f);
const currentRead = deferred<CursorCloudRun>(), currentReading = deferred();
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);
oldRead.resolve(previous); await oldStop;
expect(await savedCheckpoint(f.root, f.session)).toEqual(before);
expect(peer.cancelRun).not.toHaveBeenCalled();
const readCount = peer.getRun.mock.calls.length;
const duplicateStop = f.session.interrupt!();
const duplicateStop = f.session.interrupt!({});
await new Promise<void>(accept => setImmediate(accept));
expect(peer.getRun).toHaveBeenCalledTimes(readCount);
currentRead.resolve({ ...peer.native! }); await Promise.all([currentStop, duplicateStop]);
@@ -480,7 +484,7 @@ describe("Cursor Cloud native driver", () => {
if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "First task" } });
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" });
}
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(f.events.slice(eventCount)).toEqual([]);
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);
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);
@@ -507,7 +511,7 @@ describe("Cursor Cloud native driver", () => {
expect(await savedCheckpoint(f.root, recovered)).toEqual({ ...before, relayTurnToken: before.relayTurnToken ?? null });
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 recovered.interrupt!();
await recovered.interrupt!({});
});
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);
}
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(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 originalWrite = CursorCloudCheckpointStore.prototype.write;
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);
if (value.phase === "creating" && !intercepted) {
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 rejected = expect(started).rejects.toBe(failure);
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();
release.resolve(); await rejected; await stopped;
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();
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(); }
});
@@ -571,10 +575,10 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer);
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 f.session.interrupt!(); await acknowledgeTerminal(f.session);
await f.session.interrupt!({}); await acknowledgeTerminal(f.session);
const held = deferred(), release = deferred(), stoppingWritten = deferred();
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);
if (value.phase === "creating") return persisted.then(async () => { held.resolve(); await release.promise; });
if (value.phase === "stopping") void persisted.then(() => stoppingWritten.resolve());
@@ -582,7 +586,7 @@ describe("Cursor Cloud native driver", () => {
});
try {
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();
release.resolve();
const next = await started; await stopped;
@@ -605,7 +609,7 @@ describe("Cursor Cloud native driver", () => {
const after = await savedCheckpoint(f.root, f.session);
expect(after).toEqual({ ...before, sourceSequence: before.sourceSequence + 1 });
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 } });
});
@@ -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" }) });
await expect(f.session.reconcile!()).rejects.toThrow("confirmed Agent/Run identity");
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 } });
});
@@ -660,7 +664,7 @@ describe("Cursor Cloud native driver", () => {
const activeSnapshot = await restored.snapshot(); await restored.detachControllerForRestart!();
await expect(new CursorCloudDriver(f.options).recoverSession(activeSnapshot)).rejects.toThrow("instructions changed");
const final = (await new CursorCloudDriver(revised).recoverSession(activeSnapshot)).session!; sessions.push(final);
await final.interrupt!();
await final.interrupt!({});
await acknowledgeTerminal(final);
await final.startTurn({ message: { role: "user", text: "One more task" } });
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-/) }));
await rpc(path, { id: "missing", method: "tools/call", params: { name: "not_admitted", arguments: {} } }, relayHeaders(f.peer), initialized.session);
expect(f.handler).toHaveBeenCalledTimes(1);
await f.session.interrupt!();
await f.session.interrupt!({});
expect(f.peer.cancelRun).toHaveBeenCalledTimes(1);
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);
@@ -849,7 +853,7 @@ describe("Cursor Cloud native driver", () => {
await waitFor(() => peer.getAgent.mock.calls.length > 0);
expect(peer.createAgent).toHaveBeenCalledTimes(1);
expect((await restored.read?.())?.effectiveModelAcknowledged).toBe(false);
await restored.interrupt!();
await restored.interrupt!({});
const restoredSnapshot = await restored.snapshot(); await restored.close({ reason: "done" });
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-/);
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();
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 () => {
@@ -893,7 +897,7 @@ describe("Cursor Cloud native driver", () => {
if (phase === "follow-up") {
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 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);
@@ -905,11 +909,11 @@ describe("Cursor Cloud native driver", () => {
expect(fetch).toHaveBeenCalledTimes(before.fetch);
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);
await f.session.interrupt!();
await f.session.interrupt!({});
expect(peer.getAgent).toHaveBeenCalledTimes(before.getAgent);
expect(peer.getRun).toHaveBeenCalledTimes(before.getRun);
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);
expect(recovered.recovered).toBe(true);
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" } });
await vi.waitFor(async () => expect(await restored.read()).toMatchObject({ phase: "running", turnId: next.turnId }));
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 => {
@@ -928,7 +932,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer, { client: mutations.client });
if (phase === "follow-up") {
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;
mutations.fetch.mockImplementationOnce(async (url, init) => {
@@ -954,7 +958,7 @@ describe("Cursor Cloud native driver", () => {
if (phase === "follow-up") {
await f.session.startTurn({ message: { role: "user", text: "Previous accepted task" } });
await peer.created.promise;
await f.session.interrupt!();
await f.session.interrupt!({});
await acknowledgeTerminal(f.session);
}
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 originalWrite = CursorCloudCheckpointStore.prototype.write;
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) {
pausedRejection = true; rejectionHeld.resolve();
return releaseRejection.promise.then(() => originalWrite.call(this, value));
@@ -989,7 +993,7 @@ describe("Cursor Cloud native driver", () => {
});
let stopDone = false;
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; });
try {
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);
if (mutations) expect(mutations.fetch).toHaveBeenCalledTimes(fetchesBefore!);
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);
expect(recovered.recovered).toBe(true);
const restored = recovered.session!; sessions.push(restored);
@@ -1037,7 +1041,7 @@ describe("Cursor Cloud native driver", () => {
distinctFollowup(peer); peer.release = deferred();
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 restored.interrupt!();
await restored.interrupt!({});
expect(await restored.read()).toMatchObject({ phase: "terminal", pendingTerminalDelivery: { turnId: next.turnId } });
} finally {
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 originalWrite = CursorCloudCheckpointStore.prototype.write;
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) {
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" });
peer.createRun.mockRejectedValue(new CursorCloudClientError("http_error", { status: 422 }));
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)); }
return originalWrite.call(this, value);
});
@@ -1105,7 +1109,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer, mutations ? { client: mutations.client } : {});
if (phase === "follow-up") {
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();
if (mutations) {
@@ -1119,14 +1123,14 @@ describe("Cursor Cloud native driver", () => {
});
peer.getAgent.mockRejectedValue(new CursorCloudClientError("http_error", { status: 404 }));
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"
? Promise.reject(new Error("injected rejection checkpoint failure")) : originalWrite.call(this, value);
});
try {
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 stopped = f.session.interrupt!().then(() => null, error => error);
const stopped = f.session.interrupt!({}).then(() => null, error => error);
rejected.resolve();
expect(await stopped).toMatchObject({ message: expect.stringContaining("remains fenced") });
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 }));
const f = await fixture(peer), turn = await f.session.startTurn({ message: { role: "user", text: "Unknown create" } });
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 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);
@@ -1159,7 +1163,7 @@ describe("Cursor Cloud native driver", () => {
const f = await fixture(peer, { cancelTimeoutMs: 120 });
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 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((await f.session.read?.())?.phase).toBe("stopping");
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 attempts: Array<NonNullable<typeof before.pendingTerminalDelivery>> = [];
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);
attempts.push(structuredClone(value.pendingTerminalDelivery));
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 originalWrite = CursorCloudCheckpointStore.prototype.write;
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) {
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);
expect(late.status).toBe(403); expect(f.handler).not.toHaveBeenCalled();
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 snapshot = f.session.snapshot().finally(() => { snapshotted = true; });
await new Promise<void>(accept => setImmediate(accept));
@@ -1290,7 +1294,7 @@ describe("Cursor Cloud native driver", () => {
const originalWrite = CursorCloudCheckpointStore.prototype.write;
const deliveries: unknown[] = [];
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") {
deliveries.push(structuredClone(value.pendingTerminalDelivery));
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);
expect(peer.createAgent).toHaveBeenCalledTimes(1); expect(peer.createRun).not.toHaveBeenCalled();
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 () => {
@@ -11,7 +11,7 @@ const result = { schema: "paperclip.run_result.v1", reportedWorkDisposition: "do
async function fixture(overrides: Partial<DotDriverOptions> = {}) {
const identity = { companyId: randomUUID(), agentId: randomUUID(), issueId: randomUUID(), runId: randomUUID(), sessionId: 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 assertAuthority = vi.fn(async () => {});
const driver = new DotHarnessDriver({ identity, principal, expiresAt: Date.now() + 60_000,
@@ -96,7 +96,7 @@ it.each([
return handle;
});
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);
// Hold the SDK poll until the real process has exited. The authenticated
// 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;
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") {
await expect(session.close({ reason: "Test complete" })).resolves.toBeUndefined();
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,
normalizedSessionId: input.session.normalizedSessionId!, turnId: "turn-" + input.binding.runId, itemId: "item-" + input.binding.runId },
runnerBinary: resolve("runner/target/debug/paperclip-runnerd"),
onSpawn: process => { spawned = process; },
onSpawn: async process => { spawned = process; },
controlPlaneRegistration: async authority => {
if (emptyController) {
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);
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 => ({
requestId: randomUUID(), bindingId: input.provider.binding.bindingId, bindingGeneration: 1, runId: input.binding.runId,
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"),
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: 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);
return { connectUrl: authority.connectUrl, release: () => authority.stop() }; },
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");
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 {
const prepared = commandSpy.mock.calls.find(call => call[0] === "run.prepare");
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"),
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: 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);
return { connectUrl: authority.connectUrl, release: () => authority.stop() }; },
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 commandSpy = vi.spyOn(controlPlane.DurablePrpControlPlane.prototype, "queueCommand");
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 {
await session.startTurn({ message: { role: "user", text: "Answer boundary questions." } });
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"),
identity: { runnerInstanceId: randomUUID(), environmentLeaseId: randomUUID(), runId: 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 () => {},
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),
});
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[] = [];
let eventReadFailure: unknown;
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.");
const input = execution(root);
const events: PrpEvent[] = [];
const dispatches: PrpEvent[] = [];
const deliveries: PrpEvent[] = [];
const dispatches: Array<{ sourceEventId: string; payload: Record<string, unknown> }> = [];
const deliveries: Array<{ sourceEventId: string; payload: Record<string, unknown> }> = [];
const settlements = new Map<string, Record<string, unknown>>();
let send: ((operation: ExternalProviderOperation) => Promise<void>) | 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 };
const driver = new RunnerdMuseDriver({
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); },
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; }; } },
@@ -12,10 +12,14 @@ const report = {
evidence: [], verification: [],
};
type RpcResponse = {
result: { isError?: boolean; tools: Array<{ name: string; inputSchema: unknown }> };
};
async function rpc(bridge: OpenCodeMcpBridge, id: number, method: string, params?: unknown) {
return (await fetch(bridge.url, { method: "POST", headers: {
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 () => {
@@ -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) });
bridges.push(bridge);
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");
for (const [index, input] of [report, { ...report, continuation: null }].entries()) {
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 });
try {
expect(await session.read()).toMatchObject({ messages: [] });
expect(await session.read!()).toMatchObject({ messages: [] });
} finally {
await session.close({ reason: "test" });
}
@@ -338,7 +338,7 @@ describe("OpenCodeServerDriver", () => {
});
const descriptor = await driver.descriptor();
expect(
descriptor.capabilities.typedEventFamilies.find(
descriptor.capabilities.typedEventFamilies?.find(
(family) => family.family === "plan",
),
).toMatchObject({ availability: "unsupported" });
@@ -751,7 +751,7 @@ describe("OpenCodeServerDriver", () => {
// run.terminal is owned by the native-session layer above this driver.
// Keep both absences explicit instead of synthesizing Codex-shaped input.
expect(
(await driver.descriptor()).capabilities.typedEventFamilies.find(
(await driver.descriptor()).capabilities.typedEventFamilies?.find(
(family) => family.family === "plan",
),
).toMatchObject({ availability: "unsupported" });
@@ -1105,7 +1105,7 @@ describe("OpenCodeServerDriver", () => {
expect(JSON.stringify(events)).not.toContain(
"submitted prompt must not become assistant text",
);
expect(await session.usage()).toMatchObject({
expect(await session.usage!()).toMatchObject({
input: 3,
output: 2,
costUsd: 0.001,
@@ -1772,7 +1772,7 @@ describe("OpenCodeServerDriver", () => {
// A prompt tools map replaces OpenCode's session permissions. Question is
// enabled in config without overwriting the allow/ask/deny or path policy.
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,
);
const sessionRoot = join(root, "context");
@@ -2871,13 +2871,13 @@ describe("OpenCodeServerDriver", () => {
await session.startTurn({ message: { role: "user", text: "finish" } });
await started;
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));
expect(stopped).toBe(false);
releaseFeedback();
await stop;
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.some(event => event.eventType === "item.completed" && (event.payload.item as { is_error?: boolean })?.is_error)).toBe(false);
} finally {
@@ -57,7 +57,7 @@ describe("per-turn OpenCode reasoning", () => {
});
expect(config.provider.openrouter.models["deepseek/deepseek-v4-flash-0731"].options).toBeUndefined();
} finally {
try { await session?.close(); }
try { await session?.close({ reason: "fixture complete" }); }
finally { await rm(root, { recursive: true, force: true }); }
}
});
@@ -23,6 +23,8 @@ afterEach(async () => {
await Promise.all(bridges.splice(0).map((bridge) => bridge.close()));
});
type ToolCallBody = { result: { content: unknown } };
async function rpc(
bridge: RunnerToolBridge,
body: Record<string, unknown>,
@@ -563,7 +565,7 @@ describe("Copilot semantic receipt opt-in", () => {
} });
bridges.push(bridge);
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(readNativeSemanticReceipt({ contents: body.result.content })).toEqual(receipts[0]);
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);
if (!normalizedFirst.ok || !normalizedSecond.ok) throw new Error("Invalid fixture");
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) });
pending.get("second")!.capture(normalizedFirst.result)();
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(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 => {
expect(call.captureNormalizedInput).toBeUndefined(); return modelMetadata;
} }); 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);
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();
});
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 { 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 {
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
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 { prpSchemaBundle } from "../protocol/generated/schema-bundle.js";
@@ -76,7 +76,7 @@ describe("live ACP request recovery", () => {
recovered = await original.snapshot();
expect(recovered.pendingRuntimeRequests).toHaveLength(2);
} finally {
await original.close();
await original.close({ reason: "test complete" });
}
class AdoptedTransport extends FakeCodexTransport {
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");
expect(deliver).toHaveBeenCalledTimes(1);
} 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.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 admission = candidate();
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.
profileDigest: `sha256:${"a".repeat(64)}`, requestedModel: admission.model, effectiveModel: admission.model,
} };
@@ -15,6 +15,7 @@ import type {
} from "../drivers/codex/app-server-transport.js";
import type { CapabilityIssueThreadSnapshot, CapabilityThreadItem } from "../issue-thread/types.js";
import type { CapabilityLiveTransportFactory } from "./live-session.js";
import type { CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js";
import {
createCapabilityTurnStreamDecoder,
readCapabilityTurnStream,
@@ -343,10 +344,25 @@ class FakeCleanRoomTransport implements CodexAppServerTransport {
function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransportFactory {
return (options) => {
const evidence = {
const evidence: CapabilityRunnerdProcessEvidence = {
runnerPid: 7100,
runnerProcessGroupId: 7100,
providerPid: null,
providerProcessStartedAt: null,
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,
runnerExitCode: null,
runnerSignal: null,
@@ -359,7 +375,7 @@ function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransport
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 };
limits: { maxTurns: number; maxMessageBytes: number };
view: CapabilityIssueThreadSnapshot;
toolResult?: unknown;
toolTurnId?: string | null;
configuration: {
provider: "codex" | "opencode";
model: string | null;
@@ -801,9 +819,9 @@ describe("Capability clean-room chat server", () => {
expect(after.toolTurnId).toMatch(/^turn-/);
const turn = after.view.turns.find((candidate: { id: string }) => candidate.id === after.toolTurnId);
expect(turn).toBeDefined();
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 === "durable_comment" && item.body === "Progress entered manually from DevTools.")).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 === "durable_comment" && item.body === "Progress entered manually from DevTools.")).toBe(true);
expect(after.view.evidence.state.at(-1)?.toRevision).toBeGreaterThan(0);
});
@@ -24,7 +24,7 @@ import {
type CapabilityLiveTurnEvent,
} from "./live-session.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 { captureTurnRejection } from "../../test/capture-turn-rejection.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" });
try {
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" });
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 {
return (options) => {
const evidence = {
const evidence: CapabilityRunnerdProcessEvidence = {
runnerPid: 4100 + state.transports.length,
runnerProcessGroupId: 4100 + state.transports.length,
providerPid: null,
providerProcessStartedAt: null,
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,
runnerExitCode: null,
runnerSignal: null,
@@ -337,7 +352,7 @@ function fakeTransportFactory(state: FakeProviderState): CapabilityLiveTransport
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");
}
if (
snapshot.terminalTurns.length > 0 &&
snapshot.terminalTurns!.length > 0 &&
(this.#rejectTerminalSaves || this.#terminalSaveFailuresRemaining > 0)
) {
if (this.#terminalSaveFailuresRemaining > 0) {
@@ -647,7 +662,7 @@ describe("Capability live runnerd and Codex session", () => {
outputTokens: 4,
})).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-after-recovery",
]);
@@ -1159,7 +1174,7 @@ describe("Capability live runnerd and Codex session", () => {
expect(first.snapshot.providerThreadId).toBe(second.snapshot.providerThreadId);
expect(state.attachments).toHaveLength(1);
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 },
]);
@@ -1497,7 +1512,7 @@ describe("Capability live runnerd and Codex session", () => {
const result = await session.sendMessage("report overlapping usage");
expect(result.snapshot.usageLedger).toHaveLength(1);
expect(result.snapshot.usageLedger[0]).toMatchObject({
expect(result.snapshot.usageLedger![0]).toMatchObject({
providerRequests: 1,
inputTokens: 100,
outputTokens: 10,
@@ -1596,7 +1611,7 @@ describe("Capability live runnerd and Codex session", () => {
const result = await session.sendMessage("report late usage");
expect(result.snapshot.usageLedger).toHaveLength(1);
expect(result.snapshot.usageLedger[0]).toMatchObject({
expect(result.snapshot.usageLedger![0]).toMatchObject({
providerRequests: 2,
inputTokens: 100,
outputTokens: 10,
@@ -106,10 +106,10 @@ it.each([
const recovered = await restarted.backend.recoverSession!(snapshot, { signal: new AbortController().signal });
expect(recovered).toMatchObject({ recovered: true });
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.
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");
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] },
} };
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" } });
});
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" },
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 },
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") },
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: [] };
@@ -147,7 +147,7 @@ it.skipIf(process.platform === "win32")("recovers and adopts ACPX through the re
environment: { PAPERCLIP_WORKSPACE_CWD: f.root }, dynamicTools: [] });
let recovered = await backend().recoverSession!(f.nativeSnapshot!, { signal: new AbortController().signal });
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);
await recovered.session!.detachControllerForRestart!();
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");
const pid = source.evidence().runnerPid;
if (mode === "adopt") {
const startedAt = source.transport.processInfo().startedAt;
const startedAt = source.transport.processInfo!().startedAt;
await source.detachControllerForRestart();
const launch = vi.fn(() => { throw new Error("duplicate source launch"); });
source = f.recover({ runnerProcessLauncher: launch, authorizeRunnerAdoption: async () => { throw new Error("old source configuration"); },
@@ -38,7 +38,7 @@ async function verifyLiveRunnerAdoption(
evidence: [], verification: [], attentionRequests: [], artifacts: [],
}));
const server = createServer();
let authority: DurablePrpControlPlane | null = null;
let authority = null as DurablePrpControlPlane | null;
const checkpoint = vi.fn(async () => undefined);
server.on("upgrade", (request, socket, head) => {
if (!authority) {
@@ -136,7 +136,7 @@ async function verifyLiveRunnerAdoption(
);
}
runnerPid = first.evidence().runnerPid;
runnerStartedAt = first.transport.processInfo().startedAt;
runnerStartedAt = first.transport.processInfo!().startedAt;
expect(runnerPid).toEqual(expect.any(Number));
if (goalMidTurn) {
@@ -404,7 +404,7 @@ async function verifyLiveRunnerAdoption(
} else expect(redispatchedTool).not.toHaveBeenCalled();
}
expect(adopted.evidence().runnerPid).toBe(runnerPid);
expect(adopted.transport.processInfo().startedAt).toBe(runnerStartedAt);
expect(adopted.transport.processInfo!().startedAt).toBe(runnerStartedAt);
if (startWithoutCheckpoint) {
const retained = JSON.parse(await readFile(controlPlaneStatePath, "utf8"));
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);
} finally { clearTimeout(queueDeadline); }
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(signal).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 * as durableControlPlane from "../control-plane/durable-prp-control-plane.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",
"after-result",
@@ -478,14 +487,15 @@ it.each([
).toBe(false);
return;
}
const commit = core.store.commit.bind(core.store);
const store = committableStore(core);
const commit = store.commit.bind(store);
let lossObserved = false;
let signalLoss!: () => void;
const loss = new Promise<void>((resolveLoss) => {
signalLoss = resolveLoss;
});
commitSpy = vi
.spyOn(core.store, "commit")
.spyOn(store, "commit")
.mockImplementation((candidate) => {
// The selected process-crash boundary stays unavailable until the
// 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("thread/resume");
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");
} finally {
await bundle.transport.close();
@@ -286,7 +286,7 @@ it.each([
// output plus another full provider-owned prefix behind the old suspend.
const runnerFile = join(original, "runner/runner-state.json");
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++) {
const sourceSeq = runnerBefore.nextSourceSeq++;
const event = {
@@ -388,7 +388,7 @@ it.each([
sessionId: identity.normalizedSessionId,
},
backend: {
kind: "codex",
kind: "runner" as const,
name: maintenanceFixtureBackendName(fixtureId),
},
identity,
@@ -406,7 +406,7 @@ it.each([
recordEpoch,
signal: maintenanceAbort.signal,
};
const close = vi.fn(async () => {
const close = vi.fn(async (): Promise<void> => {
throw new NativeSessionCloseUnrecoverableError();
});
const start = vi.fn(async () => {
@@ -424,9 +424,10 @@ it.each([
capabilities: async () => capabilities,
events: async function* () {},
startTurn: start,
result: async () => { throw new Error("fixture never produces a result"); },
close,
snapshot: async () => ({
backendKind: "codex",
backendKind: "runner",
sessionId: input.binding.sessionId,
identity: input.binding,
providerSessionId: thread.thread.id,
@@ -471,6 +472,7 @@ it.each([
normalizedSessionId: input.binding.sessionId,
driverKind: "codex_app_server",
protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
},
completionContract: {
id: "contract-maintenance",
@@ -211,6 +211,7 @@ it("does not retry a real memoized transport close whose suspension proof is una
normalizedSessionId: identity.sessionId,
driverKind: "codex_app_server",
protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
},
provider: { kind: "codex", model: null },
completionContract: {
@@ -499,7 +500,7 @@ it.each([
message: { role: "user", text: "Keep working until interrupted." },
});
if (label === "interrupted") {
await session.interrupt({
await session.interrupt!({
turnId: turn.turnId,
reason: "Stopped by the user",
});
@@ -525,7 +526,7 @@ it.each([
},
});
} finally {
await session.close().catch(() => undefined);
await session.close({ reason: "test complete" }).catch(() => undefined);
await rm(stateDirectory, {
recursive: 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 { createCapabilityRunnerdCodexTransport, readRunnerdArtifactBinding } from "./runnerd-codex-transport.js";
import {
committableStore,
defaultCapabilityRunnerdBinary,
fakeCodex,
fakeCodexArgs,
@@ -163,8 +164,9 @@ it.each([
providerPid = bundle.evidence().codexPid;
const rotations: (typeof core.store.state)[] = [];
const preparedStates: (typeof core.store.state)[] = [];
const commit = core.store.commit.bind(core.store);
vi.spyOn(core.store, "commit").mockImplementation((candidate) => {
const store = committableStore(core);
const commit = store.commit.bind(store);
vi.spyOn(store, "commit").mockImplementation((candidate) => {
commit(candidate);
// Capture the durable old epoch at publication. The authenticated
// 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(instructionRoot, "AGENTS.md"), "Pinned instructions\n");
const server = createServer();
let authority: DurablePrpControlPlane | null = null, generation = 0;
let authority = null as DurablePrpControlPlane | null, generation = 0;
let checkpoint: durableControlPlane.DurablePrpCheckpoint | undefined;
const registered = new WeakSet<DurablePrpControlPlane>();
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;
expect(providerPid).toEqual(expect.any(Number));
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);
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);
@@ -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(calls).toBe(2);
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.commands.filter(command => command.type === "turn.start")).toHaveLength(1);
expect(noSpawn).not.toHaveBeenCalled();
@@ -283,7 +283,7 @@ it.each(["resolved", "rejected"] as const)(
expect(error).toMatchObject({ message: expect.stringContaining("runner_process_termination_unconfirmed") });
if (outcome === "rejected") expect(error).toHaveProperty("cause", probeError);
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();
} finally {
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 {
child: { pid: 42, exitCode: null, kill: () => false },
completion: Promise.reject(probeError),
isExitConfirmed: () => false,
isExitConfirmed: (): boolean => false,
};
child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" });
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(launch).toHaveBeenCalledTimes(2);
expect(bundle.transport.processInfo()).toMatchObject({ exited: false });
expect(bundle.transport.processInfo!()).toMatchObject({ exited: false });
} finally {
const providerPid = bundle.evidence().codexPid;
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 {
child: { pid: undefined, exitCode: null, kill: () => false },
completion: Promise.reject(stagingError),
isExitConfirmed: () => true,
isExitConfirmed: (): boolean => true,
};
const child = spawn(spec.command, [...spec.args], { cwd: spec.cwd, env: spec.environment, stdio: "ignore" });
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";
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"); });
let configuration: Record<string, unknown> | undefined;
let configuration: Record<string, unknown> | null | undefined;
let recovered = false;
let requestedRun = "";
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 registrationStarted!: () => void;
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; });
let finishRelease!: () => void;
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;
});
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,
};
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(terminalParams).toMatchObject({
threadId: opened.thread.id,
threadId: (opened.thread as { id: string }).id,
turnId: "provider-turn-1",
});
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(
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);
} finally {
await session?.close();
await session?.close({ reason: "test complete" });
await bundle.transport.close();
await rm(stateDirectory, { recursive: true, force: true });
}
@@ -4882,7 +4882,7 @@ it("binds an immediately failed durable turn before exposing its terminal", asyn
],
});
} finally {
await session.close();
await session.close({ reason: "test complete" });
await rm(stateDirectory, {
recursive: true,
force: true,
@@ -5089,9 +5089,10 @@ it("captures exact provider frames and correlates Rust and TypeScript interpreta
if (event.eventType === "turn.completed") break;
}
} finally {
await session.close();
await session.close({ reason: "test complete" });
}
const snapshot = await session.snapshot();
if (snapshot.lastSourceSequence === undefined) throw new Error("Expected a source sequence in the session snapshot");
for (
let sourceSeq = 1;
sourceSeq <= snapshot.lastSourceSequence;
@@ -5791,7 +5792,7 @@ it.each([
expect(outcomes[1].result).toMatchObject({ content: [{ text: expect.stringContaining(feedback) }] });
if (adoptAfterTurn) {
const snapshot = await session.snapshot();
const runnerStartedAt = bundle.transport.processInfo().startedAt;
const runnerStartedAt = bundle.transport.processInfo!().startedAt;
survivingPid = bundle.evidence().runnerPid;
expect(survivingPid).toEqual(expect.any(Number));
const artifact = readRunnerdArtifactBinding(defaultCapabilityRunnerdBinary());
@@ -5833,7 +5834,7 @@ it.each([
expect(recovery.session).toBeDefined();
session = recovery.session!;
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(duplicateLauncher).not.toHaveBeenCalled();
}
@@ -5843,7 +5844,7 @@ it.each([
expect(allRequests.map(request => request.variant)).toEqual(supportsReasoning
? ["paperclip-no-reasoning", "paperclip-default"] : [undefined, undefined]);
},
closeSession: async () => { await session?.close(); },
closeSession: async () => { await session?.close({ reason: "test complete" }); },
closeTransport: async () => {
try { await adopted?.transport.close(); }
finally {
@@ -3,7 +3,7 @@ import fs from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
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";
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);
let core!: DurablePrpControlPlane;
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;
return start.call(this, port);
});
@@ -81,7 +81,7 @@ it.each(["drain", "timeout", "external_close"] as const)("joins an output flush
let stderr = "";
owned.stderr?.on("data", chunk => { stderr += String(chunk); });
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("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(text.join("")).toBe("Streaming progress. ".repeat(800));
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" });
completed = (async () => {
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 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"]);
const provider = JSON.parse(await readFile(join(root, "state", "runner", "codex-provider-state.json"), "utf8"));
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 = {
schema: "paperclip.native-execution-input.v1",
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" },
workspace: { cwd: root, repoUrl: null, repoRef: null, branchName: 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;
}
},
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) {
const source = durableEvents.filter(event => event.sourceInstanceId === request.sourceInstanceId);
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();
for (const runId of ["activity-first", "activity-next"]) {
if (runId === "activity-next") {
attaching = session.attachRun!({ runId });
attaching = Promise.resolve(session.attachRun!({ runId }));
await releasing;
await vi.waitFor(async () => {
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 {
releasePrevious();
await attaching?.catch(() => undefined);
await session?.close();
await session?.close({ reason: "test complete" });
await bundle.transport.close();
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[0].envelope.payload).toMatchObject({ runId, eventType: "run.terminal", payload: { runTerminalState: "succeeded", reportedWorkDisposition: "yielded" } });
} finally {
await session?.close();
await session?.close({ reason: "test complete" });
await bundle.transport.close();
await rm(root, { recursive: true, force: true });
}
@@ -61,7 +61,7 @@ async function sourceFixture(registration?: CapabilityRunnerdCodexTransportOptio
runId: identity.runId, executionWorkspaceId: "workspace" },
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 },
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" },
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: [] };
@@ -231,7 +231,7 @@ it.each([["launch", "default"], ["adopt", "default"], ["adopt-denied", "default"
expect((await read(fixture.coreFile)).preparedLaunchConfigurations).toEqual(before.preparedLaunchConfigurations);
await expect(readFile(join(fixture.root, "launch-preparation.json"))).rejects.toHaveProperty("code", "ENOENT");
if (mode !== "launch") {
const evidence = resumed.evidence(), startedAt = resumed.transport.processInfo().startedAt;
const evidence = resumed.evidence(), startedAt = resumed.transport.processInfo!().startedAt;
await resumed.detachControllerForRestart();
prepare.mockClear();
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 {
const opened = await within(first.transport.request("thread/start", { cwd: root, dynamicTools: [] }), "source open");
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();
expect(runnerPid).toBeGreaterThan(0); expect(providerPid).toBeGreaterThan(0);
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) };
});
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[]) =>
`${JSON.stringify({ kind: "interpretation", frameId, emittedEventIds })}\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, "missing")).toEqual({ frameId: null, nativeChannelSettled: true });
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", () => {
@@ -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).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;
for (let i = 0; i < 1000; i++) index.locate(path, "missing");
expect(readSync).toHaveBeenCalledTimes(calls);
@@ -124,8 +127,8 @@ describe("runnerd trace frame index", () => {
}
expect(readSync).toHaveBeenCalledTimes(72);
for (let i = 0; i < 24; i++) {
expect(vi.mocked(readSync).mock.calls[24 + i]![4]).toBe(1024 * 1024);
expect(vi.mocked(readSync).mock.calls[48 + i]![4]).toBe(2 * 1024 * 1024);
expect(readPosition(24 + i)).toBe(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 }] });
for await (const event of bundle.transport.notifications()) {
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);
break;
}
@@ -136,7 +136,7 @@ describe("Scenario chat deployment boundary", () => {
it("requires exact Origin and Fetch Metadata on every mutation", async () => {
await withHttpRuntime(async ({ request }) => {
const body = JSON.stringify({ scenarioId: SCENARIO_ID });
const invalidHeaders = [
const invalidHeaders: Record<string, string>[] = [
{ origin: `${PUBLIC_ORIGIN}/` },
{ "sec-fetch-site": "none" },
{ "sec-fetch-site": "cross-site" },
@@ -14,6 +14,7 @@ import {
type CapabilityLiveTransportFactory,
type CapabilityLiveTurnEvent,
} from "./live-session.js";
import type { CapabilityRunnerdProcessEvidence } from "./runnerd-codex-transport.js";
import {
createCapabilityTurnStreamDecoder,
encodeCapabilityTurnStreamFrame,
@@ -161,10 +162,25 @@ class GatedTransport implements CodexAppServerTransport {
function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory {
return (options) => {
const evidence = {
const evidence: CapabilityRunnerdProcessEvidence = {
runnerPid: 7100,
runnerProcessGroupId: 7100,
providerPid: null,
providerProcessStartedAt: null,
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,
runnerExitCode: null,
runnerSignal: null,
@@ -172,7 +188,7 @@ function gatedTransportFactory(gate: Gate): CapabilityLiveTransportFactory {
diagnostics: [],
};
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",
effect: "lost_ack",
remaining: 1,
code: "budget_ack_lost",
}],
});
await adapter.start();
@@ -333,7 +333,7 @@ class StubDriver implements HarnessDriver {
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);
result.set("origin", new URL(url).origin);
result.set("sec-fetch-site", "same-origin");
@@ -82,6 +82,7 @@ function reduce(events: readonly PrpEvent[]): SessionSnapshot {
environmentLeaseId: "package-local",
runnerInstanceId: "live-console-demo-server",
normalizedSessionId: events[0]?.normalizedSessionId ?? "session",
driverSessionId: "live-console-demo",
},
capabilities: {
schema: "paperclip.prp.capabilities.v1",
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import type { LocalRunnerRunMetadata, LocalRunnerRunTrace } from "../contracts/local-runner.js";
import {
applyLocalRunnerLiveEvent,
createLocalRunnerLiveSnapshot,
@@ -13,6 +14,16 @@ import {
startLocalRunnerScenario,
} 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", () => {
it("runs lifecycle, tool, file, structured-result, and process-exit events", async () => {
const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 });
@@ -108,7 +119,7 @@ describe("Local runner and fake harness", () => {
),
).toBe(true);
expect(
replayLocalRunnerEvents(trace, trace.events).timeline.find(
replayLocalRunnerEvents(traceMetadata(trace), trace.events).timeline.find(
(event) => event.eventType === "harness.diagnostic",
)?.summary,
).toBe(
@@ -160,7 +171,7 @@ describe("Local runner and fake harness", () => {
it("produces the same snapshot for validated live events and replay", async () => {
const trace = await runLocalRunnerScenario({ scenario: "happy-path", delayMs: 1 });
let live = createLocalRunnerLiveSnapshot(trace);
let live = createLocalRunnerLiveSnapshot(traceMetadata(trace));
for (const event of trace.events) {
const next = applyLocalRunnerLiveEvent(live, event);
expect(next.ok).toBe(true);
@@ -168,7 +179,7 @@ describe("Local runner and fake harness", () => {
live = next.snapshot;
}
}
const replay = replayLocalRunnerEvents(trace, trace.events);
const replay = replayLocalRunnerEvents(traceMetadata(trace), trace.events);
expect(localRunnerSnapshotsMatch(live, replay)).toBe(true);
});
@@ -33,6 +33,7 @@ import {
PAPERCLIP_EXECUTION_PROMPT_REVISION,
canonicalNativeRuntimeContextDigest,
nativeRuntimePromptDigest,
type NativeRuntimeContextSnapshot,
} from "./contracts/runtime-context.js";
import {
executeNativeSession,
@@ -129,6 +130,7 @@ const input: NativeExecutionInputV1 = {
normalizedSessionId: identity.sessionId,
driverKind: "codex_app_server",
protocolVersion: 1,
lifecyclePolicy: { mode: "per_turn", idleTimeoutMs: null },
},
provider: { kind: "codex", model: null },
completionContract: {
@@ -145,13 +147,15 @@ const input: NativeExecutionInputV1 = {
credentialBindings: [],
};
type RuntimeContextFields = Omit<NativeRuntimeContextSnapshot, "aggregateDigest">;
function preparedInput(): NativeExecutionInputV5 {
const digest = "0".repeat(64);
const context = {
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 } },
skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const;
} satisfies RuntimeContextFields;
const requirement = input.completionContract.contract.criteria[0]!.requirement;
const prompt = `${input.task.prompt}\n\n${requirement}`;
return {
@@ -256,7 +260,7 @@ describe("executeNativeSession recovery", () => {
const completeRun = vi.fn(async () => undefined);
const port: ControlPlanePort = {
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) }; },
completeRun,
};
@@ -318,7 +322,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {}, async checkpointSession() {},
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 completeRun() {},
};
@@ -390,7 +394,7 @@ describe("executeNativeSession recovery", () => {
async openRun() {},
async loadSessionCheckpoint() { return structuredClone(checkpoint); },
async checkpointSession(value) { checkpoints.push(structuredClone(value)); },
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const existing = journal.find(value => value.sourceEventId === event.sourceEventId);
if (existing) expect(event).toEqual(existing);
else journal.push(structuredClone(event as PrpEvent));
@@ -479,7 +483,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
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 completeRun() {},
};
@@ -533,7 +537,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
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 completeRun() {},
};
@@ -555,7 +559,7 @@ describe("executeNativeSession recovery", () => {
resume: true, typedEvents: true, steering: false,
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 session: NativeSession = {
identity: () => identity,
@@ -574,7 +578,7 @@ describe("executeNativeSession recovery", () => {
},
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,
disposition: "committed",
}));
@@ -728,7 +732,7 @@ describe("executeNativeSession recovery", () => {
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 } },
skills: [], mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const;
} satisfies RuntimeContextFields;
const completed = await executeNativeSession({
input: snapshotBeforeUpdate ? {
...input, schema: "paperclip.native-execution-input.v4", executionMode: "default", planningContext: null,
@@ -748,7 +752,7 @@ describe("executeNativeSession recovery", () => {
controlPlane: {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
appended.push(event);
return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" };
},
@@ -885,7 +889,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
appended.push(event);
return {
cursor: event.sourceSeq,
@@ -1034,7 +1038,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
appended.push(event);
return {
cursor: event.sourceSeq,
@@ -1185,7 +1189,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
return {
cursor: event.sourceSeq,
highestContiguousSourceSeq: event.sourceSeq,
@@ -1241,7 +1245,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
return { cursor: event.sourceSeq, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" };
},
async replayEvents() {
@@ -1574,7 +1578,7 @@ describe("executeNativeSession recovery", () => {
},
controlPlane: {
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",
}; },
async replayEvents() { return { events: [], highestContiguousSourceSeq: 0 }; },
@@ -2568,12 +2572,12 @@ describe("executeNativeSession recovery", () => {
},
skills: [],
mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const;
} satisfies RuntimeContextFields;
const openSession = vi.fn();
const backend: NativeSessionBackend = {
async descriptor() {
return {
kind: "future-provider",
kind: "mock",
name: "future-provider",
version: "1",
capabilities: {
@@ -2741,6 +2745,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true,
steering: false,
interruption: true,
structuredResult: true,
};
},
async *events() {},
@@ -2854,7 +2859,7 @@ describe("executeNativeSession recovery", () => {
const snapshot = vi.fn(async () => { throw snapshotFailure; });
const session: NativeSession = {
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 startTurn() { throw new Error("unexpected turn"); },
async result() { return null; },
@@ -2886,6 +2891,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true,
steering: false,
interruption: true,
structuredResult: true,
};
},
async *events() {},
@@ -3509,7 +3515,7 @@ describe("executeNativeSession recovery", () => {
}
},
);
const checkpointSession = vi.fn<ControlPlanePort["checkpointSession"]>(
const checkpointSession = vi.fn<NonNullable<ControlPlanePort["checkpointSession"]>>(
async () => undefined,
);
const port: ControlPlanePort = {
@@ -3744,8 +3750,9 @@ describe("executeNativeSession recovery", () => {
timeoutMs: 10,
keepSessionOpen: true,
onSession: (current) => retainedSessions.push(current),
onPostCompletionEnrichmentFailure: ({ stage }) =>
enrichmentFailures.push(stage),
onPostCompletionEnrichmentFailure: ({ stage }) => {
enrichmentFailures.push(stage);
},
});
await enrichmentStalled;
if (stalledSignal !== undefined)
@@ -4650,12 +4657,12 @@ describe("executeNativeSession recovery", () => {
async *events() { throw new Error("provider failed after editing instructions"); },
startTurn: async () => ({ turnId: "instruction-turn" }),
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: [] }),
close: async () => { order.push("closing"); await closed; order.push("stopped"); },
};
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,
};
const controlPlane: ControlPlanePort = {
@@ -4747,7 +4754,7 @@ describe("executeNativeSession recovery", () => {
close: vi.fn(async () => { throw failure; }),
};
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),
};
const controlPlane: ControlPlanePort = {
@@ -4982,7 +4989,7 @@ describe("executeNativeSession recovery", () => {
runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 5_000,
controlPlane: {
async openRun() {}, async checkpointSession() {}, completeRun,
async appendEvent(event, options) {
async appendEvent(event: PrpEvent, options) {
if (options?.deferPostCommitObservers) deferredEventTypes.push(event.eventType);
events.push(structuredClone(event)); return {
cursor: events.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed",
@@ -5080,7 +5087,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
events.push(structuredClone(event as PrpEvent));
return {
cursor: events.length,
@@ -5199,7 +5206,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
events.push(structuredClone(event as PrpEvent));
const sourceEvents = events.filter(
(candidate) => candidate.sourceInstanceId === event.sourceInstanceId,
@@ -5417,7 +5424,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
if (event.eventType === "item.completed") {
persistingAnswer = true;
await persistence;
@@ -5925,7 +5932,7 @@ describe("executeNativeSession recovery", () => {
const openReplacementSession = vi.fn();
const backend: NativeSessionBackend = {
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,
recoverSession: async () => ({ recovered: false, reason: "saved launch profile changed",
failureClass: PROVIDER_STATE_INCOMPATIBLE }),
@@ -6093,6 +6100,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true,
steering: false,
interruption: true,
structuredResult: true,
};
},
async *events() {},
@@ -6136,6 +6144,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true,
steering: false,
interruption: true,
structuredResult: true,
},
};
},
@@ -6224,6 +6233,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true,
steering: false,
interruption: true,
structuredResult: true,
};
},
async *events() {},
@@ -6268,6 +6278,7 @@ describe("executeNativeSession recovery", () => {
typedEvents: true,
steering: false,
interruption: true,
structuredResult: true,
},
};
},
@@ -6375,7 +6386,7 @@ describe("executeNativeSession recovery", () => {
},
async replayEvents(replay) {
return {
events: replay.afterSourceSeq === 0 ? [runnerEvent(1)] : [],
events: replay.afterSourceSeq === 0 ? [runnerEvent(1, "turn.started")] : [],
highestContiguousSourceSeq: 1,
};
},
@@ -6479,7 +6490,7 @@ describe("executeNativeSession recovery", () => {
},
async replayEvents(replay) {
return {
events: replay.afterSourceSeq === 0 ? [runnerEvent(1)] : [],
events: replay.afterSourceSeq === 0 ? [runnerEvent(1, "turn.started")] : [],
highestContiguousSourceSeq: 1,
};
},
@@ -6530,7 +6541,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:1",
sourceSeq: 1,
sourceInstanceId: "provider-recovery",
sourceKind: "provider",
sourceKind: "runner",
runId: identity.runId,
normalizedSessionId: identity.sessionId,
turnId: "turn-recovery",
@@ -6594,7 +6605,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint);
},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list);
@@ -6718,7 +6729,7 @@ describe("executeNativeSession recovery", () => {
async checkpointSession(snapshot) {
checkpoints.push(structuredClone(snapshot));
},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const target =
event.sourceInstanceId === "runner-recovery"
? runnerEvents
@@ -6852,7 +6863,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint);
},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
events.push(structuredClone(event));
return {
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 } },
skills: [skill],
mcp: { assignmentSetId: "none", digest, bindingId: null },
} as const;
const legacyContext = { ...context, skills: [] as const };
} satisfies RuntimeContextFields;
const legacyContext = { ...context, skills: [] } satisfies RuntimeContextFields;
const prepared = preparedInput();
const executionInput = preparedMode
? {
@@ -7049,7 +7060,7 @@ describe("executeNativeSession recovery", () => {
terminal: null,
terminalTurns: [],
};
const startTurn = vi.fn(async () => ({ turnId: "turn-replacement" }));
const startTurn = vi.fn<NativeSession["startTurn"]>(async () => ({ turnId: "turn-replacement" }));
const replacementSession: NativeSession = {
identity: () => identity,
async capabilities() {
@@ -7108,7 +7119,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint);
},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
events.push(structuredClone(event));
return {
cursor: events.length,
@@ -7194,7 +7205,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:2",
sourceSeq: 2,
sourceInstanceId: "provider-recovery",
sourceKind: "provider",
sourceKind: "runner",
runId: identity.runId,
normalizedSessionId: identity.sessionId,
turnId: "turn-continuation",
@@ -7204,7 +7215,7 @@ describe("executeNativeSession recovery", () => {
emittedAt: "2026-08-09T00:00:01.000Z",
payload: {},
};
const startTurn = vi.fn(async () => ({ turnId: "turn-continuation" }));
const startTurn = vi.fn<NativeSession["startTurn"]>(async () => ({ turnId: "turn-continuation" }));
const session: NativeSession = {
identity: () => identity,
async capabilities() {
@@ -7273,7 +7284,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint);
},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list);
@@ -7490,7 +7501,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:2",
sourceSeq: 2,
sourceInstanceId: "provider-recovery",
sourceKind: "provider",
sourceKind: "runner",
runId: identity.runId,
normalizedSessionId: identity.sessionId,
turnId: "turn-disposition",
@@ -7571,7 +7582,7 @@ describe("executeNativeSession recovery", () => {
prematureDispositionCheckpoint = true;
durableCheckpoint = structuredClone(snapshot);
},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
if (event.eventType === terminalType && failNextAppend) {
failNextAppend = false;
throw appendFailure;
@@ -7674,7 +7685,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "runner-recovery:run-native:4",
sourceSeq: 4,
sourceInstanceId: "runner-recovery",
sourceKind: "provider",
sourceKind: "runner",
runId: identity.runId,
normalizedSessionId: identity.sessionId,
turnId: "turn-disposition",
@@ -7784,7 +7795,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint);
},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list);
@@ -7935,7 +7946,7 @@ describe("executeNativeSession recovery", () => {
return structuredClone(checkpoint);
},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const list = bySource.get(event.sourceInstanceId) ?? [];
list.push(structuredClone(event));
bySource.set(event.sourceInstanceId, list);
@@ -8085,7 +8096,7 @@ describe("executeNativeSession recovery", () => {
pendingRuntimeRequests: [],
lineage: [],
};
const events = [
const events: PrpEvent[] = [
{
...controlEvent(1, "run.result.accepted", { 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; } },
controlPlane: {
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,
}, runnerInstanceId: "runner-recovery", controlPlaneInstanceId: "control-recovery", timeoutMs: 1000,
resolveMissingResult: async () => null,
@@ -8239,7 +8250,7 @@ describe("executeNativeSession recovery", () => {
sourceEventId: "provider-recovery:1",
sourceSeq: 1,
sourceInstanceId: "provider-recovery",
sourceKind: "provider",
sourceKind: "runner",
runId: identity.runId,
normalizedSessionId: identity.sessionId,
turnId: "turn-waiting",
@@ -8420,7 +8431,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {}, completeRun: complete,
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 }; },
};
const outcome = expect(executeNativeSession({ input, backend, controlPlane: port, timeoutMs: 1_000,
@@ -8475,7 +8486,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {}, completeRun: complete,
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 }; },
};
const execution = executeNativeSession({ input, backend, controlPlane: port, timeoutMs: operationMs ?? 120_000,
@@ -8523,7 +8534,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {}, completeRun: complete,
async checkpointSession(value) { checkpoint = structuredClone(value); },
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
const duplicate = history.some(row => row.sourceEventId === event.sourceEventId);
if (!duplicate) history.push(event);
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");
}
},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
if (!history.some(row => row.sourceEventId === event.sourceEventId)) history.push(event);
return { cursor: history.length, highestContiguousSourceSeq: event.sourceSeq, disposition: "committed" };
},
@@ -8673,14 +8684,14 @@ describe("executeNativeSession recovery", () => {
}),
sourceEventId: "provider-recovery:1",
sourceInstanceId: "provider-recovery",
sourceKind: "provider",
sourceKind: "runner",
turnId: "turn-waiting",
};
const turnInterrupted: PrpEvent = {
...controlEvent(3, "turn.interrupted", { reason: "governed_wait" }),
sourceEventId: "provider-recovery:3",
sourceInstanceId: "provider-recovery",
sourceKind: "provider",
sourceKind: "runner",
turnId: "turn-waiting",
};
let releaseCancelled!: () => void;
@@ -8945,7 +8956,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
events.push(structuredClone(event as PrpEvent));
if (event.eventType === "runtime_request.created") releaseCreated();
const sourceEvents = events.filter(
@@ -9491,7 +9502,7 @@ describe("executeNativeSession recovery", () => {
const port: ControlPlanePort = {
async openRun() {},
async checkpointSession() {},
async appendEvent(event) {
async appendEvent(event: PrpEvent) {
if (event.eventType === "runtime_request.resolved") {
markSettlementAppendStarted();
await settlementAppendReleased;
@@ -9521,11 +9532,11 @@ describe("executeNativeSession recovery", () => {
};
const originalSetTimeout = globalThis.setTimeout;
let queuedHandoffCallback: (() => void) | null = null;
let queuedHandoffCallback = null as (() => void) | null;
const timeoutSpy = vi.spyOn(globalThis, "setTimeout").mockImplementation(((
callback,
delay,
...args
callback: (...args: unknown[]) => void,
delay?: number,
...args: unknown[]
) => {
if (delay === 123_456) {
queuedHandoffCallback = () => callback(...args);
@@ -9576,7 +9587,7 @@ it("fails preconfigured recovery without replacing a missing provider session",
const backend: NativeSessionBackend = {
descriptor: async () => ({ kind: "mock", name: "preconfigured", version: "1",
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,
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({
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");
expect(openSession).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 { paperclipSemanticAction } from "../catalog/semantic-action-catalog.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 { 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 { 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 { 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 { providerDescriptorSchema } from "./generated/schema-bundle.js";
@@ -19,7 +19,7 @@ describe("provider runtime descriptor", () => {
}
const event: PrpEvent = {
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: {
sessionGoals: { availability: "unsupported", reason: "provider has no native goals", actions: [],
autonomousUpdates: false, persistentAcrossResume: false, maxObjectiveChars: 4000,
@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
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";
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 { readFile } from "node:fs/promises";
@@ -77,7 +77,7 @@ describe("PRP v1 JSON Schema contract", () => {
expect(result.fixture.futureFixtureHint).toEqual({
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);
if (!result.ok) return;
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" });
const withoutEnvelope = structuredClone(result.fixture);
@@ -10,6 +10,7 @@ function workspaceEvent(eventType: "workspace.change.updated" | "workspace.diff.
sourceInstanceId: "runner-1",
sourceKind: "runner",
runId: "run-1",
normalizedSessionId: "session-1",
turnId: "turn-1",
eventType,
schemaVersion: 1,
@@ -44,7 +45,7 @@ describe("PRP workspace change events", () => {
it("rejects traversal paths and incomplete recorded diffs", () => {
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);
const incomplete = workspaceEvent("workspace.diff.recorded");
incomplete.payload.complete = false;
@@ -59,6 +60,7 @@ describe("PRP workspace change events", () => {
sourceInstanceId: "runner-1",
sourceKind: "runner",
runId: "run-1",
normalizedSessionId: "session-1",
turnId: "turn-1",
eventType: "workspace.file.referenced",
schemaVersion: 1,
Loaded 100 of 200 files, more files were not shown because too many files have changed in this diff. Show more