Verify Pi setup with its pinned runtime and preserve account choices

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-21 15:52:27 -05:00
1 parent 4c2c98f6db
commit 8104d32a06
8 files changed
+50 -15

No files matched your search

@@ -2259,10 +2259,3 @@ export async function probeAcpxClaudeInstallation(model: string): Promise<void>
export async function probeAcpxGrokInstallation(model: string): Promise<void> {
await verifyQualifiedAcpxInstallation(resolveQualifiedAcpxProfile("grok", model));
}
/** Verify the pinned Pi closure and snapshot lease without launching a provider. */
export async function probeAcpxPiInstallation(model: string): Promise<void> {
const installation = await verifyQualifiedAcpxInstallation(resolveQualifiedAcpxProfile("pi", model));
const lease = await installation.openCommand();
await lease.close();
}
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { assertAcpxProfileEnvironment, classifyAcpxProfileError } from "./profile-installation.js";
import { describe, expect, it, vi } from "vitest";
import { assertAcpxProfileEnvironment, classifyAcpxProfileError, probeAcpxPiInstallation } from "./profile-installation.js";
describe("Cursor actionable admission errors", () => {
it("requires an explicit bound credential and supports both company-secret names", () => {
@@ -24,3 +24,16 @@ describe("Cursor actionable admission errors", () => {
expect(classifyAcpxProfileError("codex", new Error("unauthorized"))).toBeNull();
});
});
const piProbe = vi.hoisted(() => ({ verify: vi.fn(), open: vi.fn(), close: vi.fn() }));
vi.mock("./pi-installation.js", () => ({ verifyPiInstallation: piProbe.verify }));
it("probes Pi through its pinned distribution and closes the verified snapshot lease", async () => {
piProbe.verify.mockResolvedValue({ openCommand: piProbe.open });
piProbe.open.mockResolvedValue({ close: piProbe.close });
await probeAcpxPiInstallation("openrouter/anthropic/claude-sonnet-4.5");
expect(piProbe.verify).toHaveBeenCalledWith(expect.objectContaining({ agent: "pi", qualificationModel: "openrouter/anthropic/claude-sonnet-4.5" }));
expect(piProbe.close).toHaveBeenCalledTimes(1);
piProbe.verify.mockRejectedValueOnce(new Error("Pi distribution failed verification"));
await expect(probeAcpxPiInstallation("openrouter/anthropic/claude-sonnet-4.5")).rejects.toThrow("failed verification");
expect(piProbe.close).toHaveBeenCalledTimes(1);
});
@@ -65,3 +65,8 @@ export async function probeAcpxCursorInstallation(model: string): Promise<void>
const installation = await verifyAcpxProfileInstallation(resolveQualifiedAcpxProfile("cursor", model));
await (await installation.openCommand()).close();
}
export async function probeAcpxPiInstallation(model: string): Promise<void> {
const installation = await verifyAcpxProfileInstallation(resolveQualifiedAcpxProfile("pi", model));
await (await installation.openCommand()).close();
}
+2 -1
View File
@@ -91,7 +91,8 @@ export * from "./semantic-tools/index.js";
export * as acceptedCapabilitySemanticTools from "./semantic-tools/index.js";
export * from "./compatibility.js";
export { probeAcpxClaudeInstallation, probeAcpxGrokInstallation, probeAcpxPiInstallation } from "./drivers/acpx/installation-integrity.js";
export { probeAcpxClaudeInstallation, probeAcpxGrokInstallation } from "./drivers/acpx/installation-integrity.js";
export { probeAcpxPiInstallation } from "./drivers/acpx/profile-installation.js";
export { bundledRemoteProviderPackManifestPath, bundledRemoteRunnerBinary } from "./live/bundled-remote-provider-pack.js";
export { RunnerdMuseDriver, describeRunnerdMuseDriver, type RunnerdMuseDriverOptions } from "./drivers/muse/runnerd-muse-driver.js";
+2 -2
View File
@@ -6,9 +6,9 @@ export * from "./runnerd-codex-transport.js";
export * from "./turn-stream.js";
export * from "./linux-process-start.js";
export { probeAcpxClaudeInstallation, probeAcpxGrokInstallation, probeAcpxPiInstallation } from "../drivers/acpx/installation-integrity.js";
export { probeAcpxClaudeInstallation, probeAcpxGrokInstallation } from "../drivers/acpx/installation-integrity.js";
export { probeCopilotMetadata, validateCopilotMetadata, type CopilotMetadataResult } from "../drivers/copilot-metadata-probe.js";
export { probeAcpxCursorInstallation } from "../drivers/acpx/profile-installation.js";
export { probeAcpxCursorInstallation, probeAcpxPiInstallation } from "../drivers/acpx/profile-installation.js";
export { bundledRemoteProviderPackManifestPath, bundledRemoteRunnerBinary } from "./bundled-remote-provider-pack.js";
@@ -161,3 +161,22 @@ it("offers Pi account selection before choosing its required model", async () =>
await click("Connect another account");
expect(document.body.textContent).toContain("Advanced provider setup");
});
it.each(["openai", "anthropic", "google", "openrouter"] as const)("keeps a matching saved Pi %s API-key account selectable", async provider => {
const saved = account({ provider, method: "api_key", name: "Pi provider account", status: "connected" });
mocks.list.mockResolvedValue({ currentUserId: "owner", connections: [saved], canManageConnections: true });
flushSync(() => root.render(<QueryClientProvider client={client}>
<AiConnectionField companyId="company" agentId="agent" agentName="Pi" adapterType="pi_local" model={`${provider}/provider-model`}
value={{ provider, method: "api_key", mode: "delegated", connectionId: saved.id, grantId: saved.grantId }} onChange={onChange} />
</QueryClientProvider>));
await settle();
expect(container.textContent).toContain("Pi provider account");
expect(container.textContent).not.toContain("incompatible");
expect(container.textContent).not.toContain("does not support");
const trigger = container.querySelector('[role="combobox"][aria-label="Connection"]')!;
flushSync(() => trigger.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowDown", bubbles: true })));
await settle();
const choice = [...document.querySelectorAll<HTMLElement>('[role="option"]')].find(node => node.textContent?.includes("Pi provider account"));
expect(choice).toBeDefined();
expect(choice?.getAttribute("aria-disabled")).not.toBe("true");
});
@@ -145,6 +145,7 @@ export function AiConnectionField({
</label>}
{value?.mode !== "router" && <AiConnectionSelect
adapterType={adapterType}
model={model}
requirement={{ companyId, provider }}
connections={accounts.data?.connections ?? []}
value={value}
@@ -41,7 +41,8 @@ export function AiConnectionSelect({
onReconnect,
onChange,
adapterType,
}: AiConnectionPickerProps & { adapterType: string }) {
model,
}: AiConnectionPickerProps & { adapterType: string; model?: string }) {
const selectId = useId();
const personal = personalAiDefault(connections, requirement, currentUserId);
const selected =
@@ -50,6 +51,8 @@ export function AiConnectionSelect({
: connections.find(
(c) => c.id === value?.connectionId && c.grantId === value?.grantId,
);
const compatibilityModel = (provider: string) => adapterType === "pi_local"
? model || `${provider}/` : provider === "openrouter" ? "openrouter/" : undefined;
const compatible = connections
.filter(
(c) =>
@@ -57,7 +60,7 @@ export function AiConnectionSelect({
isAiConnectionCompatible(
c,
adapterType,
c.provider === "openrouter" ? "openrouter/" : undefined,
compatibilityModel(c.provider),
),
)
.sort(
@@ -80,7 +83,7 @@ export function AiConnectionSelect({
mode: "responsible_user",
},
adapterType,
requirement.provider === "openrouter" ? "openrouter/" : undefined,
compatibilityModel(requirement.provider),
);
const incompatible =
selected && !compatible.some((c) => c.id === selected.id);