mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-10 03:08:10 +02:00
Declare and validate bound ACP permission request envelopes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
c8fc6047f3
commit
170652d2d4
7 files changed
+408
-3
No files matched your search
@@ -90,7 +90,7 @@
|
||||
{
|
||||
"path": "schemas/request.schema.json",
|
||||
"id": "https://paperclip.dev/schemas/prp/v1/request.schema.json",
|
||||
"sha256": "c8d00e10150c90963f19641c075f50da7f67617dd817f41b356287ad85c53f2b"
|
||||
"sha256": "985a3b0a6ae36ae5ad4cf7d708dc401db7c108f683a81cd44a03093fc4ca7862"
|
||||
},
|
||||
{
|
||||
"path": "schemas/result.schema.json",
|
||||
|
||||
@@ -3,6 +3,181 @@
|
||||
"$id": "https://paperclip.dev/schemas/prp/v1/request.schema.json",
|
||||
"title": "PRP request",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema",
|
||||
"requestKind",
|
||||
"requestId",
|
||||
"type",
|
||||
"status",
|
||||
"prompt",
|
||||
"choices",
|
||||
"origin",
|
||||
"turnId"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "paperclip.runtime_request.v2"
|
||||
},
|
||||
"requestKind": {
|
||||
"const": "permission_approval"
|
||||
},
|
||||
"requestId": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"type": {
|
||||
"const": "permission"
|
||||
},
|
||||
"status": {
|
||||
"enum": [
|
||||
"pending",
|
||||
"resolved",
|
||||
"expired",
|
||||
"cancelled"
|
||||
]
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 4000
|
||||
},
|
||||
"choices": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 4,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"label"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"enum": [
|
||||
"accept",
|
||||
"accept_for_session",
|
||||
"decline",
|
||||
"cancel"
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "accept"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
},
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "accept_for_session"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
},
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "decline"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
},
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "cancel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"origin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"adapter"
|
||||
],
|
||||
"properties": {
|
||||
"adapter": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"turnId": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 240
|
||||
},
|
||||
"itemId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"maxLength": 240
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["schema", "requestKind", "requestId", "type", "status", "prompt", "input"],
|
||||
|
||||
@@ -9,6 +9,13 @@ function request(kinds = ["allow_once", "allow_always", "reject_once"]): AcpPerm
|
||||
} } as AcpPermissionRequest;
|
||||
}
|
||||
describe("ACP permission normalization", () => {
|
||||
it("keeps requests answerable when a provider omits its operation title", () => {
|
||||
for (const title of [undefined, "", " "]) {
|
||||
const value = request();
|
||||
value.raw.toolCall.title = title;
|
||||
expect(normalizeAcpxPermission(value).title).toBe("Approve provider operation");
|
||||
}
|
||||
});
|
||||
it("offers only native choices and preserves cancellation", () => {
|
||||
const value = normalizeAcpxPermission(request(["allow_once"]));
|
||||
expect(value.choices.map(c => c.key)).toEqual(["accept", "cancel"]);
|
||||
|
||||
@@ -47,7 +47,8 @@ export function normalizeAcpxPermission(request: AcpPermissionRequest, options:
|
||||
throw new Error("ACP permission request omitted its tool identity");
|
||||
}
|
||||
return {
|
||||
title: typeof call.title === "string" ? call.title.slice(0, 4_000) : "Approve provider operation",
|
||||
title: typeof call.title === "string" && call.title.trim()
|
||||
? call.title.slice(0, 4_000) : "Approve provider operation",
|
||||
kind: request.inferredKind ?? "other",
|
||||
toolCallId: call.toolCallId,
|
||||
choices,
|
||||
|
||||
@@ -3002,6 +3002,181 @@ export const requestSchema = {
|
||||
"$id": "https://paperclip.dev/schemas/prp/v1/request.schema.json",
|
||||
"title": "PRP request",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"schema",
|
||||
"requestKind",
|
||||
"requestId",
|
||||
"type",
|
||||
"status",
|
||||
"prompt",
|
||||
"choices",
|
||||
"origin",
|
||||
"turnId"
|
||||
],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"const": "paperclip.runtime_request.v2"
|
||||
},
|
||||
"requestKind": {
|
||||
"const": "permission_approval"
|
||||
},
|
||||
"requestId": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"type": {
|
||||
"const": "permission"
|
||||
},
|
||||
"status": {
|
||||
"enum": [
|
||||
"pending",
|
||||
"resolved",
|
||||
"expired",
|
||||
"cancelled"
|
||||
]
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 4000
|
||||
},
|
||||
"choices": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 4,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key",
|
||||
"label"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"enum": [
|
||||
"accept",
|
||||
"accept_for_session",
|
||||
"decline",
|
||||
"cancel"
|
||||
]
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "accept"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
},
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "accept_for_session"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
},
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "decline"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
},
|
||||
{
|
||||
"contains": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {
|
||||
"const": "cancel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"minContains": 0,
|
||||
"maxContains": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"details": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"origin": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"adapter"
|
||||
],
|
||||
"properties": {
|
||||
"adapter": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"method": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"turnId": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 240
|
||||
},
|
||||
"itemId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"maxLength": 240
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
import equalModule from "ajv/dist/runtime/equal.js";
|
||||
import ucs2LengthModule from "ajv/dist/runtime/ucs2length.js";
|
||||
|
||||
"use strict";export const fixtureValidator: unknown = validate20;const schema31 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/fixture.schema.json","title":"PRP scripted replay fixture","type":"object","required":["schema","fixtureVersion","protocolVersion","name","description","identity","capabilities","commands","events","result"],"properties":{"schema":{"const":"paperclip.prp.fixture.v1"},"fixtureVersion":{"const":1},"protocolVersion":{"enum":[1,2]},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":1000},"identity":{"$ref":"https://paperclip.dev/schemas/prp/v1/identity.schema.json"},"capabilities":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/capabilities.schema.json"}]},"commands":{"type":"array","items":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/command.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/command.schema.json"}]}},"events":{"type":"array","minItems":1,"items":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/event.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/event.schema.json"}]}},"requests":{"type":"array","items":{"$ref":"https://paperclip.dev/schemas/prp/v1/request.schema.json"}},"result":{"$ref":"https://paperclip.dev/schemas/prp/v1/result.schema.json"}},"additionalProperties":true};const schema41 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json","title":"PRP negotiated capabilities","type":"object","required":["schema","sessionReusePolicy","driver","steer","interrupt","resume","runtimeRequests","structuredResult","typedEvents"],"properties":{"schema":{"const":"paperclip.prp.capabilities.v1"},"sessionReusePolicy":{"enum":["new_per_run","reuse_per_issue","reuse_per_workspace"]},"driver":{"type":"object","required":["kind","version"],"properties":{"kind":{"type":"string","minLength":1,"maxLength":80},"version":{"type":"string","minLength":1,"maxLength":80}},"additionalProperties":true},"steer":{"type":"boolean"},"interrupt":{"type":"boolean"},"resume":{"type":"boolean"},"runtimeRequests":{"type":"boolean"},"structuredResult":{"type":"boolean"},"typedEvents":{"type":"boolean"},"typedEventFamilies":{"type":"array","maxItems":64,"items":{"type":"object","required":["family","version","availability","detailLevel"],"properties":{"family":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.-]*$"},"version":{"const":1},"availability":{"enum":["available","unsupported","policy_disabled"]},"detailLevel":{"enum":["summary","structured"]}},"additionalProperties":false}},"semanticTools":{"type":"object","required":["schema","schemaVersion","operations"],"properties":{"schema":{"const":"paperclip.prp.semantic_tools.v1"},"schemaVersion":{"const":1},"operations":{"type":"array","items":{"type":"object","required":["operationId","version","availability","redactionDisposition"],"properties":{"operationId":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"},"version":{"const":1},"availability":{"enum":["available","denied","unsupported"]},"redactionDisposition":{"const":"digest_only"},"requiredClaims":{"type":"array","items":{"type":"string","minLength":1,"maxLength":160},"uniqueItems":true},"reasonCode":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"}},"additionalProperties":true}}},"additionalProperties":true},"unsupported":{"type":"array","items":{"type":"string","minLength":1},"uniqueItems":true}},"additionalProperties":true};const schema45 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/command.schema.json","title":"PRP runner command","type":"object","required":["schema","commandId","controllerSeq","type","issuedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.command.v1"},"commandId":{"type":"string","minLength":1,"maxLength":160},"controllerSeq":{"type":"integer","minimum":1},"type":{"enum":["run.prepare","run.attach","session.open","turn.start","turn.steer","turn.interrupt","turn.stop","request.resolve","interaction.receipt","semantic_tool.result","session.snapshot","session.close","session.budget.increase","session.destroy","run.cancel","runner.drain","runner.suspend","runner.shutdown"]},"issuedAt":{"type":"string","format":"date-time"},"deadlineAt":{"type":"string","format":"date-time"},"precondition":{"type":"object","properties":{"runnerState":{"type":"array","items":{"type":"string"}},"runState":{"type":"array","items":{"type":"string"}},"sessionState":{"type":"array","items":{"type":"string"}},"activeTurnId":{"type":["string","null"]}},"additionalProperties":true},"payload":{"type":"object","additionalProperties":true}},"additionalProperties":true};const schema46 = {"$schema":"https://json-schema.org/draft/2020-1
Line truncated
|
||||
"use strict";export const fixtureValidator: unknown = validate20;const schema31 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/fixture.schema.json","title":"PRP scripted replay fixture","type":"object","required":["schema","fixtureVersion","protocolVersion","name","description","identity","capabilities","commands","events","result"],"properties":{"schema":{"const":"paperclip.prp.fixture.v1"},"fixtureVersion":{"const":1},"protocolVersion":{"enum":[1,2]},"name":{"type":"string","minLength":1,"maxLength":120},"description":{"type":"string","minLength":1,"maxLength":1000},"identity":{"$ref":"https://paperclip.dev/schemas/prp/v1/identity.schema.json"},"capabilities":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/capabilities.schema.json"}]},"commands":{"type":"array","items":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/command.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/command.schema.json"}]}},"events":{"type":"array","minItems":1,"items":{"oneOf":[{"$ref":"https://paperclip.dev/schemas/prp/v1/event.schema.json"},{"$ref":"https://paperclip.dev/schemas/prp/v2/event.schema.json"}]}},"requests":{"type":"array","items":{"$ref":"https://paperclip.dev/schemas/prp/v1/request.schema.json"}},"result":{"$ref":"https://paperclip.dev/schemas/prp/v1/result.schema.json"}},"additionalProperties":true};const schema41 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/capabilities.schema.json","title":"PRP negotiated capabilities","type":"object","required":["schema","sessionReusePolicy","driver","steer","interrupt","resume","runtimeRequests","structuredResult","typedEvents"],"properties":{"schema":{"const":"paperclip.prp.capabilities.v1"},"sessionReusePolicy":{"enum":["new_per_run","reuse_per_issue","reuse_per_workspace"]},"driver":{"type":"object","required":["kind","version"],"properties":{"kind":{"type":"string","minLength":1,"maxLength":80},"version":{"type":"string","minLength":1,"maxLength":80}},"additionalProperties":true},"steer":{"type":"boolean"},"interrupt":{"type":"boolean"},"resume":{"type":"boolean"},"runtimeRequests":{"type":"boolean"},"structuredResult":{"type":"boolean"},"typedEvents":{"type":"boolean"},"typedEventFamilies":{"type":"array","maxItems":64,"items":{"type":"object","required":["family","version","availability","detailLevel"],"properties":{"family":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.-]*$"},"version":{"const":1},"availability":{"enum":["available","unsupported","policy_disabled"]},"detailLevel":{"enum":["summary","structured"]}},"additionalProperties":false}},"semanticTools":{"type":"object","required":["schema","schemaVersion","operations"],"properties":{"schema":{"const":"paperclip.prp.semantic_tools.v1"},"schemaVersion":{"const":1},"operations":{"type":"array","items":{"type":"object","required":["operationId","version","availability","redactionDisposition"],"properties":{"operationId":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]*$"},"version":{"const":1},"availability":{"enum":["available","denied","unsupported"]},"redactionDisposition":{"const":"digest_only"},"requiredClaims":{"type":"array","items":{"type":"string","minLength":1,"maxLength":160},"uniqueItems":true},"reasonCode":{"type":"string","minLength":1,"maxLength":160,"pattern":"^[a-z][a-z0-9_.:-]*$"}},"additionalProperties":true}}},"additionalProperties":true},"unsupported":{"type":"array","items":{"type":"string","minLength":1},"uniqueItems":true}},"additionalProperties":true};const schema45 = {"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://paperclip.dev/schemas/prp/v1/command.schema.json","title":"PRP runner command","type":"object","required":["schema","commandId","controllerSeq","type","issuedAt","payload"],"properties":{"schema":{"const":"paperclip.prp.command.v1"},"commandId":{"type":"string","minLength":1,"maxLength":160},"controllerSeq":{"type":"integer","minimum":1},"type":{"enum":["run.prepare","run.attach","session.open","turn.start","turn.steer","turn.interrupt","turn.stop","request.resolve","interaction.receipt","semantic_tool.result","session.snapshot","session.close","session.budget.increase","session.destroy","run.cancel","runner.drain","runner.suspend","runner.shutdown"]},"issuedAt":{"type":"string","format":"date-time"},"deadlineAt":{"type":"string","format":"date-time"},"precondition":{"type":"object","properties":{"runnerState":{"type":"array","items":{"type":"string"}},"runState":{"type":"array","items":{"type":"string"}},"sessionState":{"type":"array","items":{"type":"string"}},"activeTurnId":{"type":["string","null"]}},"additionalProperties":true},"payload":{"type":"object","additionalProperties":true}},"additionalProperties":true};const schema46 = {"$schema":"https://json-schema.org/draft/2020-1
Line truncated
|
||||
@@ -0,0 +1,47 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user