@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,121 @@
1
+ import { z } from 'zod';
2
+ import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
3
+ const text = (max) => z.string().trim().min(1).max(max);
4
+ const optionalText = (max) => z.string().trim().min(1).max(max).nullable().default(null);
5
+ /**
6
+ * Attribution for an Agent-native Knowledge write. This is provenance, not an
7
+ * authorization decision. agentId is deliberately nullable because humans,
8
+ * schedulers and migrations may write without an Agent identity.
9
+ */
10
+ export const knowledgeActorContextSchema = z.object({
11
+ tenantId: idSchema,
12
+ userId: idSchema,
13
+ agentId: idSchema.nullable().default(null),
14
+ }).strict();
15
+ export const projectIntentScopeSchema = z.object({
16
+ scopeId: idSchema,
17
+ title: text(256),
18
+ description: text(4000),
19
+ sourceRequirements: z.array(text(1000)).max(256).default([]),
20
+ sourceFamilies: z.array(idSchema).max(256).default([]),
21
+ notes: optionalText(4000),
22
+ }).strict();
23
+ export const projectIdentitySchema = z.object({
24
+ name: text(256),
25
+ summary: text(4000),
26
+ domains: z.array(text(256)).max(256).default([]),
27
+ notes: optionalText(4000),
28
+ }).strict();
29
+ export const projectIntentBasisSchema = z.object({
30
+ schemaVersion: schemaVersionSchema,
31
+ projectKey: projectKeySchema,
32
+ actor: knowledgeActorContextSchema,
33
+ projectIdentity: projectIdentitySchema,
34
+ scopes: z.array(projectIntentScopeSchema).max(1000).default([]),
35
+ }).strict().superRefine((value, ctx) => {
36
+ const seen = new Set();
37
+ for (let i = 0; i < value.scopes.length; i += 1) {
38
+ const scopeId = value.scopes[i].scopeId;
39
+ if (seen.has(scopeId))
40
+ ctx.addIssue({ code: 'custom', path: ['scopes', i, 'scopeId'], message: 'scopeId must be unique' });
41
+ seen.add(scopeId);
42
+ }
43
+ });
44
+ export const projectIntentSnapshotSchema = projectIntentBasisSchema.extend({
45
+ projectRef: stableUriSchema,
46
+ revisionId: sha256Schema,
47
+ revisionRef: stableUriSchema,
48
+ }).strict();
49
+ export const projectIntentPointerSchema = z.object({
50
+ schemaVersion: schemaVersionSchema,
51
+ projectKey: projectKeySchema,
52
+ tenantId: idSchema,
53
+ projectRef: stableUriSchema,
54
+ revisionId: sha256Schema,
55
+ revisionRef: stableUriSchema,
56
+ manifestRelativePath: text(2048),
57
+ updatedAt: isoDateTimeSchema,
58
+ }).strict();
59
+ export const collectionRefreshSchema = z.discriminatedUnion('mode', [
60
+ z.object({ mode: z.literal('MANUAL') }).strict(),
61
+ z.object({ mode: z.literal('INTERVAL'), intervalSeconds: z.number().int().min(60).max(31_536_000) }).strict(),
62
+ ]);
63
+ /** Project-neutral executable target. Project/domain semantics stay on ProjectSource. */
64
+ const relativeTargetPathSchema = z.string().trim().min(1).max(2048).superRefine((value, ctx) => {
65
+ if (value.includes('\\') || value.startsWith('/') || value.endsWith('/')) {
66
+ ctx.addIssue({ code: 'custom', message: 'FILE target path must be a normalized relative file path' });
67
+ return;
68
+ }
69
+ const parts = value.split('/');
70
+ if (parts.some((part) => !part || part === '.' || part === '..'))
71
+ ctx.addIssue({ code: 'custom', message: 'FILE target path must not contain empty, dot or parent segments' });
72
+ });
73
+ const executorParameterScalarSchema = z.union([z.string().max(4000), z.number().finite(), z.boolean(), z.null()]);
74
+ const executorParameterValueSchema = z.union([executorParameterScalarSchema, z.array(executorParameterScalarSchema).max(256)]);
75
+ const urlAcquisitionTargetSchema = z.object({ kind: z.literal('URL'), locator: z.url() }).strict();
76
+ const fileAcquisitionTargetSchema = z.object({ kind: z.literal('FILE'), rootBinding: projectKeySchema, relativePath: relativeTargetPathSchema }).strict();
77
+ const executorAcquisitionTargetSchema = z.object({
78
+ kind: z.literal('EXECUTOR'), executorId: idSchema, bindingRef: stableUriSchema.nullable().default(null), parameters: z.record(z.string().trim().min(1).max(128), executorParameterValueSchema).default({}),
79
+ }).strict().superRefine((value, ctx) => {
80
+ if (Object.keys(value.parameters).length > 128)
81
+ ctx.addIssue({ code: 'custom', path: ['parameters'], message: 'EXECUTOR target supports at most 128 parameters' });
82
+ if (Buffer.byteLength(JSON.stringify(value.parameters), 'utf8') > 32 * 1024)
83
+ ctx.addIssue({ code: 'custom', path: ['parameters'], message: 'EXECUTOR target parameters exceed 32 KiB' });
84
+ });
85
+ export const acquisitionTargetSchema = z.discriminatedUnion('kind', [urlAcquisitionTargetSchema, fileAcquisitionTargetSchema, executorAcquisitionTargetSchema]);
86
+ const metadataScalarSchema = z.union([z.string().max(4000), z.number(), z.boolean(), z.null()]);
87
+ export const projectSourceBasisSchema = z.object({
88
+ schemaVersion: schemaVersionSchema,
89
+ projectKey: projectKeySchema,
90
+ actor: knowledgeActorContextSchema,
91
+ target: acquisitionTargetSchema,
92
+ enabled: z.boolean().default(true),
93
+ access: accessClassificationSchema.default('PUBLIC'),
94
+ scopeIds: z.array(idSchema).max(256).default([]),
95
+ sourceFamilies: z.array(idSchema).max(256).default([]),
96
+ purpose: optionalText(4000),
97
+ refresh: collectionRefreshSchema.default({ mode: 'MANUAL' }),
98
+ metadata: z.record(z.string().trim().min(1).max(128), metadataScalarSchema).default({}),
99
+ }).strict();
100
+ export const projectSourceSnapshotSchema = projectSourceBasisSchema.extend({
101
+ projectRef: stableUriSchema,
102
+ projectIntentRef: stableUriSchema.nullable().default(null),
103
+ targetId: sha256Schema,
104
+ sourceId: idSchema,
105
+ revisionId: sha256Schema,
106
+ sourceRef: stableUriSchema,
107
+ revisionRef: stableUriSchema,
108
+ }).strict();
109
+ export const projectSourcePointerSchema = z.object({
110
+ schemaVersion: schemaVersionSchema,
111
+ projectKey: projectKeySchema,
112
+ projectRef: stableUriSchema,
113
+ projectIntentRef: stableUriSchema.nullable(),
114
+ sourceId: idSchema,
115
+ targetId: sha256Schema,
116
+ sourceRef: stableUriSchema,
117
+ revisionId: sha256Schema,
118
+ revisionRef: stableUriSchema,
119
+ manifestRelativePath: text(2048),
120
+ updatedAt: isoDateTimeSchema,
121
+ }).strict();
@@ -0,0 +1,194 @@
1
+ import { z } from 'zod';
2
+ import { artifactKindSchema, artifactStatusSchema } from './artifact.js';
3
+ import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema } from './common.js';
4
+ export const driveRelativePathSchema = z.string().trim().min(1).max(2048).refine((value) => {
5
+ if (value.startsWith('/') || value.startsWith('\\'))
6
+ return false;
7
+ if (value.includes('\\'))
8
+ return false;
9
+ return !value.split('/').some((part) => !part || part === '.' || part === '..');
10
+ }, 'Drive relative path must be normalized and traversal-free');
11
+ export const driveArtifactProjectionSchema = z.object({
12
+ schemaVersion: schemaVersionSchema,
13
+ projectionType: z.literal('GOOGLE_DRIVE'),
14
+ projectionId: sha256Schema,
15
+ relativePath: driveRelativePathSchema,
16
+ projectKey: projectKeySchema,
17
+ projectRef: stableUriSchema.optional(),
18
+ artifactRef: stableUriSchema,
19
+ identityRef: stableUriSchema,
20
+ contentHash: sha256Schema,
21
+ mediaType: z.string().trim().min(1).max(256),
22
+ byteLength: z.number().int().nonnegative(),
23
+ artifactKind: artifactKindSchema,
24
+ artifactStatus: artifactStatusSchema,
25
+ artifactVersion: z.number().int().positive(),
26
+ }).strict().superRefine((value, ctx) => {
27
+ if (!value.relativePath.startsWith(`Knowledge V2/${value.projectKey}/`))
28
+ ctx.addIssue({ code: 'custom', path: ['relativePath'], message: 'Drive path project must match projectKey' });
29
+ if (value.projectRef) {
30
+ try {
31
+ const project = new URL(value.projectRef);
32
+ if (project.protocol !== 'knowledge-project:' || project.hostname.toLowerCase() !== value.projectKey.toLowerCase())
33
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match projectKey' });
34
+ }
35
+ catch {
36
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'Invalid projectRef' });
37
+ }
38
+ }
39
+ try {
40
+ const artifact = new URL(value.artifactRef);
41
+ const identity = new URL(value.identityRef);
42
+ if (artifact.hostname.toLowerCase() !== value.projectKey.toLowerCase() || identity.hostname.toLowerCase() !== value.projectKey.toLowerCase())
43
+ ctx.addIssue({ code: 'custom', path: ['artifactRef'], message: 'Artifact refs must match projectKey' });
44
+ }
45
+ catch {
46
+ ctx.addIssue({ code: 'custom', path: ['artifactRef'], message: 'Artifact refs must be valid stable URLs' });
47
+ }
48
+ });
49
+ export const driveReviewPlaneProjectionSchema = z.object({
50
+ schemaVersion: schemaVersionSchema,
51
+ projectionType: z.literal('GOOGLE_DRIVE_REVIEW'),
52
+ projectionId: sha256Schema,
53
+ relativePath: driveRelativePathSchema,
54
+ workspaceRoot: driveRelativePathSchema,
55
+ reviewStage: z.enum(['PROGRAM', 'INBOX', 'REVIEWED', 'USER_APPROVED', 'PROMOTION_RECEIPTS', 'GUIDES', 'REJECTED_SUPERSEDED']),
56
+ projectKey: projectKeySchema,
57
+ projectRef: stableUriSchema.optional(),
58
+ artifactRef: stableUriSchema,
59
+ identityRef: stableUriSchema,
60
+ contentHash: sha256Schema,
61
+ mediaType: z.string().trim().min(1).max(256),
62
+ byteLength: z.number().int().nonnegative(),
63
+ artifactKind: artifactKindSchema,
64
+ artifactStatus: artifactStatusSchema,
65
+ artifactVersion: z.number().int().positive(),
66
+ }).strict().superRefine((value, ctx) => {
67
+ if (!value.relativePath.startsWith(`${value.workspaceRoot}/`))
68
+ ctx.addIssue({ code: 'custom', path: ['relativePath'], message: 'Review projection path must stay under workspaceRoot' });
69
+ if (value.projectRef) {
70
+ try {
71
+ const project = new URL(value.projectRef);
72
+ if (project.protocol !== 'knowledge-project:' || project.hostname.toLowerCase() !== value.projectKey.toLowerCase())
73
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match projectKey' });
74
+ }
75
+ catch {
76
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'Invalid projectRef' });
77
+ }
78
+ }
79
+ try {
80
+ const artifact = new URL(value.artifactRef);
81
+ const identity = new URL(value.identityRef);
82
+ if (artifact.hostname.toLowerCase() !== value.projectKey.toLowerCase() || identity.hostname.toLowerCase() !== value.projectKey.toLowerCase())
83
+ ctx.addIssue({ code: 'custom', path: ['artifactRef'], message: 'Artifact refs must match projectKey' });
84
+ }
85
+ catch {
86
+ ctx.addIssue({ code: 'custom', path: ['artifactRef'], message: 'Artifact refs must be valid stable URLs' });
87
+ }
88
+ });
89
+ export const driveUploadProjectionSchema = z.union([driveArtifactProjectionSchema, driveReviewPlaneProjectionSchema]);
90
+ export const driveProviderIdSchema = z.string().trim().regex(/^[A-Za-z0-9_-]{10,512}$/);
91
+ export const driveProviderDeliverySchema = z.object({
92
+ provider: z.literal('GOOGLE_DRIVE'),
93
+ fileId: driveProviderIdSchema,
94
+ mimeType: z.string().trim().min(1).max(256),
95
+ byteLength: z.number().int().nonnegative(),
96
+ md5Checksum: z.string().regex(/^[a-f0-9]{32}$/),
97
+ version: z.string().trim().min(1).max(128).nullable(),
98
+ modifiedTime: isoDateTimeSchema.nullable(),
99
+ }).strict();
100
+ export const driveDeliveryReceiptSchema = z.object({
101
+ schemaVersion: schemaVersionSchema,
102
+ projectionId: sha256Schema,
103
+ projectKey: projectKeySchema,
104
+ projectRef: stableUriSchema.optional(),
105
+ artifactRef: stableUriSchema,
106
+ contentHash: sha256Schema,
107
+ relativePath: driveRelativePathSchema,
108
+ deliveredAt: isoDateTimeSchema,
109
+ provider: driveProviderDeliverySchema,
110
+ }).strict().superRefine((value, ctx) => {
111
+ if (value.projectRef) {
112
+ try {
113
+ const project = new URL(value.projectRef);
114
+ if (project.protocol !== 'knowledge-project:' || project.hostname.toLowerCase() !== value.projectKey.toLowerCase())
115
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match projectKey' });
116
+ }
117
+ catch {
118
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'Invalid projectRef' });
119
+ }
120
+ }
121
+ });
122
+ export const driveUploadFolderBindingSchema = z.object({
123
+ relativePath: driveRelativePathSchema,
124
+ name: z.string().trim().min(1).max(255),
125
+ parentFileId: driveProviderIdSchema,
126
+ fileId: driveProviderIdSchema,
127
+ disposition: z.enum(['EXISTING', 'RESERVED']),
128
+ }).strict();
129
+ export const driveUploadFileBindingSchema = z.object({
130
+ name: z.string().trim().min(1).max(255),
131
+ parentFileId: driveProviderIdSchema,
132
+ fileId: driveProviderIdSchema,
133
+ disposition: z.enum(['EXISTING', 'RESERVED']),
134
+ }).strict();
135
+ export const driveUploadIntentSchema = z.object({
136
+ schemaVersion: schemaVersionSchema,
137
+ projectionId: sha256Schema,
138
+ projectKey: projectKeySchema,
139
+ projectRef: stableUriSchema.optional(),
140
+ artifactRef: stableUriSchema,
141
+ contentHash: sha256Schema,
142
+ relativePath: driveRelativePathSchema,
143
+ rootParentFolderId: driveProviderIdSchema,
144
+ sharedDriveId: driveProviderIdSchema.nullable(),
145
+ reservedAt: isoDateTimeSchema,
146
+ folders: z.array(driveUploadFolderBindingSchema).max(32),
147
+ file: driveUploadFileBindingSchema,
148
+ }).strict().superRefine((value, ctx) => {
149
+ if (value.projectRef) {
150
+ try {
151
+ const project = new URL(value.projectRef);
152
+ if (project.protocol !== 'knowledge-project:' || project.hostname.toLowerCase() !== value.projectKey.toLowerCase())
153
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match projectKey' });
154
+ }
155
+ catch {
156
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'Invalid projectRef' });
157
+ }
158
+ }
159
+ });
160
+ export const driveProjectionOutboxEntrySchema = z.object({
161
+ schemaVersion: schemaVersionSchema,
162
+ deliveryId: sha256Schema,
163
+ state: z.enum(['PENDING', 'DELIVERED', 'BLOCKED', 'FAILED_RETRYABLE', 'FAILED_TERMINAL']),
164
+ projection: driveUploadProjectionSchema,
165
+ outboxDirectory: z.string().trim().min(1).max(4096).startsWith('/'),
166
+ descriptorPath: z.string().trim().min(1).max(4096).startsWith('/'),
167
+ payloadPath: z.string().trim().min(1).max(4096).startsWith('/'),
168
+ uploadIntentPath: z.string().trim().min(1).max(4096).startsWith('/').nullable(),
169
+ uploadIntent: driveUploadIntentSchema.nullable(),
170
+ deliveryPath: z.string().trim().min(1).max(4096).startsWith('/').nullable(),
171
+ delivery: driveDeliveryReceiptSchema.nullable(),
172
+ }).strict().superRefine((value, ctx) => {
173
+ if ((value.uploadIntentPath === null) !== (value.uploadIntent === null))
174
+ ctx.addIssue({ code: 'custom', path: ['uploadIntent'], message: 'upload intent path/value must appear together' });
175
+ if (value.state !== 'DELIVERED' && (value.deliveryPath !== null || value.delivery !== null))
176
+ ctx.addIssue({ code: 'custom', path: ['delivery'], message: 'Only DELIVERED entry may carry Drive provider delivery' });
177
+ if (value.state === 'DELIVERED' && (value.deliveryPath === null || value.delivery === null))
178
+ ctx.addIssue({ code: 'custom', path: ['delivery'], message: 'DELIVERED entry requires delivery' });
179
+ });
180
+ export const driveProjectionStagingReceiptSchema = z.object({
181
+ schemaVersion: schemaVersionSchema,
182
+ deliveryId: sha256Schema,
183
+ projectionId: sha256Schema,
184
+ projectRef: stableUriSchema.optional(),
185
+ artifactRef: stableUriSchema,
186
+ contentHash: sha256Schema,
187
+ relativePath: driveRelativePathSchema,
188
+ runtimeNodeId: idSchema,
189
+ stagedAt: isoDateTimeSchema,
190
+ outboxDirectory: z.string().trim().min(1).max(4096).startsWith('/'),
191
+ descriptorPath: z.string().trim().min(1).max(4096).startsWith('/'),
192
+ payloadPath: z.string().trim().min(1).max(4096).startsWith('/'),
193
+ reused: z.boolean(),
194
+ }).strict();
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import { isoDateTimeSchema, projectKeySchema, sha256Schema, stableUriSchema } from './common.js';
3
+ export const publicReferenceCatalogueScopeSchema = z.object({
4
+ countryCode: z.string().regex(/^[A-Z]{2}$/),
5
+ topicKey: projectKeySchema,
6
+ scopeRef: stableUriSchema,
7
+ // Publication permission is distinct from an independent completeness proof.
8
+ coverage: z.enum(['OBSERVED', 'PARTIAL']),
9
+ }).strict();
10
+ export const publicReferenceApprovedSourceSchema = z.object({ sourceRef: stableUriSchema, contentHash: sha256Schema }).strict();
11
+ export const publicReferenceLegacyReviewSchema = z.object({
12
+ authority: z.literal('AUTHORIZED_PUBLISHER'),
13
+ reviewedAt: isoDateTimeSchema,
14
+ note: z.string().trim().min(1).max(2000),
15
+ approvedSources: z.array(publicReferenceApprovedSourceSchema).min(1).max(256),
16
+ }).strict().superRefine((value, ctx) => {
17
+ if (new Set(value.approvedSources.map(s => s.sourceRef)).size !== value.approvedSources.length)
18
+ ctx.addIssue({ code: 'custom', message: 'approvedSources must be unique' });
19
+ });
20
+ export const publicReferencePolicySchema = z.object({
21
+ schemaVersion: z.literal(1), policyId: projectKeySchema, revision: z.number().int().positive(), enabled: z.boolean(),
22
+ sourceProjectKey: projectKeySchema,
23
+ entries: z.array(z.object({
24
+ corpusKey: projectKeySchema, catalogue: publicReferenceCatalogueScopeSchema,
25
+ recordTypes: z.array(projectKeySchema).min(1).max(128), authorities: z.array(z.string().trim().min(1).max(256)).min(1).max(128),
26
+ minimumRecordCount: z.number().int().positive().max(100_000),
27
+ maximumAssessmentAgeSeconds: z.number().int().positive().max(31_536_000),
28
+ sources: z.array(z.object({
29
+ collectorId: z.string().trim().min(1).max(128), sourceIds: z.array(z.string().trim().min(1).max(128)).min(1).max(256),
30
+ origin: z.string().url().refine(value => { const u = new URL(value); return u.protocol === 'https:' && u.origin === value; }),
31
+ pathPrefixes: z.array(z.string().regex(/^\//).max(2048)).min(1).max(128),
32
+ }).strict()).min(1).max(256),
33
+ }).strict()).min(1).max(128),
34
+ }).strict().superRefine((value, ctx) => {
35
+ if (Buffer.byteLength(JSON.stringify(value)) > 1024 * 1024)
36
+ ctx.addIssue({ code: 'custom', message: 'Policy exceeds 1 MiB' });
37
+ if (new Set(value.entries.map(entry => entry.corpusKey)).size !== value.entries.length)
38
+ ctx.addIssue({ code: 'custom', message: 'Policy corpus keys must be unique' });
39
+ });
40
+ export const publicReferenceMachineReviewSchema = z.object({
41
+ authority: z.literal('POLICY_ENGINE'), reviewedAt: isoDateTimeSchema,
42
+ note: z.literal('DETERMINISTIC_PUBLIC_SOURCE_VALIDATION'),
43
+ validationRule: z.literal('public-reference.policy.v1'), policyId: projectKeySchema,
44
+ policyRevision: z.number().int().positive(), policyDigest: sha256Schema,
45
+ executorRevision: z.string().regex(/^[a-f0-9]{40,64}$/),
46
+ approvedSources: z.array(publicReferenceApprovedSourceSchema).max(256),
47
+ }).strict();
48
+ export const publicReferenceReviewSchema = z.union([publicReferenceLegacyReviewSchema, publicReferenceMachineReviewSchema]);
49
+ export const publicReferencePublishInputSchema = z.object({
50
+ publicationKey: projectKeySchema, corpusKey: projectKeySchema, releaseId: sha256Schema, recordsContentHash: sha256Schema,
51
+ catalogue: publicReferenceCatalogueScopeSchema, review: publicReferenceReviewSchema,
52
+ }).strict();
53
+ export const publicReferenceWithdrawInputSchema = z.object({
54
+ publicationRef: z.string().regex(/^public-reference:\/\/[a-f0-9]{64}$/),
55
+ reason: z.string().trim().min(1).max(2000),
56
+ }).strict();
57
+ export const publicReferencePublicationBasisSchema = z.object({
58
+ schemaVersion: z.union([z.literal(1), z.literal(2)]), visibility: z.literal('PUBLIC_REFERENCE'),
59
+ sourceProjectKey: projectKeySchema, corpusKey: projectKeySchema,
60
+ sourceReleaseId: sha256Schema, sourceReleaseRef: stableUriSchema,
61
+ recordsContentHash: sha256Schema, recordsByteLength: z.number().int().positive().max(512 * 1024 * 1024),
62
+ recordCount: z.number().int().positive().max(100_000),
63
+ recordTypes: z.array(projectKeySchema).min(1).max(128),
64
+ sourceCreatedAt: isoDateTimeSchema,
65
+ catalogue: publicReferenceCatalogueScopeSchema, review: publicReferenceReviewSchema,
66
+ publishedAt: isoDateTimeSchema, checkedAt: isoDateTimeSchema, validUntil: isoDateTimeSchema,
67
+ }).strict();
68
+ export const publicReferencePublicationSchema = publicReferencePublicationBasisSchema.extend({
69
+ publicationId: sha256Schema,
70
+ publicationRef: z.string().regex(/^public-reference:\/\/[a-f0-9]{64}$/),
71
+ }).strict().superRefine((value, ctx) => {
72
+ if ((value.schemaVersion === 2) !== (value.review.authority === 'POLICY_ENGINE') || !value.review.approvedSources.length)
73
+ ctx.addIssue({ code: 'custom', message: 'Publication validation authority invalid' });
74
+ if (value.publicationRef !== 'public-reference://' + value.publicationId.slice(7))
75
+ ctx.addIssue({ code: 'custom', message: 'publication identity mismatch' });
76
+ if (Date.parse(value.validUntil) <= Date.parse(value.publishedAt) || Date.parse(value.checkedAt) > Date.parse(value.publishedAt)
77
+ || Date.parse(value.review.reviewedAt) > Date.parse(value.publishedAt) || Date.parse(value.review.reviewedAt) < Date.parse(value.sourceCreatedAt))
78
+ ctx.addIssue({ code: 'custom', message: 'publication chronology invalid' });
79
+ });
80
+ export const publicReferenceCatalogueQuerySchema = z.object({
81
+ countryCode: z.string().regex(/^[A-Z]{2}$/).optional(), topicKey: projectKeySchema.optional(),
82
+ after: sha256Schema.optional(), limit: z.number().int().min(1).max(100).default(20),
83
+ }).strict();
84
+ export function publicReferencePublicationBasis(value) {
85
+ const { publicationId: _id, publicationRef: _ref, ...basis } = value;
86
+ return publicReferencePublicationBasisSchema.parse(basis);
87
+ }
@@ -0,0 +1,55 @@
1
+ /** Canonical Reference facts and producer annotations; no consumer admission policy. */
2
+ import { z } from 'zod';
3
+ import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema } from './common.js';
4
+ export const referenceRecordTypeSchema = z.string().trim().min(1).max(128).regex(/^[A-Z][A-Z0-9_]*$/);
5
+ export const referenceCurrentnessActionSchema = z.enum(['ALLOW', 'REVIEW_REQUIRED', 'DENY']);
6
+ export const referenceSelectorKeySchema = z.string().trim().min(1).max(64).regex(/^[A-Za-z][A-Za-z0-9._-]{0,63}$/);
7
+ export const referenceSelectorValueSchema = z.string().trim().min(1).max(256);
8
+ export const referenceSelectorsSchema = z.record(referenceSelectorKeySchema, z.array(referenceSelectorValueSchema).min(1).max(256)).superRefine((value, ctx) => {
9
+ const keys = Object.keys(value);
10
+ if (keys.length > 32)
11
+ ctx.addIssue({ code: 'custom', message: 'Reference selectors cannot exceed 32 keys' });
12
+ for (const key of keys) {
13
+ const values = value[key] ?? [];
14
+ if (new Set(values).size !== values.length)
15
+ ctx.addIssue({ code: 'custom', path: [key], message: 'Reference selector values must be unique' });
16
+ }
17
+ });
18
+ export const referenceCurrentnessStateSchema = z.enum(['CURRENT', 'DUE', 'STALE', 'UNAVAILABLE', 'NOT_COVERED']);
19
+ export const referenceCurrentnessSchema = z.object({
20
+ state: referenceCurrentnessStateSchema, action: referenceCurrentnessActionSchema, assessedAt: isoDateTimeSchema, validUntil: isoDateTimeSchema.nullable(),
21
+ }).strict();
22
+ function validateReferenceIdentity(value, ctx) {
23
+ const expected = `reference://${value.projectKey}/${encodeURIComponent(value.corpusKey)}/${encodeURIComponent(value.recordKey)}/v/${value.version}`;
24
+ if (value.recordRef !== expected)
25
+ ctx.addIssue({ code: 'custom', path: ['recordRef'], message: 'recordRef must match project/corpus/recordKey/version' });
26
+ if (value.version === 1 && value.supersedesRef !== null)
27
+ ctx.addIssue({ code: 'custom', path: ['supersedesRef'], message: 'first reference version cannot supersede' });
28
+ if (value.version > 1) {
29
+ const previous = `reference://${value.projectKey}/${encodeURIComponent(value.corpusKey)}/${encodeURIComponent(value.recordKey)}/v/${value.version - 1}`;
30
+ if (value.supersedesRef !== previous)
31
+ ctx.addIssue({ code: 'custom', path: ['supersedesRef'], message: 'reference version must supersede immediately previous version' });
32
+ }
33
+ }
34
+ export const referenceCorpusSummarySchema = z.object({
35
+ schemaVersion: schemaVersionSchema, corpusKey: projectKeySchema, recordKey: idSchema, recordRef: stableUriSchema, recordType: referenceRecordTypeSchema, projectKey: projectKeySchema, projectRef: stableUriSchema.optional(),
36
+ version: z.number().int().positive(), supersedesRef: stableUriSchema.nullable(),
37
+ access: accessClassificationSchema, authority: z.string().trim().min(1).max(1000), jurisdiction: z.string().trim().min(1).max(128).nullable(), selectors: referenceSelectorsSchema.optional(),
38
+ sourceRefs: z.array(stableUriSchema).min(1).max(256), digest: sha256Schema, effectiveFrom: isoDateTimeSchema.nullable(), effectiveUntil: isoDateTimeSchema.nullable(),
39
+ currentness: referenceCurrentnessSchema,
40
+ }).strict().superRefine((value, ctx) => {
41
+ if (value.projectRef) {
42
+ try {
43
+ const url = new URL(value.projectRef);
44
+ if (url.protocol !== 'knowledge-project:' || url.hostname.toLowerCase() !== value.projectKey.toLowerCase())
45
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match Reference project' });
46
+ }
47
+ catch {
48
+ ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'Invalid projectRef' });
49
+ }
50
+ }
51
+ if (value.effectiveFrom && value.effectiveUntil && Date.parse(value.effectiveUntil) <= Date.parse(value.effectiveFrom))
52
+ ctx.addIssue({ code: 'custom', path: ['effectiveUntil'], message: 'effectiveUntil must be after effectiveFrom' });
53
+ validateReferenceIdentity(value, ctx);
54
+ });
55
+ export const referenceCorpusRecordSchema = referenceCorpusSummarySchema.extend({ payload: z.json() }).strict();
@@ -0,0 +1,159 @@
1
+ import { z } from 'zod';
2
+ import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema } from './common.js';
3
+ export const remoteArchiveFormatSchema = z.enum(['TAR_GZ', 'ZIP']);
4
+ export const remoteManifestFormatSchema = z.enum(['FILES_V1', 'LEGACY_PAGES_V1']);
5
+ export const remoteBundlePathSchema = z.string().trim().min(1).max(1024).superRefine((value, ctx) => {
6
+ if (value.includes('\0') || value.includes('\\') || value.startsWith('/') || value.endsWith('/')) {
7
+ ctx.addIssue({ code: 'custom', message: 'remote bundle member path must be a relative file path' });
8
+ return;
9
+ }
10
+ const parts = value.split('/');
11
+ if (parts.some((part) => !part || part === '.' || part === '..')) {
12
+ ctx.addIssue({ code: 'custom', message: 'remote bundle member path must not contain empty, dot or parent segments' });
13
+ }
14
+ });
15
+ export const remoteFileBundleDeclarationSchema = z.object({
16
+ schemaVersion: schemaVersionSchema,
17
+ projectKey: projectKeySchema,
18
+ artifactSha256: sha256Schema,
19
+ byteLength: z.number().int().positive(),
20
+ archiveFormat: remoteArchiveFormatSchema,
21
+ manifestPath: remoteBundlePathSchema,
22
+ manifestSha256: sha256Schema,
23
+ artifactKind: idSchema,
24
+ familyId: idSchema,
25
+ targetId: idSchema,
26
+ jurisdiction: z.string().trim().min(1).max(128).nullable(),
27
+ sourceWorker: idSchema,
28
+ remoteArtifactRef: stableUriSchema.nullable(),
29
+ declaredAt: isoDateTimeSchema,
30
+ }).strict();
31
+ export const remoteFileManifestEntrySchema = z.object({
32
+ path: remoteBundlePathSchema,
33
+ sha256: sha256Schema,
34
+ byteLength: z.number().int().nonnegative(),
35
+ mediaType: z.string().trim().min(1).max(256).nullable().default(null),
36
+ sourceUrl: z.string().url().max(2048).nullable().default(null),
37
+ }).strict();
38
+ export const remoteFileBundleManifestSchema = z.object({
39
+ schemaVersion: schemaVersionSchema,
40
+ projectKey: projectKeySchema,
41
+ artifactKind: idSchema,
42
+ familyId: idSchema,
43
+ targetId: idSchema,
44
+ jurisdiction: z.string().trim().min(1).max(128).nullable(),
45
+ generatedAt: isoDateTimeSchema,
46
+ files: z.array(remoteFileManifestEntrySchema).min(1).max(10000),
47
+ }).strict().superRefine((value, ctx) => {
48
+ const seen = new Set();
49
+ value.files.forEach((entry, index) => {
50
+ if (seen.has(entry.path))
51
+ ctx.addIssue({ code: 'custom', path: ['files', index, 'path'], message: 'duplicate manifest file path' });
52
+ seen.add(entry.path);
53
+ });
54
+ });
55
+ export const remoteVerifiedFileSchema = z.object({
56
+ path: remoteBundlePathSchema,
57
+ contentHash: sha256Schema,
58
+ byteLength: z.number().int().nonnegative(),
59
+ mediaType: z.string().trim().min(1).max(256).nullable(),
60
+ sourceUrl: z.string().url().max(2048).nullable(),
61
+ }).strict();
62
+ export const remoteFileVerificationReceiptSchema = z.object({
63
+ schemaVersion: schemaVersionSchema,
64
+ verificationRef: stableUriSchema,
65
+ projectKey: projectKeySchema,
66
+ declarationDigest: sha256Schema,
67
+ artifactSha256: sha256Schema,
68
+ byteLength: z.number().int().positive(),
69
+ archiveFormat: remoteArchiveFormatSchema,
70
+ manifestPath: remoteBundlePathSchema,
71
+ manifestSha256: sha256Schema,
72
+ manifestFormat: remoteManifestFormatSchema,
73
+ manifestGeneratedAt: isoDateTimeSchema.nullable(),
74
+ artifactKind: idSchema,
75
+ familyId: idSchema,
76
+ targetId: idSchema,
77
+ jurisdiction: z.string().trim().min(1).max(128).nullable(),
78
+ sourceWorker: idSchema,
79
+ remoteArtifactRef: stableUriSchema.nullable(),
80
+ verifierNodeId: idSchema,
81
+ verifiedAt: isoDateTimeSchema,
82
+ files: z.array(remoteVerifiedFileSchema).min(1).max(10000),
83
+ }).strict().superRefine((value, ctx) => {
84
+ const expected = `remote-file-verification://${value.projectKey}/${value.artifactSha256.slice('sha256:'.length)}`;
85
+ if (value.verificationRef !== expected)
86
+ ctx.addIssue({ code: 'custom', path: ['verificationRef'], message: 'verificationRef must match project and artifact hash' });
87
+ const seen = new Set();
88
+ value.files.forEach((entry, index) => {
89
+ if (seen.has(entry.path))
90
+ ctx.addIssue({ code: 'custom', path: ['files', index, 'path'], message: 'duplicate verified file path' });
91
+ seen.add(entry.path);
92
+ });
93
+ });
94
+ export const remoteFileProjectSourceBindingSchema = z.object({
95
+ schemaVersion: schemaVersionSchema,
96
+ bindingRef: stableUriSchema,
97
+ verificationRef: stableUriSchema,
98
+ projectKey: projectKeySchema,
99
+ artifactSha256: sha256Schema,
100
+ projectRef: stableUriSchema,
101
+ projectIntentRef: stableUriSchema,
102
+ sourceId: idSchema,
103
+ sourceRef: stableUriSchema,
104
+ sourceRevisionId: sha256Schema,
105
+ sourceRevisionRef: stableUriSchema,
106
+ projectSourceTargetId: sha256Schema,
107
+ boundAt: isoDateTimeSchema,
108
+ }).strict().superRefine((value, ctx) => {
109
+ const artifactDigest = value.artifactSha256.slice('sha256:'.length);
110
+ const revisionDigest = value.sourceRevisionId.slice('sha256:'.length);
111
+ const expectedVerification = `remote-file-verification://${value.projectKey}/${artifactDigest}`;
112
+ const expectedBinding = `remote-file-source-binding://${value.projectKey}/${artifactDigest}/${revisionDigest}`;
113
+ const expectedSource = `project-source://${value.projectKey}/${value.sourceId}`;
114
+ const expectedRevision = `${expectedSource}/v/${revisionDigest}`;
115
+ if (value.verificationRef !== expectedVerification)
116
+ ctx.addIssue({ code: 'custom', path: ['verificationRef'], message: 'verificationRef must match project and artifact hash' });
117
+ if (value.bindingRef !== expectedBinding)
118
+ ctx.addIssue({ code: 'custom', path: ['bindingRef'], message: 'bindingRef must match project, artifact hash and source revision' });
119
+ if (value.sourceRef !== expectedSource)
120
+ ctx.addIssue({ code: 'custom', path: ['sourceRef'], message: 'sourceRef must match project and source id' });
121
+ if (value.sourceRevisionRef !== expectedRevision)
122
+ ctx.addIssue({ code: 'custom', path: ['sourceRevisionRef'], message: 'sourceRevisionRef must match source and revision id' });
123
+ });
124
+ export const remoteFileIngestReceiptSchema = z.object({
125
+ schemaVersion: schemaVersionSchema,
126
+ ingestRef: stableUriSchema,
127
+ verificationRef: stableUriSchema,
128
+ projectKey: projectKeySchema,
129
+ artifactSha256: sha256Schema,
130
+ sourceBindingRef: stableUriSchema.optional(),
131
+ consumerKey: idSchema,
132
+ resultRefs: z.array(stableUriSchema).min(1).max(256),
133
+ ingestedAt: isoDateTimeSchema,
134
+ }).strict().superRefine((value, ctx) => {
135
+ const digest = value.artifactSha256.slice('sha256:'.length);
136
+ const expectedVerification = `remote-file-verification://${value.projectKey}/${digest}`;
137
+ let sourceRevisionDigest = null;
138
+ if (value.sourceBindingRef) {
139
+ const prefix = `remote-file-source-binding://${value.projectKey}/${digest}/`;
140
+ if (!value.sourceBindingRef.startsWith(prefix))
141
+ ctx.addIssue({ code: 'custom', path: ['sourceBindingRef'], message: 'sourceBindingRef must match project and artifact hash' });
142
+ else {
143
+ const candidate = value.sourceBindingRef.slice(prefix.length);
144
+ if (!/^[a-f0-9]{64}$/.test(candidate))
145
+ ctx.addIssue({ code: 'custom', path: ['sourceBindingRef'], message: 'sourceBindingRef must end with source revision digest' });
146
+ else
147
+ sourceRevisionDigest = candidate;
148
+ }
149
+ }
150
+ const expectedIngest = sourceRevisionDigest
151
+ ? `remote-file-ingest://${value.projectKey}/${digest}/${encodeURIComponent(value.consumerKey)}/${sourceRevisionDigest}`
152
+ : `remote-file-ingest://${value.projectKey}/${digest}/${encodeURIComponent(value.consumerKey)}`;
153
+ if (value.verificationRef !== expectedVerification)
154
+ ctx.addIssue({ code: 'custom', path: ['verificationRef'], message: 'verificationRef must match project and artifact hash' });
155
+ if (value.ingestRef !== expectedIngest)
156
+ ctx.addIssue({ code: 'custom', path: ['ingestRef'], message: 'ingestRef must match project, artifact hash, consumer and optional source binding' });
157
+ if (new Set(value.resultRefs).size !== value.resultRefs.length)
158
+ ctx.addIssue({ code: 'custom', path: ['resultRefs'], message: 'resultRefs must be unique' });
159
+ });
@@ -0,0 +1,38 @@
1
+ /** 存储/执行范围显式区分租户与历史项目;范围本身不是授权凭据。 */
2
+ import { createHash } from 'node:crypto';
3
+ import { z } from 'zod';
4
+ import { projectKeySchema, stableUriSchema } from './common.js';
5
+ import { materialTenantIdSchema } from './material-context.js';
6
+ export const resourceScopeSchema = z.discriminatedUnion('type', [
7
+ z.object({ type: z.literal('TENANT'), tenantId: materialTenantIdSchema }).strict(),
8
+ z.object({ type: z.literal('PROJECT'), tenantId: materialTenantIdSchema, projectKey: projectKeySchema }).strict(),
9
+ ]);
10
+ export const materialAuthorityPolicySchema = z.enum(['LEGACY_PROJECT', 'TENANT_NATIVE', 'TENANT_PROJECT_SCOPE']);
11
+ export function tenantStorageKey(tenantId) {
12
+ return createHash('sha256').update(materialTenantIdSchema.parse(tenantId), 'utf8').digest('hex');
13
+ }
14
+ export const tenantResourceIdentitySchema = z.object({
15
+ tenantId: materialTenantIdSchema,
16
+ resourceType: z.string().regex(/^[a-z][a-z0-9._-]{0,63}$/),
17
+ resourceId: z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/),
18
+ version: z.number().int().positive().safe(),
19
+ }).strict();
20
+ export function tenantResourceRefs(input) {
21
+ const value = tenantResourceIdentitySchema.parse(input);
22
+ const identityRef = `tenant-resource://${tenantStorageKey(value.tenantId)}/${value.resourceType}/${value.resourceId}`;
23
+ return { identityRef, ref: `${identityRef}/v/${value.version}` };
24
+ }
25
+ /** 必须精确匹配规范身份;URL 的容错正规化不能改变对象定位。 */
26
+ export function parseTenantResourceRef(ref) {
27
+ if (typeof ref !== 'string' || ref.length > 2048)
28
+ return null;
29
+ const match = /^tenant-resource:\/\/([a-f0-9]{64})\/([a-z][a-z0-9._-]{0,63})\/([A-Za-z0-9][A-Za-z0-9._-]{0,127})\/v\/([1-9][0-9]*)$/.exec(ref);
30
+ if (!match)
31
+ return null;
32
+ const version = Number(match[4]);
33
+ if (!Number.isSafeInteger(version))
34
+ return null;
35
+ return { storageKey: match[1], resourceType: match[2], resourceId: match[3], version };
36
+ }
37
+ export const tenantResourceRefSchema = stableUriSchema.refine(value => parseTenantResourceRef(value) !== null, 'Noncanonical tenant resource ref');
38
+ export const TENANT_CORE_CAPABILITIES = { resource: 'TENANT_RESOURCE_V1', authority: 'TENANT_AUTHORITY_V1', source: 'TENANT_SOURCE_V1', workflow: 'TENANT_WORKFLOW_V1', budget: 'TENANT_BUDGET_V1' };