@sonate/schemas 2.0.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,200 @@
1
+ /**
2
+ * @sonate/schemas - Schema Validator
3
+ * Runtime validation using AJV (JSON Schema) and Zod
4
+ */
5
+
6
+ import Ajv from 'ajv';
7
+ import { z } from 'zod';
8
+
9
+ import receiptSchema from './receipt.schema.json';
10
+ import type { TrustReceipt, VerificationResult } from './receipt.types';
11
+
12
+ /**
13
+ * Initialize AJV for JSON Schema validation
14
+ */
15
+ const ajv = new Ajv({
16
+ strict: false,
17
+ validateFormats: false,
18
+ });
19
+
20
+ /**
21
+ * Compile receipt schema for validation
22
+ */
23
+ const validateReceiptSchema = ajv.compile(receiptSchema);
24
+
25
+ /**
26
+ * Zod schema for runtime validation with better errors
27
+ */
28
+ const ReceiptZodSchema = z.object({
29
+ id: z.string().regex(/^[a-f0-9]{64}$/, 'Invalid receipt ID format'),
30
+ version: z.literal('2.0.0'),
31
+ timestamp: z.string().datetime('Invalid ISO 8601 timestamp'),
32
+ session_id: z.string().min(1),
33
+ agent_did: z.string().regex(/^did:(web|sonate):.+$/, 'Invalid agent DID'),
34
+ human_did: z.string().regex(/^did:(web|sonate):.+$/, 'Invalid human DID'),
35
+ policy_version: z.string(),
36
+ mode: z.enum(['constitutional', 'directive']),
37
+ interaction: z.object({
38
+ prompt: z.string(),
39
+ response: z.string(),
40
+ model: z.string(),
41
+ provider: z.enum(['openai', 'anthropic', 'aws-bedrock', 'local']).optional(),
42
+ temperature: z.number().min(0).max(2).optional(),
43
+ max_tokens: z.number().int().positive().optional(),
44
+ reasoning: z.object({
45
+ thought_process: z.string().optional(),
46
+ confidence: z.number().min(0).max(1).optional(),
47
+ retrieved_context: z.array(z.string()).optional(),
48
+ }).optional(),
49
+ }),
50
+ telemetry: z.object({
51
+ resonance_score: z.number().min(0).max(1).optional(),
52
+ resonance_rm: z.number().min(0).max(1).optional(),
53
+ trust_resonance_gap: z.number().min(0).max(1).optional(),
54
+ resonance_input_mode: z.enum(['paired_turns', 'labeled_sections', 'single_text_fallback']).optional(),
55
+ resonance_quality: z.enum(['STRONG', 'ADVANCED', 'BREAKTHROUGH']).optional(),
56
+ bedau_index: z.number().min(0).max(1).optional(),
57
+ coherence_score: z.number().min(0).max(1).optional(),
58
+ truth_debt: z.number().min(0).max(1).optional(),
59
+ volatility: z.number().min(0).max(1).optional(),
60
+ ciq_metrics: z.object({
61
+ clarity: z.number().min(0).max(1).optional(),
62
+ integrity: z.number().min(0).max(1).optional(),
63
+ quality: z.number().min(0).max(1).optional(),
64
+ }).optional(),
65
+ resonance_components: z.object({
66
+ vector_alignment: z.number().min(0).max(1).optional(),
67
+ context_continuity: z.number().min(0).max(1).optional(),
68
+ semantic_mirroring: z.number().min(0).max(1).optional(),
69
+ entropy_delta: z.number().min(0).max(1).optional(),
70
+ }).optional(),
71
+ resonance_stakes: z.object({
72
+ level: z.enum(['HIGH', 'MEDIUM', 'LOW']).optional(),
73
+ confidence: z.number().min(0).max(1).optional(),
74
+ }).optional(),
75
+ resonance_adversarial: z.object({
76
+ detected: z.boolean().optional(),
77
+ penalty: z.number().min(0).max(1).optional(),
78
+ keyword_density: z.number().min(0).max(1).optional(),
79
+ ethics_bypass_score: z.number().min(0).max(1).optional(),
80
+ }).optional(),
81
+ }).optional(),
82
+ policy_state: z.object({
83
+ constraints_applied: z.array(z.string()).optional(),
84
+ violations: z.array(z.object({
85
+ rule: z.string(),
86
+ severity: z.enum(['warning', 'violation', 'critical']),
87
+ action: z.enum(['warn', 'slow', 'halt', 'escalate']),
88
+ })).optional(),
89
+ consent_verified: z.boolean().optional(),
90
+ override_available: z.boolean().optional(),
91
+ }).optional(),
92
+ chain: z.object({
93
+ previous_hash: z.string().regex(/^[a-f0-9]{64}$|^GENESIS$/),
94
+ chain_hash: z.string().regex(/^[a-f0-9]{64}$/),
95
+ chain_length: z.number().int().positive().optional(),
96
+ }),
97
+ signature: z.object({
98
+ algorithm: z.literal('Ed25519'),
99
+ value: z.string(),
100
+ key_version: z.string(),
101
+ timestamp_signed: z.string().datetime().optional(),
102
+ }),
103
+ metadata: z.object({
104
+ tags: z.array(z.string()).optional(),
105
+ context: z.record(z.any()).optional(),
106
+ user_agent: z.string().optional(),
107
+ }).optional(),
108
+ });
109
+
110
+ /**
111
+ * Validate receipt against JSON Schema
112
+ */
113
+ export function validateReceiptJSON(receipt: unknown): VerificationResult {
114
+ const checks = {
115
+ schema_valid: false,
116
+ signature_valid: false,
117
+ chain_valid: false,
118
+ chain_hash_valid: false,
119
+ };
120
+ const errors: string[] = [];
121
+ const warnings: string[] = [];
122
+
123
+ try {
124
+ // Step 1: Validate schema
125
+ const valid = validateReceiptSchema(receipt);
126
+
127
+ if (!valid) {
128
+ checks.schema_valid = false;
129
+ errors.push('Schema validation failed');
130
+ if (validateReceiptSchema.errors) {
131
+ validateReceiptSchema.errors.forEach(err => {
132
+ errors.push(`${err.instancePath || 'root'}: ${err.message}`);
133
+ });
134
+ }
135
+ return { valid: false, checks, errors, warnings };
136
+ }
137
+ checks.schema_valid = true;
138
+
139
+ const typedReceipt = receipt as unknown as TrustReceipt;
140
+
141
+ // Step 2: Validate chain structure
142
+ if (!typedReceipt.chain?.previous_hash || !typedReceipt.chain?.chain_hash) {
143
+ errors.push('Invalid chain structure');
144
+ return { valid: false, checks, errors, warnings };
145
+ }
146
+ checks.chain_valid = true;
147
+
148
+ // Step 3: Verify signature structure (not crypto verification - that's separate)
149
+ if (!typedReceipt.signature?.value) {
150
+ errors.push('Invalid signature structure');
151
+ return { valid: false, checks, errors, warnings };
152
+ }
153
+ checks.signature_valid = true; // Crypto verification happens separately
154
+
155
+ // Step 4: Validate chain hash format
156
+ const chainHashValid = /^[a-f0-9]{64}$/.test(typedReceipt.chain.chain_hash);
157
+ if (!chainHashValid) {
158
+ errors.push('Invalid chain hash format');
159
+ return { valid: false, checks, errors, warnings };
160
+ }
161
+ checks.chain_hash_valid = true;
162
+
163
+ return {
164
+ valid: errors.length === 0,
165
+ checks,
166
+ errors,
167
+ warnings,
168
+ };
169
+ } catch (err) {
170
+ errors.push(`Validation error: ${err instanceof Error ? err.message : String(err)}`);
171
+ return { valid: false, checks, errors, warnings };
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Validate receipt using Zod (with better error messages)
177
+ */
178
+ export function validateReceiptZod(receipt: unknown): ReturnType<typeof ReceiptZodSchema.safeParse> {
179
+ return ReceiptZodSchema.safeParse(receipt);
180
+ }
181
+
182
+ /**
183
+ * Check if receipt has required fields for processing
184
+ */
185
+ export function isReceiptProcessable(receipt: unknown): receipt is TrustReceipt {
186
+ if (typeof receipt !== 'object' || receipt === null) {
187
+ return false;
188
+ }
189
+ const result = validateReceiptJSON(receipt);
190
+ return result.valid && Object.values(result.checks).every(check => check);
191
+ }
192
+
193
+ /**
194
+ * Export receipt validator
195
+ */
196
+ export const receiptValidator = {
197
+ validateJSON: validateReceiptJSON,
198
+ validateZod: validateReceiptZod,
199
+ isProcessable: isReceiptProcessable,
200
+ };