@mcoda/codali 0.1.90 → 0.1.94

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.
Files changed (105) hide show
  1. package/dist/cli/DatasetCommand.d.ts +38 -0
  2. package/dist/cli/DatasetCommand.d.ts.map +1 -0
  3. package/dist/cli/DatasetCommand.js +959 -0
  4. package/dist/cli/EvalCommand.d.ts +3 -0
  5. package/dist/cli/EvalCommand.d.ts.map +1 -1
  6. package/dist/cli/EvalCommand.js +55 -1
  7. package/dist/cli/ImprovementCommand.d.ts +97 -0
  8. package/dist/cli/ImprovementCommand.d.ts.map +1 -0
  9. package/dist/cli/ImprovementCommand.js +1879 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +15 -0
  12. package/dist/dataset-cli.d.ts +3 -0
  13. package/dist/dataset-cli.d.ts.map +1 -0
  14. package/dist/dataset-cli.js +30 -0
  15. package/dist/eval/CodaliGatewayLiveHarness.d.ts +90 -1
  16. package/dist/eval/CodaliGatewayLiveHarness.d.ts.map +1 -1
  17. package/dist/eval/CodaliGatewayLiveHarness.js +443 -9
  18. package/dist/eval/GatewayDatasetEval.d.ts +70 -0
  19. package/dist/eval/GatewayDatasetEval.d.ts.map +1 -0
  20. package/dist/eval/GatewayDatasetEval.js +477 -0
  21. package/dist/eval/GatewayEvalSuite.d.ts +52 -0
  22. package/dist/eval/GatewayEvalSuite.d.ts.map +1 -1
  23. package/dist/eval/GatewayEvalSuite.js +135 -1
  24. package/dist/gateway/CodaliGateway.d.ts +4 -0
  25. package/dist/gateway/CodaliGateway.d.ts.map +1 -1
  26. package/dist/gateway/CodaliGateway.js +37 -1
  27. package/dist/gateway/CodaliGatewaySchemas.d.ts.map +1 -1
  28. package/dist/gateway/CodaliGatewaySchemas.js +0 -4
  29. package/dist/gateway/CodaliGatewayStore.d.ts.map +1 -1
  30. package/dist/gateway/CodaliGatewayStore.js +1 -0
  31. package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
  32. package/dist/gateway/GatewayPlanner.js +355 -41
  33. package/dist/gateway/GatewayStateMachine.d.ts +1 -0
  34. package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
  35. package/dist/gateway/GatewayStateMachine.js +221 -5
  36. package/dist/improvement/CandidateReleaseBuilder.d.ts +201 -0
  37. package/dist/improvement/CandidateReleaseBuilder.d.ts.map +1 -0
  38. package/dist/improvement/CandidateReleaseBuilder.js +1175 -0
  39. package/dist/improvement/DatasetEligibilityGate.d.ts +75 -0
  40. package/dist/improvement/DatasetEligibilityGate.d.ts.map +1 -0
  41. package/dist/improvement/DatasetEligibilityGate.js +497 -0
  42. package/dist/improvement/DatasetExportManifestReader.d.ts +88 -0
  43. package/dist/improvement/DatasetExportManifestReader.d.ts.map +1 -0
  44. package/dist/improvement/DatasetExportManifestReader.js +333 -0
  45. package/dist/improvement/DocdexRetrievalCandidateBuilder.d.ts +192 -0
  46. package/dist/improvement/DocdexRetrievalCandidateBuilder.d.ts.map +1 -0
  47. package/dist/improvement/DocdexRetrievalCandidateBuilder.js +625 -0
  48. package/dist/improvement/EvalReplayCandidateBuilder.d.ts +173 -0
  49. package/dist/improvement/EvalReplayCandidateBuilder.d.ts.map +1 -0
  50. package/dist/improvement/EvalReplayCandidateBuilder.js +363 -0
  51. package/dist/improvement/FineTuneJobPlanner.d.ts +281 -0
  52. package/dist/improvement/FineTuneJobPlanner.d.ts.map +1 -0
  53. package/dist/improvement/FineTuneJobPlanner.js +823 -0
  54. package/dist/improvement/ImprovementEvalRunner.d.ts +50 -0
  55. package/dist/improvement/ImprovementEvalRunner.d.ts.map +1 -0
  56. package/dist/improvement/ImprovementEvalRunner.js +648 -0
  57. package/dist/improvement/ImprovementPolicy.d.ts +272 -0
  58. package/dist/improvement/ImprovementPolicy.d.ts.map +1 -0
  59. package/dist/improvement/ImprovementPolicy.js +1450 -0
  60. package/dist/improvement/ModelRouterCandidateBuilder.d.ts +186 -0
  61. package/dist/improvement/ModelRouterCandidateBuilder.d.ts.map +1 -0
  62. package/dist/improvement/ModelRouterCandidateBuilder.js +880 -0
  63. package/dist/improvement/OperatorInspector.d.ts +182 -0
  64. package/dist/improvement/OperatorInspector.d.ts.map +1 -0
  65. package/dist/improvement/OperatorInspector.js +604 -0
  66. package/dist/improvement/ProductionGovernance.d.ts +91 -0
  67. package/dist/improvement/ProductionGovernance.d.ts.map +1 -0
  68. package/dist/improvement/ProductionGovernance.js +329 -0
  69. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts +152 -0
  70. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.d.ts.map +1 -0
  71. package/dist/improvement/PromptSchemaToolMetadataCandidateBuilder.js +480 -0
  72. package/dist/improvement/PublishOrchestrator.d.ts +113 -0
  73. package/dist/improvement/PublishOrchestrator.d.ts.map +1 -0
  74. package/dist/improvement/PublishOrchestrator.js +590 -0
  75. package/dist/improvement/ReleaseOutcomeReporter.d.ts +133 -0
  76. package/dist/improvement/ReleaseOutcomeReporter.d.ts.map +1 -0
  77. package/dist/improvement/ReleaseOutcomeReporter.js +501 -0
  78. package/dist/improvement/StorageServiceImprovementClient.d.ts +71 -0
  79. package/dist/improvement/StorageServiceImprovementClient.d.ts.map +1 -0
  80. package/dist/improvement/StorageServiceImprovementClient.js +245 -0
  81. package/dist/index.d.ts +43 -0
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +21 -0
  84. package/dist/runtime/CodaliRuntime.d.ts +0 -2
  85. package/dist/runtime/CodaliRuntime.d.ts.map +1 -1
  86. package/dist/runtime/CodaliRuntime.js +0 -5
  87. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +138 -0
  88. package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -0
  89. package/dist/storage/CodaliDatasetPrivacyEngine.js +417 -0
  90. package/dist/storage/CodaliFeedbackReviewIngestion.d.ts +50 -0
  91. package/dist/storage/CodaliFeedbackReviewIngestion.d.ts.map +1 -0
  92. package/dist/storage/CodaliFeedbackReviewIngestion.js +141 -0
  93. package/dist/storage/CodaliStorageContracts.d.ts +337 -0
  94. package/dist/storage/CodaliStorageContracts.d.ts.map +1 -0
  95. package/dist/storage/CodaliStorageContracts.js +1555 -0
  96. package/dist/storage/DatasetExportJob.d.ts +51 -0
  97. package/dist/storage/DatasetExportJob.d.ts.map +1 -0
  98. package/dist/storage/DatasetExportJob.js +358 -0
  99. package/dist/storage/DatasetReviewQueue.d.ts +98 -0
  100. package/dist/storage/DatasetReviewQueue.d.ts.map +1 -0
  101. package/dist/storage/DatasetReviewQueue.js +426 -0
  102. package/dist/storage/GatewayDatasetStore.d.ts +233 -0
  103. package/dist/storage/GatewayDatasetStore.d.ts.map +1 -0
  104. package/dist/storage/GatewayDatasetStore.js +2070 -0
  105. package/package.json +5 -4
@@ -0,0 +1,1555 @@
1
+ export const CODALI_STORAGE_CONTRACT_SCHEMA_VERSION = "codali.storage.v1";
2
+ export const CODALI_STORAGE_CONTRACT_SCHEMA_VERSIONS = [
3
+ CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
4
+ ];
5
+ export const CODALI_STORAGE_CONTRACT_MIN_COMPATIBLE_SCHEMA_VERSION = CODALI_STORAGE_CONTRACT_SCHEMA_VERSION;
6
+ export const CODALI_STORAGE_CONTRACT_FIXTURE_SCHEMA_VERSION = "codali.storage.fixtures.v1";
7
+ export const CODALI_STORAGE_CONTRACT_SCHEMA_COMPATIBILITY = {
8
+ current: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
9
+ minCompatible: CODALI_STORAGE_CONTRACT_MIN_COMPATIBLE_SCHEMA_VERSION,
10
+ supported: CODALI_STORAGE_CONTRACT_SCHEMA_VERSIONS,
11
+ fixture: CODALI_STORAGE_CONTRACT_FIXTURE_SCHEMA_VERSION,
12
+ };
13
+ export const CODALI_STORAGE_CONTRACT_DISTRIBUTION = {
14
+ mode: "published_package",
15
+ packageName: "@mcoda/codali",
16
+ modulePath: "@mcoda/codali",
17
+ sourcePath: "packages/codali/src/storage/CodaliStorageContracts.ts",
18
+ fixturePath: "docs/contracts/codali-storage/v1/contract-fixtures.json",
19
+ };
20
+ const stringSchema = { type: "string" };
21
+ const booleanSchema = { type: "boolean" };
22
+ const integerSchema = { type: "integer", minimum: 0 };
23
+ const numberSchema = { type: "number" };
24
+ const schema = (name, title, required, properties) => ({
25
+ $schema: "https://json-schema.org/draft/2020-12/schema",
26
+ $id: `https://mcoda.local/contracts/codali-storage/${CODALI_STORAGE_CONTRACT_SCHEMA_VERSION}/${name}.schema.json`,
27
+ title,
28
+ type: "object",
29
+ additionalProperties: true,
30
+ required,
31
+ properties: {
32
+ schema_version: {
33
+ const: CODALI_STORAGE_CONTRACT_SCHEMA_VERSION,
34
+ description: "External payload schema version. Internal structures expose schemaVersion.",
35
+ },
36
+ ...properties,
37
+ },
38
+ });
39
+ export const CODALI_STORAGE_CONTRACT_JSON_SCHEMAS = {
40
+ privacyMetadata: schema("privacyMetadata", "Codali storage privacy metadata", [
41
+ "schema_version",
42
+ "classification",
43
+ "contains_personal_data",
44
+ "redaction_status",
45
+ "upload_allowed",
46
+ "export_allowed",
47
+ "training_allowed",
48
+ ], {
49
+ classification: { enum: ["public", "internal", "confidential", "restricted"] },
50
+ contains_personal_data: booleanSchema,
51
+ redaction_status: { enum: ["not_required", "pending", "redacted", "blocked"] },
52
+ upload_allowed: booleanSchema,
53
+ export_allowed: booleanSchema,
54
+ training_allowed: booleanSchema,
55
+ policy_tags: { type: "array", items: stringSchema },
56
+ retention_until: stringSchema,
57
+ deletion_requested_at: stringSchema,
58
+ redaction_summary: stringSchema,
59
+ metadata: { type: "object" },
60
+ }),
61
+ objectRef: schema("objectRef", "Codali storage object reference", [
62
+ "schema_version",
63
+ "ref_id",
64
+ "kind",
65
+ "content_hash",
66
+ "byte_size",
67
+ "mime_type",
68
+ "privacy_flags",
69
+ "owner_scope",
70
+ "deletion_group_id",
71
+ "retention_class",
72
+ ], {
73
+ ref_id: stringSchema,
74
+ kind: { enum: ["payload", "trace", "artifact", "dataset", "export", "evidence"] },
75
+ uri: stringSchema,
76
+ bucket: stringSchema,
77
+ key: stringSchema,
78
+ content_hash: stringSchema,
79
+ byte_size: integerSchema,
80
+ mime_type: stringSchema,
81
+ privacy_flags: { type: "object" },
82
+ owner_scope: { type: "object" },
83
+ owner_scope_hash: stringSchema,
84
+ deletion_group_id: stringSchema,
85
+ retention_class: {
86
+ enum: ["transient", "standard", "dataset", "legal_hold", "do_not_store"],
87
+ },
88
+ media_type: stringSchema,
89
+ size_bytes: integerSchema,
90
+ sha256: stringSchema,
91
+ created_at: stringSchema,
92
+ metadata: { type: "object" },
93
+ }),
94
+ gatewayRecord: schema("gatewayRecord", "Codali storage gateway record", [
95
+ "schema_version",
96
+ "record_type",
97
+ "record_id",
98
+ "run_id",
99
+ "created_at",
100
+ "status",
101
+ "query",
102
+ "privacy",
103
+ ], {
104
+ record_type: { const: "gateway_record" },
105
+ record_id: stringSchema,
106
+ run_id: stringSchema,
107
+ created_at: stringSchema,
108
+ status: { enum: ["succeeded", "failed", "partial", "needs_clarification"] },
109
+ query: stringSchema,
110
+ answer_summary: stringSchema,
111
+ request_ref: { type: "object" },
112
+ response_ref: { type: "object" },
113
+ trace_ref: { type: "object" },
114
+ answer_ref: { type: "object" },
115
+ model: { type: "object" },
116
+ usage: { type: "object" },
117
+ privacy: { type: "object" },
118
+ metadata: { type: "object" },
119
+ }),
120
+ datasetRecord: schema("datasetRecord", "Codali storage dataset record", [
121
+ "schema_version",
122
+ "record_type",
123
+ "record_id",
124
+ "dataset_kind",
125
+ "created_at",
126
+ "input_ref",
127
+ "privacy",
128
+ ], {
129
+ record_type: { const: "dataset_record" },
130
+ record_id: stringSchema,
131
+ dataset_kind: {
132
+ enum: ["gateway_answer", "tool_trace", "model_call", "evaluation", "curated_example"],
133
+ },
134
+ created_at: stringSchema,
135
+ source_gateway_record_id: stringSchema,
136
+ input_ref: { type: "object" },
137
+ output_ref: { type: "object" },
138
+ evidence_refs: { type: "array", items: { type: "object" } },
139
+ quality: { type: "object" },
140
+ privacy: { type: "object" },
141
+ metadata: { type: "object" },
142
+ }),
143
+ exportManifest: schema("exportManifest", "Codali storage export manifest", [
144
+ "schema_version",
145
+ "record_type",
146
+ "manifest_id",
147
+ "created_at",
148
+ "export_kind",
149
+ "export_format",
150
+ "record_count",
151
+ "records",
152
+ "artifact_refs",
153
+ "checksum",
154
+ "privacy_summary",
155
+ "lineage",
156
+ "deletion_group_snapshot",
157
+ "privacy",
158
+ ], {
159
+ record_type: { const: "export_manifest" },
160
+ manifest_id: stringSchema,
161
+ created_at: stringSchema,
162
+ export_kind: {
163
+ enum: [
164
+ "eval-replay",
165
+ "prompt-regression",
166
+ "extractor-sft",
167
+ "tool-router-sft",
168
+ "planner-sft",
169
+ "verifier-sft",
170
+ "query-expander-sft",
171
+ "repair-sft",
172
+ "context-refiner-sft",
173
+ "rag-reranker",
174
+ "model-router",
175
+ ],
176
+ },
177
+ export_format: { enum: ["jsonl", "json", "parquet", "bundle"] },
178
+ record_count: integerSchema,
179
+ records: { type: "array", items: { type: "object" } },
180
+ artifact_refs: { type: "array", items: { type: "object" } },
181
+ checksum: stringSchema,
182
+ privacy_summary: { type: "object" },
183
+ lineage: { type: "object" },
184
+ deletion_group_snapshot: { type: "object" },
185
+ generated_by: stringSchema,
186
+ privacy: { type: "object" },
187
+ metadata: { type: "object" },
188
+ }),
189
+ feedbackRecord: schema("feedbackRecord", "Codali storage feedback record", [
190
+ "schema_version",
191
+ "record_type",
192
+ "feedback_id",
193
+ "created_at",
194
+ "source",
195
+ "run_id",
196
+ "deletion_group_id",
197
+ "product_scope",
198
+ "requester_scope",
199
+ "candidate_records",
200
+ "target_type",
201
+ "target_id",
202
+ "privacy",
203
+ ], {
204
+ record_type: { const: "feedback_record" },
205
+ feedback_id: stringSchema,
206
+ created_at: stringSchema,
207
+ source: { enum: ["user", "operator", "automated_eval"] },
208
+ run_id: stringSchema,
209
+ deletion_group_id: stringSchema,
210
+ product_scope: { type: "object" },
211
+ requester_scope: { type: "object" },
212
+ candidate_records: { type: "array", items: { type: "object" } },
213
+ target_type: stringSchema,
214
+ target_id: stringSchema,
215
+ rating: numberSchema,
216
+ comment: stringSchema,
217
+ labels: { type: "array", items: stringSchema },
218
+ privacy: { type: "object" },
219
+ metadata: { type: "object" },
220
+ }),
221
+ reviewRecord: schema("reviewRecord", "Codali storage review record", [
222
+ "schema_version",
223
+ "record_type",
224
+ "review_id",
225
+ "created_at",
226
+ "reviewer_type",
227
+ "run_id",
228
+ "deletion_group_id",
229
+ "product_scope",
230
+ "requester_scope",
231
+ "candidate_records",
232
+ "target_type",
233
+ "target_id",
234
+ "decision",
235
+ "privacy",
236
+ ], {
237
+ record_type: { const: "review_record" },
238
+ review_id: stringSchema,
239
+ created_at: stringSchema,
240
+ reviewer_type: { enum: ["human", "automated"] },
241
+ reviewer_id: stringSchema,
242
+ run_id: stringSchema,
243
+ deletion_group_id: stringSchema,
244
+ product_scope: { type: "object" },
245
+ requester_scope: { type: "object" },
246
+ candidate_records: { type: "array", items: { type: "object" } },
247
+ target_type: stringSchema,
248
+ target_id: stringSchema,
249
+ decision: { enum: ["approved", "rejected", "needs_changes", "escalated"] },
250
+ reasons: { type: "array", items: stringSchema },
251
+ labels: { type: "array", items: stringSchema },
252
+ promotion_target: { enum: ["gold", "silver", "reject"] },
253
+ promoted_record_ids: { type: "array", items: stringSchema },
254
+ privacy: { type: "object" },
255
+ metadata: { type: "object" },
256
+ }),
257
+ improvementRecord: schema("improvementRecord", "Codali storage improvement record", [
258
+ "schema_version",
259
+ "record_type",
260
+ "improvement_id",
261
+ "created_at",
262
+ "improvement_kind",
263
+ "status",
264
+ "summary",
265
+ "source_record_ids",
266
+ "training_eligible",
267
+ "privacy",
268
+ ], {
269
+ record_type: { const: "improvement_record" },
270
+ improvement_id: stringSchema,
271
+ created_at: stringSchema,
272
+ improvement_kind: {
273
+ enum: ["prompt", "policy", "model_selection", "tooling", "evaluation", "other"],
274
+ },
275
+ status: { enum: ["proposed", "accepted", "rejected", "applied"] },
276
+ summary: stringSchema,
277
+ source_record_ids: { type: "array", items: stringSchema },
278
+ training_eligible: booleanSchema,
279
+ export_manifest_id: stringSchema,
280
+ candidate_ref: { type: "object" },
281
+ privacy: { type: "object" },
282
+ metadata: { type: "object" },
283
+ }),
284
+ };
285
+ const PRIVACY_CLASSIFICATIONS = [
286
+ "public",
287
+ "internal",
288
+ "confidential",
289
+ "restricted",
290
+ ];
291
+ const REDACTION_STATUSES = [
292
+ "not_required",
293
+ "pending",
294
+ "redacted",
295
+ "blocked",
296
+ ];
297
+ const OBJECT_REF_KINDS = [
298
+ "payload",
299
+ "trace",
300
+ "artifact",
301
+ "dataset",
302
+ "export",
303
+ "evidence",
304
+ ];
305
+ const OBJECT_RETENTION_CLASSES = [
306
+ "transient",
307
+ "standard",
308
+ "dataset",
309
+ "legal_hold",
310
+ "do_not_store",
311
+ ];
312
+ const RECORD_TYPES = [
313
+ "gateway_record",
314
+ "dataset_record",
315
+ "export_manifest",
316
+ "feedback_record",
317
+ "review_record",
318
+ "improvement_record",
319
+ ];
320
+ const GATEWAY_STATUSES = [
321
+ "succeeded",
322
+ "failed",
323
+ "partial",
324
+ "needs_clarification",
325
+ ];
326
+ const DATASET_KINDS = [
327
+ "gateway_answer",
328
+ "tool_trace",
329
+ "model_call",
330
+ "evaluation",
331
+ "curated_example",
332
+ ];
333
+ const EXPORT_FORMATS = [
334
+ "jsonl",
335
+ "json",
336
+ "parquet",
337
+ "bundle",
338
+ ];
339
+ export const CODALI_STORAGE_EXPORT_KINDS = [
340
+ "eval-replay",
341
+ "prompt-regression",
342
+ "extractor-sft",
343
+ "tool-router-sft",
344
+ "planner-sft",
345
+ "verifier-sft",
346
+ "query-expander-sft",
347
+ "repair-sft",
348
+ "context-refiner-sft",
349
+ "rag-reranker",
350
+ "model-router",
351
+ ];
352
+ const FEEDBACK_SOURCES = [
353
+ "user",
354
+ "operator",
355
+ "automated_eval",
356
+ ];
357
+ const REVIEWER_TYPES = [
358
+ "human",
359
+ "automated",
360
+ ];
361
+ const REVIEW_DECISIONS = [
362
+ "approved",
363
+ "rejected",
364
+ "needs_changes",
365
+ "escalated",
366
+ ];
367
+ const REQUESTER_SCOPE_VISIBILITIES = [
368
+ "requester",
369
+ "conversation",
370
+ "product",
371
+ "tenant",
372
+ ];
373
+ const REVIEW_PROMOTION_TARGETS = [
374
+ "gold",
375
+ "silver",
376
+ "reject",
377
+ ];
378
+ const IMPROVEMENT_KINDS = [
379
+ "prompt",
380
+ "policy",
381
+ "model_selection",
382
+ "tooling",
383
+ "evaluation",
384
+ "other",
385
+ ];
386
+ const IMPROVEMENT_STATUSES = [
387
+ "proposed",
388
+ "accepted",
389
+ "rejected",
390
+ "applied",
391
+ ];
392
+ export const isCodaliStorageValidationOk = (result) => result.ok;
393
+ export const isCodaliStoragePrivacyExportAllowed = (privacy) => privacy.exportAllowed &&
394
+ (!privacy.containsPersonalData || privacy.redactionStatus === "redacted");
395
+ export const isCodaliStoragePrivacyTrainingAllowed = (privacy) => privacy.trainingAllowed &&
396
+ (!privacy.containsPersonalData || privacy.redactionStatus === "redacted");
397
+ export const isCodaliStoragePrivacyUploadAllowed = (privacy) => privacy.uploadAllowed &&
398
+ (!privacy.containsPersonalData || privacy.redactionStatus === "redacted");
399
+ export const isCodaliStorageRecordExportAllowed = (record) => isCodaliStoragePrivacyExportAllowed(record.privacy);
400
+ export const validateCodaliStoragePrivacyMetadata = (input) => {
401
+ const bag = { issues: [] };
402
+ const record = requireRecord(input, "$", bag);
403
+ if (!record)
404
+ return fail(bag);
405
+ const schemaVersion = readSchemaVersion(record, "$", bag);
406
+ const classification = readRequiredEnum(record, ["classification"], PRIVACY_CLASSIFICATIONS, "$.classification", bag);
407
+ const containsPersonalData = readRequiredBoolean(record, ["containsPersonalData", "contains_personal_data"], "$.containsPersonalData", bag);
408
+ const redactionStatus = readRequiredEnum(record, ["redactionStatus", "redaction_status"], REDACTION_STATUSES, "$.redactionStatus", bag);
409
+ const uploadAllowed = readRequiredBoolean(record, ["uploadAllowed", "upload_allowed"], "$.uploadAllowed", bag);
410
+ const exportAllowed = readRequiredBoolean(record, ["exportAllowed", "export_allowed"], "$.exportAllowed", bag);
411
+ const trainingAllowed = readRequiredBoolean(record, ["trainingAllowed", "training_allowed"], "$.trainingAllowed", bag);
412
+ if (containsPersonalData === true &&
413
+ redactionStatus !== undefined &&
414
+ redactionStatus !== "redacted" &&
415
+ (uploadAllowed === true || exportAllowed === true || trainingAllowed === true)) {
416
+ addIssue(bag, "$.privacy", "privacy_allowance_requires_redaction", "Personal data must be redacted before upload, export, or training can be allowed.");
417
+ }
418
+ if (!schemaVersion ||
419
+ !classification ||
420
+ containsPersonalData === undefined ||
421
+ !redactionStatus ||
422
+ uploadAllowed === undefined ||
423
+ exportAllowed === undefined ||
424
+ trainingAllowed === undefined) {
425
+ return fail(bag);
426
+ }
427
+ const privacy = {
428
+ schemaVersion,
429
+ classification,
430
+ containsPersonalData,
431
+ redactionStatus,
432
+ uploadAllowed,
433
+ exportAllowed,
434
+ trainingAllowed,
435
+ };
436
+ copyOptionalStringArray(record, privacy, "policyTags", ["policyTags", "policy_tags"], "$.policyTags", bag);
437
+ copyOptionalString(record, privacy, "retentionUntil", ["retentionUntil", "retention_until"], "$.retentionUntil", bag);
438
+ copyOptionalString(record, privacy, "deletionRequestedAt", ["deletionRequestedAt", "deletion_requested_at"], "$.deletionRequestedAt", bag);
439
+ copyOptionalString(record, privacy, "redactionSummary", ["redactionSummary", "redaction_summary"], "$.redactionSummary", bag);
440
+ copyOptionalMetadata(record, privacy, bag);
441
+ return bag.issues.length > 0 ? fail(bag) : ok(privacy);
442
+ };
443
+ export const validateCodaliStorageObjectRef = (input) => {
444
+ const bag = { issues: [] };
445
+ const record = requireRecord(input, "$", bag);
446
+ if (!record)
447
+ return fail(bag);
448
+ const schemaVersion = readSchemaVersion(record, "$", bag);
449
+ const refId = readRequiredNonEmptyString(record, ["refId", "ref_id"], "$.refId", bag);
450
+ const kind = readRequiredEnum(record, ["kind"], OBJECT_REF_KINDS, "$.kind", bag);
451
+ const contentHash = readRequiredNonEmptyString(record, ["contentHash", "content_hash", "sha256"], "$.contentHash", bag);
452
+ const byteSize = readRequiredNonNegativeInteger(record, ["byteSize", "byte_size", "sizeBytes", "size_bytes"], "$.byteSize", bag);
453
+ const mimeType = readRequiredNonEmptyString(record, ["mimeType", "mime_type", "mediaType", "media_type"], "$.mimeType", bag);
454
+ const privacyFlags = readObjectPrivacyFlags(record, "$.privacyFlags", bag);
455
+ const ownerScope = readObjectOwnerScope(record, "$.ownerScope", bag);
456
+ const deletionGroupId = readRequiredNonEmptyString(record, ["deletionGroupId", "deletion_group_id"], "$.deletionGroupId", bag);
457
+ const retentionClass = readRequiredEnum(record, ["retentionClass", "retention_class"], OBJECT_RETENTION_CLASSES, "$.retentionClass", bag);
458
+ if (retentionClass === "do_not_store") {
459
+ addIssue(bag, "$.retentionClass", "object_ref_do_not_store", "Object refs with do_not_store retention cannot be persisted.");
460
+ }
461
+ if (!schemaVersion ||
462
+ !refId ||
463
+ !kind ||
464
+ !contentHash ||
465
+ byteSize === undefined ||
466
+ !mimeType ||
467
+ !privacyFlags ||
468
+ !ownerScope ||
469
+ !deletionGroupId ||
470
+ !retentionClass) {
471
+ return fail(bag);
472
+ }
473
+ const objectRef = {
474
+ schemaVersion,
475
+ refId,
476
+ kind,
477
+ contentHash,
478
+ byteSize,
479
+ mimeType,
480
+ privacyFlags,
481
+ ownerScope,
482
+ deletionGroupId,
483
+ retentionClass,
484
+ mediaType: mimeType,
485
+ sizeBytes: byteSize,
486
+ sha256: contentHash,
487
+ };
488
+ copyOptionalString(record, objectRef, "uri", ["uri"], "$.uri", bag);
489
+ copyOptionalString(record, objectRef, "bucket", ["bucket"], "$.bucket", bag);
490
+ copyOptionalString(record, objectRef, "key", ["key"], "$.key", bag);
491
+ copyOptionalString(record, objectRef, "ownerScopeHash", ["ownerScopeHash", "owner_scope_hash"], "$.ownerScopeHash", bag);
492
+ copyOptionalString(record, objectRef, "createdAt", ["createdAt", "created_at"], "$.createdAt", bag);
493
+ copyOptionalMetadata(record, objectRef, bag);
494
+ if (!objectRef.uri && !objectRef.bucket && !objectRef.key) {
495
+ addIssue(bag, "$", "object_location_required", "Object refs must include at least one location hint: uri, bucket, or key.");
496
+ }
497
+ return bag.issues.length > 0 ? fail(bag) : ok(objectRef);
498
+ };
499
+ export const validateCodaliStorageGatewayRecord = (input) => {
500
+ const bag = { issues: [] };
501
+ const record = requireRecord(input, "$", bag);
502
+ if (!record)
503
+ return fail(bag);
504
+ const schemaVersion = readSchemaVersion(record, "$", bag);
505
+ const recordType = readRecordType(record, "gateway_record", bag);
506
+ const recordId = readRequiredNonEmptyString(record, ["recordId", "record_id"], "$.recordId", bag);
507
+ const runId = readRequiredNonEmptyString(record, ["runId", "run_id"], "$.runId", bag);
508
+ const createdAt = readRequiredNonEmptyString(record, ["createdAt", "created_at"], "$.createdAt", bag);
509
+ const status = readRequiredEnum(record, ["status"], GATEWAY_STATUSES, "$.status", bag);
510
+ const query = readRequiredNonEmptyString(record, ["query"], "$.query", bag);
511
+ const privacy = readPrivacy(record, "$.privacy", bag);
512
+ if (!schemaVersion || !recordType || !recordId || !runId || !createdAt || !status || !query || !privacy) {
513
+ return fail(bag);
514
+ }
515
+ const gatewayRecord = {
516
+ schemaVersion,
517
+ recordType,
518
+ recordId,
519
+ runId,
520
+ createdAt,
521
+ status,
522
+ query,
523
+ privacy,
524
+ };
525
+ copyOptionalString(record, gatewayRecord, "answerSummary", ["answerSummary", "answer_summary"], "$.answerSummary", bag);
526
+ copyOptionalObjectRef(record, gatewayRecord, "requestRef", ["requestRef", "request_ref"], "$.requestRef", bag);
527
+ copyOptionalObjectRef(record, gatewayRecord, "responseRef", ["responseRef", "response_ref"], "$.responseRef", bag);
528
+ copyOptionalObjectRef(record, gatewayRecord, "traceRef", ["traceRef", "trace_ref"], "$.traceRef", bag);
529
+ copyOptionalObjectRef(record, gatewayRecord, "answerRef", ["answerRef", "answer_ref"], "$.answerRef", bag);
530
+ copyOptionalModel(record, gatewayRecord, bag);
531
+ copyOptionalUsage(record, gatewayRecord, bag);
532
+ copyOptionalMetadata(record, gatewayRecord, bag);
533
+ return bag.issues.length > 0 ? fail(bag) : ok(gatewayRecord);
534
+ };
535
+ export const validateCodaliStorageDatasetRecord = (input) => {
536
+ const bag = { issues: [] };
537
+ const record = requireRecord(input, "$", bag);
538
+ if (!record)
539
+ return fail(bag);
540
+ const schemaVersion = readSchemaVersion(record, "$", bag);
541
+ const recordType = readRecordType(record, "dataset_record", bag);
542
+ const recordId = readRequiredNonEmptyString(record, ["recordId", "record_id"], "$.recordId", bag);
543
+ const datasetKind = readRequiredEnum(record, ["datasetKind", "dataset_kind"], DATASET_KINDS, "$.datasetKind", bag);
544
+ const createdAt = readRequiredNonEmptyString(record, ["createdAt", "created_at"], "$.createdAt", bag);
545
+ const inputRef = readRequiredObjectRef(record, ["inputRef", "input_ref"], "$.inputRef", bag);
546
+ const privacy = readPrivacy(record, "$.privacy", bag);
547
+ if (!schemaVersion || !recordType || !recordId || !datasetKind || !createdAt || !inputRef || !privacy) {
548
+ return fail(bag);
549
+ }
550
+ const datasetRecord = {
551
+ schemaVersion,
552
+ recordType,
553
+ recordId,
554
+ datasetKind,
555
+ createdAt,
556
+ inputRef,
557
+ privacy,
558
+ };
559
+ copyOptionalString(record, datasetRecord, "sourceGatewayRecordId", ["sourceGatewayRecordId", "source_gateway_record_id"], "$.sourceGatewayRecordId", bag);
560
+ copyOptionalObjectRef(record, datasetRecord, "outputRef", ["outputRef", "output_ref"], "$.outputRef", bag);
561
+ const evidenceRefs = readOptionalObjectRefArray(record, ["evidenceRefs", "evidence_refs"], "$.evidenceRefs", bag);
562
+ if (evidenceRefs)
563
+ datasetRecord.evidenceRefs = evidenceRefs;
564
+ copyOptionalQuality(record, datasetRecord, bag);
565
+ copyOptionalMetadata(record, datasetRecord, bag);
566
+ return bag.issues.length > 0 ? fail(bag) : ok(datasetRecord);
567
+ };
568
+ export const validateCodaliStorageExportManifest = (input) => {
569
+ const bag = { issues: [] };
570
+ const record = requireRecord(input, "$", bag);
571
+ if (!record)
572
+ return fail(bag);
573
+ const schemaVersion = readSchemaVersion(record, "$", bag);
574
+ const recordType = readRecordType(record, "export_manifest", bag);
575
+ const manifestId = readRequiredNonEmptyString(record, ["manifestId", "manifest_id"], "$.manifestId", bag);
576
+ const createdAt = readRequiredNonEmptyString(record, ["createdAt", "created_at"], "$.createdAt", bag);
577
+ const exportKind = readRequiredEnum(record, ["exportKind", "export_kind"], CODALI_STORAGE_EXPORT_KINDS, "$.exportKind", bag);
578
+ const exportFormat = readRequiredEnum(record, ["exportFormat", "export_format"], EXPORT_FORMATS, "$.exportFormat", bag);
579
+ const recordCount = readRequiredNonNegativeInteger(record, ["recordCount", "record_count"], "$.recordCount", bag);
580
+ const records = readManifestRecordRefs(record, bag);
581
+ const artifactRefs = readOptionalObjectRefArray(record, ["artifactRefs", "artifact_refs"], "$.artifactRefs", bag);
582
+ if (readAlias(record, ["artifactRefs", "artifact_refs"]) === undefined) {
583
+ addIssue(bag, "$.artifactRefs", "required", "Export manifest artifact_refs are required.");
584
+ }
585
+ else if (artifactRefs && artifactRefs.length === 0) {
586
+ addIssue(bag, "$.artifactRefs", "expected_non_empty_array", "Export manifest artifact_refs must include at least one artifact.");
587
+ }
588
+ const checksum = readRequiredNonEmptyString(record, ["checksum"], "$.checksum", bag);
589
+ const privacySummary = readOptionalExportPrivacySummary(record, bag);
590
+ if (readAlias(record, ["privacySummary", "privacy_summary"]) === undefined) {
591
+ addIssue(bag, "$.privacySummary", "required", "Export manifest privacy_summary is required.");
592
+ }
593
+ const lineage = readOptionalExportLineage(record, bag);
594
+ if (readAlias(record, ["lineage"]) === undefined) {
595
+ addIssue(bag, "$.lineage", "required", "Export manifest lineage is required.");
596
+ }
597
+ const deletionGroupSnapshot = readOptionalExportDeletionGroupSnapshot(record, ["deletionGroupSnapshot", "deletion_group_snapshot"], bag);
598
+ if (readAlias(record, ["deletionGroupSnapshot", "deletion_group_snapshot"]) === undefined) {
599
+ addIssue(bag, "$.deletionGroupSnapshot", "required", "Export manifest deletion_group_snapshot is required.");
600
+ }
601
+ const privacy = readPrivacy(record, "$.privacy", bag);
602
+ if (!schemaVersion ||
603
+ !recordType ||
604
+ !manifestId ||
605
+ !createdAt ||
606
+ !exportKind ||
607
+ !exportFormat ||
608
+ recordCount === undefined ||
609
+ !records ||
610
+ !artifactRefs ||
611
+ !checksum ||
612
+ !privacySummary ||
613
+ !lineage ||
614
+ !deletionGroupSnapshot ||
615
+ !privacy) {
616
+ return fail(bag);
617
+ }
618
+ if (recordCount !== records.length) {
619
+ addIssue(bag, "$.recordCount", "record_count_mismatch", "Export manifest record_count must match records length.");
620
+ }
621
+ if (privacySummary.recordCount !== recordCount) {
622
+ addIssue(bag, "$.privacySummary.recordCount", "record_count_mismatch", "Export manifest privacy_summary record_count must match record_count.");
623
+ }
624
+ if (lineage.exportKind !== exportKind) {
625
+ addIssue(bag, "$.lineage.exportKind", "export_kind_mismatch", "Export manifest lineage export_kind must match export_kind.");
626
+ }
627
+ if (!artifactRefs.some((ref) => ref.contentHash === checksum)) {
628
+ addIssue(bag, "$.checksum", "checksum_not_in_artifact_refs", "Export manifest checksum must match one artifact content_hash.");
629
+ }
630
+ const lineageRecordIds = new Set(lineage.sourceRecordIds);
631
+ const lineageObjectHashes = new Set(lineage.sourceObjectHashes);
632
+ for (const ref of records) {
633
+ if (!lineageRecordIds.has(ref.recordId)) {
634
+ addIssue(bag, "$.lineage.sourceRecordIds", "lineage_missing_record_id", "Export manifest lineage must include every manifest record id.");
635
+ break;
636
+ }
637
+ if (ref.objectRef && !lineageObjectHashes.has(ref.objectRef.contentHash)) {
638
+ addIssue(bag, "$.lineage.sourceObjectHashes", "lineage_missing_object_hash", "Export manifest lineage must include every manifest record object content_hash.");
639
+ break;
640
+ }
641
+ }
642
+ const deletionSnapshotIds = new Set(deletionGroupSnapshot.deletionGroupIds);
643
+ for (const ref of records) {
644
+ const recordDeletionGroups = deletionGroupSnapshot.byRecordId[ref.recordId];
645
+ if (!recordDeletionGroups || recordDeletionGroups.length === 0) {
646
+ addIssue(bag, "$.deletionGroupSnapshot.byRecordId", "deletion_snapshot_missing_record_id", "Export manifest deletion_group_snapshot must include every manifest record id.");
647
+ break;
648
+ }
649
+ if (ref.objectRef && !recordDeletionGroups.includes(ref.objectRef.deletionGroupId)) {
650
+ addIssue(bag, "$.deletionGroupSnapshot.byRecordId", "deletion_snapshot_missing_object_group", "Export manifest deletion_group_snapshot must include every manifest record object deletion_group_id.");
651
+ break;
652
+ }
653
+ for (const deletionGroupId of recordDeletionGroups) {
654
+ if (!deletionSnapshotIds.has(deletionGroupId)) {
655
+ addIssue(bag, "$.deletionGroupSnapshot.deletionGroupIds", "deletion_snapshot_missing_group_id", "Export manifest deletion_group_snapshot deletion_group_ids must include every by_record_id group.");
656
+ break;
657
+ }
658
+ }
659
+ }
660
+ const manifest = {
661
+ schemaVersion,
662
+ recordType,
663
+ manifestId,
664
+ createdAt,
665
+ exportKind,
666
+ exportFormat,
667
+ recordCount,
668
+ records,
669
+ artifactRefs,
670
+ checksum,
671
+ privacySummary,
672
+ lineage,
673
+ deletionGroupSnapshot,
674
+ privacy,
675
+ };
676
+ copyOptionalString(record, manifest, "generatedBy", ["generatedBy", "generated_by"], "$.generatedBy", bag);
677
+ copyOptionalMetadata(record, manifest, bag);
678
+ return bag.issues.length > 0 ? fail(bag) : ok(manifest);
679
+ };
680
+ export const validateCodaliStorageFeedbackRecord = (input) => {
681
+ const bag = { issues: [] };
682
+ const record = requireRecord(input, "$", bag);
683
+ if (!record)
684
+ return fail(bag);
685
+ const schemaVersion = readSchemaVersion(record, "$", bag);
686
+ const recordType = readRecordType(record, "feedback_record", bag);
687
+ const feedbackId = readRequiredNonEmptyString(record, ["feedbackId", "feedback_id"], "$.feedbackId", bag);
688
+ const createdAt = readRequiredNonEmptyString(record, ["createdAt", "created_at"], "$.createdAt", bag);
689
+ const source = readRequiredEnum(record, ["source"], FEEDBACK_SOURCES, "$.source", bag);
690
+ const runId = readRequiredNonEmptyString(record, ["runId", "run_id"], "$.runId", bag);
691
+ const deletionGroupId = readRequiredNonEmptyString(record, ["deletionGroupId", "deletion_group_id"], "$.deletionGroupId", bag);
692
+ const productScope = readProductScope(record, "$.productScope", bag);
693
+ const requesterScope = readRequesterScope(record, "$.requesterScope", bag);
694
+ const candidateRecords = readCandidateRecordRefs(record, "$.candidateRecords", bag);
695
+ const targetType = readRequiredEnum(record, ["targetType", "target_type"], RECORD_TYPES, "$.targetType", bag);
696
+ const targetId = readRequiredNonEmptyString(record, ["targetId", "target_id"], "$.targetId", bag);
697
+ const privacy = readPrivacy(record, "$.privacy", bag);
698
+ if (!schemaVersion ||
699
+ !recordType ||
700
+ !feedbackId ||
701
+ !createdAt ||
702
+ !source ||
703
+ !runId ||
704
+ !deletionGroupId ||
705
+ !productScope ||
706
+ !requesterScope ||
707
+ !candidateRecords ||
708
+ !targetType ||
709
+ !targetId ||
710
+ !privacy) {
711
+ return fail(bag);
712
+ }
713
+ requireTargetInCandidateRecords(candidateRecords, targetType, targetId, "$.targetId", bag);
714
+ const feedback = {
715
+ schemaVersion,
716
+ recordType,
717
+ feedbackId,
718
+ createdAt,
719
+ source,
720
+ runId,
721
+ deletionGroupId,
722
+ productScope,
723
+ requesterScope,
724
+ candidateRecords,
725
+ targetType,
726
+ targetId,
727
+ privacy,
728
+ };
729
+ copyOptionalUnitNumber(record, feedback, "rating", ["rating"], "$.rating", bag);
730
+ copyOptionalString(record, feedback, "comment", ["comment"], "$.comment", bag);
731
+ copyOptionalStringArray(record, feedback, "labels", ["labels"], "$.labels", bag);
732
+ copyOptionalMetadata(record, feedback, bag);
733
+ return bag.issues.length > 0 ? fail(bag) : ok(feedback);
734
+ };
735
+ export const validateCodaliStorageReviewRecord = (input) => {
736
+ const bag = { issues: [] };
737
+ const record = requireRecord(input, "$", bag);
738
+ if (!record)
739
+ return fail(bag);
740
+ const schemaVersion = readSchemaVersion(record, "$", bag);
741
+ const recordType = readRecordType(record, "review_record", bag);
742
+ const reviewId = readRequiredNonEmptyString(record, ["reviewId", "review_id"], "$.reviewId", bag);
743
+ const createdAt = readRequiredNonEmptyString(record, ["createdAt", "created_at"], "$.createdAt", bag);
744
+ const reviewerType = readRequiredEnum(record, ["reviewerType", "reviewer_type"], REVIEWER_TYPES, "$.reviewerType", bag);
745
+ const runId = readRequiredNonEmptyString(record, ["runId", "run_id"], "$.runId", bag);
746
+ const deletionGroupId = readRequiredNonEmptyString(record, ["deletionGroupId", "deletion_group_id"], "$.deletionGroupId", bag);
747
+ const productScope = readProductScope(record, "$.productScope", bag);
748
+ const requesterScope = readRequesterScope(record, "$.requesterScope", bag);
749
+ const candidateRecords = readCandidateRecordRefs(record, "$.candidateRecords", bag);
750
+ const targetType = readRequiredEnum(record, ["targetType", "target_type"], RECORD_TYPES, "$.targetType", bag);
751
+ const targetId = readRequiredNonEmptyString(record, ["targetId", "target_id"], "$.targetId", bag);
752
+ const decision = readRequiredEnum(record, ["decision"], REVIEW_DECISIONS, "$.decision", bag);
753
+ const privacy = readPrivacy(record, "$.privacy", bag);
754
+ if (!schemaVersion ||
755
+ !recordType ||
756
+ !reviewId ||
757
+ !createdAt ||
758
+ !reviewerType ||
759
+ !runId ||
760
+ !deletionGroupId ||
761
+ !productScope ||
762
+ !requesterScope ||
763
+ !candidateRecords ||
764
+ !targetType ||
765
+ !targetId ||
766
+ !decision ||
767
+ !privacy) {
768
+ return fail(bag);
769
+ }
770
+ requireTargetInCandidateRecords(candidateRecords, targetType, targetId, "$.targetId", bag);
771
+ const review = {
772
+ schemaVersion,
773
+ recordType,
774
+ reviewId,
775
+ createdAt,
776
+ reviewerType,
777
+ runId,
778
+ deletionGroupId,
779
+ productScope,
780
+ requesterScope,
781
+ candidateRecords,
782
+ targetType,
783
+ targetId,
784
+ decision,
785
+ privacy,
786
+ };
787
+ copyOptionalString(record, review, "reviewerId", ["reviewerId", "reviewer_id"], "$.reviewerId", bag);
788
+ copyOptionalStringArray(record, review, "reasons", ["reasons"], "$.reasons", bag);
789
+ copyOptionalStringArray(record, review, "labels", ["labels"], "$.labels", bag);
790
+ const promotionTarget = readOptionalEnum(record, ["promotionTarget", "promotion_target"], REVIEW_PROMOTION_TARGETS, "$.promotionTarget", bag);
791
+ if (promotionTarget)
792
+ review.promotionTarget = promotionTarget;
793
+ copyOptionalStringArray(record, review, "promotedRecordIds", ["promotedRecordIds", "promoted_record_ids"], "$.promotedRecordIds", bag);
794
+ copyOptionalMetadata(record, review, bag);
795
+ return bag.issues.length > 0 ? fail(bag) : ok(review);
796
+ };
797
+ export const validateCodaliStorageImprovementRecord = (input) => {
798
+ const bag = { issues: [] };
799
+ const record = requireRecord(input, "$", bag);
800
+ if (!record)
801
+ return fail(bag);
802
+ const schemaVersion = readSchemaVersion(record, "$", bag);
803
+ const recordType = readRecordType(record, "improvement_record", bag);
804
+ const improvementId = readRequiredNonEmptyString(record, ["improvementId", "improvement_id"], "$.improvementId", bag);
805
+ const createdAt = readRequiredNonEmptyString(record, ["createdAt", "created_at"], "$.createdAt", bag);
806
+ const improvementKind = readRequiredEnum(record, ["improvementKind", "improvement_kind"], IMPROVEMENT_KINDS, "$.improvementKind", bag);
807
+ const status = readRequiredEnum(record, ["status"], IMPROVEMENT_STATUSES, "$.status", bag);
808
+ const summary = readRequiredNonEmptyString(record, ["summary"], "$.summary", bag);
809
+ const sourceRecordIds = readRequiredStringArray(record, ["sourceRecordIds", "source_record_ids"], "$.sourceRecordIds", bag);
810
+ const trainingEligible = readRequiredBoolean(record, ["trainingEligible", "training_eligible"], "$.trainingEligible", bag);
811
+ const privacy = readPrivacy(record, "$.privacy", bag);
812
+ if (!schemaVersion ||
813
+ !recordType ||
814
+ !improvementId ||
815
+ !createdAt ||
816
+ !improvementKind ||
817
+ !status ||
818
+ !summary ||
819
+ !sourceRecordIds ||
820
+ trainingEligible === undefined ||
821
+ !privacy) {
822
+ return fail(bag);
823
+ }
824
+ if (trainingEligible && !isCodaliStoragePrivacyTrainingAllowed(privacy)) {
825
+ addIssue(bag, "$.trainingEligible", "training_not_allowed_by_privacy", "training_eligible can only be true when privacy metadata allows training on eligible redacted data.");
826
+ }
827
+ const improvement = {
828
+ schemaVersion,
829
+ recordType,
830
+ improvementId,
831
+ createdAt,
832
+ improvementKind,
833
+ status,
834
+ summary,
835
+ sourceRecordIds,
836
+ trainingEligible,
837
+ privacy,
838
+ };
839
+ copyOptionalString(record, improvement, "exportManifestId", ["exportManifestId", "export_manifest_id"], "$.exportManifestId", bag);
840
+ copyOptionalObjectRef(record, improvement, "candidateRef", ["candidateRef", "candidate_ref"], "$.candidateRef", bag);
841
+ copyOptionalMetadata(record, improvement, bag);
842
+ return bag.issues.length > 0 ? fail(bag) : ok(improvement);
843
+ };
844
+ export const validateCodaliStorageRecord = (input) => {
845
+ const bag = { issues: [] };
846
+ const record = requireRecord(input, "$", bag);
847
+ if (!record)
848
+ return fail(bag);
849
+ const recordType = readAlias(record, ["recordType", "record_type"]);
850
+ switch (recordType) {
851
+ case "gateway_record":
852
+ return validateCodaliStorageGatewayRecord(input);
853
+ case "dataset_record":
854
+ return validateCodaliStorageDatasetRecord(input);
855
+ case "export_manifest":
856
+ return validateCodaliStorageExportManifest(input);
857
+ case "feedback_record":
858
+ return validateCodaliStorageFeedbackRecord(input);
859
+ case "review_record":
860
+ return validateCodaliStorageReviewRecord(input);
861
+ case "improvement_record":
862
+ return validateCodaliStorageImprovementRecord(input);
863
+ default:
864
+ addIssue(bag, "$.recordType", "unsupported_record_type", "Storage record type must be one of the versioned Codali storage record types.");
865
+ return fail(bag);
866
+ }
867
+ };
868
+ export const validateCodaliStorageContractFixtureSet = (input) => {
869
+ const bag = { issues: [] };
870
+ const record = requireRecord(input, "$", bag);
871
+ if (!record)
872
+ return fail(bag);
873
+ const schemaVersion = readRequiredLiteral(record, ["schemaVersion", "schema_version"], CODALI_STORAGE_CONTRACT_FIXTURE_SCHEMA_VERSION, "$.schemaVersion", bag);
874
+ const contractSchemaVersion = readRequiredLiteral(record, ["contractSchemaVersion", "contract_schema_version"], CODALI_STORAGE_CONTRACT_SCHEMA_VERSION, "$.contractSchemaVersion", bag);
875
+ const distribution = readDistribution(record, bag);
876
+ const fixturesRecord = readRequiredRecord(record, ["fixtures"], "$.fixtures", bag);
877
+ if (!schemaVersion || !contractSchemaVersion || !distribution || !fixturesRecord) {
878
+ return fail(bag);
879
+ }
880
+ const privacyMetadata = validateNested(validateCodaliStoragePrivacyMetadata, fixturesRecord.privacy_metadata ?? fixturesRecord.privacyMetadata, "$.fixtures.privacyMetadata", bag);
881
+ const objectRef = validateNested(validateCodaliStorageObjectRef, fixturesRecord.object_ref ?? fixturesRecord.objectRef, "$.fixtures.objectRef", bag);
882
+ const gatewayRecord = validateNested(validateCodaliStorageGatewayRecord, fixturesRecord.gateway_record ?? fixturesRecord.gatewayRecord, "$.fixtures.gatewayRecord", bag);
883
+ const datasetRecord = validateNested(validateCodaliStorageDatasetRecord, fixturesRecord.dataset_record ?? fixturesRecord.datasetRecord, "$.fixtures.datasetRecord", bag);
884
+ const exportManifest = validateNested(validateCodaliStorageExportManifest, fixturesRecord.export_manifest ?? fixturesRecord.exportManifest, "$.fixtures.exportManifest", bag);
885
+ const feedbackRecord = validateNested(validateCodaliStorageFeedbackRecord, fixturesRecord.feedback_record ?? fixturesRecord.feedbackRecord, "$.fixtures.feedbackRecord", bag);
886
+ const reviewRecord = validateNested(validateCodaliStorageReviewRecord, fixturesRecord.review_record ?? fixturesRecord.reviewRecord, "$.fixtures.reviewRecord", bag);
887
+ const improvementRecord = validateNested(validateCodaliStorageImprovementRecord, fixturesRecord.improvement_record ?? fixturesRecord.improvementRecord, "$.fixtures.improvementRecord", bag);
888
+ if (!privacyMetadata ||
889
+ !objectRef ||
890
+ !gatewayRecord ||
891
+ !datasetRecord ||
892
+ !exportManifest ||
893
+ !feedbackRecord ||
894
+ !reviewRecord ||
895
+ !improvementRecord) {
896
+ return fail(bag);
897
+ }
898
+ const fixtureSet = {
899
+ schemaVersion,
900
+ contractSchemaVersion,
901
+ distribution,
902
+ fixtures: {
903
+ privacyMetadata,
904
+ objectRef,
905
+ gatewayRecord,
906
+ datasetRecord,
907
+ exportManifest,
908
+ feedbackRecord,
909
+ reviewRecord,
910
+ improvementRecord,
911
+ },
912
+ };
913
+ return bag.issues.length > 0 ? fail(bag) : ok(fixtureSet);
914
+ };
915
+ const ok = (value) => ({
916
+ ok: true,
917
+ value,
918
+ issues: [],
919
+ });
920
+ const fail = (bag) => ({
921
+ ok: false,
922
+ issues: bag.issues,
923
+ });
924
+ const addIssue = (bag, path, code, message) => {
925
+ bag.issues.push({ path, code, message });
926
+ };
927
+ const isRecord = (input) => typeof input === "object" && input !== null && !Array.isArray(input);
928
+ const requireRecord = (input, path, bag) => {
929
+ if (isRecord(input))
930
+ return input;
931
+ addIssue(bag, path, "expected_object", "Expected an object.");
932
+ return undefined;
933
+ };
934
+ const readRequiredRecord = (record, keys, path, bag) => {
935
+ const value = readAlias(record, keys);
936
+ if (value === undefined) {
937
+ addIssue(bag, path, "required", "Required object is missing.");
938
+ return undefined;
939
+ }
940
+ if (!isRecord(value)) {
941
+ addIssue(bag, path, "expected_object", "Expected an object.");
942
+ return undefined;
943
+ }
944
+ return value;
945
+ };
946
+ const readAlias = (record, keys) => {
947
+ for (const key of keys) {
948
+ if (Object.prototype.hasOwnProperty.call(record, key))
949
+ return record[key];
950
+ }
951
+ return undefined;
952
+ };
953
+ const readSchemaVersion = (record, path, bag) => {
954
+ const value = readRequiredNonEmptyString(record, ["schemaVersion", "schema_version"], `${path}.schemaVersion`, bag);
955
+ if (!value)
956
+ return undefined;
957
+ if (!CODALI_STORAGE_CONTRACT_SCHEMA_VERSIONS.includes(value)) {
958
+ addIssue(bag, `${path}.schemaVersion`, "unsupported_schema_version", `Unsupported schema version: ${value}.`);
959
+ return undefined;
960
+ }
961
+ return value;
962
+ };
963
+ const readRequiredLiteral = (record, keys, expected, path, bag) => {
964
+ const value = readRequiredNonEmptyString(record, keys, path, bag);
965
+ if (!value)
966
+ return undefined;
967
+ if (value !== expected) {
968
+ addIssue(bag, path, "unexpected_literal", `Expected ${expected}.`);
969
+ return undefined;
970
+ }
971
+ return expected;
972
+ };
973
+ const readRequiredNonEmptyString = (record, keys, path, bag) => {
974
+ const value = readAlias(record, keys);
975
+ if (value === undefined) {
976
+ addIssue(bag, path, "required", "Required string is missing.");
977
+ return undefined;
978
+ }
979
+ if (typeof value !== "string" || value.trim().length === 0) {
980
+ addIssue(bag, path, "expected_non_empty_string", "Expected a non-empty string.");
981
+ return undefined;
982
+ }
983
+ return value;
984
+ };
985
+ const readOptionalString = (record, keys, path, bag) => {
986
+ const value = readAlias(record, keys);
987
+ if (value === undefined)
988
+ return undefined;
989
+ if (typeof value !== "string") {
990
+ addIssue(bag, path, "expected_string", "Expected a string.");
991
+ return undefined;
992
+ }
993
+ return value;
994
+ };
995
+ const readRequiredBoolean = (record, keys, path, bag) => {
996
+ const value = readAlias(record, keys);
997
+ if (value === undefined) {
998
+ addIssue(bag, path, "required", "Required boolean is missing.");
999
+ return undefined;
1000
+ }
1001
+ if (typeof value !== "boolean") {
1002
+ addIssue(bag, path, "expected_boolean", "Expected a boolean.");
1003
+ return undefined;
1004
+ }
1005
+ return value;
1006
+ };
1007
+ const readOptionalBoolean = (record, keys, path, bag) => {
1008
+ const value = readAlias(record, keys);
1009
+ if (value === undefined)
1010
+ return undefined;
1011
+ if (typeof value !== "boolean") {
1012
+ addIssue(bag, path, "expected_boolean", "Expected a boolean.");
1013
+ return undefined;
1014
+ }
1015
+ return value;
1016
+ };
1017
+ const readRequiredEnum = (record, keys, allowed, path, bag) => {
1018
+ const value = readRequiredNonEmptyString(record, keys, path, bag);
1019
+ if (!value)
1020
+ return undefined;
1021
+ if (!allowed.includes(value)) {
1022
+ addIssue(bag, path, "expected_enum", `Expected one of: ${allowed.join(", ")}.`);
1023
+ return undefined;
1024
+ }
1025
+ return value;
1026
+ };
1027
+ const readOptionalEnum = (record, keys, allowed, path, bag) => {
1028
+ const value = readAlias(record, keys);
1029
+ if (value === undefined)
1030
+ return undefined;
1031
+ if (typeof value !== "string" || value.trim().length === 0) {
1032
+ addIssue(bag, path, "expected_non_empty_string", "Expected a non-empty string.");
1033
+ return undefined;
1034
+ }
1035
+ if (!allowed.includes(value)) {
1036
+ addIssue(bag, path, "expected_enum", `Expected one of: ${allowed.join(", ")}.`);
1037
+ return undefined;
1038
+ }
1039
+ return value;
1040
+ };
1041
+ const readRequiredNonNegativeInteger = (record, keys, path, bag) => {
1042
+ const value = readAlias(record, keys);
1043
+ if (value === undefined) {
1044
+ addIssue(bag, path, "required", "Required integer is missing.");
1045
+ return undefined;
1046
+ }
1047
+ return validateNonNegativeInteger(value, path, bag);
1048
+ };
1049
+ const readOptionalNonNegativeInteger = (record, keys, path, bag) => {
1050
+ const value = readAlias(record, keys);
1051
+ if (value === undefined)
1052
+ return undefined;
1053
+ return validateNonNegativeInteger(value, path, bag);
1054
+ };
1055
+ const validateNonNegativeInteger = (value, path, bag) => {
1056
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
1057
+ addIssue(bag, path, "expected_non_negative_integer", "Expected a non-negative integer.");
1058
+ return undefined;
1059
+ }
1060
+ return value;
1061
+ };
1062
+ const readOptionalUnitNumber = (record, keys, path, bag) => {
1063
+ const value = readAlias(record, keys);
1064
+ if (value === undefined)
1065
+ return undefined;
1066
+ if (typeof value !== "number" || Number.isNaN(value) || value < 0 || value > 1) {
1067
+ addIssue(bag, path, "expected_unit_number", "Expected a number between 0 and 1.");
1068
+ return undefined;
1069
+ }
1070
+ return value;
1071
+ };
1072
+ const readRequiredStringArray = (record, keys, path, bag) => {
1073
+ const value = readAlias(record, keys);
1074
+ if (value === undefined) {
1075
+ addIssue(bag, path, "required", "Required string array is missing.");
1076
+ return undefined;
1077
+ }
1078
+ return validateStringArray(value, path, bag);
1079
+ };
1080
+ const readOptionalStringArray = (record, keys, path, bag) => {
1081
+ const value = readAlias(record, keys);
1082
+ if (value === undefined)
1083
+ return undefined;
1084
+ return validateStringArray(value, path, bag);
1085
+ };
1086
+ const validateStringArray = (value, path, bag) => {
1087
+ if (!Array.isArray(value)) {
1088
+ addIssue(bag, path, "expected_array", "Expected an array.");
1089
+ return undefined;
1090
+ }
1091
+ const result = [];
1092
+ value.forEach((item, index) => {
1093
+ if (typeof item !== "string" || item.trim().length === 0) {
1094
+ addIssue(bag, `${path}[${index}]`, "expected_non_empty_string", "Expected a non-empty string.");
1095
+ return;
1096
+ }
1097
+ result.push(item);
1098
+ });
1099
+ return result;
1100
+ };
1101
+ const readRecordType = (record, expected, bag) => readRequiredLiteral(record, ["recordType", "record_type"], expected, "$.recordType", bag);
1102
+ const readPrivacy = (record, path, bag) => {
1103
+ const value = readAlias(record, ["privacy"]);
1104
+ if (value === undefined) {
1105
+ addIssue(bag, path, "required", "Privacy metadata is required.");
1106
+ return undefined;
1107
+ }
1108
+ return validateNested(validateCodaliStoragePrivacyMetadata, value, path, bag);
1109
+ };
1110
+ const readObjectPrivacyFlags = (record, path, bag) => {
1111
+ const value = readRequiredRecord(record, ["privacyFlags", "privacy_flags"], path, bag);
1112
+ if (!value)
1113
+ return undefined;
1114
+ const containsPersonalData = readRequiredBoolean(value, ["containsPersonalData", "contains_personal_data"], `${path}.containsPersonalData`, bag);
1115
+ const containsSecrets = readRequiredBoolean(value, ["containsSecrets", "contains_secrets"], `${path}.containsSecrets`, bag);
1116
+ const containsTenantPrivateData = readRequiredBoolean(value, ["containsTenantPrivateData", "contains_tenant_private_data"], `${path}.containsTenantPrivateData`, bag);
1117
+ const containsSourceCode = readRequiredBoolean(value, ["containsSourceCode", "contains_source_code"], `${path}.containsSourceCode`, bag);
1118
+ const containsCustomerData = readRequiredBoolean(value, ["containsCustomerData", "contains_customer_data"], `${path}.containsCustomerData`, bag);
1119
+ const trainingAllowed = readRequiredBoolean(value, ["trainingAllowed", "training_allowed"], `${path}.trainingAllowed`, bag);
1120
+ const evalAllowed = readRequiredBoolean(value, ["evalAllowed", "eval_allowed"], `${path}.evalAllowed`, bag);
1121
+ const replayAllowed = readRequiredBoolean(value, ["replayAllowed", "replay_allowed"], `${path}.replayAllowed`, bag);
1122
+ const exportAllowed = readRequiredBoolean(value, ["exportAllowed", "export_allowed"], `${path}.exportAllowed`, bag);
1123
+ if (containsPersonalData === undefined ||
1124
+ containsSecrets === undefined ||
1125
+ containsTenantPrivateData === undefined ||
1126
+ containsSourceCode === undefined ||
1127
+ containsCustomerData === undefined ||
1128
+ trainingAllowed === undefined ||
1129
+ evalAllowed === undefined ||
1130
+ replayAllowed === undefined ||
1131
+ exportAllowed === undefined) {
1132
+ return undefined;
1133
+ }
1134
+ return {
1135
+ containsPersonalData,
1136
+ containsSecrets,
1137
+ containsTenantPrivateData,
1138
+ containsSourceCode,
1139
+ containsCustomerData,
1140
+ trainingAllowed,
1141
+ evalAllowed,
1142
+ replayAllowed,
1143
+ exportAllowed,
1144
+ };
1145
+ };
1146
+ const readObjectOwnerScope = (record, path, bag) => {
1147
+ const value = readRequiredRecord(record, ["ownerScope", "owner_scope"], path, bag);
1148
+ if (!value)
1149
+ return undefined;
1150
+ const tenantHash = readRequiredNonEmptyString(value, ["tenantHash", "tenant_hash"], `${path}.tenantHash`, bag);
1151
+ const productId = readRequiredNonEmptyString(value, ["productId", "product_id"], `${path}.productId`, bag);
1152
+ const ownerType = readRequiredNonEmptyString(value, ["ownerType", "owner_type"], `${path}.ownerType`, bag);
1153
+ const ownerId = readRequiredNonEmptyString(value, ["ownerId", "owner_id"], `${path}.ownerId`, bag);
1154
+ if (!tenantHash || !productId || !ownerType || !ownerId)
1155
+ return undefined;
1156
+ const ownerScope = {
1157
+ tenantHash,
1158
+ productId,
1159
+ ownerType,
1160
+ ownerId,
1161
+ };
1162
+ copyOptionalString(value, ownerScope, "deploymentId", ["deploymentId", "deployment_id"], `${path}.deploymentId`, bag);
1163
+ copyOptionalString(value, ownerScope, "runId", ["runId", "run_id"], `${path}.runId`, bag);
1164
+ return ownerScope;
1165
+ };
1166
+ const readProductScope = (record, path, bag) => {
1167
+ const value = readRequiredRecord(record, ["productScope", "product_scope"], path, bag);
1168
+ if (!value)
1169
+ return undefined;
1170
+ const productId = readRequiredNonEmptyString(value, ["productId", "product_id"], `${path}.productId`, bag);
1171
+ const tenantHash = readRequiredNonEmptyString(value, ["tenantHash", "tenant_hash"], `${path}.tenantHash`, bag);
1172
+ if (!productId || !tenantHash)
1173
+ return undefined;
1174
+ const productScope = {
1175
+ productId,
1176
+ tenantHash,
1177
+ };
1178
+ copyOptionalString(value, productScope, "deploymentId", ["deploymentId", "deployment_id"], `${path}.deploymentId`, bag);
1179
+ copyOptionalString(value, productScope, "environment", ["environment"], `${path}.environment`, bag);
1180
+ copyOptionalMetadata(value, productScope, bag);
1181
+ return productScope;
1182
+ };
1183
+ const readRequesterScope = (record, path, bag) => {
1184
+ const value = readRequiredRecord(record, ["requesterScope", "requester_scope"], path, bag);
1185
+ if (!value)
1186
+ return undefined;
1187
+ const requesterHash = readRequiredNonEmptyString(value, ["requesterHash", "requester_hash"], `${path}.requesterHash`, bag);
1188
+ const visibility = readRequiredEnum(value, ["visibility"], REQUESTER_SCOPE_VISIBILITIES, `${path}.visibility`, bag);
1189
+ const tenantWide = readOptionalBoolean(value, ["tenantWide", "tenant_wide"], `${path}.tenantWide`, bag) ?? false;
1190
+ if (visibility === "tenant" && tenantWide !== true) {
1191
+ addIssue(bag, `${path}.tenantWide`, "tenant_wide_scope_requires_explicit_allowance", "Requester feedback cannot become tenant-wide visible unless tenant_wide is explicitly true.");
1192
+ }
1193
+ if (!requesterHash || !visibility)
1194
+ return undefined;
1195
+ const requesterScope = {
1196
+ requesterHash,
1197
+ visibility,
1198
+ tenantWide,
1199
+ };
1200
+ copyOptionalString(value, requesterScope, "conversationHash", ["conversationHash", "conversation_hash"], `${path}.conversationHash`, bag);
1201
+ copyOptionalString(value, requesterScope, "requesterType", ["requesterType", "requester_type"], `${path}.requesterType`, bag);
1202
+ copyOptionalMetadata(value, requesterScope, bag);
1203
+ return requesterScope;
1204
+ };
1205
+ const readRequiredObjectRef = (record, keys, path, bag) => {
1206
+ const value = readAlias(record, keys);
1207
+ if (value === undefined) {
1208
+ addIssue(bag, path, "required", "Object ref is required.");
1209
+ return undefined;
1210
+ }
1211
+ return validateNested(validateCodaliStorageObjectRef, value, path, bag);
1212
+ };
1213
+ const readCandidateRecordRefs = (record, path, bag) => {
1214
+ const value = readAlias(record, ["candidateRecords", "candidate_records"]);
1215
+ if (value === undefined) {
1216
+ addIssue(bag, path, "required", "Candidate record links are required.");
1217
+ return undefined;
1218
+ }
1219
+ if (!Array.isArray(value)) {
1220
+ addIssue(bag, path, "expected_array", "Expected an array.");
1221
+ return undefined;
1222
+ }
1223
+ if (value.length === 0) {
1224
+ addIssue(bag, path, "candidate_records_required", "At least one candidate record link is required.");
1225
+ return undefined;
1226
+ }
1227
+ const refs = [];
1228
+ value.forEach((item, index) => {
1229
+ const itemPath = `${path}[${index}]`;
1230
+ const itemRecord = requireRecord(item, itemPath, bag);
1231
+ if (!itemRecord)
1232
+ return;
1233
+ const recordType = readRequiredEnum(itemRecord, ["recordType", "record_type"], RECORD_TYPES, `${itemPath}.recordType`, bag);
1234
+ const recordId = readRequiredNonEmptyString(itemRecord, ["recordId", "record_id"], `${itemPath}.recordId`, bag);
1235
+ if (!recordType || !recordId)
1236
+ return;
1237
+ const ref = {
1238
+ recordType,
1239
+ recordId,
1240
+ };
1241
+ const datasetKind = readOptionalEnum(itemRecord, ["datasetKind", "dataset_kind"], DATASET_KINDS, `${itemPath}.datasetKind`, bag);
1242
+ if (datasetKind)
1243
+ ref.datasetKind = datasetKind;
1244
+ copyOptionalObjectRef(itemRecord, ref, "objectRef", ["objectRef", "object_ref"], `${itemPath}.objectRef`, bag);
1245
+ copyOptionalStringArray(itemRecord, ref, "labels", ["labels"], `${itemPath}.labels`, bag);
1246
+ copyOptionalString(itemRecord, ref, "role", ["role"], `${itemPath}.role`, bag);
1247
+ copyOptionalMetadata(itemRecord, ref, bag);
1248
+ refs.push(ref);
1249
+ });
1250
+ return refs.length > 0 ? refs : undefined;
1251
+ };
1252
+ const requireTargetInCandidateRecords = (candidateRecords, targetType, targetId, path, bag) => {
1253
+ if (!candidateRecords.some((candidate) => candidate.recordType === targetType && candidate.recordId === targetId)) {
1254
+ addIssue(bag, path, "target_not_in_candidate_records", "Feedback and review targets must also be listed in candidate_records.");
1255
+ }
1256
+ };
1257
+ const readOptionalObjectRefArray = (record, keys, path, bag) => {
1258
+ const value = readAlias(record, keys);
1259
+ if (value === undefined)
1260
+ return undefined;
1261
+ if (!Array.isArray(value)) {
1262
+ addIssue(bag, path, "expected_array", "Expected an array.");
1263
+ return undefined;
1264
+ }
1265
+ const result = [];
1266
+ value.forEach((item, index) => {
1267
+ const validated = validateNested(validateCodaliStorageObjectRef, item, `${path}[${index}]`, bag);
1268
+ if (validated)
1269
+ result.push(validated);
1270
+ });
1271
+ return result;
1272
+ };
1273
+ const readManifestRecordRefs = (record, bag) => {
1274
+ const value = readAlias(record, ["records"]);
1275
+ if (value === undefined) {
1276
+ addIssue(bag, "$.records", "required", "Export manifest records are required.");
1277
+ return undefined;
1278
+ }
1279
+ if (!Array.isArray(value)) {
1280
+ addIssue(bag, "$.records", "expected_array", "Expected an array.");
1281
+ return undefined;
1282
+ }
1283
+ const refs = [];
1284
+ value.forEach((item, index) => {
1285
+ const path = `$.records[${index}]`;
1286
+ const itemRecord = requireRecord(item, path, bag);
1287
+ if (!itemRecord)
1288
+ return;
1289
+ const recordType = readRequiredEnum(itemRecord, ["recordType", "record_type"], RECORD_TYPES, `${path}.recordType`, bag);
1290
+ const recordId = readRequiredNonEmptyString(itemRecord, ["recordId", "record_id"], `${path}.recordId`, bag);
1291
+ const schemaVersion = readSchemaVersion(itemRecord, path, bag);
1292
+ const objectRef = readOptionalObjectRef(itemRecord, ["objectRef", "object_ref"], `${path}.objectRef`, bag);
1293
+ if (readAlias(itemRecord, ["objectRef", "object_ref"]) === undefined) {
1294
+ addIssue(bag, `${path}.objectRef`, "manifest_record_object_ref_required", "Export manifest records must include object_ref for reproducibility.");
1295
+ }
1296
+ if (recordType && recordId && schemaVersion) {
1297
+ refs.push({ recordType, recordId, schemaVersion, ...(objectRef ? { objectRef } : {}) });
1298
+ }
1299
+ });
1300
+ return refs;
1301
+ };
1302
+ const readRequiredCountRecord = (record, keys, path, bag) => {
1303
+ const value = readRequiredRecord(record, keys, path, bag);
1304
+ if (!value)
1305
+ return undefined;
1306
+ const output = {};
1307
+ for (const [key, count] of Object.entries(value)) {
1308
+ if (!key.trim()) {
1309
+ addIssue(bag, `${path}.${key}`, "expected_non_empty_string", "Count keys must be non-empty.");
1310
+ continue;
1311
+ }
1312
+ const validated = validateNonNegativeInteger(count, `${path}.${key}`, bag);
1313
+ if (validated !== undefined)
1314
+ output[key] = validated;
1315
+ }
1316
+ return output;
1317
+ };
1318
+ const readOptionalExportPrivacySummary = (record, bag) => {
1319
+ const value = readAlias(record, ["privacySummary", "privacy_summary"]);
1320
+ if (value === undefined)
1321
+ return undefined;
1322
+ const summaryRecord = requireRecord(value, "$.privacySummary", bag);
1323
+ if (!summaryRecord)
1324
+ return undefined;
1325
+ const recordCount = readRequiredNonNegativeInteger(summaryRecord, ["recordCount", "record_count"], "$.privacySummary.recordCount", bag);
1326
+ const containsPersonalData = readRequiredBoolean(summaryRecord, ["containsPersonalData", "contains_personal_data"], "$.privacySummary.containsPersonalData", bag);
1327
+ const containsSecrets = readRequiredBoolean(summaryRecord, ["containsSecrets", "contains_secrets"], "$.privacySummary.containsSecrets", bag);
1328
+ const containsTenantPrivateData = readRequiredBoolean(summaryRecord, ["containsTenantPrivateData", "contains_tenant_private_data"], "$.privacySummary.containsTenantPrivateData", bag);
1329
+ const containsSourceCode = readRequiredBoolean(summaryRecord, ["containsSourceCode", "contains_source_code"], "$.privacySummary.containsSourceCode", bag);
1330
+ const containsCustomerData = readRequiredBoolean(summaryRecord, ["containsCustomerData", "contains_customer_data"], "$.privacySummary.containsCustomerData", bag);
1331
+ const exportAllowedCount = readRequiredNonNegativeInteger(summaryRecord, ["exportAllowedCount", "export_allowed_count"], "$.privacySummary.exportAllowedCount", bag);
1332
+ const trainingAllowedCount = readRequiredNonNegativeInteger(summaryRecord, ["trainingAllowedCount", "training_allowed_count"], "$.privacySummary.trainingAllowedCount", bag);
1333
+ const evalAllowedCount = readRequiredNonNegativeInteger(summaryRecord, ["evalAllowedCount", "eval_allowed_count"], "$.privacySummary.evalAllowedCount", bag);
1334
+ const replayAllowedCount = readRequiredNonNegativeInteger(summaryRecord, ["replayAllowedCount", "replay_allowed_count"], "$.privacySummary.replayAllowedCount", bag);
1335
+ const classifications = readRequiredCountRecord(summaryRecord, ["classifications"], "$.privacySummary.classifications", bag);
1336
+ const redactionStatuses = readRequiredCountRecord(summaryRecord, ["redactionStatuses", "redaction_statuses"], "$.privacySummary.redactionStatuses", bag);
1337
+ if (recordCount === undefined ||
1338
+ containsPersonalData === undefined ||
1339
+ containsSecrets === undefined ||
1340
+ containsTenantPrivateData === undefined ||
1341
+ containsSourceCode === undefined ||
1342
+ containsCustomerData === undefined ||
1343
+ exportAllowedCount === undefined ||
1344
+ trainingAllowedCount === undefined ||
1345
+ evalAllowedCount === undefined ||
1346
+ replayAllowedCount === undefined ||
1347
+ !classifications ||
1348
+ !redactionStatuses) {
1349
+ return undefined;
1350
+ }
1351
+ const summary = {
1352
+ recordCount,
1353
+ containsPersonalData,
1354
+ containsSecrets,
1355
+ containsTenantPrivateData,
1356
+ containsSourceCode,
1357
+ containsCustomerData,
1358
+ exportAllowedCount,
1359
+ trainingAllowedCount,
1360
+ evalAllowedCount,
1361
+ replayAllowedCount,
1362
+ classifications,
1363
+ redactionStatuses,
1364
+ };
1365
+ const policyTags = readOptionalStringArray(summaryRecord, ["policyTags", "policy_tags"], "$.privacySummary.policyTags", bag);
1366
+ if (policyTags)
1367
+ summary.policyTags = policyTags;
1368
+ return summary;
1369
+ };
1370
+ const readOptionalExportLineage = (record, bag) => {
1371
+ const value = readAlias(record, ["lineage"]);
1372
+ if (value === undefined)
1373
+ return undefined;
1374
+ const lineageRecord = requireRecord(value, "$.lineage", bag);
1375
+ if (!lineageRecord)
1376
+ return undefined;
1377
+ const exportKind = readRequiredEnum(lineageRecord, ["exportKind", "export_kind"], CODALI_STORAGE_EXPORT_KINDS, "$.lineage.exportKind", bag);
1378
+ const sourceRecordIds = readRequiredStringArray(lineageRecord, ["sourceRecordIds", "source_record_ids"], "$.lineage.sourceRecordIds", bag);
1379
+ const sourceObjectHashes = readRequiredStringArray(lineageRecord, ["sourceObjectHashes", "source_object_hashes"], "$.lineage.sourceObjectHashes", bag);
1380
+ if (!exportKind || !sourceRecordIds || !sourceObjectHashes)
1381
+ return undefined;
1382
+ const lineage = {
1383
+ exportKind,
1384
+ sourceRecordIds,
1385
+ sourceObjectHashes,
1386
+ };
1387
+ const sourceGatewayRecordIds = readOptionalStringArray(lineageRecord, ["sourceGatewayRecordIds", "source_gateway_record_ids"], "$.lineage.sourceGatewayRecordIds", bag);
1388
+ if (sourceGatewayRecordIds)
1389
+ lineage.sourceGatewayRecordIds = sourceGatewayRecordIds;
1390
+ copyOptionalString(lineageRecord, lineage, "generatedBy", ["generatedBy", "generated_by"], "$.lineage.generatedBy", bag);
1391
+ return lineage;
1392
+ };
1393
+ const readRequiredStringArrayRecord = (record, keys, path, bag) => {
1394
+ const value = readRequiredRecord(record, keys, path, bag);
1395
+ if (!value)
1396
+ return undefined;
1397
+ const output = {};
1398
+ for (const [key, item] of Object.entries(value)) {
1399
+ if (!key.trim()) {
1400
+ addIssue(bag, `${path}.${key}`, "expected_non_empty_string", "String-array keys must be non-empty.");
1401
+ continue;
1402
+ }
1403
+ const validated = validateStringArray(item, `${path}.${key}`, bag);
1404
+ if (validated)
1405
+ output[key] = validated;
1406
+ }
1407
+ return output;
1408
+ };
1409
+ const readOptionalExportDeletionGroupSnapshot = (record, keys, bag) => {
1410
+ const value = readAlias(record, keys);
1411
+ if (value === undefined)
1412
+ return undefined;
1413
+ const snapshotRecord = requireRecord(value, "$.deletionGroupSnapshot", bag);
1414
+ if (!snapshotRecord)
1415
+ return undefined;
1416
+ const capturedAt = readRequiredNonEmptyString(snapshotRecord, ["capturedAt", "captured_at"], "$.deletionGroupSnapshot.capturedAt", bag);
1417
+ const deletionGroupIds = readRequiredStringArray(snapshotRecord, ["deletionGroupIds", "deletion_group_ids"], "$.deletionGroupSnapshot.deletionGroupIds", bag);
1418
+ const byRecordId = readRequiredStringArrayRecord(snapshotRecord, ["byRecordId", "by_record_id"], "$.deletionGroupSnapshot.byRecordId", bag);
1419
+ if (!capturedAt || !deletionGroupIds || !byRecordId)
1420
+ return undefined;
1421
+ return {
1422
+ capturedAt,
1423
+ deletionGroupIds,
1424
+ byRecordId,
1425
+ };
1426
+ };
1427
+ const readOptionalObjectRef = (record, keys, path, bag) => {
1428
+ const value = readAlias(record, keys);
1429
+ if (value === undefined)
1430
+ return undefined;
1431
+ return validateNested(validateCodaliStorageObjectRef, value, path, bag);
1432
+ };
1433
+ const copyOptionalObjectRef = (record, output, target, keys, path, bag) => {
1434
+ const value = readOptionalObjectRef(record, keys, path, bag);
1435
+ if (value) {
1436
+ Object.assign(output, { [target]: value });
1437
+ }
1438
+ };
1439
+ const copyOptionalString = (record, output, target, keys, path, bag) => {
1440
+ const value = readOptionalString(record, keys, path, bag);
1441
+ if (value !== undefined) {
1442
+ Object.assign(output, { [target]: value });
1443
+ }
1444
+ };
1445
+ const copyOptionalStringArray = (record, output, target, keys, path, bag) => {
1446
+ const value = readOptionalStringArray(record, keys, path, bag);
1447
+ if (value) {
1448
+ Object.assign(output, { [target]: value });
1449
+ }
1450
+ };
1451
+ const copyOptionalNonNegativeInteger = (record, output, target, keys, path, bag) => {
1452
+ const value = readOptionalNonNegativeInteger(record, keys, path, bag);
1453
+ if (value !== undefined) {
1454
+ Object.assign(output, { [target]: value });
1455
+ }
1456
+ };
1457
+ const copyOptionalUnitNumber = (record, output, target, keys, path, bag) => {
1458
+ const value = readOptionalUnitNumber(record, keys, path, bag);
1459
+ if (value !== undefined) {
1460
+ Object.assign(output, { [target]: value });
1461
+ }
1462
+ };
1463
+ const copyOptionalMetadata = (record, output, bag) => {
1464
+ const value = readAlias(record, ["metadata"]);
1465
+ if (value === undefined)
1466
+ return;
1467
+ if (!isRecord(value)) {
1468
+ addIssue(bag, "$.metadata", "expected_object", "Expected metadata to be an object.");
1469
+ return;
1470
+ }
1471
+ output.metadata = value;
1472
+ };
1473
+ const copyOptionalRecord = (record, output, target, keys, path, bag) => {
1474
+ const value = readAlias(record, keys);
1475
+ if (value === undefined)
1476
+ return;
1477
+ if (!isRecord(value)) {
1478
+ addIssue(bag, path, "expected_object", "Expected an object.");
1479
+ return;
1480
+ }
1481
+ Object.assign(output, { [target]: value });
1482
+ };
1483
+ const copyOptionalModel = (record, output, bag) => {
1484
+ const value = readAlias(record, ["model"]);
1485
+ if (value === undefined)
1486
+ return;
1487
+ const modelRecord = requireRecord(value, "$.model", bag);
1488
+ if (!modelRecord)
1489
+ return;
1490
+ const model = {
1491
+ provider: readOptionalString(modelRecord, ["provider"], "$.model.provider", bag),
1492
+ model: readOptionalString(modelRecord, ["model"], "$.model.model", bag),
1493
+ agentId: readOptionalString(modelRecord, ["agentId", "agent_id"], "$.model.agentId", bag),
1494
+ role: readOptionalString(modelRecord, ["role"], "$.model.role", bag),
1495
+ };
1496
+ output.model = withoutUndefined(model);
1497
+ };
1498
+ const copyOptionalUsage = (record, output, bag) => {
1499
+ const value = readAlias(record, ["usage"]);
1500
+ if (value === undefined)
1501
+ return;
1502
+ const usageRecord = requireRecord(value, "$.usage", bag);
1503
+ if (!usageRecord)
1504
+ return;
1505
+ const usage = {
1506
+ inputTokens: readOptionalNonNegativeInteger(usageRecord, ["inputTokens", "input_tokens"], "$.usage.inputTokens", bag),
1507
+ outputTokens: readOptionalNonNegativeInteger(usageRecord, ["outputTokens", "output_tokens"], "$.usage.outputTokens", bag),
1508
+ totalTokens: readOptionalNonNegativeInteger(usageRecord, ["totalTokens", "total_tokens"], "$.usage.totalTokens", bag),
1509
+ };
1510
+ output.usage = withoutUndefined(usage);
1511
+ };
1512
+ const copyOptionalQuality = (record, output, bag) => {
1513
+ const value = readAlias(record, ["quality"]);
1514
+ if (value === undefined)
1515
+ return;
1516
+ const qualityRecord = requireRecord(value, "$.quality", bag);
1517
+ if (!qualityRecord)
1518
+ return;
1519
+ const quality = {
1520
+ score: readOptionalUnitNumber(qualityRecord, ["score"], "$.quality.score", bag),
1521
+ labels: readOptionalStringArray(qualityRecord, ["labels"], "$.quality.labels", bag),
1522
+ reviewed: readOptionalBoolean(qualityRecord, ["reviewed"], "$.quality.reviewed", bag),
1523
+ };
1524
+ output.quality = withoutUndefined(quality);
1525
+ };
1526
+ const readDistribution = (record, bag) => {
1527
+ const value = readRequiredRecord(record, ["distribution"], "$.distribution", bag);
1528
+ if (!value)
1529
+ return undefined;
1530
+ const mode = readRequiredLiteral(value, ["mode"], CODALI_STORAGE_CONTRACT_DISTRIBUTION.mode, "$.distribution.mode", bag);
1531
+ const packageName = readRequiredLiteral(value, ["packageName", "package_name"], CODALI_STORAGE_CONTRACT_DISTRIBUTION.packageName, "$.distribution.packageName", bag);
1532
+ const modulePath = readRequiredLiteral(value, ["modulePath", "module_path"], CODALI_STORAGE_CONTRACT_DISTRIBUTION.modulePath, "$.distribution.modulePath", bag);
1533
+ const sourcePath = readRequiredLiteral(value, ["sourcePath", "source_path"], CODALI_STORAGE_CONTRACT_DISTRIBUTION.sourcePath, "$.distribution.sourcePath", bag);
1534
+ const fixturePath = readRequiredLiteral(value, ["fixturePath", "fixture_path"], CODALI_STORAGE_CONTRACT_DISTRIBUTION.fixturePath, "$.distribution.fixturePath", bag);
1535
+ return mode && packageName && modulePath && sourcePath && fixturePath
1536
+ ? CODALI_STORAGE_CONTRACT_DISTRIBUTION
1537
+ : undefined;
1538
+ };
1539
+ const validateNested = (validator, input, path, bag) => {
1540
+ const result = validator(input);
1541
+ if (result.ok)
1542
+ return result.value;
1543
+ for (const issue of result.issues) {
1544
+ addIssue(bag, issue.path === "$" ? path : `${path}${issue.path.slice(1)}`, issue.code, issue.message);
1545
+ }
1546
+ return undefined;
1547
+ };
1548
+ const withoutUndefined = (input) => {
1549
+ const output = {};
1550
+ for (const [key, value] of Object.entries(input)) {
1551
+ if (value !== undefined)
1552
+ output[key] = value;
1553
+ }
1554
+ return output;
1555
+ };