Files
PaperClipAI/packages/paperclip-runner/src/drivers/acpx/profile-installation.ts
T
DottaandPaperclip b866fa4bdc feat(runner): add pinned Kimi Code native runtime (#18028)
Add the pinned Kimi Code runtime, native instruction and model admission, scoped credentials, and pending-candidate recovery contracts. Retain current master release attestations and provider-pack compatibility.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-30 21:58:12 -05:00

90 lines
6.0 KiB
TypeScript

import { verifyKimiInstallation } from "./kimi-installation.js";
import { assertAcpxExecutionAdmission, type AcpxCandidateStartupAdmission } from "./candidate-admission.js";
import { assertCopilotCredentials, classifyCopilotFailure } from "./copilot-profile.js";
import { verifyCopilotInstallation } from "./copilot-installation.js";
import { verifyCursorInstallation } from "./cursor-installation.js";
import { verifyAntigravityInstallation } from "./antigravity-installation.js";
import { assertCursorWorkspacePolicy } from "./cursor-launch-policy.js";
import { resolveQualifiedAcpxProfile, type QualifiedAcpxAgent, type QualifiedAcpxProfile } from "./qualified-profiles.js";
import { verifyPiInstallation } from "./pi-installation.js";
import { verifyQualifiedAcpxInstallation, type VerifiedAcpxInstallation } from "./installation-integrity.js";
/** Closed build-owned registry. Provider branches add their pinned installations here. */
export async function verifyAcpxProfileInstallation(profile: QualifiedAcpxProfile, candidateAdmission?: AcpxCandidateStartupAdmission): Promise<VerifiedAcpxInstallation> {
assertAcpxExecutionAdmission(profile, candidateAdmission);
if (profile.agent === "antigravity") return verifyAntigravityInstallation(profile, candidateAdmission);
if (profile.agent === "kimi") return verifyKimiInstallation(profile);
if (profile.agent === "pi") return verifyPiInstallation(profile);
if (profile.agent === "copilot") return verifyCopilotInstallation(profile);
if (profile.agent === "cursor") {
try { return await verifyCursorInstallation(profile); }
catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") throw Object.assign(new Error("Cursor runtime assets are missing. Run paperclipai runtime setup cursor on this execution host."), { code: "CURSOR_RUNTIME_MISSING", retryable: false });
throw error;
}
}
if (profile.agent !== "claude" && profile.agent !== "codex" && profile.agent !== "grok") {
throw new Error(`ACPX ${profile.agent} verified candidate distribution is not installed in this build`);
}
return verifyQualifiedAcpxInstallation(profile);
}
/** Provider policy admission is repeated immediately before each process launch. */
export async function assertAcpxProfileWorkspace(agent: QualifiedAcpxAgent, workspace: string): Promise<void> {
if (agent === "cursor") await assertCursorWorkspacePolicy(workspace);
}
/** Called with the sanitized launch environment, never ambient process.env. */
export function assertAcpxProfileEnvironment(agent: QualifiedAcpxAgent, environment: Readonly<NodeJS.ProcessEnv>): void {
if (agent === "copilot") assertCopilotCredentials(environment);
if (agent === "kimi" && !environment.KIMI_API_KEY?.trim()) {
throw Object.assign(new Error("Kimi Code credentials are missing. Bind a company secret to KIMI_API_KEY in the agent environment."), { code: "KIMI_CREDENTIALS_MISSING", retryable: false });
}
if (agent === "antigravity" && !environment.GEMINI_API_KEY?.trim() && !environment.PAPERCLIP_ACPX_ANTIGRAVITY_AUTH_JSON_SECRET?.trim()) {
throw Object.assign(new Error("Google Antigravity requires an explicitly bound Google runtime connection. Connect a Google account or Gemini API key before starting a task."), { code: "ANTIGRAVITY_CREDENTIALS_MISSING", retryable: false });
}
if (agent === "cursor" && !environment.CURSOR_API_KEY?.trim() && !environment.CURSOR_AUTH_TOKEN?.trim()) {
throw Object.assign(new Error("Cursor credentials are missing. Bind a company secret to CURSOR_API_KEY or CURSOR_AUTH_TOKEN in the agent environment."), { code: "CURSOR_CREDENTIALS_MISSING", retryable: false });
}
}
/** Provider admission diagnostics expose no raw provider strings or credentials. */
export function classifyAcpxProfileError(agent: QualifiedAcpxAgent, error: unknown): Error | null {
if (agent === "copilot") {
const failure = classifyCopilotFailure(error);
if (failure.code === "COPILOT_REQUEST_FAILED") return null;
return Object.assign(new Error(failure.message), { code: failure.code, retryable: false });
}
if (agent !== "cursor" || !(error instanceof Error)) return null;
const message = error.message;
let code: string;
let detail: string;
if (/usage limit|quota exceeded|insufficient credits|entitlement|subscription required|upgrade (?:your|the) plan/i.test(message)) {
code = "CURSOR_ENTITLEMENT_UNAVAILABLE";
detail = "Cursor declined access because of account entitlement or quota. Check the subscription, selected model access, and account spending cap.";
} else if (/invalid (?:api key|auth(?:entication)? token)|authentication failed|unauthenticated|unauthorized|not authenticated/i.test(message)) {
code = "CURSOR_AUTHENTICATION_FAILED";
detail = "Cursor rejected the bound credential. Update the company secret used by CURSOR_API_KEY or CURSOR_AUTH_TOKEN.";
} else if (/(?:model.*(?:not found|unavailable|unsupported|not available|not allowed)|(?:unknown|invalid) model|effective model mismatch)/i.test(message)) {
code = "CURSOR_MODEL_UNAVAILABLE";
detail = "Cursor could not use the explicitly selected model. Verify its exact identifier and account access, then select a supported model explicitly.";
} else return null;
return Object.assign(new Error(detail), { code, retryable: false });
}
/** Probe trusted installed bytes without credentials or a billable prompt. */
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();
}
export async function probeAcpxKimiInstallation(model: string): Promise<void> {
const installation = await verifyAcpxProfileInstallation(resolveQualifiedAcpxProfile("kimi", model));
await (await installation.openCommand()).close();
}