mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-12 22:39:44 +01:00
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>
90 lines
6.0 KiB
TypeScript
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();
|
|
}
|