From 654bc5aa21f1554d30cd8e53782b04ced8a16652 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 27 Oct 2026 20:40:15 -0500 Subject: [PATCH] fix(runner): label credential validation before configuration Co-Authored-By: Paperclip --- packages/paperclip-runner/acpx-profiles.json | 2 +- .../src/generated_acpx_profiles.rs | 2 +- .../drivers/acpx/copilot-installation.test.ts | 2 +- .../src/drivers/acpx/generated-profiles.ts | 2 +- .../src/drivers/acpx/runtime-host.test.ts | 22 +++++++++++++++++++ .../src/drivers/acpx/runtime-host.ts | 2 ++ .../copilot-profile-v70-identity.json | 4 ++-- 7 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/paperclip-runner/acpx-profiles.json b/packages/paperclip-runner/acpx-profiles.json index 00309cb963e..1df6ae1c2de 100644 --- a/packages/paperclip-runner/acpx-profiles.json +++ b/packages/paperclip-runner/acpx-profiles.json @@ -37,7 +37,7 @@ "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41", + "commandDigest": "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", "requiresProviderPolicy": true }, "claude": { diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs b/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs index bb9e2991f06..d5ed6013196 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs @@ -47,7 +47,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: None, agent_runtime_version: None, command_digest: - "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41", + "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", requires_provider_policy: true, }, "claude" => AcpxReleaseProfile { diff --git a/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts b/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts index 33bbff8409e..0a9c647d407 100644 --- a/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts @@ -89,7 +89,7 @@ describe("Copilot build-owned installation", () => { it("admits the current profile with environment launch preparation", () => { expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(70); expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined(); - expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41"); + expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc"); }); it("preserves v69 while binding closed startup diagnostics in v70", async () => { diff --git a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts index 09aeea2965b..b42655d2f28 100644 --- a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts +++ b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts @@ -53,7 +53,7 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41", + "commandDigest": "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", "permissionPolicy": "interactive" }, "claude": { diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts index bc488a6a7e6..8d14bb2f9da 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.test.ts @@ -203,6 +203,28 @@ afterEach(async () => { }); describe("ACPX runtime host", () => { + it.each(["copilot", "cursor"] as const)("reports missing %s credentials as credential admission", async agent => { + const fixture = await hostFixture(); + const openRuntime = vi.fn(async () => runtimePort()); + const failure = await AcpxRuntimeHost.open({ ...fixture.options, agent, model:"gpt-5.6-sol", + permissionMode:"approve-all",environment:{} }, fixture.dependencies({openRuntime})).catch(error => error); + expect(failure).toMatchObject({code:agent === "copilot" ? "COPILOT_AUTH_REQUIRED" : "CURSOR_CREDENTIALS_MISSING"}); + expect(acpxSidecarErrorCode(failure)).toBe("ACPX_ADMISSION_CREDENTIAL"); + expect(openRuntime).not.toHaveBeenCalled(); + expect(fixture.commandClose).not.toHaveBeenCalled(); + }); + it("retains binding admission when the selected workspace is missing", async () => { + const fixture = await hostFixture(); + await rm(fixture.options.workingDirectory,{recursive:true}); + const openRuntime = vi.fn(async () => runtimePort()); + const dependencies = fixture.dependencies({openRuntime}); + const verifyInstallation = vi.fn(dependencies.verifyInstallation!); + const failure = await AcpxRuntimeHost.open({ ...fixture.options,agent:"codex",model:"gpt-5.6-sol", + permissionMode:"approve-all" }, {...dependencies,verifyInstallation}).catch(error => error); + expect(acpxSidecarErrorCode(failure)).toBe("ACPX_ADMISSION_BINDING"); + expect(verifyInstallation).not.toHaveBeenCalled(); + expect(openRuntime).not.toHaveBeenCalled(); + }); it("retains the admission stage when provider classification replaces the error", async () => { const fixture = await hostFixture(); const openRuntime = vi.fn(async () => { throw new Error("Unauthorized private-token-canary"); }); diff --git a/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts b/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts index f9658074522..38b39f80593 100644 --- a/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts +++ b/packages/paperclip-runner/src/drivers/acpx/runtime-host.ts @@ -526,7 +526,9 @@ export class AcpxRuntimeHost { }, ); options.signal?.throwIfAborted(); + admissionStage = "credential"; assertAcpxProfileEnvironment(options.agent, sandbox.launchEnvironment); + admissionStage = "configuration"; let launchEnvironment = sandbox.launchEnvironment; // This copy is registered by the authenticated controller for its company, // agent and run. Environment/config values cannot widen the grant. Each diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json index 40463e44999..8908e2f0fb1 100644 --- a/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v70-identity.json @@ -1,5 +1,5 @@ { - "commandDigest": "sha256:754e74c58bc9b22b49d38e0b9ff0a4708caff46abf6ac531975ea2d91c27de41", + "commandDigest": "sha256:618c3b69bc98b9a7e3c1b3411700eab8b90fd9245ba35d0bc85484319247fdbc", "declaration": { "schema": "paperclip.acpx_profile_declaration.v1", "agent": "copilot", @@ -77,7 +77,7 @@ "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", - "runtimeHostSourceSha256": "be14218ad4477e083cfc32f6680fcfed776056a4084615d6b0efd1e521e66232", + "runtimeHostSourceSha256": "c779acab9ae4bbab4a13002d2e12c8c1acb208d0c4b575e7c5697fc3e7bb44c3", "runtimeAdapterSourceSha256": "7c47c05306ebe7d1ca6b5d046a96f5726a48adec4d27e7c16788f505f25b5db2", "runtimeSandboxSourceSha256": "981449a4c6d10f682ecf86d834eed07faca30a7d9b419fb8db4973a442a343ec", "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca",