mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-13 13:38:01 +02:00
Admit current ACP profiles and verify cross-language pin agreement
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
25de20d702
commit
ec0991d4ba
4 files changed
+43
-6
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { QUALIFIED_ACPX_PROFILES } from "../drivers/acpx/qualified-profiles.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { buildNativeModelEnvelope, parseNativeExecutionInput, NATIVE_EXECUTION_INPUT_SCHEMA, type NativeExecutionInputV1 } from "./native-execution.js";
|
||||
@@ -280,7 +281,7 @@ describe("NativeExecutionInputV1", () => {
|
||||
})).toThrow("eventExpiryDays");
|
||||
});
|
||||
|
||||
it.each([1, 2, 3, 4, 5] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
|
||||
it.each([1, 2, 3, 4, 5, 6] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
|
||||
const provider = {
|
||||
kind: "acpx",
|
||||
agent: "pi",
|
||||
@@ -312,7 +313,7 @@ describe("NativeExecutionInputV1", () => {
|
||||
profile: provider.profile,
|
||||
});
|
||||
expect(parseNativeExecutionInput(parsed)).toEqual(parsed);
|
||||
for (const unsupportedVersion of [0, 6, 1.5, "5", null]) {
|
||||
for (const unsupportedVersion of [0, 7, 1.5, "6", null]) {
|
||||
expect(() => parseNativeExecutionInput({
|
||||
...input,
|
||||
session: { ...input.session, driverKind: "acpx_runtime" },
|
||||
@@ -332,6 +333,26 @@ describe("NativeExecutionInputV1", () => {
|
||||
})).toThrow("does not match");
|
||||
});
|
||||
|
||||
it.each(Object.values(QUALIFIED_ACPX_PROFILES))(
|
||||
"admits the current $agent profile through the native execution boundary",
|
||||
(declaration) => {
|
||||
const { qualificationModel, reportedModelId: _reported, permissionPolicy,
|
||||
modelPolicy: _modelPolicy, qualificationStatus: _status, ...profile } = declaration;
|
||||
const provider = {
|
||||
kind: "acpx", agent: declaration.agent,
|
||||
model: qualificationModel || "explicit-provider-model",
|
||||
permissionPolicy, profile,
|
||||
};
|
||||
const parsed = parseNativeExecutionInput({
|
||||
...input,
|
||||
session: { ...input.session, driverKind: "acpx_runtime" },
|
||||
provider,
|
||||
});
|
||||
expect(parsed.provider).toEqual(provider);
|
||||
expect(parseNativeExecutionInput(parsed)).toEqual(parsed);
|
||||
},
|
||||
);
|
||||
|
||||
it("defaults legacy lifecycle state to per-turn and validates warm timeouts", () => {
|
||||
const legacy = structuredClone(input) as Record<string, unknown>;
|
||||
delete (legacy.session as Record<string, unknown>).lifecyclePolicy;
|
||||
|
||||
@@ -90,7 +90,7 @@ export interface NativeAcpxProfileSnapshot {
|
||||
protocolVersion: 1;
|
||||
acpxVersion: "0.13.1";
|
||||
agent: NativeAcpxAgent;
|
||||
agentProfileVersion: 1 | 2 | 3 | 4 | 5;
|
||||
agentProfileVersion: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
agentServerPackage: string;
|
||||
agentServerVersion: string;
|
||||
agentRuntimePackage: string | null;
|
||||
@@ -596,7 +596,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
|
||||
|| profile.protocolVersion !== 1
|
||||
|| profile.acpxVersion !== "0.13.1"
|
||||
|| profile.agent !== provider.agent
|
||||
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4 && profile.agentProfileVersion !== 5)
|
||||
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4 && profile.agentProfileVersion !== 5 && profile.agentProfileVersion !== 6)
|
||||
) {
|
||||
throw new NativeExecutionInputError("input.provider.profile does not match the qualified ACPX v1 profile");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
import {
|
||||
QUALIFIED_ACPX_PROFILES,
|
||||
@@ -6,6 +7,21 @@ import {
|
||||
} from "./qualified-profiles.js";
|
||||
|
||||
describe("qualified ACPX profiles", () => {
|
||||
it.each(Object.values(QUALIFIED_ACPX_PROFILES))(
|
||||
"keeps the Rust admission digest synchronized with the current $agent profile",
|
||||
(profile) => {
|
||||
const rust = readFileSync(new URL("../../../runner/crates/runner-core/src/acpx_provider_backend.rs", import.meta.url), "utf8");
|
||||
const start = rust.indexOf(`"${profile.agent}" => (`);
|
||||
expect(start).toBeGreaterThan(0);
|
||||
const end = rust.indexOf("\n ),", start);
|
||||
expect(end).toBeGreaterThan(start);
|
||||
const admission = rust.slice(start, end);
|
||||
expect(admission).toContain(JSON.stringify(profile.agentServerPackage));
|
||||
expect(admission).toContain(JSON.stringify(profile.agentServerVersion));
|
||||
expect(admission).toContain(JSON.stringify(profile.commandDigest));
|
||||
},
|
||||
);
|
||||
|
||||
it("binds each agent to one immutable package and model declaration", () => {
|
||||
for (const agent of ["pi", "claude", "codex", "grok"] as const) {
|
||||
const profile = QUALIFIED_ACPX_PROFILES[agent];
|
||||
|
||||
@@ -2,7 +2,7 @@ export const QUALIFIED_ACPX_VERSION = "0.13.1" as const;
|
||||
export const ACPX_DRIVER_KIND = "acpx_runtime" as const;
|
||||
export const ACPX_DRIVER_PROTOCOL_VERSION = 1 as const;
|
||||
|
||||
import type { NativeAcpxAgent } from "../../contracts/native-execution.js";
|
||||
import type { NativeAcpxAgent, NativeAcpxProfileSnapshot } from "../../contracts/native-execution.js";
|
||||
|
||||
export type QualifiedAcpxAgent = NativeAcpxAgent;
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface QualifiedAcpxProfile {
|
||||
readonly protocolVersion: typeof ACPX_DRIVER_PROTOCOL_VERSION;
|
||||
readonly acpxVersion: typeof QUALIFIED_ACPX_VERSION;
|
||||
readonly agent: QualifiedAcpxAgent;
|
||||
readonly agentProfileVersion: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
readonly agentProfileVersion: NativeAcpxProfileSnapshot["agentProfileVersion"];
|
||||
readonly qualificationStatus?: "pending";
|
||||
readonly modelPolicy?: "explicit-provider-verified";
|
||||
/** Wire identity: an npm package name or a runner-owned builtin: identifier. */
|
||||
|
||||
Reference in new issue
Block a user