@native-knowledge/knowledge-sdk 0.1.0-rc.1 → 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +29 -0
  3. package/README.md +26 -38
  4. package/dist/{types/sdk/agent-entry.d.ts → agent-entry.d.ts} +3 -3
  5. package/dist/agent-entry.js +153 -0
  6. package/dist/{types/sdk/agent-guidance-error.d.ts → agent-guidance-error.d.ts} +1 -1
  7. package/dist/agent-guidance-error.js +46 -0
  8. package/dist/auth.d.ts +124 -0
  9. package/dist/auth.js +165 -0
  10. package/dist/{types/sdk/index.d.ts → index.d.ts} +15 -14
  11. package/dist/index.js +159 -6570
  12. package/dist/internal/contracts/artifact.js +103 -0
  13. package/dist/internal/contracts/collection-control.js +69 -0
  14. package/dist/internal/contracts/collection.js +174 -0
  15. package/dist/internal/contracts/common.js +16 -0
  16. package/dist/internal/contracts/currentness.js +221 -0
  17. package/dist/internal/contracts/event.js +35 -0
  18. package/dist/internal/contracts/evidence.js +171 -0
  19. package/dist/{types → internal}/contracts/index.d.ts +0 -1
  20. package/dist/internal/contracts/index.js +54 -0
  21. package/dist/internal/contracts/material-capability.js +8 -0
  22. package/dist/internal/contracts/material-carrier.js +15 -0
  23. package/dist/internal/contracts/material-context.js +9 -0
  24. package/dist/internal/contracts/material-read.js +12 -0
  25. package/dist/internal/contracts/observation.js +39 -0
  26. package/dist/internal/contracts/persistence.js +239 -0
  27. package/dist/internal/contracts/project-intent.js +121 -0
  28. package/dist/internal/contracts/projection.js +194 -0
  29. package/dist/internal/contracts/public-reference.js +87 -0
  30. package/dist/internal/contracts/reference-record.js +55 -0
  31. package/dist/internal/contracts/remote-file.js +159 -0
  32. package/dist/internal/contracts/resource-scope.js +38 -0
  33. package/dist/internal/contracts/review-packet.js +61 -0
  34. package/dist/internal/contracts/review-plane.js +89 -0
  35. package/dist/internal/contracts/rich-document.js +184 -0
  36. package/dist/internal/contracts/runtime-compatibility.js +55 -0
  37. package/dist/internal/contracts/runtime.js +68 -0
  38. package/dist/internal/contracts/scheduler.js +256 -0
  39. package/dist/internal/contracts/signal.js +77 -0
  40. package/dist/internal/contracts/source-governance.js +194 -0
  41. package/dist/internal/contracts/source.js +32 -0
  42. package/dist/internal/contracts/tenant-budget.js +34 -0
  43. package/dist/internal/contracts/tenant-collection-request.js +58 -0
  44. package/dist/internal/contracts/tenant-collection.js +66 -0
  45. package/dist/internal/contracts/tenant-currentness.js +26 -0
  46. package/dist/internal/contracts/tenant-management.js +27 -0
  47. package/dist/internal/contracts/tenant-material-api.js +46 -0
  48. package/dist/internal/contracts/tenant-material-json-schema.js +16 -0
  49. package/dist/internal/contracts/tenant-pdf-text.js +13 -0
  50. package/dist/internal/contracts/tenant-project-control.js +19 -0
  51. package/dist/internal/contracts/tenant-public-reference.js +26 -0
  52. package/dist/internal/contracts/tenant-resource-api.js +70 -0
  53. package/dist/internal/contracts/tenant-resource.js +118 -0
  54. package/dist/internal/contracts/tenant-rich-document.js +17 -0
  55. package/dist/internal/contracts/tenant-source-binding.js +33 -0
  56. package/dist/internal/contracts/tenant-source-bridge.js +9 -0
  57. package/dist/internal/contracts/tenant-source-cutover.js +26 -0
  58. package/dist/internal/contracts/tenant-source-document.js +38 -0
  59. package/dist/internal/contracts/tenant-source-lease.js +5 -0
  60. package/dist/internal/contracts/tenant-source.js +83 -0
  61. package/dist/internal/contracts/tenant-web-collection.js +118 -0
  62. package/dist/internal/contracts/tenant-web-content-plan.js +39 -0
  63. package/dist/internal/contracts/tenant-web-content-result.js +15 -0
  64. package/dist/internal/contracts/tenant-web-content.js +16 -0
  65. package/dist/internal/contracts/tenant-web-crawl.js +55 -0
  66. package/dist/internal/contracts/tenant-web-dynamic.js +68 -0
  67. package/dist/internal/contracts/tenant-web-results.js +41 -0
  68. package/dist/internal/contracts/tenant-web-workflow.js +29 -0
  69. package/dist/internal/serving-contracts/agent-catalog.js +56 -0
  70. package/dist/internal/serving-contracts/agent-entry.js +57 -0
  71. package/dist/internal/serving-contracts/agent-guidance-shapes.js +12 -0
  72. package/dist/internal/serving-contracts/agent-input-hints.js +104 -0
  73. package/dist/internal/serving-contracts/body-search.js +16 -0
  74. package/dist/internal/serving-contracts/consumer.js +45 -0
  75. package/dist/internal/serving-contracts/core.js +76 -0
  76. package/dist/internal/serving-contracts/http.js +64 -0
  77. package/dist/{types → internal}/serving-contracts/index.d.ts +1 -0
  78. package/dist/internal/serving-contracts/index.js +25 -0
  79. package/dist/internal/serving-contracts/management.js +15 -0
  80. package/dist/internal/serving-contracts/material-capabilities.js +14 -0
  81. package/dist/internal/serving-contracts/material-index-status.js +5 -0
  82. package/dist/internal/serving-contracts/mcp.js +140 -0
  83. package/dist/internal/serving-contracts/tenant-governance.js +13 -0
  84. package/dist/internal/serving-contracts/tenant-management.js +89 -0
  85. package/dist/internal/serving-contracts/tenant-project.js +39 -0
  86. package/dist/internal/serving-contracts/tenant-runtime.js +37 -0
  87. package/dist/internal/serving-contracts/tenant-session.d.ts +87 -0
  88. package/dist/internal/serving-contracts/tenant-session.js +8 -0
  89. package/dist/internal/serving-contracts/tenant-web-content.js +20 -0
  90. package/dist/internal/serving-contracts/tenant-web-crawl.js +13 -0
  91. package/dist/internal/serving-contracts/tenant-web-dynamic.js +20 -0
  92. package/dist/internal/serving-contracts/tenant-web-workflow.js +36 -0
  93. package/dist/internal/serving-contracts/tenant-web.js +53 -0
  94. package/dist/internal/serving-contracts/tenant-workspace.js +28 -0
  95. package/dist/internal/serving-contracts/workflow.js +37 -0
  96. package/dist/material-stream.js +29 -0
  97. package/dist/{types/sdk/oauth.d.ts → oauth.d.ts} +18 -18
  98. package/dist/oauth.js +539 -0
  99. package/dist/{types/sdk/resource.d.ts → resource.d.ts} +5 -5
  100. package/dist/resource.js +123 -0
  101. package/dist/session.d.ts +4 -0
  102. package/dist/session.js +3 -0
  103. package/dist/tenant-client.d.ts +169 -0
  104. package/dist/tenant-client.js +58 -0
  105. package/dist/{types/sdk/tenant-management.d.ts → tenant-management.d.ts} +6 -6
  106. package/dist/tenant-management.js +149 -0
  107. package/dist/{types/sdk/tenant-project.d.ts → tenant-project.d.ts} +1 -1
  108. package/dist/tenant-project.js +63 -0
  109. package/dist/{types/sdk/tenant-runtime.d.ts → tenant-runtime.d.ts} +1 -1
  110. package/dist/tenant-runtime.js +17 -0
  111. package/dist/{types/sdk/tenant-web-content.d.ts → tenant-web-content.d.ts} +1 -1
  112. package/dist/tenant-web-content.js +70 -0
  113. package/dist/{types/sdk/tenant-web-crawl.d.ts → tenant-web-crawl.d.ts} +1 -1
  114. package/dist/tenant-web-crawl.js +39 -0
  115. package/dist/{types/sdk/tenant-web-dynamic.d.ts → tenant-web-dynamic.d.ts} +1 -1
  116. package/dist/tenant-web-dynamic.js +39 -0
  117. package/dist/{types/sdk/tenant-web-workflow.d.ts → tenant-web-workflow.d.ts} +1 -1
  118. package/dist/tenant-web-workflow.js +63 -0
  119. package/dist/{types/sdk/tenant-web.d.ts → tenant-web.d.ts} +1 -1
  120. package/dist/tenant-web.js +39 -0
  121. package/dist/{types/sdk/tenant-workspace.d.ts → tenant-workspace.d.ts} +1 -1
  122. package/dist/tenant-workspace.js +21 -0
  123. package/dist/token-provider.d.ts +14 -0
  124. package/dist/token-provider.js +21 -0
  125. package/package.json +27 -15
  126. package/dist/types/contracts/resource-transfer.d.ts +0 -24
  127. package/dist/types/sdk/resource-integrity.d.ts +0 -21
  128. package/dist/types/sdk/transport.d.ts +0 -16
  129. /package/dist/{types → internal}/contracts/artifact.d.ts +0 -0
  130. /package/dist/{types → internal}/contracts/collection-control.d.ts +0 -0
  131. /package/dist/{types → internal}/contracts/collection.d.ts +0 -0
  132. /package/dist/{types → internal}/contracts/common.d.ts +0 -0
  133. /package/dist/{types → internal}/contracts/currentness.d.ts +0 -0
  134. /package/dist/{types → internal}/contracts/event.d.ts +0 -0
  135. /package/dist/{types → internal}/contracts/evidence.d.ts +0 -0
  136. /package/dist/{types → internal}/contracts/material-capability.d.ts +0 -0
  137. /package/dist/{types → internal}/contracts/material-carrier.d.ts +0 -0
  138. /package/dist/{types → internal}/contracts/material-context.d.ts +0 -0
  139. /package/dist/{types → internal}/contracts/material-read.d.ts +0 -0
  140. /package/dist/{types → internal}/contracts/observation.d.ts +0 -0
  141. /package/dist/{types → internal}/contracts/persistence.d.ts +0 -0
  142. /package/dist/{types → internal}/contracts/project-intent.d.ts +0 -0
  143. /package/dist/{types → internal}/contracts/projection.d.ts +0 -0
  144. /package/dist/{types → internal}/contracts/public-reference.d.ts +0 -0
  145. /package/dist/{types → internal}/contracts/reference-record.d.ts +0 -0
  146. /package/dist/{types → internal}/contracts/remote-file.d.ts +0 -0
  147. /package/dist/{types → internal}/contracts/resource-scope.d.ts +0 -0
  148. /package/dist/{types → internal}/contracts/review-packet.d.ts +0 -0
  149. /package/dist/{types → internal}/contracts/review-plane.d.ts +0 -0
  150. /package/dist/{types → internal}/contracts/rich-document.d.ts +0 -0
  151. /package/dist/{types → internal}/contracts/runtime-compatibility.d.ts +0 -0
  152. /package/dist/{types → internal}/contracts/runtime.d.ts +0 -0
  153. /package/dist/{types → internal}/contracts/scheduler.d.ts +0 -0
  154. /package/dist/{types → internal}/contracts/signal.d.ts +0 -0
  155. /package/dist/{types → internal}/contracts/source-governance.d.ts +0 -0
  156. /package/dist/{types → internal}/contracts/source.d.ts +0 -0
  157. /package/dist/{types → internal}/contracts/tenant-budget.d.ts +0 -0
  158. /package/dist/{types → internal}/contracts/tenant-collection-request.d.ts +0 -0
  159. /package/dist/{types → internal}/contracts/tenant-collection.d.ts +0 -0
  160. /package/dist/{types → internal}/contracts/tenant-currentness.d.ts +0 -0
  161. /package/dist/{types → internal}/contracts/tenant-management.d.ts +0 -0
  162. /package/dist/{types → internal}/contracts/tenant-material-api.d.ts +0 -0
  163. /package/dist/{types → internal}/contracts/tenant-pdf-text.d.ts +0 -0
  164. /package/dist/{types → internal}/contracts/tenant-project-control.d.ts +0 -0
  165. /package/dist/{types → internal}/contracts/tenant-public-reference.d.ts +0 -0
  166. /package/dist/{types → internal}/contracts/tenant-resource-api.d.ts +0 -0
  167. /package/dist/{types → internal}/contracts/tenant-resource.d.ts +0 -0
  168. /package/dist/{types → internal}/contracts/tenant-rich-document.d.ts +0 -0
  169. /package/dist/{types → internal}/contracts/tenant-source-binding.d.ts +0 -0
  170. /package/dist/{types → internal}/contracts/tenant-source-bridge.d.ts +0 -0
  171. /package/dist/{types → internal}/contracts/tenant-source-cutover.d.ts +0 -0
  172. /package/dist/{types → internal}/contracts/tenant-source-document.d.ts +0 -0
  173. /package/dist/{types → internal}/contracts/tenant-source-lease.d.ts +0 -0
  174. /package/dist/{types → internal}/contracts/tenant-source.d.ts +0 -0
  175. /package/dist/{types → internal}/contracts/tenant-web-collection.d.ts +0 -0
  176. /package/dist/{types → internal}/contracts/tenant-web-content-plan.d.ts +0 -0
  177. /package/dist/{types → internal}/contracts/tenant-web-content-result.d.ts +0 -0
  178. /package/dist/{types → internal}/contracts/tenant-web-content.d.ts +0 -0
  179. /package/dist/{types → internal}/contracts/tenant-web-crawl.d.ts +0 -0
  180. /package/dist/{types → internal}/contracts/tenant-web-dynamic.d.ts +0 -0
  181. /package/dist/{types → internal}/contracts/tenant-web-results.d.ts +0 -0
  182. /package/dist/{types → internal}/contracts/tenant-web-workflow.d.ts +0 -0
  183. /package/dist/{types → internal}/serving-contracts/agent-catalog.d.ts +0 -0
  184. /package/dist/{types → internal}/serving-contracts/agent-entry.d.ts +0 -0
  185. /package/dist/{types → internal}/serving-contracts/agent-guidance-shapes.d.ts +0 -0
  186. /package/dist/{types → internal}/serving-contracts/agent-input-hints.d.ts +0 -0
  187. /package/dist/{types → internal}/serving-contracts/body-search.d.ts +0 -0
  188. /package/dist/{types → internal}/serving-contracts/consumer.d.ts +0 -0
  189. /package/dist/{types → internal}/serving-contracts/core.d.ts +0 -0
  190. /package/dist/{types → internal}/serving-contracts/http.d.ts +0 -0
  191. /package/dist/{types → internal}/serving-contracts/management.d.ts +0 -0
  192. /package/dist/{types → internal}/serving-contracts/material-capabilities.d.ts +0 -0
  193. /package/dist/{types → internal}/serving-contracts/material-index-status.d.ts +0 -0
  194. /package/dist/{types → internal}/serving-contracts/mcp.d.ts +0 -0
  195. /package/dist/{types → internal}/serving-contracts/tenant-governance.d.ts +0 -0
  196. /package/dist/{types → internal}/serving-contracts/tenant-management.d.ts +0 -0
  197. /package/dist/{types → internal}/serving-contracts/tenant-project.d.ts +0 -0
  198. /package/dist/{types → internal}/serving-contracts/tenant-runtime.d.ts +0 -0
  199. /package/dist/{types → internal}/serving-contracts/tenant-web-content.d.ts +0 -0
  200. /package/dist/{types → internal}/serving-contracts/tenant-web-crawl.d.ts +0 -0
  201. /package/dist/{types → internal}/serving-contracts/tenant-web-dynamic.d.ts +0 -0
  202. /package/dist/{types → internal}/serving-contracts/tenant-web-workflow.d.ts +0 -0
  203. /package/dist/{types → internal}/serving-contracts/tenant-web.d.ts +0 -0
  204. /package/dist/{types → internal}/serving-contracts/tenant-workspace.d.ts +0 -0
  205. /package/dist/{types → internal}/serving-contracts/workflow.d.ts +0 -0
  206. /package/dist/{types/sdk/material-stream.d.ts → material-stream.d.ts} +0 -0
@@ -0,0 +1,61 @@
1
+ import { z } from 'zod';
2
+ import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ import { currentnessAssessmentSchema } from './currentness.js';
4
+ import { evidenceReviewAttachmentSchema } from './evidence.js';
5
+ export const reviewPacketEvidenceSchema = z.object({
6
+ evidenceRef: stableUriSchema,
7
+ evidenceId: idSchema,
8
+ sourceId: idSchema,
9
+ sourceIds: z.array(idSchema).max(64).default([]),
10
+ projectRef: stableUriSchema.optional(),
11
+ sourceRevisionRef: stableUriSchema.optional(),
12
+ collectionBatchRef: stableUriSchema.optional(),
13
+ collectionTaskId: idSchema.optional(),
14
+ canonicalUri: stableUriSchema.optional(),
15
+ observedAt: isoDateTimeSchema,
16
+ access: accessClassificationSchema,
17
+ contentHash: sha256Schema,
18
+ mediaType: z.string().trim().min(1).max(256).optional(),
19
+ byteLength: z.number().int().nonnegative().optional(),
20
+ currentness: currentnessAssessmentSchema.optional(),
21
+ attachment: evidenceReviewAttachmentSchema,
22
+ }).strict().superRefine((value, ctx) => {
23
+ if (value.attachment.evidenceRef !== value.evidenceRef)
24
+ ctx.addIssue({ code: 'custom', path: ['attachment', 'evidenceRef'], message: 'attachment evidenceRef must match evidence row' });
25
+ if (value.attachment.contentHash !== value.contentHash)
26
+ ctx.addIssue({ code: 'custom', path: ['attachment', 'contentHash'], message: 'attachment contentHash must match evidence row' });
27
+ if (value.mediaType && value.attachment.mediaType !== value.mediaType.toLowerCase())
28
+ ctx.addIssue({ code: 'custom', path: ['attachment', 'mediaType'], message: 'attachment mediaType must match evidence row' });
29
+ if (value.byteLength !== undefined && value.attachment.byteLength !== value.byteLength)
30
+ ctx.addIssue({ code: 'custom', path: ['attachment', 'byteLength'], message: 'attachment byteLength must match evidence row' });
31
+ });
32
+ export const reviewPacketSchema = z.object({
33
+ schemaVersion: schemaVersionSchema,
34
+ packetType: z.literal('KNOWLEDGE_REVIEW_PACKET'),
35
+ projectKey: projectKeySchema,
36
+ projectRef: stableUriSchema.optional(),
37
+ sourceEvent: z.object({
38
+ eventId: idSchema,
39
+ type: z.enum(['SOURCE_CHANGED', 'BOUNDARY_RISK', 'CURRENTNESS_RISK']),
40
+ createdAt: isoDateTimeSchema,
41
+ runtimeNodeId: idSchema.optional(),
42
+ sourceRef: stableUriSchema.optional(),
43
+ sourceRevisionRef: stableUriSchema.optional(),
44
+ collectionBatchRef: stableUriSchema.optional(),
45
+ contentHash: sha256Schema.optional(),
46
+ }).strict(),
47
+ evidence: z.array(reviewPacketEvidenceSchema).max(256),
48
+ reviewPolicy: z.object({
49
+ semanticSynthesisRequired: z.literal(true),
50
+ rawChunkApprovalForbidden: z.literal(true),
51
+ highRiskAutoApprovalForbidden: z.literal(true),
52
+ decisions: z.tuple([z.literal('PASS'), z.literal('REWORK'), z.literal('REJECT')]),
53
+ }).strict(),
54
+ }).strict().superRefine((value, ctx) => {
55
+ if (value.projectRef)
56
+ for (let i = 0; i < value.evidence.length; i += 1) {
57
+ const row = value.evidence[i];
58
+ if (row.projectRef && row.projectRef !== value.projectRef)
59
+ ctx.addIssue({ code: 'custom', path: ['evidence', i, 'projectRef'], message: 'Evidence projectRef must match packet projectRef' });
60
+ }
61
+ });
@@ -0,0 +1,89 @@
1
+ import { z } from 'zod';
2
+ import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ import { artifactKindSchema } from './artifact.js';
4
+ import { driveRelativePathSchema } from './projection.js';
5
+ export const reviewPlaneStageSchema = z.enum([
6
+ 'PROGRAM',
7
+ 'INBOX',
8
+ 'REVIEWED',
9
+ 'USER_APPROVED',
10
+ 'PROMOTION_RECEIPTS',
11
+ 'GUIDES',
12
+ 'REJECTED_SUPERSEDED',
13
+ ]);
14
+ export const reviewPlaneWorkspaceSchema = z.object({
15
+ schemaVersion: schemaVersionSchema,
16
+ projectKey: projectKeySchema,
17
+ provider: z.literal('GOOGLE_DRIVE'),
18
+ rootPath: driveRelativePathSchema,
19
+ folders: z.object({
20
+ PROGRAM: driveRelativePathSchema,
21
+ INBOX: driveRelativePathSchema,
22
+ REVIEWED: driveRelativePathSchema,
23
+ USER_APPROVED: driveRelativePathSchema,
24
+ PROMOTION_RECEIPTS: driveRelativePathSchema,
25
+ GUIDES: driveRelativePathSchema.optional(),
26
+ REJECTED_SUPERSEDED: driveRelativePathSchema,
27
+ }).strict(),
28
+ }).strict().superRefine((value, ctx) => {
29
+ const seen = new Set();
30
+ for (const [key, folder] of Object.entries(value.folders)) {
31
+ if (folder === undefined)
32
+ continue;
33
+ if (seen.has(folder))
34
+ ctx.addIssue({ code: 'custom', path: ['folders', key], message: 'review-plane folders must be unique' });
35
+ seen.add(folder);
36
+ }
37
+ });
38
+ export const reviewPlaneApprovalSchema = z.object({
39
+ schemaVersion: schemaVersionSchema,
40
+ approvalId: idSchema,
41
+ projectKey: projectKeySchema,
42
+ sourceProvider: z.literal('GOOGLE_DRIVE'),
43
+ sourcePath: driveRelativePathSchema,
44
+ sourceFileId: z.string().trim().min(1).max(512).nullable(),
45
+ sourceContentHash: sha256Schema,
46
+ sourceArtifactRef: stableUriSchema.nullable(),
47
+ reviewedAt: isoDateTimeSchema,
48
+ approvedAt: isoDateTimeSchema,
49
+ decision: z.literal('PASS'),
50
+ approvalAuthority: z.literal('EXPLICIT_HUMAN'),
51
+ hostDisposition: z.enum(['APPROVED_RESEARCH', 'APPROVED_KNOWLEDGE', 'APPROVED_INSIGHT']),
52
+ targetArtifactId: idSchema,
53
+ targetVersion: z.number().int().positive(),
54
+ targetTitle: z.string().trim().min(1).max(500),
55
+ mediaType: z.string().trim().min(1).max(256),
56
+ observedAt: isoDateTimeSchema.nullable(),
57
+ validUntil: isoDateTimeSchema.nullable(),
58
+ access: accessClassificationSchema,
59
+ evidenceRefs: z.array(stableUriSchema).max(256),
60
+ approvalEvidenceRefs: z.array(stableUriSchema).max(256),
61
+ supersedesRef: stableUriSchema.nullable(),
62
+ }).strict().superRefine((value, ctx) => {
63
+ if (Date.parse(value.approvedAt) < Date.parse(value.reviewedAt))
64
+ ctx.addIssue({ code: 'custom', path: ['approvedAt'], message: 'approvedAt must not precede reviewedAt' });
65
+ if (value.validUntil && value.observedAt && Date.parse(value.validUntil) < Date.parse(value.observedAt))
66
+ ctx.addIssue({ code: 'custom', path: ['validUntil'], message: 'validUntil must not precede observedAt' });
67
+ if (value.targetVersion === 1 && value.supersedesRef !== null)
68
+ ctx.addIssue({ code: 'custom', path: ['supersedesRef'], message: 'version 1 cannot supersede' });
69
+ if (value.targetVersion > 1 && value.supersedesRef === null)
70
+ ctx.addIssue({ code: 'custom', path: ['supersedesRef'], message: 'later version requires exact predecessor ref' });
71
+ });
72
+ export const reviewPlanePromotionReceiptSchema = z.object({
73
+ schemaVersion: schemaVersionSchema,
74
+ projectKey: projectKeySchema,
75
+ approvalId: idSchema,
76
+ sourcePath: driveRelativePathSchema,
77
+ sourceFileId: z.string().trim().min(1).max(512).nullable(),
78
+ sourceContentHash: sha256Schema,
79
+ promotedAt: isoDateTimeSchema,
80
+ targetKind: artifactKindSchema,
81
+ draftRef: stableUriSchema,
82
+ readyRef: stableUriSchema,
83
+ reviewRef: stableUriSchema,
84
+ approvedRef: stableUriSchema,
85
+ approvedContentHash: sha256Schema,
86
+ exactBytesVerified: z.literal(true),
87
+ humanApprovalVerified: z.literal(true),
88
+ automaticPromotion: z.literal(false),
89
+ }).strict();
@@ -0,0 +1,184 @@
1
+ import { z } from 'zod';
2
+ import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema } from './common.js';
3
+ export const RICH_DOCUMENT_MEDIA_TYPE = 'application/vnd.knowledge-v2.rich-document+json';
4
+ export const richDocumentRoleSchema = z.enum(['SOURCE_MIRROR', 'KNOWLEDGE', 'INSIGHT']);
5
+ export const publicationProfileSchema = z.enum(['HUMAN_ARTICLE_V1', 'KNOWLEDGE_CARD_V1']);
6
+ export const publicationReadinessReasonSchema = z.enum(['ROLE_MISMATCH', 'TITLE_REQUIRED', 'SUMMARY_REQUIRED', 'TEXT_TOO_SHORT', 'STRUCTURE_TOO_THIN', 'SOURCE_EVIDENCE_REQUIRED', 'CITATION_COVERAGE_REQUIRED', 'EVIDENCE_MISMATCH', 'MEDIA_UNAVAILABLE']);
7
+ export const richDocumentMediaAvailabilitySchema = z.enum(['MIRRORED', 'REMOTE_ONLY', 'UNAVAILABLE']);
8
+ const ordinalSchema = z.number().int().nonnegative().max(100_000);
9
+ const humanText = (max) => z.string().max(max);
10
+ const httpUrlSchema = z.string().url().max(2048).superRefine((value, ctx) => { const protocol = new URL(value).protocol; if (!['http:', 'https:'].includes(protocol))
11
+ ctx.addIssue({ code: 'custom', message: 'rich document media/source URL must use HTTP(S)' }); });
12
+ const richInlineSchema = z.object({ text: humanText(64_000), href: httpUrlSchema.nullable() }).strict();
13
+ const inlines = z.array(richInlineSchema).max(10_000).optional();
14
+ const textBlockBase = { ordinal: ordinalSchema };
15
+ export const richHeadingBlockSchema = z.object({ ...textBlockBase, kind: z.literal('HEADING'), level: z.number().int().min(1).max(6), text: humanText(16_000), inlines }).strict();
16
+ export const richParagraphBlockSchema = z.object({ ...textBlockBase, kind: z.literal('PARAGRAPH'), text: humanText(64_000), inlines }).strict();
17
+ export const richListItemBlockSchema = z.object({ ...textBlockBase, kind: z.literal('LIST_ITEM'), text: humanText(32_000), ordered: z.boolean(), level: z.number().int().min(0).max(16), index: z.number().int().positive().nullable(), inlines }).strict();
18
+ export const richQuoteBlockSchema = z.object({ ...textBlockBase, kind: z.literal('QUOTE'), text: humanText(64_000), inlines }).strict();
19
+ export const richCodeBlockSchema = z.object({ ...textBlockBase, kind: z.literal('CODE'), text: humanText(128_000), language: z.string().trim().min(1).max(128).nullable() }).strict();
20
+ export const richDividerBlockSchema = z.object({ ...textBlockBase, kind: z.literal('DIVIDER') }).strict();
21
+ export const richTableBlockSchema = z.object({ ...textBlockBase, kind: z.literal('TABLE'), rows: z.array(z.object({ cells: z.array(z.object({ text: humanText(16_000), header: z.boolean() }).strict()).max(128) }).strict()).min(1).max(1000) }).strict();
22
+ const mediaBase = {
23
+ ordinal: ordinalSchema,
24
+ sourceUrl: httpUrlSchema,
25
+ canonicalUrl: httpUrlSchema.nullable(),
26
+ availability: richDocumentMediaAvailabilitySchema,
27
+ mediaRef: stableUriSchema.nullable(),
28
+ evidenceRef: stableUriSchema.nullable(),
29
+ contentHash: sha256Schema.nullable(),
30
+ mediaType: z.string().trim().min(1).max(256).nullable(),
31
+ byteLength: z.number().int().nonnegative().nullable(),
32
+ caption: humanText(16_000).nullable(),
33
+ };
34
+ function mediaConsistency(value, ctx) {
35
+ const mirrored = Boolean(value.mediaRef && value.evidenceRef && value.contentHash && value.mediaType && value.byteLength !== null);
36
+ if (value.availability === 'MIRRORED' && !mirrored)
37
+ ctx.addIssue({ code: 'custom', message: 'MIRRORED media requires EvidenceRef, hash, mediaType and byteLength' });
38
+ if (value.availability !== 'MIRRORED' && (value.mediaRef || value.evidenceRef || value.contentHash || value.mediaType || value.byteLength !== null))
39
+ ctx.addIssue({ code: 'custom', message: 'non-MIRRORED media cannot claim mirrored Evidence' });
40
+ }
41
+ export const richImageBlockSchema = z.object({ ...mediaBase, kind: z.literal('IMAGE'), alt: humanText(4000).nullable(), width: z.number().int().positive().max(100_000).nullable(), height: z.number().int().positive().max(100_000).nullable() }).strict().superRefine(mediaConsistency);
42
+ export const richVideoBlockSchema = z.object({ ...mediaBase, kind: z.literal('VIDEO'), title: humanText(4000).nullable(), posterUrl: httpUrlSchema.nullable(), posterEvidenceRef: stableUriSchema.nullable() }).strict().superRefine(mediaConsistency);
43
+ export const richAudioBlockSchema = z.object({ ...mediaBase, kind: z.literal('AUDIO'), title: humanText(4000).nullable() }).strict().superRefine(mediaConsistency);
44
+ export const richEmbedBlockSchema = z.object({ ...textBlockBase, kind: z.literal('EMBED'), sourceUrl: httpUrlSchema, provider: z.string().trim().min(1).max(128).nullable(), title: humanText(4000).nullable(), caption: humanText(16_000).nullable() }).strict();
45
+ export const richDocumentBlockSchema = z.discriminatedUnion('kind', [
46
+ richHeadingBlockSchema, richParagraphBlockSchema, richListItemBlockSchema, richQuoteBlockSchema, richCodeBlockSchema,
47
+ richImageBlockSchema, richVideoBlockSchema, richAudioBlockSchema, richEmbedBlockSchema, richDividerBlockSchema, richTableBlockSchema,
48
+ ]);
49
+ export const richDocumentCitationSchema = z.object({
50
+ blockOrdinal: ordinalSchema,
51
+ evidenceRefs: z.array(stableUriSchema).min(1).max(32),
52
+ }).strict().superRefine((value, ctx) => { if (new Set(value.evidenceRefs).size !== value.evidenceRefs.length)
53
+ ctx.addIssue({ code: 'custom', path: ['evidenceRefs'], message: 'citation EvidenceRefs must be unique' }); });
54
+ export const richCompositionDraftSchema = z.object({
55
+ summary: humanText(4000),
56
+ byline: humanText(1000).nullable().optional(),
57
+ language: z.string().trim().min(1).max(64).nullable().optional(),
58
+ blocks: z.array(richDocumentBlockSchema).min(1).max(10_000),
59
+ citations: z.array(richDocumentCitationSchema).min(1).max(10_000),
60
+ }).strict();
61
+ export function validateRichDocumentContent(value, ctx, names = { primary: 'primarySourceEvidenceRef', citation: 'evidenceRefs' }) {
62
+ if (!value.sourceEvidenceRefs.includes(value.primarySourceEvidenceRef))
63
+ ctx.addIssue({ code: 'custom', path: [names.primary], message: 'primary source must be included in sourceEvidenceRefs' });
64
+ for (let index = 0; index < value.blocks.length; index += 1)
65
+ if (value.blocks[index].ordinal !== index)
66
+ ctx.addIssue({ code: 'custom', path: ['blocks', index, 'ordinal'], message: 'block ordinals must be contiguous and equal array position' });
67
+ for (let index = 0; index < value.blocks.length; index += 1) {
68
+ const block = value.blocks[index];
69
+ if ('inlines' in block && block.inlines && block.inlines.map(part => part.text).join('') !== block.text)
70
+ ctx.addIssue({ code: 'custom', path: ['blocks', index, 'inlines'], message: 'inline text must equal block text' });
71
+ }
72
+ const claimed = new Set(value.mediaEvidenceRefs);
73
+ if (claimed.size !== value.mediaEvidenceRefs.length)
74
+ ctx.addIssue({ code: 'custom', path: ['mediaEvidenceRefs'], message: 'mediaEvidenceRefs must be unique' });
75
+ for (let index = 0; index < value.blocks.length; index += 1) {
76
+ const block = value.blocks[index];
77
+ if (['IMAGE', 'VIDEO', 'AUDIO'].includes(block.kind)) {
78
+ const media = block;
79
+ if (media.evidenceRef && !claimed.has(media.evidenceRef))
80
+ ctx.addIssue({ code: 'custom', path: ['blocks', index, 'evidenceRef'], message: 'mirrored media EvidenceRef must be listed in mediaEvidenceRefs' });
81
+ if (media.kind === 'VIDEO' && media.posterEvidenceRef && !claimed.has(media.posterEvidenceRef))
82
+ ctx.addIssue({ code: 'custom', path: ['blocks', index, 'posterEvidenceRef'], message: 'video poster EvidenceRef must be listed in mediaEvidenceRefs' });
83
+ }
84
+ }
85
+ const citationOrdinals = new Set(), allowedCitationRefs = new Set([...value.sourceEvidenceRefs, ...value.mediaEvidenceRefs]);
86
+ for (let index = 0; index < value.citations.length; index += 1) {
87
+ const citation = value.citations[index];
88
+ if (citation.blockOrdinal >= value.blocks.length)
89
+ ctx.addIssue({ code: 'custom', path: ['citations', index, 'blockOrdinal'], message: 'citation block ordinal must identify an existing block' });
90
+ if (citationOrdinals.has(citation.blockOrdinal))
91
+ ctx.addIssue({ code: 'custom', path: ['citations', index, 'blockOrdinal'], message: 'only one citation record is allowed per block' });
92
+ citationOrdinals.add(citation.blockOrdinal);
93
+ for (const ref of citation.evidenceRefs)
94
+ if (!allowedCitationRefs.has(ref))
95
+ ctx.addIssue({ code: 'custom', path: ['citations', index, names.citation], message: 'citation EvidenceRef must be a source or media EvidenceRef' });
96
+ }
97
+ }
98
+ export const richDocumentSchema = z.object({
99
+ schemaVersion: schemaVersionSchema,
100
+ format: z.literal('RICH_DOCUMENT_V1'),
101
+ role: richDocumentRoleSchema,
102
+ publicationProfile: publicationProfileSchema.nullable().default(null),
103
+ projectKey: projectKeySchema,
104
+ projectRef: stableUriSchema,
105
+ sourceEvidenceRefs: z.array(stableUriSchema).min(1).max(256),
106
+ primarySourceEvidenceRef: stableUriSchema,
107
+ sourceContentHash: sha256Schema,
108
+ sourceUri: stableUriSchema,
109
+ sourceMediaType: z.string().trim().min(1).max(256),
110
+ sourceByteLength: z.number().int().nonnegative(),
111
+ title: humanText(1000).nullable(),
112
+ byline: humanText(1000).nullable(),
113
+ publishedAt: isoDateTimeSchema.nullable(),
114
+ language: z.string().trim().min(1).max(64).nullable(),
115
+ topics: z.array(z.string().trim().min(1).max(160)).max(32).optional(),
116
+ summary: humanText(4000).nullable(),
117
+ textContent: humanText(2_000_000),
118
+ blocks: z.array(richDocumentBlockSchema).max(10_000),
119
+ mediaEvidenceRefs: z.array(stableUriSchema).max(512),
120
+ citations: z.array(richDocumentCitationSchema).max(10_000).default([]),
121
+ extractedAt: isoDateTimeSchema,
122
+ extractor: z.object({ id: idSchema, version: z.string().trim().min(1).max(128) }).strict(),
123
+ }).strict().superRefine((value, ctx) => {
124
+ if (value.role === 'SOURCE_MIRROR' && value.publicationProfile !== null)
125
+ ctx.addIssue({ code: 'custom', path: ['publicationProfile'], message: 'source mirror cannot claim a publication profile' });
126
+ if (value.role !== 'SOURCE_MIRROR' && value.publicationProfile === null)
127
+ ctx.addIssue({ code: 'custom', path: ['publicationProfile'], message: 'published Knowledge/Insight requires a publication profile' });
128
+ try {
129
+ const u = new URL(value.projectRef);
130
+ if (u.protocol !== 'knowledge-project:' || u.hostname.toLowerCase() !== value.projectKey.toLowerCase())
131
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match rich document project' });
132
+ }
133
+ catch {
134
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'invalid projectRef' });
135
+ }
136
+ validateRichDocumentContent(value, ctx);
137
+ });
138
+ export const richSourceDocumentManifestSchema = z.object({
139
+ schemaVersion: schemaVersionSchema,
140
+ projectKey: projectKeySchema,
141
+ documentRef: stableUriSchema,
142
+ sourceEvidenceRef: stableUriSchema,
143
+ sourceContentHash: sha256Schema,
144
+ documentContentHash: sha256Schema,
145
+ documentByteLength: z.number().int().positive(),
146
+ extractedAt: isoDateTimeSchema,
147
+ extractor: z.object({ id: idSchema, version: z.string().trim().min(1).max(128) }).strict(),
148
+ }).strict().superRefine((value, ctx) => { try {
149
+ const u = new URL(value.documentRef);
150
+ if (u.protocol !== 'rich-document:' || u.hostname.toLowerCase() !== value.projectKey.toLowerCase() || !/^\/[a-f0-9]{64}\/[a-f0-9]{64}$/.test(u.pathname))
151
+ ctx.addIssue({ code: 'custom', path: ['documentRef'], message: 'invalid Rich Source Document identity' });
152
+ }
153
+ catch {
154
+ ctx.addIssue({ code: 'custom', path: ['documentRef'], message: 'invalid Rich Source Document identity' });
155
+ } });
156
+ export const richSourceDocumentPointerSchema = z.object({
157
+ schemaVersion: schemaVersionSchema,
158
+ projectKey: projectKeySchema,
159
+ sourceEvidenceRef: stableUriSchema,
160
+ documentRef: stableUriSchema,
161
+ documentContentHash: sha256Schema,
162
+ manifestRelativePath: z.string().trim().min(1).max(4096).refine(value => !value.startsWith('/') && !value.split('/').includes('..'), 'manifestRelativePath must remain project-relative'),
163
+ updatedAt: isoDateTimeSchema,
164
+ }).strict();
165
+ export const publicationReadinessReportSchema = z.object({
166
+ schemaVersion: schemaVersionSchema,
167
+ profile: publicationProfileSchema,
168
+ ready: z.boolean(),
169
+ reasons: z.array(publicationReadinessReasonSchema).max(16),
170
+ metrics: z.object({ textCharacters: z.number().int().nonnegative(), blockCount: z.number().int().nonnegative(), textBlockCount: z.number().int().nonnegative(), mediaBlockCount: z.number().int().nonnegative(), sourceEvidenceCount: z.number().int().nonnegative(), citationCount: z.number().int().nonnegative(), citedBlockCount: z.number().int().nonnegative() }).strict(),
171
+ }).strict();
172
+ export const richMediaLinkSchema = z.object({
173
+ schemaVersion: schemaVersionSchema,
174
+ projectKey: projectKeySchema,
175
+ mediaRef: stableUriSchema,
176
+ sourceEvidenceRef: stableUriSchema,
177
+ mediaEvidenceRef: stableUriSchema,
178
+ contentHash: sha256Schema,
179
+ mediaType: z.string().trim().min(1).max(256),
180
+ byteLength: z.number().int().nonnegative(),
181
+ sourceUrl: httpUrlSchema,
182
+ canonicalUrl: httpUrlSchema,
183
+ linkedAt: isoDateTimeSchema,
184
+ }).strict();
@@ -0,0 +1,55 @@
1
+ /** Release admission contract. Intermediate, unreleased wave snapshots are not rollback targets. */
2
+ export const tenantCoreReadCapabilities = [
3
+ 'TENANT_RESOURCE_V1', 'TENANT_SOURCE_V1', 'TENANT_SOURCE_DOCUMENT_V2', 'TENANT_WORKFLOW_V1',
4
+ 'TENANT_ROUTES_V1', 'TENANT_CURRENTNESS_V2', 'TENANT_PUBLIC_REFERENCE_V1',
5
+ 'TENANT_PROJECT_CONTEXT_V1', 'TENANT_OAUTH_MANAGEMENT_V1', 'TENANT_RUNTIME_STATUS_V1',
6
+ ];
7
+ export const tenantCoreWriteCapabilities = ['TENANT_COLLECTION_V2', 'TENANT_ADMISSION_STOP_V1', 'TENANT_ADMISSION_V2'];
8
+ export function runtimeCompatibility(component) {
9
+ return { schemaVersion: 1, component, identityReadMinimum: 11, identityReadMaximum: 21,
10
+ capabilities: component === 'core' ? [...tenantCoreReadCapabilities, ...tenantCoreWriteCapabilities, 'TENANT_NATIVE_CLIENT_AUTH_V1'] : [...tenantCoreReadCapabilities, 'TENANT_NATIVE_CLIENT_AUTH_V1'],
11
+ tenantProviders: component === 'core' ? [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] : [], requiredCoreProviders: component === 'admin-web' ? [20] : [],
12
+ budgetReadVersions: component === 'core' ? [2] : [], budgetMigrationVersions: component === 'core' ? [1] : [] };
13
+ }
14
+ /** Conservative published compatibility floor: native Identity >=12 requires the complete current Tenant reader. */
15
+ export function assertRuntimeFormatCompatibility(raw, facts, component, mode) {
16
+ const fail = (code) => { throw new Error('RUNTIME_COMPATIBILITY_' + code); };
17
+ if (!['core', 'admin-web'].includes(component) || !['INSTALL', 'START', 'ROLLBACK', 'RECOVERY'].includes(mode))
18
+ fail('CONTEXT_INVALID');
19
+ if (!Number.isSafeInteger(facts.identityVersion) || facts.identityVersion < 11 || facts.identityVersion > 21)
20
+ fail('IDENTITY_UNSUPPORTED');
21
+ if (facts.budgetVersion !== null && ![1, 2].includes(facts.budgetVersion))
22
+ fail('BUDGET_UNSUPPORTED');
23
+ if (raw === undefined) {
24
+ if (facts.identityVersion >= 12 || facts.budgetVersion !== null)
25
+ fail('DECLARATION_REQUIRED');
26
+ return;
27
+ }
28
+ if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
29
+ fail('DECLARATION_INVALID');
30
+ const value = raw, expected = runtimeCompatibility(component), keys = Object.keys(expected);
31
+ if (Object.keys(value).length !== keys.length || Object.keys(value).some(key => !keys.includes(key)) || value.schemaVersion !== 1 || value.component !== component)
32
+ fail('DECLARATION_INVALID');
33
+ if (!Number.isSafeInteger(value.identityReadMinimum) || !Number.isSafeInteger(value.identityReadMaximum) || value.identityReadMinimum < 11 || value.identityReadMaximum > 21 || value.identityReadMinimum > value.identityReadMaximum)
34
+ fail('DECLARATION_INVALID');
35
+ for (const key of ['capabilities', 'tenantProviders', 'requiredCoreProviders', 'budgetReadVersions', 'budgetMigrationVersions']) {
36
+ const list = value[key], allowed = expected[key];
37
+ if (!Array.isArray(list) || new Set(list).size !== list.length || list.some(item => !allowed.includes(item)))
38
+ fail('DECLARATION_INVALID');
39
+ }
40
+ if (facts.identityVersion < value.identityReadMinimum || facts.identityVersion > value.identityReadMaximum)
41
+ fail('IDENTITY_UNSUPPORTED');
42
+ if (facts.identityVersion >= 12 && expected.capabilities.some(cap => (facts.identityVersion >= 21 || cap !== 'TENANT_NATIVE_CLIENT_AUTH_V1') && !value.capabilities.includes(cap)))
43
+ fail('CAPABILITY_REQUIRED');
44
+ // New Provider21/22 are advertised, but historical Identity >=12 does not imply the new opt-in writer.
45
+ if (facts.identityVersion >= 12 && ['tenantProviders', 'requiredCoreProviders'].some(key => (key === 'tenantProviders' && component === 'core' ? [16, 17, 18, 19, 20] : expected[key]).some(version => !value[key].includes(version))))
46
+ fail('PROVIDER_REQUIRED');
47
+ if (facts.identityVersion >= 21 && component === 'core' && !value.tenantProviders.includes(26))
48
+ fail('PROVIDER_REQUIRED');
49
+ if (component === 'core' && facts.budgetVersion !== null && !value.capabilities.includes('TENANT_ADMISSION_V2'))
50
+ fail('CAPABILITY_REQUIRED');
51
+ if (component === 'core' && facts.budgetVersion !== null && !value.budgetReadVersions.includes(facts.budgetVersion)) {
52
+ if (mode !== 'INSTALL' || !value.budgetMigrationVersions.includes(facts.budgetVersion))
53
+ fail('BUDGET_MIGRATION_REQUIRED');
54
+ }
55
+ }
@@ -0,0 +1,68 @@
1
+ import { z } from 'zod';
2
+ import { idSchema, isoDateTimeSchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ export const runtimeCapabilitySchema = z.string().trim().min(1).max(128);
4
+ export const runtimeNodeSchema = z.object({
5
+ schemaVersion: schemaVersionSchema,
6
+ nodeId: idSchema,
7
+ capabilities: z.array(runtimeCapabilitySchema).max(128),
8
+ codeRevision: z.string().trim().min(7).max(64),
9
+ observedAt: isoDateTimeSchema,
10
+ }).strict();
11
+ export const workAssignmentSchema = z.object({
12
+ schemaVersion: schemaVersionSchema,
13
+ assignmentId: idSchema,
14
+ workloadId: idSchema,
15
+ runtimeNodeId: idSchema,
16
+ projectRef: stableUriSchema.optional(),
17
+ revision: z.number().int().positive(),
18
+ enabled: z.boolean(),
19
+ effectiveFrom: isoDateTimeSchema.optional(),
20
+ effectiveUntil: isoDateTimeSchema.optional(),
21
+ }).strict().superRefine((value, ctx) => {
22
+ if (value.effectiveFrom && value.effectiveUntil && Date.parse(value.effectiveUntil) <= Date.parse(value.effectiveFrom)) {
23
+ ctx.addIssue({
24
+ code: 'custom',
25
+ path: ['effectiveUntil'],
26
+ message: 'effectiveUntil must be after effectiveFrom',
27
+ });
28
+ }
29
+ });
30
+ export const runStatusSchema = z.enum(['SUCCEEDED', 'FAILED', 'PARTIAL', 'SKIPPED', 'BLOCKED']);
31
+ /** Fixed numeric diagnostics only; not a response body, URL or new finality authority. */
32
+ export function nativeAcquisitionHttpStatus(errorCode, value) {
33
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < 100 || value > 599)
34
+ return undefined;
35
+ const terminal = errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' && (value < 200 || value >= 300 && value < 500 && ![401, 403, 429].includes(value));
36
+ const controlled = errorCode === 'TENANT_SOURCE_ACCESS_CONTROLLED' && [401, 403].includes(value);
37
+ return terminal || controlled ? value : undefined;
38
+ }
39
+ export const runReceiptSchema = z.object({
40
+ schemaVersion: schemaVersionSchema,
41
+ runId: idSchema,
42
+ workloadId: idSchema,
43
+ runtimeNodeId: idSchema,
44
+ projectRef: stableUriSchema.optional(),
45
+ projectIntentRef: stableUriSchema.optional(),
46
+ collectionBatchRef: stableUriSchema.optional(),
47
+ assignmentRevision: z.number().int().positive(),
48
+ codeRevision: z.string().trim().min(7).max(64),
49
+ startedAt: isoDateTimeSchema,
50
+ finishedAt: isoDateTimeSchema,
51
+ status: runStatusSchema,
52
+ inputFingerprint: sha256Schema.optional(),
53
+ outputFingerprint: sha256Schema.optional(),
54
+ evidenceCount: z.number().int().nonnegative(),
55
+ observationCount: z.number().int().nonnegative(),
56
+ errorCode: z.string().trim().min(1).max(128).optional(),
57
+ acquisitionHttpStatus: z.number().int().min(100).max(599).optional(),
58
+ }).strict().superRefine((value, ctx) => {
59
+ if (Date.parse(value.finishedAt) < Date.parse(value.startedAt)) {
60
+ ctx.addIssue({ code: 'custom', path: ['finishedAt'], message: 'finishedAt must not precede startedAt' });
61
+ }
62
+ if (value.status === 'FAILED' && !value.errorCode) {
63
+ ctx.addIssue({ code: 'custom', path: ['errorCode'], message: 'FAILED runs require errorCode' });
64
+ }
65
+ if (value.acquisitionHttpStatus !== undefined && (!value.collectionBatchRef || nativeAcquisitionHttpStatus(value.errorCode, value.acquisitionHttpStatus) === undefined || (value.errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' ? value.status !== 'FAILED' : value.status !== 'BLOCKED'))) {
66
+ ctx.addIssue({ code: 'custom', path: ['acquisitionHttpStatus'], message: 'HTTP diagnostic must match the native acquisition failure class' });
67
+ }
68
+ });