@kontourai/survey 1.6.0 → 1.6.1
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/bin/survey-review-console.mjs +0 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +1 -1
- package/dist/src/review-authorizing.d.ts +1 -1
- package/dist/src/review-authorizing.js +10 -0
- package/dist/src/review-proof.d.ts +40 -7
- package/dist/src/review-proof.js +95 -2
- package/package.json +1 -1
|
File without changes
|
package/dist/src/index.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ export { buildSurveyTrustBundle } from "./to-surface.js";
|
|
|
12
12
|
export type { BuildSurveyTrustBundleOptions } from "./to-surface.js";
|
|
13
13
|
export { buildSurveyLearningProjections } from "./learning-projections.js";
|
|
14
14
|
export type { LearningProjection, LearningProjectionKind, LearningProjectionSeverity, LearningProjectionSignal, } from "./learning-projections.js";
|
|
15
|
-
export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalReviewProofJson, hashCanonicalReviewProofPayload, REVIEW_PROOF_CONTRACT_VERSION, REVIEW_PROOF_PACKAGE_NAME, REVIEW_PROOF_SCHEMA, REVIEW_PROOF_SCHEMA_VERSION, } from "./review-proof.js";
|
|
16
|
-
export type { CanonicalReviewProofPayload, ReviewProofInput, } from "./review-proof.js";
|
|
15
|
+
export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalReviewProofJson, hashCanonicalReviewProofPayload, verifyCanonicalReviewProofPayload, REVIEW_PROOF_CONTRACT_VERSION, REVIEW_PROOF_PACKAGE_NAME, REVIEW_PROOF_SCHEMA, REVIEW_PROOF_SCHEMA_VERSION, } from "./review-proof.js";
|
|
16
|
+
export type { CanonicalReviewProofPayload, CanonicalReviewProofPayloadV1, CanonicalReviewProofPayloadV2, ReviewProofInput, } from "./review-proof.js";
|
|
17
17
|
export { fieldObservation } from "./field-observation.js";
|
|
18
18
|
export type { FieldObservationInput } from "./field-observation.js";
|
|
19
19
|
export { repeatedObservation } from "./repeated-observation.js";
|
package/dist/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export { reviewedCandidateResolution } from "./reviewed-candidate-resolution.js"
|
|
|
5
5
|
export { reviewedCurrentProposedResolution } from "./reviewed-current-proposed-resolution.js";
|
|
6
6
|
export { buildSurveyTrustBundle } from "./to-surface.js";
|
|
7
7
|
export { buildSurveyLearningProjections } from "./learning-projections.js";
|
|
8
|
-
export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalReviewProofJson, hashCanonicalReviewProofPayload, REVIEW_PROOF_CONTRACT_VERSION, REVIEW_PROOF_PACKAGE_NAME, REVIEW_PROOF_SCHEMA, REVIEW_PROOF_SCHEMA_VERSION, } from "./review-proof.js";
|
|
8
|
+
export { buildCanonicalReviewProofPayload, buildReviewProofAnchor, canonicalReviewProofJson, hashCanonicalReviewProofPayload, verifyCanonicalReviewProofPayload, REVIEW_PROOF_CONTRACT_VERSION, REVIEW_PROOF_PACKAGE_NAME, REVIEW_PROOF_SCHEMA, REVIEW_PROOF_SCHEMA_VERSION, } from "./review-proof.js";
|
|
9
9
|
export { fieldObservation } from "./field-observation.js";
|
|
10
10
|
export { repeatedObservation } from "./repeated-observation.js";
|
|
11
11
|
export { sourceOfAuthorityObservation, sourceOfAuthorityObservationBuilder, SourceOfAuthorityObservationBuilder, } from "./source-of-authority-observation.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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";
|
|
2
|
+
export type ReviewAuthorizingIssueCode = "not-an-object" | "missing-kind" | "unknown-kind" | "missing-statement" | "invalid-source" | "missing-prompt" | "missing-response" | "missing-prompt-ref" | "missing-rendered-prompt" | "missing-action" | "invalid-action" | "missing-authority-ref";
|
|
3
3
|
export interface ReviewAuthorizingIssue {
|
|
4
4
|
readonly code: ReviewAuthorizingIssueCode;
|
|
5
5
|
readonly message: string;
|
|
@@ -43,6 +43,7 @@ function validateExplicitStatement(block) {
|
|
|
43
43
|
message: "explicit-statement authorizing block requires a non-empty 'statement' string.",
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
+
validateOptionalSource(block.source, issues);
|
|
46
47
|
return issues;
|
|
47
48
|
}
|
|
48
49
|
function validateExchange(block) {
|
|
@@ -59,8 +60,17 @@ function validateExchange(block) {
|
|
|
59
60
|
message: "exchange authorizing block requires a non-empty 'response' string (both halves required for self-contained testimony).",
|
|
60
61
|
});
|
|
61
62
|
}
|
|
63
|
+
validateOptionalSource(block.source, issues);
|
|
62
64
|
return issues;
|
|
63
65
|
}
|
|
66
|
+
function validateOptionalSource(source, issues) {
|
|
67
|
+
if (source !== undefined && (typeof source !== "string" || source.trim() === "")) {
|
|
68
|
+
issues.push({
|
|
69
|
+
code: "invalid-source",
|
|
70
|
+
message: "authorizing 'source', when present, must be a non-empty string.",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
64
74
|
function validateAuthorizedAction(block) {
|
|
65
75
|
const issues = [];
|
|
66
76
|
if (!block.promptRef || typeof block.promptRef !== "string" || block.promptRef.trim() === "") {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { IntegrityAnchor } from "@kontourai/surface";
|
|
2
|
-
import type { Candidate, CandidateSet, ClaimTarget, Extraction, RawSource, ReviewOutcome } from "./types.js";
|
|
2
|
+
import type { Candidate, CandidateSet, ClaimTarget, Extraction, RawSource, ReviewAuthorizing, ReviewOutcome } from "./types.js";
|
|
3
3
|
export declare const REVIEW_PROOF_SCHEMA = "survey.review-proof";
|
|
4
|
-
export declare const REVIEW_PROOF_SCHEMA_VERSION =
|
|
4
|
+
export declare const REVIEW_PROOF_SCHEMA_VERSION = 2;
|
|
5
5
|
export declare const REVIEW_PROOF_PACKAGE_NAME = "@kontourai/survey";
|
|
6
|
-
export declare const REVIEW_PROOF_CONTRACT_VERSION = "
|
|
6
|
+
export declare const REVIEW_PROOF_CONTRACT_VERSION = "2";
|
|
7
|
+
declare const LEGACY_REVIEW_PROOF_SCHEMA_VERSION = 1;
|
|
8
|
+
declare const LEGACY_REVIEW_PROOF_CONTRACT_VERSION = "1";
|
|
7
9
|
export interface ReviewProofInput {
|
|
8
10
|
rawSource: RawSource;
|
|
9
11
|
extraction: Extraction;
|
|
@@ -15,13 +17,13 @@ export interface ReviewProofInput {
|
|
|
15
17
|
observedAt?: string;
|
|
16
18
|
metadata?: Record<string, unknown>;
|
|
17
19
|
}
|
|
18
|
-
export interface
|
|
20
|
+
export interface CanonicalReviewProofPayloadV1 {
|
|
19
21
|
schemaVersion: 1;
|
|
20
22
|
proof: {
|
|
21
23
|
schema: typeof REVIEW_PROOF_SCHEMA;
|
|
22
|
-
schemaVersion: typeof
|
|
24
|
+
schemaVersion: typeof LEGACY_REVIEW_PROOF_SCHEMA_VERSION;
|
|
23
25
|
packageName: typeof REVIEW_PROOF_PACKAGE_NAME;
|
|
24
|
-
packageVersion: typeof
|
|
26
|
+
packageVersion: typeof LEGACY_REVIEW_PROOF_CONTRACT_VERSION;
|
|
25
27
|
issuer: string;
|
|
26
28
|
producer: string;
|
|
27
29
|
issuedAt: string;
|
|
@@ -110,7 +112,38 @@ export interface CanonicalReviewProofPayload {
|
|
|
110
112
|
eventMethod?: string;
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
|
-
|
|
115
|
+
type CanonicalReviewAuthorizing = (Extract<ReviewAuthorizing, {
|
|
116
|
+
kind: "explicit-statement";
|
|
117
|
+
}> & {
|
|
118
|
+
authorityRef?: never;
|
|
119
|
+
}) | (Extract<ReviewAuthorizing, {
|
|
120
|
+
kind: "exchange";
|
|
121
|
+
}> & {
|
|
122
|
+
authorityRef?: never;
|
|
123
|
+
}) | (Extract<ReviewAuthorizing, {
|
|
124
|
+
kind: "authorized-action";
|
|
125
|
+
}> & {
|
|
126
|
+
source?: never;
|
|
127
|
+
});
|
|
128
|
+
export type CanonicalReviewProofPayloadV2 = Omit<CanonicalReviewProofPayloadV1, "schemaVersion" | "proof" | "reviewOutcome"> & {
|
|
129
|
+
schemaVersion: 2;
|
|
130
|
+
proof: Omit<CanonicalReviewProofPayloadV1["proof"], "schemaVersion" | "packageVersion"> & {
|
|
131
|
+
schemaVersion: typeof REVIEW_PROOF_SCHEMA_VERSION;
|
|
132
|
+
packageVersion: typeof REVIEW_PROOF_CONTRACT_VERSION;
|
|
133
|
+
};
|
|
134
|
+
reviewOutcome?: NonNullable<CanonicalReviewProofPayloadV1["reviewOutcome"]> & {
|
|
135
|
+
authorizing?: CanonicalReviewAuthorizing;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
export type CanonicalReviewProofPayload = CanonicalReviewProofPayloadV1 | CanonicalReviewProofPayloadV2;
|
|
139
|
+
export declare function buildCanonicalReviewProofPayload(input: ReviewProofInput): CanonicalReviewProofPayloadV2;
|
|
114
140
|
export declare function canonicalReviewProofJson(payload: CanonicalReviewProofPayload): string;
|
|
115
141
|
export declare function hashCanonicalReviewProofPayload(payload: CanonicalReviewProofPayload): string;
|
|
142
|
+
/**
|
|
143
|
+
* Verifies the integrity hash of a persisted v1 or v2 canonical review proof.
|
|
144
|
+
* Envelope compatibility and v2 authorizing admissibility are checked before
|
|
145
|
+
* the expected hash is compared.
|
|
146
|
+
*/
|
|
147
|
+
export declare function verifyCanonicalReviewProofPayload(payload: unknown, expectedHash: string): boolean;
|
|
116
148
|
export declare function buildReviewProofAnchor(input: ReviewProofInput): IntegrityAnchor;
|
|
149
|
+
export {};
|
package/dist/src/review-proof.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { validateAuthorizing } from "./review-authorizing.js";
|
|
2
3
|
export const REVIEW_PROOF_SCHEMA = "survey.review-proof";
|
|
3
|
-
export const REVIEW_PROOF_SCHEMA_VERSION =
|
|
4
|
+
export const REVIEW_PROOF_SCHEMA_VERSION = 2;
|
|
4
5
|
export const REVIEW_PROOF_PACKAGE_NAME = "@kontourai/survey";
|
|
5
6
|
// Version of the review proof contract emitted by this helper. This is intentionally
|
|
6
7
|
// independent from the npm package release version because it participates in hashes.
|
|
7
|
-
export const REVIEW_PROOF_CONTRACT_VERSION = "
|
|
8
|
+
export const REVIEW_PROOF_CONTRACT_VERSION = "2";
|
|
9
|
+
const LEGACY_REVIEW_PROOF_SCHEMA_VERSION = 1;
|
|
10
|
+
const LEGACY_REVIEW_PROOF_CONTRACT_VERSION = "1";
|
|
8
11
|
export function buildCanonicalReviewProofPayload(input) {
|
|
9
12
|
assertCandidateConsistency(input);
|
|
10
13
|
return {
|
|
@@ -78,6 +81,7 @@ export function buildCanonicalReviewProofPayload(input) {
|
|
|
78
81
|
reviewedAt: input.reviewOutcome.reviewedAt,
|
|
79
82
|
rationale: input.reviewOutcome.rationale,
|
|
80
83
|
evidenceIds: input.reviewOutcome.evidenceIds ? [...input.reviewOutcome.evidenceIds].sort() : undefined,
|
|
84
|
+
authorizing: copyCanonicalAuthorizing(input.reviewOutcome.authorizing),
|
|
81
85
|
}
|
|
82
86
|
: undefined,
|
|
83
87
|
claim: {
|
|
@@ -111,6 +115,45 @@ export function buildCanonicalReviewProofPayload(input) {
|
|
|
111
115
|
},
|
|
112
116
|
};
|
|
113
117
|
}
|
|
118
|
+
function copyCanonicalAuthorizing(authorizing) {
|
|
119
|
+
if (authorizing === undefined)
|
|
120
|
+
return undefined;
|
|
121
|
+
const kind = authorizing.kind;
|
|
122
|
+
let snapshot;
|
|
123
|
+
switch (kind) {
|
|
124
|
+
case "explicit-statement":
|
|
125
|
+
snapshot = {
|
|
126
|
+
kind,
|
|
127
|
+
statement: authorizing.statement,
|
|
128
|
+
source: authorizing.source,
|
|
129
|
+
};
|
|
130
|
+
break;
|
|
131
|
+
case "exchange":
|
|
132
|
+
snapshot = {
|
|
133
|
+
kind,
|
|
134
|
+
prompt: authorizing.prompt,
|
|
135
|
+
response: authorizing.response,
|
|
136
|
+
source: authorizing.source,
|
|
137
|
+
};
|
|
138
|
+
break;
|
|
139
|
+
case "authorized-action":
|
|
140
|
+
snapshot = {
|
|
141
|
+
kind,
|
|
142
|
+
promptRef: authorizing.promptRef,
|
|
143
|
+
renderedPrompt: authorizing.renderedPrompt,
|
|
144
|
+
action: authorizing.action,
|
|
145
|
+
authorityRef: authorizing.authorityRef,
|
|
146
|
+
};
|
|
147
|
+
break;
|
|
148
|
+
default:
|
|
149
|
+
snapshot = { kind };
|
|
150
|
+
}
|
|
151
|
+
const issues = validateAuthorizing(snapshot);
|
|
152
|
+
if (issues.length > 0) {
|
|
153
|
+
throw new Error(`Canonical review proof has invalid authorizing: ${issues.map((issue) => issue.message).join(" ")}`);
|
|
154
|
+
}
|
|
155
|
+
return snapshot;
|
|
156
|
+
}
|
|
114
157
|
function assertCandidateConsistency(input) {
|
|
115
158
|
if (input.extraction.sourceId !== input.rawSource.id) {
|
|
116
159
|
throw new Error(`Review proof extraction sourceId "${input.extraction.sourceId}" does not match raw source id "${input.rawSource.id}".`);
|
|
@@ -143,6 +186,56 @@ export function canonicalReviewProofJson(payload) {
|
|
|
143
186
|
export function hashCanonicalReviewProofPayload(payload) {
|
|
144
187
|
return createHash("sha256").update(canonicalReviewProofJson(payload)).digest("hex");
|
|
145
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Verifies the integrity hash of a persisted v1 or v2 canonical review proof.
|
|
191
|
+
* Envelope compatibility and v2 authorizing admissibility are checked before
|
|
192
|
+
* the expected hash is compared.
|
|
193
|
+
*/
|
|
194
|
+
export function verifyCanonicalReviewProofPayload(payload, expectedHash) {
|
|
195
|
+
try {
|
|
196
|
+
const snapshot = canonicalize(payload);
|
|
197
|
+
const canonicalJson = JSON.stringify(snapshot);
|
|
198
|
+
if (typeof canonicalJson !== "string" || !isRecord(snapshot) || !isRecord(snapshot.proof))
|
|
199
|
+
return false;
|
|
200
|
+
if (snapshot.proof.schema !== REVIEW_PROOF_SCHEMA)
|
|
201
|
+
return false;
|
|
202
|
+
if (snapshot.proof.packageName !== REVIEW_PROOF_PACKAGE_NAME)
|
|
203
|
+
return false;
|
|
204
|
+
const schemaVersion = snapshot.schemaVersion;
|
|
205
|
+
const proofSchemaVersion = snapshot.proof.schemaVersion;
|
|
206
|
+
const packageVersion = snapshot.proof.packageVersion;
|
|
207
|
+
const reviewOutcome = snapshot.reviewOutcome;
|
|
208
|
+
if (schemaVersion === LEGACY_REVIEW_PROOF_SCHEMA_VERSION) {
|
|
209
|
+
if (proofSchemaVersion !== LEGACY_REVIEW_PROOF_SCHEMA_VERSION
|
|
210
|
+
|| packageVersion !== LEGACY_REVIEW_PROOF_CONTRACT_VERSION) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
if (isRecord(reviewOutcome) && Object.prototype.hasOwnProperty.call(reviewOutcome, "authorizing")) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (schemaVersion === REVIEW_PROOF_SCHEMA_VERSION) {
|
|
218
|
+
if (proofSchemaVersion !== REVIEW_PROOF_SCHEMA_VERSION || packageVersion !== REVIEW_PROOF_CONTRACT_VERSION) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
if (reviewOutcome !== undefined && !isRecord(reviewOutcome))
|
|
222
|
+
return false;
|
|
223
|
+
if (isRecord(reviewOutcome)
|
|
224
|
+
&& Object.prototype.hasOwnProperty.call(reviewOutcome, "authorizing")
|
|
225
|
+
&& reviewOutcome.authorizing !== undefined
|
|
226
|
+
&& validateAuthorizing(reviewOutcome.authorizing).length > 0) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
return createHash("sha256").update(canonicalJson).digest("hex") === expectedHash;
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
146
239
|
export function buildReviewProofAnchor(input) {
|
|
147
240
|
const payload = buildCanonicalReviewProofPayload(input);
|
|
148
241
|
const hash = hashCanonicalReviewProofPayload(payload);
|
package/package.json
CHANGED