import { describe, expect, it } from "vitest"; import { createIssueThreadInteractionSchema } from "./validators/issue.js"; describe("issue thread interaction schemas", () => { it("parses request_confirmation payloads with default no-wake continuation", () => { const parsed = createIssueThreadInteractionSchema.parse({ kind: "request_confirmation", payload: { version: 1, prompt: "Apply this plan?", acceptLabel: "Apply", rejectLabel: "Revise", rejectRequiresReason: true, rejectReasonLabel: "What needs to change?", declineReasonPlaceholder: "Optional: tell the agent what you'd change.", detailsMarkdown: "The current plan document will be accepted as-is.", supersedeOnUserComment: true, }, }); expect(parsed).toMatchObject({ kind: "request_confirmation", continuationPolicy: "none", payload: { prompt: "Apply this plan?", acceptLabel: "Apply", rejectLabel: "Revise", rejectRequiresReason: true, rejectReasonLabel: "What needs to change?", allowDeclineReason: true, declineReasonPlaceholder: "Optional: tell the agent what you'd change.", supersedeOnUserComment: true, }, }); }); it("accepts issue document targets for request_confirmation interactions", () => { const parsed = createIssueThreadInteractionSchema.parse({ kind: "request_confirmation", continuationPolicy: "wake_assignee_on_accept", payload: { version: 1, prompt: "Accept the latest plan revision?", allowDeclineReason: false, target: { type: "issue_document", issueId: "11111111-1111-4111-8111-111111111111", documentId: "22222222-2222-4222-8222-222222222222", key: "plan", revisionId: "33333333-3333-4333-8333-333333333333", revisionNumber: 2, label: "Plan v2", href: "/issues/PAP-123#document-plan", }, }, }); expect(parsed.kind).toBe("request_confirmation"); if (parsed.kind !== "request_confirmation") return; expect(parsed.payload.target).toMatchObject({ type: "issue_document", key: "plan", revisionNumber: 2, label: "Plan v2", href: "/issues/PAP-123#document-plan", }); }); it("accepts custom targets for request_confirmation interactions", () => { const parsed = createIssueThreadInteractionSchema.parse({ kind: "request_confirmation", payload: { version: 1, prompt: "Proceed with the external checklist?", target: { type: "custom", key: "external-checklist", revisionId: "checklist-v1", revisionNumber: 1, label: "Checklist v1", href: "https://example.com/checklist", }, }, }); expect(parsed.kind).toBe("request_confirmation"); if (parsed.kind !== "request_confirmation") return; expect(parsed.payload.target).toMatchObject({ type: "custom", key: "external-checklist", label: "Checklist v1", }); }); it("rejects unsafe request_confirmation target hrefs", () => { const base = { kind: "request_confirmation", payload: { version: 1, prompt: "Proceed?", target: { type: "custom", key: "external-checklist", revisionId: "checklist-v1", label: "Checklist v1", }, }, } as const; for (const href of ["javascript:alert(1)", "data:text/html,hi", "//evil.example/path"]) { expect(() => createIssueThreadInteractionSchema.parse({ ...base, payload: { ...base.payload, target: { ...base.payload.target, href, }, }, })).toThrow("href must not use javascript:, data:, or protocol-relative URLs"); } }); });