@kontourai/survey 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ const VALID_ACTIONS = new Set(["affirmed-control", "typed"]);
2
+ /**
3
+ * Validates an `authorizing` block on a ReviewOutcome for admissibility.
4
+ *
5
+ * Per-kind required fields:
6
+ * explicit-statement — `statement` (string, non-empty)
7
+ * exchange — `prompt` and `response` (both strings, non-empty;
8
+ * both halves required for self-contained testimony)
9
+ * authorized-action — `promptRef`, `renderedPrompt`, `action`, and
10
+ * `authorityRef` (all required; action must be
11
+ * "affirmed-control" or "typed")
12
+ *
13
+ * Returns an empty array when the block is valid.
14
+ */
15
+ export function validateAuthorizing(block) {
16
+ if (block === null || typeof block !== "object" || Array.isArray(block)) {
17
+ return [{ code: "not-an-object", message: "authorizing block must be a plain object." }];
18
+ }
19
+ const record = block;
20
+ if (!("kind" in record) || record.kind === undefined) {
21
+ return [{ code: "missing-kind", message: "authorizing block is missing the required 'kind' field." }];
22
+ }
23
+ const kind = record.kind;
24
+ if (kind === "explicit-statement") {
25
+ return validateExplicitStatement(record);
26
+ }
27
+ if (kind === "exchange") {
28
+ return validateExchange(record);
29
+ }
30
+ if (kind === "authorized-action") {
31
+ return validateAuthorizedAction(record);
32
+ }
33
+ return [{
34
+ code: "unknown-kind",
35
+ message: `authorizing kind '${String(kind)}' is not admissible. Use 'explicit-statement', 'exchange', or 'authorized-action'.`,
36
+ }];
37
+ }
38
+ function validateExplicitStatement(block) {
39
+ const issues = [];
40
+ if (!block.statement || typeof block.statement !== "string" || block.statement.trim() === "") {
41
+ issues.push({
42
+ code: "missing-statement",
43
+ message: "explicit-statement authorizing block requires a non-empty 'statement' string.",
44
+ });
45
+ }
46
+ return issues;
47
+ }
48
+ function validateExchange(block) {
49
+ const issues = [];
50
+ if (!block.prompt || typeof block.prompt !== "string" || block.prompt.trim() === "") {
51
+ issues.push({
52
+ code: "missing-prompt",
53
+ message: "exchange authorizing block requires a non-empty 'prompt' string (both halves required for self-contained testimony).",
54
+ });
55
+ }
56
+ if (!block.response || typeof block.response !== "string" || block.response.trim() === "") {
57
+ issues.push({
58
+ code: "missing-response",
59
+ message: "exchange authorizing block requires a non-empty 'response' string (both halves required for self-contained testimony).",
60
+ });
61
+ }
62
+ return issues;
63
+ }
64
+ function validateAuthorizedAction(block) {
65
+ const issues = [];
66
+ if (!block.promptRef || typeof block.promptRef !== "string" || block.promptRef.trim() === "") {
67
+ issues.push({
68
+ code: "missing-prompt-ref",
69
+ message: "authorized-action authorizing block requires a non-empty 'promptRef' string.",
70
+ });
71
+ }
72
+ if (!block.renderedPrompt || typeof block.renderedPrompt !== "string" || block.renderedPrompt.trim() === "") {
73
+ issues.push({
74
+ code: "missing-rendered-prompt",
75
+ message: "authorized-action authorizing block requires a non-empty 'renderedPrompt' string.",
76
+ });
77
+ }
78
+ if (block.action === undefined) {
79
+ issues.push({
80
+ code: "missing-action",
81
+ message: "authorized-action authorizing block requires an 'action' field.",
82
+ });
83
+ }
84
+ else if (!VALID_ACTIONS.has(block.action)) {
85
+ issues.push({
86
+ code: "invalid-action",
87
+ message: `authorized-action 'action' must be 'affirmed-control' or 'typed'; received '${String(block.action)}'.`,
88
+ });
89
+ }
90
+ if (!block.authorityRef || typeof block.authorityRef !== "string" || block.authorityRef.trim() === "") {
91
+ issues.push({
92
+ code: "missing-authority-ref",
93
+ message: "authorized-action authorizing block requires a non-empty 'authorityRef' string linking an AuthorityTrace.",
94
+ });
95
+ }
96
+ return issues;
97
+ }
98
+ /**
99
+ * Type guard: returns true if a ReviewAuthorizing block passes all validation checks.
100
+ */
101
+ export function isValidAuthorizing(block) {
102
+ return validateAuthorizing(block).length === 0;
103
+ }
104
+ /**
105
+ * Helper for consumers building `authorized-action` authorizing blocks outside
106
+ * the workbench. Constructs the block and validates it; throws if the result
107
+ * would be invalid so callers catch configuration errors at build time.
108
+ *
109
+ * For workbench-internal construction, use the workbench path directly — it
110
+ * runs validateAuthorizing and degrades gracefully instead of throwing.
111
+ */
112
+ export function buildAuthorizedActionAuthorizing(input) {
113
+ const block = {
114
+ kind: "authorized-action",
115
+ promptRef: input.promptRef,
116
+ renderedPrompt: input.renderedPrompt,
117
+ action: input.action,
118
+ authorityRef: input.authorityRef,
119
+ };
120
+ const issues = validateAuthorizedAction(block);
121
+ if (issues.length > 0) {
122
+ throw new Error(`buildAuthorizedActionAuthorizing: invalid authorized-action block: ${issues.map((issue) => issue.message).join(" ")}`);
123
+ }
124
+ return block;
125
+ }
@@ -1,4 +1,4 @@
1
- import type { CandidateSetStatus, ClaimTarget, Extraction, RawSource, ReviewOutcome } from "./types.js";
1
+ import type { CandidateSetStatus, ClaimTarget, Extraction, RawSource, ReviewAuthorizing, ReviewOutcome } from "./types.js";
2
2
  export declare const reviewResourceApiVersion = "survey.kontourai.io/v1alpha1";
3
3
  export type ReviewResourceApiVersion = typeof reviewResourceApiVersion;
4
4
  export type ReviewResourceKind = "ReviewItem" | "ReviewDecision" | "ReviewSession" | "ReviewSessionEvent";
@@ -102,6 +102,10 @@ export interface ReviewDecisionSpec {
102
102
  evidenceIds?: string[];
103
103
  withinComfortZone?: boolean;
104
104
  comfortZoneNote?: string;
105
+ /** Optional testimony provenance. Populated by the workbench on the
106
+ * `authorized-action` channel; consumers on other channels may supply
107
+ * their own admissible block. */
108
+ authorizing?: ReviewAuthorizing;
105
109
  projection?: SurveyRecordProjectionHint;
106
110
  }
107
111
  export interface ReviewDecisionStatus {
@@ -0,0 +1,2 @@
1
+ export declare const REVIEW_WORKBENCH_CSS: string;
2
+ export default REVIEW_WORKBENCH_CSS;