From f8bbeeb4c38805d1ab44ad40dcbc562f3b581892 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 5 Oct 2026 13:21:49 -0500 Subject: [PATCH 1/2] test: carry verified Pi prerequisite CI corrections upstream Align adapter-section admission expectations with the declared source. Preserve the deliberately killed attempt at 500 ms while giving the resumed successful fixture its existing downstream 5-second budget. Preserve the original CI failures and all state, usage and deduplication assertions. Co-Authored-By: Paperclip --- packages/paperclip-runner/src/live/live-session.test.ts | 3 +++ ui/src/adapters/config-sections.test.tsx | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/paperclip-runner/src/live/live-session.test.ts b/packages/paperclip-runner/src/live/live-session.test.ts index cbb9d7b2202..64ac83dd7a2 100644 --- a/packages/paperclip-runner/src/live/live-session.test.ts +++ b/packages/paperclip-runner/src/live/live-session.test.ts @@ -1813,6 +1813,9 @@ describe("Capability live runnerd and Codex session", () => { sessionId: binding.sessionId, attemptId: "attempt-resumed", resumeOf: "attempt-killed", + // Only the killed attempt deliberately uses the short timeout. Allow + // durable disk writes to complete during the resumed successful turn. + turnTimeoutMs: 5_000, }); expect(resumed.snapshot().providerThreadId).toBe(state.threadId); expect(resumed.snapshot().attempts).toMatchObject([ diff --git a/ui/src/adapters/config-sections.test.tsx b/ui/src/adapters/config-sections.test.tsx index 5903e75e40d..67ae731a453 100644 --- a/ui/src/adapters/config-sections.test.tsx +++ b/ui/src/adapters/config-sections.test.tsx @@ -75,7 +75,9 @@ describe("adapter configuration sections", () => { const policy = renderSection(CodexLocalConfigFields, "paperclip_runner", "runPolicy", config); expect(adapter).toContain(''); - expect(adapter).toContain(''); + expect(adapter).toContain(''); + expect(adapter).toContain(''); + expect(adapter).toContain(''); expect(adapter).not.toContain("Runner lifecycle"); expect(policy).toContain("Runner lifecycle"); expect(policy).not.toContain("ACP agent"); From 32b5e275d56c5070de5a8ecdbec59c78b49a5f68 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 5 Oct 2026 13:37:35 -0500 Subject: [PATCH 2/2] 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 fc9e474a9b4..629b7768037 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 ddd655de7f5..29b3a76d179 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 9fe15ad220d..63e554ec8cc 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 3296f525a53..71f9e762c9d 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" },