@kontourai/survey 0.5.1 → 0.5.2
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.
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +1 -0
- package/dist/src/review-authorizing.d.ts +39 -0
- package/dist/src/review-authorizing.js +125 -0
- package/dist/src/review-resource.d.ts +5 -1
- package/dist/src/review-workbench/review-workbench.js +58 -0
- package/dist/src/types.d.ts +23 -0
- package/package.json +1 -1
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CandidateSetStatus, Candidate, CandidateSet, ClaimTarget, EscalationDimension, EscalationRecord, Extraction, Interpretation, LocatorScheme, RawSource, RawSourceKind, ReviewOutcome, ReviewStatus, SurveyInput, } from "./types.js";
|
|
1
|
+
export type { CandidateSetStatus, Candidate, CandidateSet, ClaimTarget, EscalationDimension, EscalationRecord, Extraction, Interpretation, LocatorScheme, RawSource, RawSourceKind, ReviewAuthorizing, ReviewAuthorizingAuthorizedAction, ReviewAuthorizingExchange, ReviewAuthorizingExplicitStatement, ReviewAuthorizingKind, ReviewOutcome, ReviewStatus, SurveyInput, } from "./types.js";
|
|
2
2
|
export { reviewResourceApiVersion } from "./review-resource.js";
|
|
3
3
|
export type { CandidateRole, ClaimTargetHint, ExtractionReference, ResourceEnvelope, ResourceMetadata, ReviewActor, ReviewCandidate, ReviewDecision, ReviewDecisionSpec, ReviewDecisionStatus, ReviewItem, ReviewItemSpec, ReviewItemStatus, ReviewLocator, ReviewResource, ReviewResourceApiVersion, ReviewResourceKind, ReviewSession, ReviewSessionEvent, ReviewSessionEventSpec, ReviewSessionEventStatus, ReviewSessionEventType, ReviewSessionSpec, ReviewSessionStatus, SourceReference, SurveyRecordProjectionHint, } from "./review-resource.js";
|
|
4
4
|
export { candidateReviewRecord, SurveyInputBuilder } from "./builder.js";
|
|
@@ -31,3 +31,5 @@ export { referenceUtteranceExtractor, surveyAgentUtterance, utteranceToSurveyInp
|
|
|
31
31
|
export type { ExtractedStatement, StatementBadge, UtteranceClaimExtractor, UtteranceStatement, UtteranceStatementRecords, UtteranceTrustReport, } from "./agent-utterance.js";
|
|
32
32
|
export { mappingReviewToSurface, referenceSchemaExtractor, surveySchemaMapping, } from "./schema-mapping.js";
|
|
33
33
|
export type { MappingProposalRecord, ReviewedMapping, SchemaMappingExtractor, SchemaMappingOptions, SystemFieldRef, } from "./schema-mapping.js";
|
|
34
|
+
export { buildAuthorizedActionAuthorizing, isValidAuthorizing, validateAuthorizing } from "./review-authorizing.js";
|
|
35
|
+
export type { BuildAuthorizedActionAuthorizingInput, ReviewAuthorizingIssue, ReviewAuthorizingIssueCode, } from "./review-authorizing.js";
|
package/dist/src/index.js
CHANGED
|
@@ -14,3 +14,4 @@ export { apiRecordSource, manualEntrySource, policyStandardSource, uploadedDocum
|
|
|
14
14
|
export { applyAutoAcceptPolicy, applyMappingReview, buildMappingReviewItems, lookupMapping, lookupRejectedMapping, normalizeQuestion, proposalsToCandidateSet, referenceMappingProposer, resolveQuestion, } from "./inquiry-mapping.js";
|
|
15
15
|
export { referenceUtteranceExtractor, surveyAgentUtterance, utteranceToSurveyInput, } from "./agent-utterance.js";
|
|
16
16
|
export { mappingReviewToSurface, referenceSchemaExtractor, surveySchemaMapping, } from "./schema-mapping.js";
|
|
17
|
+
export { buildAuthorizedActionAuthorizing, isValidAuthorizing, validateAuthorizing } from "./review-authorizing.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReviewAuthorizing, ReviewAuthorizingAuthorizedAction } from "./types.js";
|
|
2
|
+
export type ReviewAuthorizingIssueCode = "not-an-object" | "missing-kind" | "unknown-kind" | "missing-statement" | "missing-prompt" | "missing-response" | "missing-prompt-ref" | "missing-rendered-prompt" | "missing-action" | "invalid-action" | "missing-authority-ref";
|
|
3
|
+
export interface ReviewAuthorizingIssue {
|
|
4
|
+
readonly code: ReviewAuthorizingIssueCode;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Validates an `authorizing` block on a ReviewOutcome for admissibility.
|
|
9
|
+
*
|
|
10
|
+
* Per-kind required fields:
|
|
11
|
+
* explicit-statement — `statement` (string, non-empty)
|
|
12
|
+
* exchange — `prompt` and `response` (both strings, non-empty;
|
|
13
|
+
* both halves required for self-contained testimony)
|
|
14
|
+
* authorized-action — `promptRef`, `renderedPrompt`, `action`, and
|
|
15
|
+
* `authorityRef` (all required; action must be
|
|
16
|
+
* "affirmed-control" or "typed")
|
|
17
|
+
*
|
|
18
|
+
* Returns an empty array when the block is valid.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateAuthorizing(block: unknown): ReviewAuthorizingIssue[];
|
|
21
|
+
/**
|
|
22
|
+
* Type guard: returns true if a ReviewAuthorizing block passes all validation checks.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isValidAuthorizing(block: unknown): block is ReviewAuthorizing;
|
|
25
|
+
export interface BuildAuthorizedActionAuthorizingInput {
|
|
26
|
+
readonly promptRef: string;
|
|
27
|
+
readonly renderedPrompt: string;
|
|
28
|
+
readonly action: ReviewAuthorizingAuthorizedAction["action"];
|
|
29
|
+
readonly authorityRef: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Helper for consumers building `authorized-action` authorizing blocks outside
|
|
33
|
+
* the workbench. Constructs the block and validates it; throws if the result
|
|
34
|
+
* would be invalid so callers catch configuration errors at build time.
|
|
35
|
+
*
|
|
36
|
+
* For workbench-internal construction, use the workbench path directly — it
|
|
37
|
+
* runs validateAuthorizing and degrades gracefully instead of throwing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildAuthorizedActionAuthorizing(input: BuildAuthorizedActionAuthorizingInput): ReviewAuthorizingAuthorizedAction;
|
|
@@ -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 {
|
|
@@ -3,6 +3,8 @@ import { validateReviewSessionEventsForSnapshot, } from "./review-session-replay
|
|
|
3
3
|
import { buildSurfaceProjectionPreview, formatValue, } from "./review-surface-preview.js";
|
|
4
4
|
import { buildReviewCandidatePresentation, buildReviewItemPresentation, } from "./review-presentation.js";
|
|
5
5
|
import { reviewResourceApiVersion, } from "../review-resource.js";
|
|
6
|
+
import { validateAuthorizing, buildAuthorizedActionAuthorizing } from "../review-authorizing.js";
|
|
7
|
+
import { humanizeIdentifier } from "./review-presentation.js";
|
|
6
8
|
export { buildReviewSessionEvents, buildReviewSessionEvent, buildReviewSessionResource, candidateForDecision, currentReviewItem, currentReviewWorkbenchState, defaultReviewSessionName, deriveQueueRowStatus, initialReviewQueueSessionState, initialReviewWorkbenchState, nextUnresolvedItemName, replayReviewSessionEvents, reviewSessionSummary, reviewWorkbenchSessionStorageKey, selectedCandidateRole, workbenchDecisionDefinitions, } from "./review-queue-session.js";
|
|
7
9
|
export { buildReviewCandidatePresentation, buildReviewItemPresentation, buildReviewResultPresentation, humanizeIdentifier, } from "./review-presentation.js";
|
|
8
10
|
export { buildSurfaceProjectionPreview, } from "./review-surface-preview.js";
|
|
@@ -35,6 +37,7 @@ export function buildReviewDecision(state) {
|
|
|
35
37
|
},
|
|
36
38
|
reviewedAt: state.reviewedAt,
|
|
37
39
|
rationale: state.note,
|
|
40
|
+
authorizing: buildDecisionCardAuthorizing(state),
|
|
38
41
|
projection,
|
|
39
42
|
},
|
|
40
43
|
status: {
|
|
@@ -42,6 +45,61 @@ export function buildReviewDecision(state) {
|
|
|
42
45
|
},
|
|
43
46
|
};
|
|
44
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Stable versioned prompt identifier for the workbench decision card control.
|
|
50
|
+
* Derived from the component naming convention: <module>/<component>@<version>.
|
|
51
|
+
*/
|
|
52
|
+
const DECISION_CARD_PROMPT_REF = "review-workbench/decision-card@v1";
|
|
53
|
+
/**
|
|
54
|
+
* Constructs the `authorized-action` authorizing block for a workbench decision.
|
|
55
|
+
* On validation failure, emits a console warning and returns undefined so the
|
|
56
|
+
* outcome is recorded without authorizing (transparency-gap-not-blocker per ADR 0004).
|
|
57
|
+
*/
|
|
58
|
+
function buildDecisionCardAuthorizing(state) {
|
|
59
|
+
if (!state.decision) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const targetLabel = humanizeIdentifier(state.item.spec.target);
|
|
63
|
+
const renderedPrompt = decisionCardRenderedPrompt(state, targetLabel);
|
|
64
|
+
// Reviewer note present means the reviewer also typed a rationale — "typed".
|
|
65
|
+
// Control-only affirmation (no note) is "affirmed-control".
|
|
66
|
+
const action = state.note?.trim() ? "typed" : "affirmed-control";
|
|
67
|
+
const authorityRef = `actor:${state.actorId}`;
|
|
68
|
+
try {
|
|
69
|
+
const block = buildAuthorizedActionAuthorizing({
|
|
70
|
+
promptRef: DECISION_CARD_PROMPT_REF,
|
|
71
|
+
renderedPrompt,
|
|
72
|
+
action,
|
|
73
|
+
authorityRef,
|
|
74
|
+
});
|
|
75
|
+
const issues = validateAuthorizing(block);
|
|
76
|
+
if (issues.length > 0) {
|
|
77
|
+
console.warn("[survey] buildDecisionCardAuthorizing: authorizing block failed validation — recording outcome without authorizing.", issues);
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return block;
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.warn("[survey] buildDecisionCardAuthorizing: failed to construct authorizing block — recording outcome without authorizing.", err);
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Derives the exact decision prompt rendered on the workbench decision card
|
|
89
|
+
* for a given review item and decision.
|
|
90
|
+
*
|
|
91
|
+
* Format mirrors the review question shown in the workbench header:
|
|
92
|
+
* "For {target}, decide whether {proposed} should replace {current}."
|
|
93
|
+
* followed by the selected decision label so the block is self-contained.
|
|
94
|
+
*/
|
|
95
|
+
function decisionCardRenderedPrompt(state, targetLabel) {
|
|
96
|
+
const currentCandidate = state.item.spec.candidates.find((c) => c.role === "current");
|
|
97
|
+
const proposedCandidate = state.item.spec.candidates.find((c) => c.role === "proposed");
|
|
98
|
+
const currentValue = formatValue(currentCandidate?.value ?? "");
|
|
99
|
+
const proposedValue = formatValue(proposedCandidate?.value ?? "");
|
|
100
|
+
const decisionLabel = state.decision ? workbenchDecisionDefinitions[state.decision].label : "";
|
|
101
|
+
return `For ${targetLabel}, decide whether ${proposedValue} should replace ${currentValue}. Selected decision: ${decisionLabel}.`;
|
|
102
|
+
}
|
|
45
103
|
export class ReviewApplyActionMappingError extends Error {
|
|
46
104
|
name = "ReviewApplyActionMappingError";
|
|
47
105
|
issues;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -47,6 +47,26 @@ export interface CandidateSet {
|
|
|
47
47
|
metadata?: Record<string, unknown>;
|
|
48
48
|
}
|
|
49
49
|
export type ReviewStatus = Extract<TrustStatus, "verified" | "assumed" | "rejected" | "proposed">;
|
|
50
|
+
export type ReviewAuthorizingKind = "explicit-statement" | "exchange" | "authorized-action";
|
|
51
|
+
export interface ReviewAuthorizingExplicitStatement {
|
|
52
|
+
kind: "explicit-statement";
|
|
53
|
+
statement: string;
|
|
54
|
+
source?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ReviewAuthorizingExchange {
|
|
57
|
+
kind: "exchange";
|
|
58
|
+
prompt: string;
|
|
59
|
+
response: string;
|
|
60
|
+
source?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface ReviewAuthorizingAuthorizedAction {
|
|
63
|
+
kind: "authorized-action";
|
|
64
|
+
promptRef: string;
|
|
65
|
+
renderedPrompt: string;
|
|
66
|
+
action: "affirmed-control" | "typed";
|
|
67
|
+
authorityRef: string;
|
|
68
|
+
}
|
|
69
|
+
export type ReviewAuthorizing = ReviewAuthorizingExplicitStatement | ReviewAuthorizingExchange | ReviewAuthorizingAuthorizedAction;
|
|
50
70
|
export interface ReviewOutcome {
|
|
51
71
|
id: string;
|
|
52
72
|
candidateSetId: string;
|
|
@@ -58,6 +78,9 @@ export interface ReviewOutcome {
|
|
|
58
78
|
evidenceIds?: string[];
|
|
59
79
|
withinComfortZone?: boolean;
|
|
60
80
|
comfortZoneNote?: string;
|
|
81
|
+
/** Optional testimony provenance. When present, records how this decision
|
|
82
|
+
* was authorized — interpreted by downstream verifiers for admissibility. */
|
|
83
|
+
authorizing?: ReviewAuthorizing;
|
|
61
84
|
metadata?: Record<string, unknown>;
|
|
62
85
|
}
|
|
63
86
|
export interface EscalationRecord {
|
package/package.json
CHANGED