From 32b5e275d56c5070de5a8ecdbec59c78b49a5f68 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 5 Oct 2026 13:37:35 -0500 Subject: [PATCH] Carry existing Pi admission assertions and package-local probe import Keep Pi source assertions consistent with the held qualification candidate. Use the existing vendored runtime boundary for installed probes. All four changes already exist downstream; no final shipping input or profile pin changes. Co-Authored-By: Paperclip --- .../src/cli/acpx-runtime-sidecar.test.ts | 6 ++-- server/src/adapters/registry.test.ts | 20 ++++++++++-- server/src/adapters/registry.ts | 2 +- .../native-runtime/runtime-mode.test.ts | 32 +++++++++++++------ 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts b/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts index fc9e474a9b..629b776803 100644 --- a/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts +++ b/packages/paperclip-runner/src/cli/acpx-runtime-sidecar.test.ts @@ -1002,7 +1002,10 @@ describe("qualified ACPX runtime sidecar", () => { }); }); - it.each([["claude", "claude-sonnet-5"]])( + it.each([ + ["claude", "claude-sonnet-5"], + ["pi", "openrouter/deepseek/deepseek-v4-flash-0731"], + ])( "reports the qualified %s profile", async (agent, model) => { const sidecar = startSidecar(); @@ -1019,7 +1022,6 @@ describe("qualified ACPX runtime sidecar", () => { ); it.each([ - ["pi", "openrouter/deepseek/deepseek-v4-flash-0731"], ["cursor", "explicit-cursor-model"], ["copilot", "explicit-copilot-model"], ] as const)("initializes the declared %s candidate without promoting its profile", async (agent, model) => { diff --git a/server/src/adapters/registry.test.ts b/server/src/adapters/registry.test.ts index ddd655de7f..29b3a76d17 100644 --- a/server/src/adapters/registry.test.ts +++ b/server/src/adapters/registry.test.ts @@ -4,13 +4,15 @@ import { listServerAdapters, requireServerAdapter } from "./registry.js"; import * as executionTarget from "@paperclipai/adapter-utils/execution-target"; import { BUILTIN_ADAPTER_TYPES } from "./builtin-adapter-types.js"; -const { probeInstallation, probeGrokInstallation } = vi.hoisted(() => ({ +const { probeInstallation, probeGrokInstallation, probePiInstallation } = vi.hoisted(() => ({ probeInstallation: vi.fn(), probeGrokInstallation: vi.fn(), + probePiInstallation: vi.fn(), })); -vi.mock("@paperclipai/paperclip-runner/live", () => ({ +vi.mock("../vendor/paperclip-runner/index.js", () => ({ probeAcpxClaudeInstallation: probeInstallation, probeAcpxGrokInstallation: probeGrokInstallation, + probeAcpxPiInstallation: probePiInstallation, })); // The registry registers a login capability for the two built-in interactive @@ -95,6 +97,7 @@ describe("native ACPX environment checks", () => { beforeEach(() => { probeInstallation.mockReset().mockResolvedValue(undefined); probeGrokInstallation.mockReset().mockResolvedValue(undefined); + probePiInstallation.mockReset().mockResolvedValue(undefined); }); afterEach(() => vi.restoreAllMocks()); @@ -131,6 +134,19 @@ describe("native ACPX environment checks", () => { expect(probeInstallation).not.toHaveBeenCalled(); }); + it.each([true, false])("checks qualified Pi's own installation readiness (%s)", async (ready) => { + if (!ready) probePiInstallation.mockRejectedValueOnce(new Error("Pi installation integrity mismatch")); + const model = "openrouter/deepseek/deepseek-v4-flash-0731"; + const result = await requireServerAdapter("paperclip_runner").testEnvironment!({ + ...context, + config: { provider: "acpx", acpxAgent: "pi", model }, + }); + expect(result.status).toBe(ready ? "pass" : "fail"); + expect(probePiInstallation).toHaveBeenCalledWith(model); + expect(probeInstallation).not.toHaveBeenCalled(); + expect(probeGrokInstallation).not.toHaveBeenCalled(); + }); + it("does not use the host platform to reject a remote environment", async () => { vi.spyOn(process, "platform", "get").mockReturnValue("darwin"); const result = await requireServerAdapter("paperclip_runner").testEnvironment!({ diff --git a/server/src/adapters/registry.ts b/server/src/adapters/registry.ts index 9fe15ad220..63e554ec8c 100644 --- a/server/src/adapters/registry.ts +++ b/server/src/adapters/registry.ts @@ -432,7 +432,7 @@ const paperclipRunnerAdapter: ServerAdapterModule = { message: "The remote platform is supported. Runtime package integrity and readiness must still be verified by the remote runner before launch." }], }; } - const { probeAcpxClaudeInstallation, probeAcpxGrokInstallation, probeAcpxPiInstallation } = await import("@paperclipai/paperclip-runner/live"); + const { probeAcpxClaudeInstallation, probeAcpxGrokInstallation, probeAcpxPiInstallation } = await import("../vendor/paperclip-runner/index.js"); await (profile.acpxAgent === "pi" ? probeAcpxPiInstallation : profile.acpxAgent === "grok" ? probeAcpxGrokInstallation : probeAcpxClaudeInstallation)(profile.model); return { diff --git a/server/src/services/native-runtime/runtime-mode.test.ts b/server/src/services/native-runtime/runtime-mode.test.ts index 3296f525a5..71f9e762c9 100644 --- a/server/src/services/native-runtime/runtime-mode.test.ts +++ b/server/src/services/native-runtime/runtime-mode.test.ts @@ -109,6 +109,20 @@ describe("resolveNativeRuntimeMode", () => { }); }); + it("admits qualified Pi without enabling pending ACPX candidates", () => { + expect(resolveNativeRuntimeMode({ + ...eligible, + adapterConfig: { + provider: "acpx", + acpxAgent: "pi", + model: "openrouter/deepseek/deepseek-v4-flash-0731", + }, + })).toMatchObject({ + kind: "native", + profile: { backend: "acpx_runtime" }, + }); + }); + it("rejects malformed OpenCode and unqualified ACPX profiles", () => { expect(() => resolveNativeRuntimeMode({ ...eligible, @@ -116,16 +130,14 @@ describe("resolveNativeRuntimeMode", () => { })).toThrow(expect.objectContaining({ code: "paperclip_runner_opencode_model_invalid", })); - expect(() => resolveNativeRuntimeMode({ - ...eligible, - adapterConfig: { - provider: "acpx", - acpxAgent: "pi", - model: "openrouter/deepseek/deepseek-v4-flash-0731", - }, - })).toThrow(expect.objectContaining({ - code: "paperclip_runner_acpx_agent_unavailable", - })); + for (const acpxAgent of ["cursor", "copilot"]) { + expect(() => resolveNativeRuntimeMode({ + ...eligible, + adapterConfig: { provider: "acpx", acpxAgent, model: "explicit-provider-model" }, + })).toThrow(expect.objectContaining({ + code: "paperclip_runner_acpx_agent_unavailable", + })); + } expect(() => resolveNativeRuntimeMode({ ...eligible, adapterConfig: { provider: "acpx", acpxAgent: "claude", model: "claude-opus-5" },