@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,103 @@
1
+ import { z } from 'zod';
2
+ import { accessClassificationSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ export const artifactIdSchema = z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
4
+ export const artifactKindSchema = z.enum([
5
+ 'SIGNAL_GENERATION',
6
+ 'RESEARCH',
7
+ 'REVIEW',
8
+ 'KNOWLEDGE',
9
+ 'INSIGHT',
10
+ ]);
11
+ export const artifactStatusSchema = z.enum([
12
+ 'GENERATED',
13
+ 'DRAFT',
14
+ 'READY_FOR_REVIEW',
15
+ 'REWORK_REQUIRED',
16
+ 'APPROVED',
17
+ 'REJECTED',
18
+ 'SUPERSEDED',
19
+ ]);
20
+ export const artifactManifestSchema = z.object({
21
+ schemaVersion: schemaVersionSchema,
22
+ artifactId: artifactIdSchema,
23
+ kind: artifactKindSchema,
24
+ projectKey: projectKeySchema,
25
+ projectRef: stableUriSchema.optional(),
26
+ version: z.number().int().positive(),
27
+ ref: stableUriSchema,
28
+ identityRef: stableUriSchema,
29
+ status: artifactStatusSchema,
30
+ access: accessClassificationSchema.default('INTERNAL'),
31
+ title: z.string().trim().min(1).max(500).optional(),
32
+ createdAt: isoDateTimeSchema,
33
+ observedAt: isoDateTimeSchema.nullable().default(null),
34
+ validUntil: isoDateTimeSchema.nullable().default(null),
35
+ contentHash: sha256Schema,
36
+ mediaType: z.string().trim().min(1).max(256),
37
+ byteLength: z.number().int().nonnegative(),
38
+ evidenceRefs: z.array(stableUriSchema).max(256).default([]),
39
+ relatedRefs: z.array(stableUriSchema).max(256).default([]),
40
+ supersedesRef: stableUriSchema.nullable().default(null),
41
+ }).strict().superRefine((value, ctx) => {
42
+ if (value.projectRef) {
43
+ try {
44
+ const url = new URL(value.projectRef);
45
+ if (url.protocol !== 'knowledge-project:' || url.hostname.toLowerCase() !== value.projectKey.toLowerCase())
46
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match Artifact project' });
47
+ }
48
+ catch {
49
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'Invalid projectRef' });
50
+ }
51
+ }
52
+ if (value.validUntil && value.observedAt && Date.parse(value.validUntil) < Date.parse(value.observedAt)) {
53
+ ctx.addIssue({ code: 'custom', path: ['validUntil'], message: 'validUntil must not precede observedAt' });
54
+ }
55
+ if (value.kind === 'SIGNAL_GENERATION' && (value.version !== 1 || value.status !== 'GENERATED')) {
56
+ ctx.addIssue({ code: 'custom', path: ['version'], message: 'Signal generations are immutable generated artifacts at version 1' });
57
+ }
58
+ if (['KNOWLEDGE', 'INSIGHT'].includes(value.kind) && value.status === 'GENERATED') {
59
+ ctx.addIssue({ code: 'custom', path: ['status'], message: 'Knowledge/Insight cannot use GENERATED status' });
60
+ }
61
+ });
62
+ export const artifactWriteMetadataSchema = z.object({
63
+ schemaVersion: schemaVersionSchema,
64
+ artifactId: artifactIdSchema,
65
+ kind: artifactKindSchema,
66
+ projectKey: projectKeySchema,
67
+ projectRef: stableUriSchema.optional(),
68
+ version: z.number().int().positive(),
69
+ status: artifactStatusSchema,
70
+ access: accessClassificationSchema.default('INTERNAL'),
71
+ title: z.string().trim().min(1).max(500).optional(),
72
+ createdAt: isoDateTimeSchema,
73
+ observedAt: isoDateTimeSchema.nullable().default(null),
74
+ validUntil: isoDateTimeSchema.nullable().default(null),
75
+ mediaType: z.string().trim().min(1).max(256),
76
+ evidenceRefs: z.array(stableUriSchema).max(256).default([]),
77
+ relatedRefs: z.array(stableUriSchema).max(256).default([]),
78
+ supersedesRef: stableUriSchema.nullable().default(null),
79
+ }).strict().superRefine((value, ctx) => {
80
+ if (value.validUntil && value.observedAt && Date.parse(value.validUntil) < Date.parse(value.observedAt)) {
81
+ ctx.addIssue({ code: 'custom', path: ['validUntil'], message: 'validUntil must not precede observedAt' });
82
+ }
83
+ if (value.kind === 'SIGNAL_GENERATION') {
84
+ ctx.addIssue({ code: 'custom', path: ['kind'], message: 'Signal generations are produced only by the signal composer' });
85
+ }
86
+ if (value.kind === 'REVIEW') {
87
+ ctx.addIssue({ code: 'custom', path: ['kind'], message: 'Review artifacts are produced only by the review lifecycle' });
88
+ }
89
+ if (['KNOWLEDGE', 'INSIGHT'].includes(value.kind) && value.status !== 'DRAFT') {
90
+ ctx.addIssue({ code: 'custom', path: ['status'], message: 'Knowledge/Insight manual writes must enter as DRAFT' });
91
+ }
92
+ if (value.kind === 'RESEARCH' && value.status !== 'DRAFT') {
93
+ ctx.addIssue({ code: 'custom', path: ['status'], message: 'Research manual writes must enter as DRAFT' });
94
+ }
95
+ });
96
+ export const reviewDecisionSchema = z.object({
97
+ schemaVersion: schemaVersionSchema,
98
+ targetRef: stableUriSchema,
99
+ decision: z.enum(['PASS', 'REWORK', 'REJECT']),
100
+ reviewedAt: isoDateTimeSchema,
101
+ summary: z.string().trim().min(1).max(16000),
102
+ evidenceRefs: z.array(stableUriSchema).max(256).default([]),
103
+ }).strict();
@@ -0,0 +1,69 @@
1
+ import { z } from 'zod';
2
+ import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, stableUriSchema } from './common.js';
3
+ export const knowledgeControlScopeSchema = z.enum([
4
+ 'knowledge.read',
5
+ 'knowledge.collect',
6
+ 'knowledge.source.manage',
7
+ 'knowledge.review',
8
+ 'knowledge.publish',
9
+ ]);
10
+ export const collectionControlRequestIdSchema = idSchema.regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,255}$/);
11
+ export const collectionControlCreateRequestSchema = z.object({
12
+ projectKey: projectKeySchema,
13
+ requestId: collectionControlRequestIdSchema,
14
+ sourceIds: z.array(idSchema).min(1).max(1_000),
15
+ }).strict().transform((value) => ({
16
+ ...value,
17
+ sourceIds: [...new Set(value.sourceIds)].sort(),
18
+ }));
19
+ export const collectionControlAuditSchema = z.object({
20
+ schemaVersion: schemaVersionSchema,
21
+ projectKey: projectKeySchema,
22
+ requestId: collectionControlRequestIdSchema,
23
+ batchId: idSchema,
24
+ batchRef: stableUriSchema,
25
+ tenantId: idSchema,
26
+ clientId: idSchema,
27
+ requestedAt: isoDateTimeSchema,
28
+ sourceIds: z.array(idSchema).min(1).max(1_000),
29
+ sourceRevisionRefs: z.array(stableUriSchema).min(1).max(1_000),
30
+ admission: z.literal('ACCEPTED'),
31
+ }).strict().superRefine((value, ctx) => {
32
+ if (value.sourceIds.length !== value.sourceRevisionRefs.length)
33
+ ctx.addIssue({
34
+ code: 'custom',
35
+ path: ['sourceRevisionRefs'],
36
+ message: 'sourceRevisionRefs must align with sourceIds',
37
+ });
38
+ });
39
+ export const collectionControlStatusSchema = z.enum(['PENDING', 'SUCCEEDED', 'PARTIAL', 'FAILED']);
40
+ export const collectionControlRunSummarySchema = z.object({
41
+ runId: idSchema,
42
+ runtimeNodeId: idSchema,
43
+ startedAt: isoDateTimeSchema,
44
+ finishedAt: isoDateTimeSchema,
45
+ status: z.enum(['SUCCEEDED', 'PARTIAL', 'FAILED']),
46
+ }).strict();
47
+ export const collectionControlRequestProjectionSchema = z.object({
48
+ schemaVersion: schemaVersionSchema,
49
+ projectKey: projectKeySchema,
50
+ requestId: collectionControlRequestIdSchema,
51
+ batchId: idSchema,
52
+ batchRef: stableUriSchema,
53
+ trigger: z.literal('MANUAL'),
54
+ status: collectionControlStatusSchema,
55
+ requestedAt: isoDateTimeSchema,
56
+ requestedBy: z.object({ tenantId: idSchema, clientId: idSchema }).strict(),
57
+ sourceIds: z.array(idSchema).min(1).max(1_000),
58
+ sourceRevisionRefs: z.array(stableUriSchema).min(1).max(1_000),
59
+ run: collectionControlRunSummarySchema.nullable(),
60
+ }).strict();
61
+ export const collectionControlCreateResponseSchema = z.object({
62
+ schemaVersion: schemaVersionSchema,
63
+ inserted: z.boolean(),
64
+ request: collectionControlRequestProjectionSchema,
65
+ }).strict();
66
+ export const collectionControlGetResponseSchema = z.object({
67
+ schemaVersion: schemaVersionSchema,
68
+ request: collectionControlRequestProjectionSchema,
69
+ }).strict();
@@ -0,0 +1,174 @@
1
+ import { z } from 'zod';
2
+ import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ import { acquisitionTargetSchema, collectionRefreshSchema } from './project-intent.js';
4
+ export const collectionBatchSourceBindingSchema = z.object({
5
+ sourceId: idSchema,
6
+ sourceRef: stableUriSchema,
7
+ sourceRevisionRef: stableUriSchema,
8
+ targetId: sha256Schema,
9
+ }).strict();
10
+ /** The collector-facing task deliberately contains no project/source-family semantics. */
11
+ export const collectionTaskSchema = z.object({
12
+ taskId: idSchema,
13
+ targetId: sha256Schema,
14
+ target: acquisitionTargetSchema,
15
+ }).strict();
16
+ export const collectionBatchTriggerSchema = z.enum(['MANUAL', 'DUE']);
17
+ export const collectionBatchSchema = z.object({
18
+ schemaVersion: schemaVersionSchema,
19
+ projectKey: projectKeySchema,
20
+ projectRef: stableUriSchema,
21
+ projectIntentRef: stableUriSchema,
22
+ requestId: idSchema,
23
+ batchId: idSchema,
24
+ batchRef: stableUriSchema,
25
+ trigger: collectionBatchTriggerSchema.default('MANUAL'),
26
+ createdAt: isoDateTimeSchema,
27
+ sourceBindings: z.array(collectionBatchSourceBindingSchema).min(1).max(10_000),
28
+ tasks: z.array(collectionTaskSchema).min(1).max(10_000),
29
+ }).strict().superRefine((value, ctx) => {
30
+ const sourceIds = new Set(), bindingTargets = new Set(), taskIds = new Set(), taskTargets = new Set();
31
+ for (let i = 0; i < value.sourceBindings.length; i += 1) {
32
+ const row = value.sourceBindings[i];
33
+ if (sourceIds.has(row.sourceId))
34
+ ctx.addIssue({ code: 'custom', path: ['sourceBindings', i, 'sourceId'], message: 'sourceId must be unique in a batch' });
35
+ if (bindingTargets.has(row.targetId))
36
+ ctx.addIssue({ code: 'custom', path: ['sourceBindings', i, 'targetId'], message: 'targetId must be unique in project source bindings' });
37
+ sourceIds.add(row.sourceId);
38
+ bindingTargets.add(row.targetId);
39
+ }
40
+ for (let i = 0; i < value.tasks.length; i += 1) {
41
+ const row = value.tasks[i];
42
+ if (taskIds.has(row.taskId))
43
+ ctx.addIssue({ code: 'custom', path: ['tasks', i, 'taskId'], message: 'taskId must be unique in a batch' });
44
+ if (taskTargets.has(row.targetId))
45
+ ctx.addIssue({ code: 'custom', path: ['tasks', i, 'targetId'], message: 'targetId must be unique in a batch' });
46
+ taskIds.add(row.taskId);
47
+ taskTargets.add(row.targetId);
48
+ }
49
+ for (let i = 0; i < value.tasks.length; i += 1)
50
+ if (!bindingTargets.has(value.tasks[i].targetId))
51
+ ctx.addIssue({ code: 'custom', path: ['tasks', i, 'targetId'], message: 'task target must have a source binding' });
52
+ for (let i = 0; i < value.sourceBindings.length; i += 1)
53
+ if (!taskTargets.has(value.sourceBindings[i].targetId))
54
+ ctx.addIssue({ code: 'custom', path: ['sourceBindings', i, 'targetId'], message: 'source binding must have a collector task' });
55
+ });
56
+ export const collectorBatchViewSchema = z.object({
57
+ schemaVersion: schemaVersionSchema,
58
+ batchId: idSchema,
59
+ tasks: z.array(collectionTaskSchema).min(1).max(10_000),
60
+ }).strict();
61
+ export const sourceCollectionDueReasonSchema = z.enum([
62
+ 'DISABLED',
63
+ 'MANUAL',
64
+ 'PENDING_BATCH',
65
+ 'SPECIALIZED_EXECUTOR',
66
+ 'SPECIALIZED_COLLECTOR',
67
+ 'NEVER_ATTEMPTED',
68
+ 'INTERVAL_WAIT',
69
+ 'INTERVAL_ELAPSED',
70
+ ]);
71
+ export const sourceCollectionDueEntrySchema = z.object({
72
+ sourceId: idSchema,
73
+ sourceRef: stableUriSchema,
74
+ sourceRevisionRef: stableUriSchema,
75
+ enabled: z.boolean(),
76
+ refresh: collectionRefreshSchema,
77
+ lastAttemptAt: isoDateTimeSchema.nullable(),
78
+ lastState: z.enum(['ACQUIRED', 'ACCESS_CONTROLLED', 'HTTP_TERMINAL', 'FAILED']).nullable(),
79
+ lastBatchRef: stableUriSchema.nullable(),
80
+ lastRunId: idSchema.nullable(),
81
+ pendingBatchRef: stableUriSchema.nullable(),
82
+ nextDueAt: isoDateTimeSchema.nullable(),
83
+ due: z.boolean(),
84
+ reason: sourceCollectionDueReasonSchema,
85
+ }).strict().superRefine((value, ctx) => {
86
+ if (value.reason === 'PENDING_BATCH' && !value.pendingBatchRef)
87
+ ctx.addIssue({ code: 'custom', path: ['pendingBatchRef'], message: 'PENDING_BATCH requires pendingBatchRef' });
88
+ if (value.reason !== 'PENDING_BATCH' && value.pendingBatchRef)
89
+ ctx.addIssue({ code: 'custom', path: ['pendingBatchRef'], message: 'pendingBatchRef is only valid for PENDING_BATCH' });
90
+ if (value.due && !['NEVER_ATTEMPTED', 'INTERVAL_ELAPSED'].includes(value.reason))
91
+ ctx.addIssue({ code: 'custom', path: ['due'], message: 'due source must be never attempted or interval elapsed' });
92
+ if (value.refresh.mode === 'MANUAL' && value.nextDueAt !== null)
93
+ ctx.addIssue({ code: 'custom', path: ['nextDueAt'], message: 'MANUAL source has no automatic nextDueAt' });
94
+ });
95
+ export const projectCollectionDuePlanSchema = z.object({
96
+ schemaVersion: schemaVersionSchema,
97
+ projectKey: projectKeySchema,
98
+ projectRef: stableUriSchema,
99
+ projectIntentRef: stableUriSchema,
100
+ assessedAt: isoDateTimeSchema,
101
+ sourceCount: z.number().int().nonnegative(),
102
+ dueSourceCount: z.number().int().nonnegative(),
103
+ dueSourceIds: z.array(idSchema).max(10_000),
104
+ sources: z.array(sourceCollectionDueEntrySchema).max(10_000),
105
+ }).strict().superRefine((value, ctx) => {
106
+ const due = value.sources.filter((row) => row.due).map((row) => row.sourceId);
107
+ if (value.sourceCount !== value.sources.length)
108
+ ctx.addIssue({ code: 'custom', path: ['sourceCount'], message: 'sourceCount must match sources' });
109
+ if (value.dueSourceCount !== due.length)
110
+ ctx.addIssue({ code: 'custom', path: ['dueSourceCount'], message: 'dueSourceCount must match due sources' });
111
+ if (JSON.stringify(value.dueSourceIds) !== JSON.stringify(due))
112
+ ctx.addIssue({ code: 'custom', path: ['dueSourceIds'], message: 'dueSourceIds must match ordered due sources' });
113
+ });
114
+ export const collectionTaskRunResultSchema = z.object({
115
+ taskId: idSchema,
116
+ targetId: sha256Schema,
117
+ sourceId: idSchema,
118
+ sourceRevisionRef: stableUriSchema,
119
+ observedAt: isoDateTimeSchema,
120
+ state: z.enum(['ACQUIRED', 'ACCESS_CONTROLLED', 'HTTP_TERMINAL', 'FAILED']),
121
+ finalLocator: stableUriSchema.nullable(),
122
+ httpStatus: z.number().int().min(100).max(599).nullable(),
123
+ evidenceRef: stableUriSchema.nullable(),
124
+ contentHash: sha256Schema.nullable(),
125
+ byteLength: z.number().int().nonnegative().nullable(),
126
+ mediaType: z.string().trim().min(1).max(256).nullable(),
127
+ reusedEvidence: z.boolean(),
128
+ relatedEvidenceRefs: z.array(stableUriSchema).max(512).default([]),
129
+ richDocumentRef: stableUriSchema.nullable().default(null),
130
+ projectionJobId: idSchema.optional(),
131
+ projectionState: z.enum(['PENDING', 'SUCCEEDED', 'PARTIAL', 'FAILED', 'NOT_REQUIRED']).optional(),
132
+ errorCode: z.string().trim().min(1).max(256).nullable(),
133
+ }).strict().superRefine((value, ctx) => {
134
+ const acquired = value.state === 'ACQUIRED';
135
+ if (acquired && (!value.evidenceRef || !value.contentHash || value.byteLength === null || !value.mediaType))
136
+ ctx.addIssue({ code: 'custom', path: ['evidenceRef'], message: 'ACQUIRED result requires persisted Evidence identity' });
137
+ if (!acquired && (value.evidenceRef || value.contentHash || value.byteLength !== null || value.mediaType || value.reusedEvidence || value.relatedEvidenceRefs.length || value.richDocumentRef))
138
+ ctx.addIssue({ code: 'custom', path: ['evidenceRef'], message: 'non-ACQUIRED result cannot claim persisted Evidence' });
139
+ if (value.projectionState === 'PENDING' && (!value.projectionJobId || value.richDocumentRef || value.relatedEvidenceRefs.length || value.errorCode))
140
+ ctx.addIssue({ code: 'custom', path: ['projectionState'], message: 'pending projection requires a job and no derived result' });
141
+ if (value.projectionState === 'SUCCEEDED' && !value.richDocumentRef)
142
+ ctx.addIssue({ code: 'custom', path: ['projectionState'], message: 'successful projection requires a document' });
143
+ if (value.projectionState === 'PARTIAL' && (!acquired || !value.errorCode || !value.richDocumentRef))
144
+ ctx.addIssue({ code: 'custom', path: ['projectionState'], message: 'partial projection requires acquired Evidence, document and error' });
145
+ if (value.projectionState === 'NOT_REQUIRED' && value.richDocumentRef)
146
+ ctx.addIssue({ code: 'custom', path: ['projectionState'], message: 'unrequired projection cannot claim a document' });
147
+ if (value.projectionState === 'FAILED' && (!acquired || !value.errorCode || value.richDocumentRef))
148
+ ctx.addIssue({ code: 'custom', path: ['projectionState'], message: 'failed projection requires acquired raw Evidence, error and no document' });
149
+ if (!acquired && value.projectionState)
150
+ ctx.addIssue({ code: 'custom', path: ['projectionState'], message: 'projection requires acquired raw Evidence' });
151
+ if (value.state === 'FAILED' && !value.errorCode)
152
+ ctx.addIssue({ code: 'custom', path: ['errorCode'], message: 'FAILED result requires errorCode' });
153
+ });
154
+ export const collectionRunReceiptSchema = z.object({
155
+ schemaVersion: schemaVersionSchema,
156
+ projectKey: projectKeySchema,
157
+ projectRef: stableUriSchema,
158
+ batchId: idSchema,
159
+ batchRef: stableUriSchema,
160
+ runId: idSchema,
161
+ runtimeNodeId: idSchema,
162
+ startedAt: isoDateTimeSchema,
163
+ finishedAt: isoDateTimeSchema,
164
+ status: z.enum(['SUCCEEDED', 'PARTIAL', 'FAILED']),
165
+ results: z.array(collectionTaskRunResultSchema).min(1).max(10_000),
166
+ }).strict().superRefine((value, ctx) => {
167
+ if (Date.parse(value.finishedAt) < Date.parse(value.startedAt))
168
+ ctx.addIssue({ code: 'custom', path: ['finishedAt'], message: 'finishedAt must not precede startedAt' });
169
+ const acquired = value.results.filter((row) => row.state === 'ACQUIRED').length;
170
+ const incomplete = value.results.some(row => row.projectionState === 'FAILED' || row.projectionState === 'PARTIAL');
171
+ const expected = acquired === 0 ? 'FAILED' : acquired === value.results.length && !incomplete ? 'SUCCEEDED' : 'PARTIAL';
172
+ if (value.status !== expected)
173
+ ctx.addIssue({ code: 'custom', path: ['status'], message: `status must be ${expected} for task results` });
174
+ });
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+ export const schemaVersionSchema = z.literal(1);
3
+ export const idSchema = z.string().trim().min(1).max(256);
4
+ export const projectKeySchema = z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
5
+ export const isoDateTimeSchema = z.iso.datetime({ offset: true });
6
+ export const sha256Schema = z.string().regex(/^sha256:[a-f0-9]{64}$/);
7
+ export const stableUriSchema = z.string().trim().min(1).max(2048).regex(/^[a-z][a-z0-9+.-]*:\/\/.+/i);
8
+ export const accessClassificationSchema = z.enum([
9
+ 'PUBLIC',
10
+ 'PUBLIC_METADATA',
11
+ 'TENANT',
12
+ 'INTERNAL',
13
+ 'FDE_INTERNAL',
14
+ 'ACCESS_CONTROLLED',
15
+ 'UNKNOWN',
16
+ ]);
@@ -0,0 +1,221 @@
1
+ import { z } from 'zod';
2
+ import { isoDateTimeSchema, projectKeySchema, schemaVersionSchema, stableUriSchema } from './common.js';
3
+ export const currentnessStateSchema = z.enum(['CURRENT', 'DUE', 'STALE', 'BLOCKED', 'UNKNOWN']);
4
+ export const currentnessActionSchema = z.enum(['ALLOW', 'REVIEW_REQUIRED', 'DENY']);
5
+ export const currentnessSubjectKindSchema = z.enum(['SOURCE', 'EVIDENCE', 'ARTIFACT']);
6
+ export const currentnessPolicySchema = z.object({
7
+ schemaVersion: schemaVersionSchema,
8
+ dueAfterSeconds: z.number().int().min(60).max(366 * 24 * 60 * 60),
9
+ staleAfterSeconds: z.number().int().min(60).max(5 * 366 * 24 * 60 * 60),
10
+ staleAction: currentnessActionSchema.default('REVIEW_REQUIRED'),
11
+ unknownAction: currentnessActionSchema.default('REVIEW_REQUIRED'),
12
+ blockedAction: currentnessActionSchema.default('REVIEW_REQUIRED'),
13
+ }).strict().superRefine((value, ctx) => {
14
+ if (value.staleAfterSeconds < value.dueAfterSeconds) {
15
+ ctx.addIssue({ code: 'custom', path: ['staleAfterSeconds'], message: 'staleAfterSeconds must be >= dueAfterSeconds' });
16
+ }
17
+ });
18
+ export const currentnessReasonCodeSchema = z.enum([
19
+ 'CURRENT_WITHIN_CADENCE',
20
+ 'REFRESH_DUE',
21
+ 'STALE_AFTER_THRESHOLD',
22
+ 'SOURCE_BLOCKED',
23
+ 'SOURCE_CONTENT_CHANGED',
24
+ 'NO_SUCCESSFUL_OBSERVATION',
25
+ ]);
26
+ export const currentnessAssessmentSchema = z.object({
27
+ schemaVersion: schemaVersionSchema,
28
+ subjectRef: stableUriSchema,
29
+ subjectKind: currentnessSubjectKindSchema,
30
+ projectRef: stableUriSchema.optional(),
31
+ sourceRevisionRef: stableUriSchema.optional(),
32
+ basisRef: stableUriSchema.nullable().default(null),
33
+ state: currentnessStateSchema,
34
+ action: currentnessActionSchema,
35
+ assessedAt: isoDateTimeSchema,
36
+ observedAt: isoDateTimeSchema.nullable().default(null),
37
+ lastSuccessAt: isoDateTimeSchema.nullable().default(null),
38
+ lastAttemptAt: isoDateTimeSchema.nullable().default(null),
39
+ dueAt: isoDateTimeSchema.nullable().default(null),
40
+ staleAt: isoDateTimeSchema.nullable().default(null),
41
+ ageSeconds: z.number().int().nonnegative().nullable().default(null),
42
+ reasonCode: currentnessReasonCodeSchema,
43
+ policy: currentnessPolicySchema,
44
+ }).strict();
45
+ export const claimCurrentnessStatusSchema = z.enum([
46
+ 'CURRENT',
47
+ 'PARTIALLY_SUPERSEDED',
48
+ 'SUPERSEDED',
49
+ 'PENDING_CURRENTNESS_REVIEW',
50
+ 'HISTORICAL_REFERENCE',
51
+ 'DYNAMIC_CURRENT',
52
+ 'DYNAMIC_EXPIRED',
53
+ ]);
54
+ export const claimCurrentnessRiskSchema = z.enum(['STANDARD', 'HIGH']);
55
+ export const claimTimeSensitivitySchema = z.enum(['DURABLE', 'TIME_SENSITIVE', 'DYNAMIC']);
56
+ export const claimReviewTriggerSchema = z.enum([
57
+ 'SCHEDULED_REFRESH',
58
+ 'SAME_URL_DIFFERENT_HASH',
59
+ 'LATER_OFFICIAL_SOURCE',
60
+ 'REGULATORY_ACTION',
61
+ 'OPERATOR_TRANSITION',
62
+ 'CORRECTION',
63
+ 'DYNAMIC_EXPIRY',
64
+ 'INITIAL_REVIEW',
65
+ ]);
66
+ const evidenceUriSchema = stableUriSchema.refine((value) => value.toLowerCase().startsWith('evidence://'), 'claim provenance must use EvidenceRef');
67
+ const claimIdSchema = z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
68
+ export const claimCurrentnessChangeSchema = z.object({
69
+ schemaVersion: schemaVersionSchema,
70
+ artifactRef: stableUriSchema,
71
+ claimId: claimIdSchema,
72
+ scope: z.string().trim().min(1).max(1000),
73
+ status: claimCurrentnessStatusSchema,
74
+ risk: claimCurrentnessRiskSchema.default('STANDARD'),
75
+ timeSensitivity: claimTimeSensitivitySchema.default('DURABLE'),
76
+ effectiveFrom: isoDateTimeSchema.nullable().default(null),
77
+ effectiveUntil: isoDateTimeSchema.nullable().default(null),
78
+ discoveredAt: isoDateTimeSchema,
79
+ reviewTrigger: claimReviewTriggerSchema,
80
+ previousEvidenceRefs: z.array(evidenceUriSchema).max(64).default([]),
81
+ currentEvidenceRefs: z.array(evidenceUriSchema).max(64).default([]),
82
+ replacementArtifactRef: stableUriSchema.nullable().default(null),
83
+ reason: z.string().trim().min(1).max(4000),
84
+ }).strict().superRefine((value, ctx) => {
85
+ const prior = new Set(value.previousEvidenceRefs);
86
+ const current = new Set(value.currentEvidenceRefs);
87
+ if (prior.size !== value.previousEvidenceRefs.length)
88
+ ctx.addIssue({ code: 'custom', path: ['previousEvidenceRefs'], message: 'duplicate previous EvidenceRef' });
89
+ if (current.size !== value.currentEvidenceRefs.length)
90
+ ctx.addIssue({ code: 'custom', path: ['currentEvidenceRefs'], message: 'duplicate current EvidenceRef' });
91
+ if ([...prior].some((ref) => current.has(ref)))
92
+ ctx.addIssue({ code: 'custom', path: ['currentEvidenceRefs'], message: 'previous and current EvidenceRefs must be distinct' });
93
+ if (value.effectiveFrom && value.effectiveUntil && Date.parse(value.effectiveUntil) <= Date.parse(value.effectiveFrom)) {
94
+ ctx.addIssue({ code: 'custom', path: ['effectiveUntil'], message: 'effectiveUntil must be later than effectiveFrom' });
95
+ }
96
+ if (value.timeSensitivity === 'TIME_SENSITIVE' && value.status !== 'PENDING_CURRENTNESS_REVIEW' && !value.effectiveFrom) {
97
+ ctx.addIssue({ code: 'custom', path: ['effectiveFrom'], message: 'time-sensitive reviewed claims require source-grounded effectiveFrom' });
98
+ }
99
+ if (value.timeSensitivity === 'DYNAMIC' && !value.effectiveUntil) {
100
+ ctx.addIssue({ code: 'custom', path: ['effectiveUntil'], message: 'dynamic claims require an explicit expiry boundary' });
101
+ }
102
+ if (value.status === 'DYNAMIC_CURRENT' && value.effectiveUntil && Date.parse(value.effectiveUntil) <= Date.parse(value.discoveredAt)) {
103
+ ctx.addIssue({ code: 'custom', path: ['effectiveUntil'], message: 'dynamic current claim must still be valid when discovered' });
104
+ }
105
+ if (value.status === 'DYNAMIC_EXPIRED' && value.effectiveUntil && Date.parse(value.discoveredAt) < Date.parse(value.effectiveUntil)) {
106
+ ctx.addIssue({ code: 'custom', path: ['effectiveUntil'], message: 'dynamic expired claim requires discovery at or after expiry' });
107
+ }
108
+ if (['CURRENT', 'DYNAMIC_CURRENT'].includes(value.status) && value.currentEvidenceRefs.length === 0) {
109
+ ctx.addIssue({ code: 'custom', path: ['currentEvidenceRefs'], message: 'current claim requires exact current EvidenceRef' });
110
+ }
111
+ if (['PARTIALLY_SUPERSEDED', 'SUPERSEDED'].includes(value.status)) {
112
+ if (value.previousEvidenceRefs.length === 0)
113
+ ctx.addIssue({ code: 'custom', path: ['previousEvidenceRefs'], message: 'supersession requires previous EvidenceRef' });
114
+ if (value.currentEvidenceRefs.length === 0)
115
+ ctx.addIssue({ code: 'custom', path: ['currentEvidenceRefs'], message: 'supersession requires replacement/current EvidenceRef' });
116
+ }
117
+ if (['HISTORICAL_REFERENCE', 'DYNAMIC_EXPIRED'].includes(value.status) && value.previousEvidenceRefs.length === 0) {
118
+ ctx.addIssue({ code: 'custom', path: ['previousEvidenceRefs'], message: 'historical/expired claim requires retained prior EvidenceRef' });
119
+ }
120
+ if (value.status === 'PENDING_CURRENTNESS_REVIEW' && value.previousEvidenceRefs.length === 0 && value.currentEvidenceRefs.length === 0) {
121
+ ctx.addIssue({ code: 'custom', path: ['currentEvidenceRefs'], message: 'pending currentness review requires at least one exact EvidenceRef' });
122
+ }
123
+ });
124
+ export const claimCurrentnessDecisionSchema = claimCurrentnessChangeSchema.safeExtend({
125
+ reviewedAt: isoDateTimeSchema,
126
+ }).superRefine((value, ctx) => {
127
+ if (Date.parse(value.reviewedAt) < Date.parse(value.discoveredAt)) {
128
+ ctx.addIssue({ code: 'custom', path: ['reviewedAt'], message: 'reviewedAt must not precede discoveredAt' });
129
+ }
130
+ if (value.status === 'DYNAMIC_CURRENT' && value.effectiveUntil && Date.parse(value.reviewedAt) >= Date.parse(value.effectiveUntil)) {
131
+ ctx.addIssue({ code: 'custom', path: ['effectiveUntil'], message: 'dynamic current claim must not already be expired at review time' });
132
+ }
133
+ });
134
+ export const claimCurrentnessProposalSchema = z.object({
135
+ schemaVersion: schemaVersionSchema,
136
+ type: z.literal('CLAIM_CURRENTNESS_PROPOSAL'),
137
+ projectKey: projectKeySchema,
138
+ proposalId: z.string().trim().min(3).max(80).regex(/^[a-z0-9][a-z0-9._-]*$/),
139
+ artifactRef: stableUriSchema,
140
+ createdAt: isoDateTimeSchema,
141
+ changes: z.array(claimCurrentnessChangeSchema).min(1).max(256),
142
+ }).strict().superRefine((value, ctx) => {
143
+ const ids = new Set();
144
+ value.changes.forEach((change, index) => {
145
+ if (change.artifactRef !== value.artifactRef)
146
+ ctx.addIssue({ code: 'custom', path: ['changes', index, 'artifactRef'], message: 'claim change artifactRef must match proposal artifactRef' });
147
+ if (ids.has(change.claimId))
148
+ ctx.addIssue({ code: 'custom', path: ['changes', index, 'claimId'], message: 'duplicate claimId in proposal' });
149
+ ids.add(change.claimId);
150
+ if (Date.parse(change.discoveredAt) > Date.parse(value.createdAt))
151
+ ctx.addIssue({ code: 'custom', path: ['changes', index, 'discoveredAt'], message: 'discoveredAt must not be after proposal createdAt' });
152
+ });
153
+ });
154
+ export const claimCurrentnessLedgerSchema = z.object({
155
+ schemaVersion: schemaVersionSchema,
156
+ ledgerId: z.string().regex(/^sha256:[a-f0-9]{64}$/),
157
+ projectKey: projectKeySchema,
158
+ artifactRef: stableUriSchema,
159
+ proposalRef: stableUriSchema,
160
+ reviewRef: stableUriSchema,
161
+ reviewedAt: isoDateTimeSchema,
162
+ decisionsHash: z.string().regex(/^sha256:[a-f0-9]{64}$/),
163
+ decisions: z.array(claimCurrentnessDecisionSchema).min(1).max(256),
164
+ }).strict().superRefine((value, ctx) => {
165
+ const ids = new Set();
166
+ value.decisions.forEach((decision, index) => {
167
+ if (decision.artifactRef !== value.artifactRef)
168
+ ctx.addIssue({ code: 'custom', path: ['decisions', index, 'artifactRef'], message: 'decision artifactRef must match ledger artifactRef' });
169
+ if (decision.reviewedAt !== value.reviewedAt)
170
+ ctx.addIssue({ code: 'custom', path: ['decisions', index, 'reviewedAt'], message: 'decision reviewedAt must match ledger review' });
171
+ if (ids.has(decision.claimId))
172
+ ctx.addIssue({ code: 'custom', path: ['decisions', index, 'claimId'], message: 'duplicate claimId in ledger' });
173
+ ids.add(decision.claimId);
174
+ });
175
+ });
176
+ export const artifactSemanticStatusSchema = z.enum(['REVIEWED', 'SUPERSEDED']);
177
+ export const artifactSupportStateSchema = z.enum([
178
+ 'CURRENTLY_SUPPORTED',
179
+ 'REFRESH_DUE',
180
+ 'CURRENTNESS_REVIEW_REQUIRED',
181
+ 'CURRENTNESS_DENIED',
182
+ 'SUPERSEDED',
183
+ ]);
184
+ export const artifactSupportEvidenceSchema = z.object({
185
+ evidenceRef: stableUriSchema,
186
+ state: currentnessStateSchema,
187
+ action: currentnessActionSchema,
188
+ assessedAt: isoDateTimeSchema,
189
+ lastSuccessAt: isoDateTimeSchema.nullable(),
190
+ reasonCode: currentnessReasonCodeSchema,
191
+ }).strict();
192
+ export const artifactSupportAssessmentSchema = z.object({
193
+ schemaVersion: schemaVersionSchema,
194
+ artifactRef: stableUriSchema,
195
+ identityRef: stableUriSchema,
196
+ projectKey: projectKeySchema,
197
+ artifactKind: z.enum(['KNOWLEDGE', 'INSIGHT']),
198
+ semanticStatus: artifactSemanticStatusSchema,
199
+ supportState: artifactSupportStateSchema,
200
+ action: currentnessActionSchema,
201
+ assessedAt: isoDateTimeSchema,
202
+ validUntil: isoDateTimeSchema.nullable(),
203
+ evidence: z.array(artifactSupportEvidenceSchema).max(256),
204
+ claims: z.array(claimCurrentnessDecisionSchema).max(256).default([]),
205
+ missingEvidenceRefs: z.array(stableUriSchema).max(256),
206
+ reasonCodes: z.array(z.enum([
207
+ 'ALL_EVIDENCE_CURRENT',
208
+ 'EVIDENCE_REFRESH_DUE',
209
+ 'EVIDENCE_REVIEW_REQUIRED',
210
+ 'EVIDENCE_DENIED',
211
+ 'EVIDENCE_CURRENTNESS_MISSING',
212
+ 'ARTIFACT_EVIDENCE_REQUIRED',
213
+ 'ARTIFACT_VALIDITY_EXPIRED',
214
+ 'ARTIFACT_EXPLICITLY_SUPERSEDED',
215
+ 'CLAIM_PARTIALLY_SUPERSEDED',
216
+ 'CLAIM_PENDING_CURRENTNESS_REVIEW',
217
+ 'CLAIM_SUPERSEDED',
218
+ 'CLAIM_HISTORICAL_REFERENCE',
219
+ 'CLAIM_DYNAMIC_EXPIRED',
220
+ ])).min(1).max(32),
221
+ }).strict();
@@ -0,0 +1,35 @@
1
+ import { z } from 'zod';
2
+ import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ export const knowledgeEventTypeSchema = z.enum([
4
+ 'SOURCE_CHANGED',
5
+ 'SOURCE_FAILURE',
6
+ 'REVIEW_READY',
7
+ 'APPROVAL_READY',
8
+ 'MATERIAL_DRIFT',
9
+ 'BOUNDARY_RISK',
10
+ 'CURRENTNESS_RISK',
11
+ 'CLOSURE_MILESTONE',
12
+ 'SCHEDULER_FAILURE',
13
+ 'SIGNAL_READY',
14
+ 'GAP_READY',
15
+ 'PROGRAM_EVOLUTION_READY',
16
+ ]);
17
+ export const eventSeveritySchema = z.enum(['INFO', 'NORMAL', 'WARNING', 'CRITICAL']);
18
+ export const knowledgeEventSchema = z.object({
19
+ schemaVersion: schemaVersionSchema,
20
+ eventId: idSchema,
21
+ projectKey: projectKeySchema.optional(),
22
+ projectRef: stableUriSchema.optional(),
23
+ sourceRef: stableUriSchema.optional(),
24
+ sourceRevisionRef: stableUriSchema.optional(),
25
+ collectionBatchRef: stableUriSchema.optional(),
26
+ type: knowledgeEventTypeSchema,
27
+ severity: eventSeveritySchema,
28
+ createdAt: isoDateTimeSchema,
29
+ runtimeNodeId: idSchema.optional(),
30
+ artifactRef: stableUriSchema.optional(),
31
+ evidenceRefs: z.array(stableUriSchema).max(256).default([]),
32
+ contentHash: sha256Schema.optional(),
33
+ requiresUserAction: z.boolean(),
34
+ dedupeKey: z.string().trim().min(1).max(512),
35
+ }).strict();