fix(runner): fence warm ACPX owners on runtime contract changes

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-29 21:40:17 -05:00
1 parent d6ff17fd0d
commit 951b06d08a
17 files changed
+194 -45

No files matched your search

@@ -1,6 +1,6 @@
# Rich ACP integration and qualification report
Updated: 2026-09-29. Foundation PR #14430 is merged at
Updated: 2026-09-30 UTC. Foundation PR #14430 is merged at
`24beb005755465f71a19ec92a85da0958d1b9740`. Basic extended Runner Eval definitions
are merged in `paperclip-evals` PR #29. Provider PRs remain separate review units
and all three providers remain **pending qualification**.
@@ -9,7 +9,17 @@ The [September 30 UTC evidence checkpoint](runner-rich-acp-validation-2026-09-30
separates current paid behavior, retained failures, runtime identities, billing
coverage and incomplete qualification from historical results below.
Current candidates are **Cursor v5, Copilot v5 and Pi v8**. On corrected source
Current candidates are **Cursor v6, Copilot v6 and Pi v9**, pending fresh builds
and qualification. Their identities bind shared runtime contract
`paperclip.acpx-runtime-contract.v1`. The server includes that revision in the
warm-session configuration fingerprint for every ACPX agent, so an existing
owner cannot bypass changed cancellation and permission handling. Other
transports retain their previous configuration digest. Current profile identity
and admission tests pass (58 tests; one optional installation test skipped), and
four server tests verify changed ACPX owners retire while unchanged Codex
app-server owners remain reusable. These checks are not paid qualification.
The most recent paid profiles are **Cursor v5, Copilot v5 and Pi v8**. On corrected source
`5605c350b`, Cursor has passed all five basic paid local journeys: completion,
semantic questions, semantic plan approval, controller restart and file
editing/validation. Copilot has passed the paid Daytona completion journey;
@@ -47,7 +57,7 @@ recommends Cursor and Copilot, followed by Pi, using the existing qualified ACPX
path. Codex app-server is the richness benchmark. The legacy Cursor and Pi
adapters are outside this change.
## Current profile evidence (2026-09-29, collected through September 30 UTC)
## Retained paid profile evidence (2026-09-29, collected through September 30 UTC)
The latest paid runtime source is `5605c350b2a4dae75be3779f164d7e1a0e4a5a87`.
It includes the passive native-read evidence fix from `794e90a258`: correlation
@@ -141,10 +151,14 @@ timeout changed the run to failed. Its watcher also reported incomplete
coverage while monitoring a workspace parent that startup can modify. The
144.581-second attempt retired every owned process, left no owned semaphores,
and passed the full end audit; additional billing remained disabled at $0.
Shared cancellation settlement and explicit permission-provider provenance need
runtime repairs. The denial fixture needs an isolated target parent and coverage
diagnostics while retaining its strict completeness check. Paid runs are held
until the corrected runtime is identified and verified.
Shared cancellation settlement and explicit permission-provider provenance are
being repaired. Fixture commit `98a29fcf7` creates an isolated denied-target
parent before dispatch for local Copilot, Cursor and Pi. Its watcher retains
coverage failure reasons, parent identities and a bounded event journal; exact
native path correlation, complete observation and zero target mutation remain
required. All 80 focused fixture tests and fixture typecheck pass. Remote sealed
observers are unchanged. Paid runs are held until the corrected runtime is
identified and verified.
The Copilot cloud cell also retired all 46 observed local processes, restored
the host semaphore count from 321 to its baseline 310, removed its owned
@@ -164,7 +178,7 @@ mode values; all 56 recovery/lifecycle tests and the Runner TypeScript build
pass. The corrected-build Cursor cases above now pass the same strict validator.
The original failed receipt remains a failure.
The current macOS ARM64 pack is
The most recent paid macOS ARM64 pack is
`sha256:3940fbe1c7b197b964b8f8182c1f1ae548cff822a95ecb4539e0ca851b4f2032`.
The rebuilt Linux x64 pack is
`sha256:83702fae11105f5b9ee8f9731b86dc06becb12075f68bdc98dcdfe9f9c842473`,
@@ -7,7 +7,7 @@ import { COPILOT_VERSION, materializePinnedCopilotBinary, resolveCopilotDistribu
const MAX_ARCHIVE_BYTES = 128 * 1024 * 1024;
const MAX_EXPANDED_BYTES = 384 * 1024 * 1024;
const PROFILE_DIGEST = "sha256:ece77e40876631a69a828b91813722001d71b6fc81be47ecd4b3dced84ff9473";
const PROFILE_DIGEST = "sha256:0fe49c2f8a2b144344d0de745fed1e4568df305de3a26c3a121dec21c8d4b751";
/** Build-time only; outputRoot is the exact runner-owned platform asset directory. */
export async function buildPinnedCopilotDistribution({ outputRoot, platform = process.platform, architecture = process.arch }, { fetchImpl = fetch } = {}) {
@@ -28,7 +28,7 @@ export async function materializeCandidateProviderPack({ provider, outputRoot })
if (provider === "cursor") {
const result = await materializePinnedCursorDistribution({ destination: outputRoot });
return { version: result.version,
profileDigest: "sha256:aa8c0b2b84786982bcd06b7634bf95be6f2bc42bb8def48a8751dc50cf739b6b",
profileDigest: "sha256:377dcea64a727ce799cc112458d4b40ba4bc6574cd6c6f7233b6efd5917a6c4b",
closureDigest: `sha256:${result.closureSha256}` };
}
throw new Error(`The ${provider} candidate distribution builder is not included in this source revision`);
@@ -296,7 +296,7 @@ describe("NativeExecutionInputV1", () => {
})).toThrow("eventExpiryDays");
});
it.each([1, 2, 3, 4, 5, 6, 7, 8] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
it.each([1, 2, 3, 4, 5, 6, 7, 8, 9] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
const provider = {
kind: "acpx",
agent: "pi",
@@ -328,7 +328,7 @@ describe("NativeExecutionInputV1", () => {
profile: provider.profile,
});
expect(parseNativeExecutionInput(parsed)).toEqual(parsed);
for (const unsupportedVersion of [0, 9, 1.5, "8", null]) {
for (const unsupportedVersion of [0, 10, 1.5, "9", null]) {
expect(() => parseNativeExecutionInput({
...input,
session: { ...input.session, driverKind: "acpx_runtime" },
@@ -91,7 +91,7 @@ export interface NativeAcpxProfileSnapshot {
protocolVersion: 1;
acpxVersion: "0.13.1";
agent: NativeAcpxAgent;
agentProfileVersion: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
agentProfileVersion: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
agentServerPackage: string;
agentServerVersion: string;
agentRuntimePackage: string | null;
@@ -608,7 +608,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
|| profile.protocolVersion !== 1
|| profile.acpxVersion !== "0.13.1"
|| profile.agent !== provider.agent
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4 && profile.agentProfileVersion !== 5 && profile.agentProfileVersion !== 6 && profile.agentProfileVersion !== 7 && profile.agentProfileVersion !== 8)
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4 && profile.agentProfileVersion !== 5 && profile.agentProfileVersion !== 6 && profile.agentProfileVersion !== 7 && profile.agentProfileVersion !== 8 && profile.agentProfileVersion !== 9)
) {
throw new NativeExecutionInputError("input.provider.profile does not match the qualified ACPX v1 profile");
}
@@ -10,9 +10,10 @@ import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
vi.mock("./installation-integrity.js", () => ({ verifyNativeAcpxInstallation: vi.fn() }));
describe("Copilot build-owned installation", () => {
it("admits the current v5 declaration and binds its source policy/patch hashes", () => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v5-identity.json", import.meta.url), "utf8"));
it("admits the current v6 declaration and binds its source policy/patch hashes", () => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v6-identity.json", import.meta.url), "utf8"));
expect(identity.declaration.systemInstructionDelivery).toBe(COPILOT_SYSTEM_INSTRUCTION_DELIVERY);
expect(identity.declaration.sharedRuntimeContract).toBe("paperclip.acpx-runtime-contract.v1");
expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(identity.declaration.agentProfileVersion);
expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe(identity.commandDigest);
const sorted = Object.fromEntries(Object.entries(identity.declaration).sort(([a], [b]) => a.localeCompare(b)));
@@ -50,7 +51,7 @@ describe("Copilot build-owned installation", () => {
});
it("rejects changed profile identities before native file access", async () => {
vi.mocked(verifyNativeAcpxInstallation).mockClear();
for (const override of [{ agentServerVersion: "latest" }, { commandDigest: "sha256:untrusted" }, { agent: "cursor" }, { agentProfileVersion: 1 }, { agentProfileVersion: 2 }, { agentProfileVersion: 3 }, { agentProfileVersion: 4 }, { agentProfileVersion: 6 }]) {
for (const override of [{ agentServerVersion: "latest" }, { commandDigest: "sha256:untrusted" }, { agent: "cursor" }, { agentProfileVersion: 1 }, { agentProfileVersion: 2 }, { agentProfileVersion: 3 }, { agentProfileVersion: 4 }, { agentProfileVersion: 5 }, { agentProfileVersion: 7 }]) {
await expect(verifyCopilotInstallation({ ...QUALIFIED_ACPX_PROFILES.copilot, ...override } as never)).rejects.toThrow("exact pinned");
}
expect(verifyNativeAcpxInstallation).not.toHaveBeenCalled();
@@ -13,7 +13,7 @@ import { QUALIFIED_ACPX_PROFILES, type QualifiedAcpxProfile } from "./qualified-
const roots: string[] = [];
afterEach(async () => { vi.unstubAllEnvs(); await Promise.all(roots.splice(0).map(root => rm(root, { recursive: true, force: true }))); });
const candidate = (): QualifiedAcpxProfile => ({ ...QUALIFIED_ACPX_PROFILES.pi, agentProfileVersion: 8 });
const candidate = (): QualifiedAcpxProfile => ({ ...QUALIFIED_ACPX_PROFILES.pi, agentProfileVersion: 9 });
async function fixture() {
const root = await mkdtemp(join(tmpdir(), "pi-installation-test-")); roots.push(root);
@@ -30,8 +30,9 @@ describe("Pi installation factory", () => {
const hash = (bytes: string | Uint8Array) => createHash("sha256").update(bytes).digest("hex");
const canonical = (value: any): string => Array.isArray(value) ? `[${value.map(canonical).join(",")}]`
: value && typeof value === "object" ? `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}` : JSON.stringify(value);
const identity = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v8-identity.json", import.meta.url), "utf8"));
const identity = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v9-identity.json", import.meta.url), "utf8"));
const declaration = identity.declaration;
expect(declaration.sharedRuntimeContract).toBe("paperclip.acpx-runtime-contract.v1");
expect(identity.commandDigest).toBe(`sha256:${hash(canonical(declaration))}`);
expect(QUALIFIED_ACPX_PROFILES.pi.commandDigest).toBe(identity.commandDigest);
for (const key of ["agent", "agentProfileVersion", "acpxVersion", "agentServerVersion", "agentRuntimeVersion"] as const) {
@@ -56,13 +57,14 @@ describe("Pi installation factory", () => {
it("rejects legacy profiles and caller-selected identities", () => {
expect(() => assertPiInstallationProfile(candidate())).not.toThrow();
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 8");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 9");
expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 9");
for (const changed of [{ agentServerVersion: "latest" }, { commandDigest: `sha256:${"0".repeat(64)}` }, { reportedModelId: "different" }, { agentRuntimePackage: "ambient-pi" }]) {
expect(() => assertPiInstallationProfile({ ...candidate(), ...changed })).toThrow("trusted declaration");
}
@@ -24,7 +24,7 @@ function record(value: unknown): Record<string, unknown> {
/** Candidate identity is build-owned; model selection cannot substitute a CLI. */
export function assertPiInstallationProfile(profile: QualifiedAcpxProfile): void {
const trusted = QUALIFIED_ACPX_PROFILES.pi;
if (profile.agentProfileVersion !== 8) throw new Error("Pi rich ACP requires profile version 8; reopen the previous session");
if (profile.agentProfileVersion !== 9) throw new Error("Pi rich ACP requires profile version 9; reopen the previous session");
if (profile.agent !== "pi" || profile.driverKind !== trusted.driverKind || profile.protocolVersion !== trusted.protocolVersion || profile.acpxVersion !== trusted.acpxVersion || profile.agentServerPackage !== "pi-acp" || profile.agentServerVersion !== "0.0.33" || profile.agentRuntimePackage !== "@earendil-works/pi-coding-agent" || profile.agentRuntimeVersion !== "0.84.2" || profile.commandDigest !== trusted.commandDigest || profile.permissionPolicy !== "interactive" || profile.qualificationModel !== PI_MODEL || profile.reportedModelId !== PI_MODEL) throw new Error("Pi distribution profile differs from its trusted declaration");
}
@@ -63,10 +63,11 @@ describe("qualified ACPX profiles", () => {
});
});
it("binds Cursor v5 to inspectable native instructions, closures and exact ACPX guard patch", () => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/cursor-acp/profile-v5-identity.json", import.meta.url), "utf8"));
it("binds Cursor v6 to inspectable native instructions, closures and exact ACPX guard patch", () => {
const identity = JSON.parse(readFileSync(new URL("../../../test/fixtures/cursor-acp/profile-v6-identity.json", import.meta.url), "utf8"));
const distribution = JSON.parse(readFileSync(new URL("../../../cursor-distributions.json", import.meta.url), "utf8"));
expect(identity.declaration.distribution).toEqual(distribution);
expect(identity.declaration.sharedRuntimeContract).toBe("paperclip.acpx-runtime-contract.v1");
expect(identity.declaration.sessionModeAdmission).toBe("native-config-ack-recovery-bound-v1");
expect(identity.declaration.sessionModes).toEqual(["agent", "plan", "ask"]);
expect(identity.declaration.defaultSessionMode).toBe("agent");
@@ -47,24 +47,24 @@ export const QUALIFIED_ACPX_PROFILES: Readonly<
protocolVersion: ACPX_DRIVER_PROTOCOL_VERSION,
acpxVersion: QUALIFIED_ACPX_VERSION,
agent: "pi",
agentProfileVersion: 8,
agentProfileVersion: 9,
qualificationStatus: "pending",
agentServerPackage: "pi-acp",
agentServerVersion: "0.0.33",
agentRuntimePackage: "@earendil-works/pi-coding-agent",
agentRuntimeVersion: "0.84.2",
commandDigest:
"sha256:843d30e419914529755c9827d9151a306da1b50b643be7eab1abe797641a37ce",
"sha256:edf058835ee84de3869c4a8e8bdb71a934ffdb9f34daa37ae6faeb92371d1cdf",
qualificationModel: "openrouter/deepseek/deepseek-v4-flash-0731",
reportedModelId: "openrouter/deepseek/deepseek-v4-flash-0731",
permissionPolicy: "interactive",
},
cursor: {
driverKind: ACPX_DRIVER_KIND, protocolVersion: ACPX_DRIVER_PROTOCOL_VERSION,
acpxVersion: QUALIFIED_ACPX_VERSION, agent: "cursor", agentProfileVersion: 5,
acpxVersion: QUALIFIED_ACPX_VERSION, agent: "cursor", agentProfileVersion: 6,
agentServerPackage: "cursor-agent", agentServerVersion: "2026.09.26-dd393fe",
agentRuntimePackage: null, agentRuntimeVersion: null,
commandDigest: "sha256:aa8c0b2b84786982bcd06b7634bf95be6f2bc42bb8def48a8751dc50cf739b6b",
commandDigest: "sha256:377dcea64a727ce799cc112458d4b40ba4bc6574cd6c6f7233b6efd5917a6c4b",
// Authenticated discovery has not established a qualification model. Never
// turn this empty declaration into a default; callers must select an ID.
qualificationModel: "", reportedModelId: "", permissionPolicy: "interactive",
@@ -72,10 +72,10 @@ export const QUALIFIED_ACPX_PROFILES: Readonly<
},
copilot: {
driverKind: ACPX_DRIVER_KIND, protocolVersion: ACPX_DRIVER_PROTOCOL_VERSION,
acpxVersion: QUALIFIED_ACPX_VERSION, agent: "copilot", agentProfileVersion: 5,
acpxVersion: QUALIFIED_ACPX_VERSION, agent: "copilot", agentProfileVersion: 6,
agentServerPackage: "@github/copilot", agentServerVersion: "1.0.88",
agentRuntimePackage: null, agentRuntimeVersion: null,
commandDigest: "sha256:ece77e40876631a69a828b91813722001d71b6fc81be47ecd4b3dced84ff9473",
commandDigest: "sha256:0fe49c2f8a2b144344d0de745fed1e4568df305de3a26c3a121dec21c8d4b751",
// Authenticated discovery has not established a qualification model. Never
// turn this empty declaration into a default; callers must select an ID.
qualificationModel: "", reportedModelId: "", permissionPolicy: "interactive",
@@ -0,0 +1,30 @@
{
"commandDigest": "sha256:edf058835ee84de3869c4a8e8bdb71a934ffdb9f34daa37ae6faeb92371d1cdf",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "pi",
"agentProfileVersion": 9,
"acpxVersion": "0.13.1",
"agentServerVersion": "0.0.33",
"agentRuntimeVersion": "0.84.2",
"nodeVersion": "24.21.0",
"closure": {
"darwin-arm64": "0187153354338cf4919d213dd2977e1be1073ad3941c7b9b5c8a09c5b32828e4",
"darwin-x64": "22c593576e78edb27bbe6234aabd31a7d65f6be7503683f568d0730ebe6c75a0",
"linux-x64": "016ce3653bdfc1c7269e3bf7d4bf26e48b0d0b7d409fd153f3f1b882e986654d"
},
"wrapperSha256": "9fd9a685fb79b4f73a8dac08cbe360a6de87ffcb596cdaff1191e3ac57c24f56",
"helperSha256": "a19087c0af8fb3896fe3290280695f8f936574013bc4acde650f11f023f0f03b",
"extensionSha256": "2b74a4a2e6a42def3d9918a46732e0e1b8c618791d2b22970cfb3e9d285e55ad",
"agentFilesBinding": "registered-runtime-context-v1",
"nativeQuestions": "select-confirm-input-editor-v2",
"pathPolicy": "sdk-normalization-v1",
"mcpToolNames": "provider-safe-exact-source-v1",
"acpxPatchSha256": "79aad2d688b03362e8cfcbf7a08f78a8383869f6882a9c9ed66f1d18efb94f2b",
"nativeAssistantMessages": "sdk-boundaries-v1",
"nativeNotices": "session-extension-v1",
"messageProjectionSha256": "17d1e91b948dfa6a56e1905645add52b502041ad843c8d50a13b07956f22aadd",
"noticeProjectionSha256": "c619080ec72a577889c382d44ea9024b10efdb9d83e933c03bfdd8da79ede591",
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1"
}
}
@@ -0,0 +1,17 @@
{
"commandDigest": "sha256:0fe49c2f8a2b144344d0de745fed1e4568df305de3a26c3a121dec21c8d4b751",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "copilot",
"agentProfileVersion": 6,
"acpxVersion": "0.13.1",
"agentServerVersion": "1.0.88",
"protocolPolicyRevision": "copilot-agent-manual-v1",
"acpxPatchSha256": "79aad2d688b03362e8cfcbf7a08f78a8383869f6882a9c9ed66f1d18efb94f2b",
"policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3",
"distributionSourceSha256": "bb3dcf14972e82f6578c8250540cfbc6ec2ad953bea5d4f10d28c8b461011f3f",
"agentFilesBinding": "registered-runtime-context-v1",
"systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1",
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1"
}
}
@@ -0,0 +1,54 @@
{
"commandDigest": "sha256:377dcea64a727ce799cc112458d4b40ba4bc6574cd6c6f7233b6efd5917a6c4b",
"declaration": {
"schema": "paperclip.acpx_profile_declaration.v1",
"agent": "cursor",
"agentProfileVersion": 6,
"acpxVersion": "0.13.1",
"agentServerVersion": "2026.09.26-dd393fe",
"patchVersion": "paperclip-cursor-instructions-v2",
"distribution": {
"schema": "paperclip.cursor_distribution.v1",
"version": "2026.09.26-dd393fe",
"platforms": {
"darwin-arm64": {
"url": "https://downloads.cursor.com/lab/2026.09.26-dd393fe/darwin/arm64/agent-cli-package.tar.gz",
"archiveSha256": "538827d96a779bab854a865c8e42859e8e87db34b5f69770261b90fc8cfff191",
"closureSha256": "912a37edb67fd7737809c24049dce2db20b1330f98b15a6e43809f0e0943cb6c",
"executable": "node",
"entrypoint": "index.js",
"vendorClosureSha256": "77394184a89b0e7384971181da19c3c82d83a399b04e7944b3f94fe3d7e62b25"
},
"darwin-x64": {
"url": "https://downloads.cursor.com/lab/2026.09.26-dd393fe/darwin/x64/agent-cli-package.tar.gz",
"archiveSha256": "ed1771c44cbf0f8059c67cac0d939e6a60eb730bd066d7dc4cbb14de314cfb6e",
"closureSha256": "322f54c7bd533b202a311ce8e2c8916bfe0202790ef8a40fbb36bb89cbd506cb",
"executable": "node",
"entrypoint": "index.js",
"vendorClosureSha256": "7c8775160af74e5fb8d25a30e8412c1a6476b0661529078a686e5118fd1c5e5f"
},
"linux-x64": {
"url": "https://downloads.cursor.com/lab/2026.09.26-dd393fe/linux/x64/agent-cli-package.tar.gz",
"archiveSha256": "8085fd120f5c71f4eae7fea26a043718e5644e3071e4fab3220a0e58c51f9593",
"closureSha256": "a375fc771dfe86b9b24532f95956ec4b02c5f4f24d80f5db06565160eb3b672c",
"executable": "node",
"entrypoint": "index.js",
"vendorClosureSha256": "bf04ef8ea6a63191067a6b3e15139aa2c793d8419daab246aa3f0a012e1bbcd9"
}
},
"patchVersion": "paperclip-cursor-instructions-v2"
},
"agentFilesBinding": "registered-runtime-context-v1",
"instructionBinding": "native-global-rules-v1",
"instructionAdmission": "synchronous-protocol-guard-v1",
"acpxPatchSha256": "79aad2d688b03362e8cfcbf7a08f78a8383869f6882a9c9ed66f1d18efb94f2b",
"sessionModeAdmission": "native-config-ack-recovery-bound-v1",
"sessionModes": [
"agent",
"plan",
"ask"
],
"defaultSessionMode": "agent",
"sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1"
}
}
@@ -54,6 +54,7 @@ import {
} from "./native-harness-backup-stamp.js";
import { nativeRuntimeContextFixture } from "./runtime-context.test-fixture.js";
import { nativeToolContractFingerprintForTarget } from "./native-session-resume.js";
import * as nativeSessionResume from "./native-session-resume.js";
import { buildNativeHeartbeatPreparationSpans } from "./native-run-trace.js";
import { NativeRunnerOwnershipUnverifiedError } from "./native-runner-ownership.js";
import type { AdapterRuntimeEvent } from "../../adapters/index.js";
@@ -7061,7 +7062,7 @@ describe("native warm session supervision", () => {
});
});
it.each(["permission", "managed credential"])("replaces an idle warm provider session when its %s changes", async (change) => {
it.each(["permission", "managed credential", "ACPX runtime contract", "unchanged Codex runtime contract"])("checks warm owner compatibility for %s", async (change) => {
const firstClose = vi.fn(async () => undefined);
const secondClose = vi.fn(async () => undefined);
const firstSession = { close: firstClose };
@@ -7069,7 +7070,9 @@ describe("native warm session supervision", () => {
const base = {
...execution,
schema: "paperclip.native-execution-input.v4",
provider: { kind: "codex", model: null, approvalPolicy: "never" },
provider: change === "ACPX runtime contract"
? { kind: "acpx", agent: "claude", model: "claude-sonnet-5", permissionMode: "approve-all" }
: { kind: "codex", model: null, approvalPolicy: "never" },
binding: {
...execution.binding,
runId: "run-permission-never",
@@ -7083,7 +7086,7 @@ describe("native warm session supervision", () => {
},
session: {
normalizedSessionId: "session-warm-permission",
driverKind: "codex_app_server" as const,
driverKind: change === "ACPX runtime contract" ? "acpx_runtime" as const : "codex_app_server" as const,
protocolVersion: 1 as const,
lifecyclePolicy: { mode: "warm" as const, idleTimeoutMs: 20 },
},
@@ -7106,6 +7109,12 @@ describe("native warm session supervision", () => {
highestContiguousSourceSeq: 1,
usage: null,
};
const runtimeChange = change === "ACPX runtime contract" || change === "unchanged Codex runtime contract";
const keepsOwner = change === "unchanged Codex runtime contract";
const currentRuntimeContract = nativeSessionResume.nativeRuntimeContractForProvider(base.provider);
const contract = runtimeChange
? vi.spyOn(nativeSessionResume, "nativeRuntimeContractForProvider").mockReturnValue(undefined)
: undefined;
state.execute
.mockReset()
.mockImplementationOnce(async (options) => {
@@ -7114,8 +7123,8 @@ describe("native warm session supervision", () => {
return result;
})
.mockImplementationOnce(async (options) => {
expect(options.existingSession).toBeUndefined();
options.onSession?.(secondSession);
expect(options.existingSession).toBe(keepsOwner ? firstSession : undefined);
if (!keepsOwner) options.onSession?.(secondSession);
return result;
});
@@ -7129,21 +7138,22 @@ describe("native warm session supervision", () => {
managedAiCredentialIdentity: "first-identity",
runnerInstanceId: "runner",
});
contract?.mockReturnValue(currentRuntimeContract);
await executePaperclipNativeSession({
db: leaseDb(lowered),
execution: lowered,
managedAiCredentialIdentity: change === "managed credential" ? "second-identity" : "first-identity",
runnerInstanceId: "runner",
});
expect(firstClose).toHaveBeenCalledWith({
reason: "warm native session configuration changed",
});
if (keepsOwner) expect(firstClose).not.toHaveBeenCalled();
else expect(firstClose).toHaveBeenCalledWith({ reason: "warm native session configuration changed" });
expect(secondClose).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(20);
expect(secondClose).toHaveBeenCalledWith({
expect(keepsOwner ? firstClose : secondClose).toHaveBeenCalledWith({
reason: "warm native session idle timeout",
});
} finally {
contract?.mockRestore();
vi.useRealTimers();
}
});
@@ -138,7 +138,7 @@ import {
renderNativeRunnerStagedAttachmentPrompt,
stageNativeRunnerWakeAttachments,
} from "./native-runner-file-handoff.js";
import { nativeToolContractFingerprintForTarget } from "./native-session-resume.js";
import { nativeRuntimeContractForProvider, nativeToolContractFingerprintForTarget } from "./native-session-resume.js";
import { verifyRetainedMaintenanceNoLaunch } from "./native-maintenance-no-launch.js";
import { registerRunnerPrpAuthority } from "../../realtime/runner-prp-ws.js";
import { connectRunnerPrpIngress } from "../../realtime/runner-prp-outbound.js";
@@ -5210,6 +5210,7 @@ function nativeSessionConfigDigest(
// without newly required tools.
nativeToolContractFingerprint:
nativeToolContractFingerprintForTarget(executionTargetKind),
runtimeContract: nativeRuntimeContractForProvider(execution.provider),
}),
)
.digest("hex")}`;
@@ -10,6 +10,16 @@ import { parseNativeExecutionInput } from "../../vendor/paperclip-runner/index.j
export type NativeToolExecutionTargetKind = "local" | "remote";
/** Fence live ACPX owners when shared runtime semantics change, even if the
* underlying native executable/profile did not change. Undefined preserves
* the existing configuration digest for other provider transports.
*/
export function nativeRuntimeContractForProvider(
provider: NativeExecutionInput["provider"],
): string | undefined {
return provider.kind === "acpx" ? "paperclip.acpx-runtime-contract.v1" : undefined;
}
/**
* Persisted provider threads retain their dynamic-tool declarations. This
* fingerprint is part of checkpoint compatibility and must change whenever
+9
View File
@@ -1310,6 +1310,15 @@ See [Copilot native protection](./FIXTURES.md#copilot-native-protection) for the
expected cancelled negative test, finite attached-process oracle, evidence limits,
and required rebuilt runtime. Registration is not a qualification claim.
Local human-denial cases for Copilot, Cursor and Pi create a fresh fixture-owned
`pc-denied-*` directory before dispatch and watch its parent identity throughout
the attempt. This keeps unrelated workspace startup writes outside the target
watch. Exact prompt/native target correlation, complete watcher coverage and
zero target mutations remain required. Evidence includes coverage failure
reasons, parent device/inode changes and a bounded timestamped event journal;
an absent final file cannot hide an incomplete watch or transient mutation.
Remote cases retain their existing sealed observers.
### Cursor native interactions (candidate)