Files
PaperClipAI/packages/paperclip-runner/src/protocol/permission-request.test.ts
T
DottaandPaperclip 24beb00575 feat(runner): add rich ACP transport and durable interaction foundation (#14430)
Add shared rich ACP transport, durable questions and permissions, verified provider packaging, and bounded activity and plan presentation. Keep Cursor, Copilot, and Pi pending their separate provider qualification.

Persist interaction settlement before publication, fence failed writes until fresh recovery, and preserve owned-process cleanup. Incorporate reviewed mainline integration with extended harness coverage.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-29 08:56:21 -05:00

48 lines
2.1 KiB
TypeScript

import Ajv2020 from "ajv/dist/2020.js";
import { describe, expect, it } from "vitest";
import { questionSetSchema, requestSchema } from "./generated/schema-bundle.js";
const ajv = new Ajv2020({ allErrors: true, strict: true, strictRequired: false });
ajv.addSchema(questionSetSchema);
const validate = ajv.compile(requestSchema);
const permission = {
schema: "paperclip.runtime_request.v2",
requestKind: "permission_approval",
requestId: "permission-1",
type: "permission",
status: "pending",
prompt: "Allow the proposed edit?",
choices: [{ key: "accept", label: "Allow once" }, { key: "cancel", label: "Cancel" }],
details: { toolCallId: "edit-1", kind: "edit" },
origin: { adapter: "acpx-runtime-sidecar", provider: "acpx", method: "session/request_permission" },
turnId: "turn-1",
itemId: null,
};
describe("canonical ACP permission request schema", () => {
it("admits the durable Rust projection and direct-driver item identity", () => {
expect(validate(permission), JSON.stringify(validate.errors)).toBe(true);
expect(validate({ ...permission, itemId: "permission-1" })).toBe(true);
});
it("rejects unknown, duplicate and unbounded approval choices", () => {
for (const choices of [
[],
[{ key: "accept_always", label: "Allow forever" }],
[{ key: "accept", label: "Once" }, { key: "accept", label: "Different label" }],
[{ key: "accept", label: "x".repeat(501) }],
[{ key: "accept", label: "Once", permissionPolicy: "full-auto" }],
]) expect(validate({ ...permission, choices })).toBe(false);
});
it("requires turn and origin binding and preserves the separate input contract", () => {
const { turnId: _turn, ...withoutTurn } = permission;
const { origin: _origin, ...withoutOrigin } = permission;
expect(validate(withoutTurn)).toBe(false);
expect(validate(withoutOrigin)).toBe(false);
expect(validate({ ...permission, type: "input" })).toBe(false);
expect(validate({ ...permission, requestKind: "runtime" })).toBe(false);
expect(validate({ ...permission, resolverPolicy: "human_only" })).toBe(false);
});
});