@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,66 @@
1
+ /** 新批次只冻结真实 Tenant/Source/执行输入;用途和请求归因留在管理审计,collector 看不到它们。 */
2
+ import { createHash } from 'node:crypto';
3
+ import { z } from 'zod';
4
+ import { isoDateTimeSchema, sha256Schema, stableUriSchema } from './common.js';
5
+ import { materialTenantIdSchema } from './material-context.js';
6
+ import { tenantStorageKey, tenantResourceRefs } from './resource-scope.js';
7
+ import { tenantSourceSnapshotSchema, tenantSourceIdSchema, parseTenantSourceRevisionRef } from './tenant-source.js';
8
+ export const tenantCollectionCycleSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/);
9
+ export const tenantCollectionMaxBytesSchema = z.number().int().min(1024).max(64 * 1024 * 1024);
10
+ const hash = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
11
+ function planIdentity(tenantId, sourceRevisionRef, semanticKey, maxResourceBytes) {
12
+ const revision = 'sha256:' + hash(['TENANT_COLLECTION_PLAN_V1', sourceRevisionRef, semanticKey, maxResourceBytes]), key = tenantStorageKey(tenantId);
13
+ return { revision, planRef: `tenant-collection-plan://${key}/${revision.slice(7)}`, workloadId: `tenant:${key}:collect:${revision.slice(7)}` };
14
+ }
15
+ export const tenantCollectionPlanSchema = z.object({ schemaVersion: z.literal(1), tenantId: materialTenantIdSchema, source: tenantSourceSnapshotSchema, maxResourceBytes: tenantCollectionMaxBytesSchema, revision: sha256Schema, planRef: stableUriSchema, workloadId: z.string().min(1).max(256) }).strict().superRefine((value, ctx) => {
16
+ const expected = planIdentity(value.tenantId, value.source.revisionRef, value.source.semanticKey, value.maxResourceBytes);
17
+ if (value.source.tenantId !== value.tenantId)
18
+ ctx.addIssue({ code: 'custom', path: ['tenantId'], message: 'Plan Source must belong to the exact Tenant' });
19
+ for (const key of ['revision', 'planRef', 'workloadId'])
20
+ if (value[key] !== expected[key])
21
+ ctx.addIssue({ code: 'custom', path: [key], message: 'Frozen plan identity mismatch' });
22
+ });
23
+ export function createTenantCollectionPlan(raw, maxBytes = 16 * 1024 * 1024) {
24
+ const source = tenantSourceSnapshotSchema.parse(raw), maxResourceBytes = tenantCollectionMaxBytesSchema.parse(maxBytes);
25
+ return tenantCollectionPlanSchema.parse({ schemaVersion: 1, tenantId: source.tenantId, source, maxResourceBytes, ...planIdentity(source.tenantId, source.revisionRef, source.semanticKey, maxResourceBytes) });
26
+ }
27
+ export function parseTenantCollectionPlanRef(ref) { const match = /^tenant-collection-plan:\/\/([a-f0-9]{64})\/([a-f0-9]{64})$/.exec(ref); return match ? { storageKey: match[1], revision: 'sha256:' + match[2] } : null; }
28
+ export function tenantCollectionBatchIdentity(tenantId, planRevision, trigger, cycleId) {
29
+ const batchId = 'batch_' + hash(['TENANT_COLLECTION_BATCH_V1', tenantId, sha256Schema.parse(planRevision), trigger, tenantCollectionCycleSchema.parse(cycleId)]);
30
+ return { batchId, batchRef: `tenant-collection-batch://${tenantStorageKey(tenantId)}/${batchId}` };
31
+ }
32
+ export function parseTenantCollectionBatchRef(ref) { const match = /^tenant-collection-batch:\/\/([a-f0-9]{64})\/(batch_[a-f0-9]{64})$/.exec(ref); return match ? { storageKey: match[1], batchId: match[2] } : null; }
33
+ export function tenantCapturedEvidenceIdentity(input) {
34
+ const batch = parseTenantCollectionBatchRef(input.batchRef), source = parseTenantSourceRevisionRef(input.sourceRevisionRef), key = tenantStorageKey(input.tenantId);
35
+ if (!batch || !source || batch.storageKey !== key || source.storageKey !== key)
36
+ throw new Error('TENANT_CAPTURE_IDENTITY_MISMATCH');
37
+ const resourceId = 'ev_' + hash(['TENANT_CAPTURE_V1', input.batchRef, input.sourceRevisionRef]);
38
+ return { resourceId, ...tenantResourceRefs({ tenantId: input.tenantId, resourceType: 'evidence', resourceId, version: 1 }) };
39
+ }
40
+ export const tenantCollectionBatchSchema = z.object({
41
+ schemaVersion: z.literal(2), tenantId: materialTenantIdSchema, sourceId: tenantSourceIdSchema, sourceRevisionRef: stableUriSchema,
42
+ planRef: stableUriSchema, planRevision: sha256Schema, cycleId: tenantCollectionCycleSchema, trigger: z.enum(['MANUAL', 'DUE']), createdAt: isoDateTimeSchema,
43
+ batchId: z.string().regex(/^batch_[a-f0-9]{64}$/), batchRef: stableUriSchema,
44
+ }).strict().superRefine((value, ctx) => {
45
+ const source = parseTenantSourceRevisionRef(value.sourceRevisionRef), plan = parseTenantCollectionPlanRef(value.planRef), key = tenantStorageKey(value.tenantId), expected = tenantCollectionBatchIdentity(value.tenantId, value.planRevision, value.trigger, value.cycleId);
46
+ if (!source || source.storageKey !== key || source.sourceId !== value.sourceId || !plan || plan.storageKey !== key || plan.revision !== value.planRevision)
47
+ ctx.addIssue({ code: 'custom', path: ['sourceRevisionRef'], message: 'Batch must retain exact Tenant Source and plan identity' });
48
+ if (value.batchId !== expected.batchId || value.batchRef !== expected.batchRef)
49
+ ctx.addIssue({ code: 'custom', path: ['batchRef'], message: 'Batch identity mismatch' });
50
+ });
51
+ /** ACQUIRED 是原件事实;Catalog、登记和派生状态由各自阶段回执报告。 */
52
+ export const tenantCollectionCaptureSchema = z.object({
53
+ schemaVersion: z.literal(1), tenantId: materialTenantIdSchema, batchRef: stableUriSchema, sourceRevisionRef: stableUriSchema, taskId: z.string().regex(/^task_[a-f0-9]{64}$/),
54
+ evidenceRef: stableUriSchema, contentHash: sha256Schema, byteLength: z.number().int().nonnegative().max(64 * 1024 * 1024), mediaType: z.string().trim().min(1).max(256),
55
+ observedAt: isoDateTimeSchema, finalLocator: stableUriSchema, httpStatus: z.number().int().min(100).max(599).nullable(), acquisitionRevision: z.string().max(4096).nullable(), headersRecorded: z.boolean(),
56
+ }).strict().superRefine((value, ctx) => {
57
+ try {
58
+ if (tenantCapturedEvidenceIdentity(value).ref !== value.evidenceRef)
59
+ ctx.addIssue({ code: 'custom', path: ['evidenceRef'], message: 'Capture must retain exact batch/source Evidence identity' });
60
+ }
61
+ catch {
62
+ ctx.addIssue({ code: 'custom', path: ['batchRef'], message: 'Capture Tenant identity mismatch' });
63
+ }
64
+ if (!value.headersRecorded && (value.httpStatus !== null || value.acquisitionRevision !== null))
65
+ ctx.addIssue({ code: 'custom', path: ['headersRecorded'], message: 'Recovery cannot invent missing acquisition headers' });
66
+ });
@@ -0,0 +1,26 @@
1
+ /** 原生资料的快照时效不把 raw carrier 冒充 Evidence,也不证明外部业务事实。 */
2
+ import { z } from 'zod';
3
+ import { createHash } from 'node:crypto';
4
+ import { currentnessAssessmentSchema, currentnessPolicySchema } from './currentness.js';
5
+ import { knowledgeActorContextSchema } from './project-intent.js';
6
+ import { materialTenantIdSchema } from './material-context.js';
7
+ import { tenantStorageKey, tenantResourceRefSchema, parseTenantResourceRef } from './resource-scope.js';
8
+ import { sha256Schema, isoDateTimeSchema, stableUriSchema } from './common.js';
9
+ import { tenantInvocationAuthoritySchema, tenantManagementRequestIdSchema } from './tenant-management.js';
10
+ export function tenantSnapshotDeclarationRef(tenantId, subjectRef) { return `tenant-currentness://${tenantStorageKey(tenantId)}/snapshot/${createHash('sha256').update(tenantResourceRefSchema.parse(subjectRef)).digest('hex')}`; }
11
+ const identity = { tenantId: materialTenantIdSchema, subjectRef: tenantResourceRefSchema, resourceType: z.string().regex(/^[a-z][a-z0-9._-]{0,63}$/), contentHash: sha256Schema };
12
+ const identityValid = (value) => { const ref = parseTenantResourceRef(value.subjectRef); return ref?.storageKey === tenantStorageKey(value.tenantId) && ref.resourceType === value.resourceType; };
13
+ export const tenantSnapshotDeclarationSchema = z.object({ schemaVersion: z.literal(1), ref: stableUriSchema, ...identity, basis: z.literal('RETAINED_SNAPSHOT'), snapshotAt: isoDateTimeSchema, sourceRevisionRef: stableUriSchema.optional(), policy: currentnessPolicySchema, declaredAt: isoDateTimeSchema, actor: knowledgeActorContextSchema, authority: tenantInvocationAuthoritySchema }).strict().superRefine((v, ctx) => {
14
+ if (!identityValid(v) || v.ref !== tenantSnapshotDeclarationRef(v.tenantId, v.subjectRef) || v.actor.tenantId !== v.tenantId || !v.actor.userId || Date.parse(v.snapshotAt) > Date.parse(v.declaredAt))
15
+ ctx.addIssue({ code: 'custom', message: 'Snapshot declaration retains actual resource, actor and original observation time' });
16
+ if ([v.policy.staleAction, v.policy.unknownAction, v.policy.blockedAction].includes('ALLOW'))
17
+ ctx.addIssue({ code: 'custom', message: 'Unknown, blocked and stale snapshots cannot become ALLOW' });
18
+ });
19
+ export const tenantMaterialCurrentnessSchema = z.object({ ...currentnessAssessmentSchema.shape }).omit({ schemaVersion: true, subjectKind: true, projectRef: true }).extend({ schemaVersion: z.literal(2), subjectKind: z.literal('MATERIAL'), ...identity, basis: z.literal('RETAINED_SNAPSHOT'), basisRef: tenantResourceRefSchema, declarationRef: stableUriSchema }).strict().superRefine((v, ctx) => {
20
+ if (!identityValid(v) || v.basisRef !== v.subjectRef || v.declarationRef !== tenantSnapshotDeclarationRef(v.tenantId, v.subjectRef) || v.lastSuccessAt === null || v.observedAt !== v.lastSuccessAt || v.lastAttemptAt !== v.lastSuccessAt || Date.parse(v.lastSuccessAt) > Date.parse(v.assessedAt))
21
+ ctx.addIssue({ code: 'custom', message: 'Currentness retains exact saved snapshot identity and original time' });
22
+ if ([v.policy.staleAction, v.policy.unknownAction, v.policy.blockedAction].includes('ALLOW'))
23
+ ctx.addIssue({ code: 'custom', message: 'Snapshot policy fails closed' });
24
+ });
25
+ export const tenantSnapshotInputSchema = z.object({ requestId: tenantManagementRequestIdSchema, subjectRef: tenantResourceRefSchema, expectedContentHash: sha256Schema, freshnessSeconds: z.number().int().min(60).max(86400) }).strict();
26
+ export function tenantMaterialCurrentnessRevision(raw) { return 'sha256:' + createHash('sha256').update(JSON.stringify(tenantMaterialCurrentnessSchema.parse(raw))).digest('hex'); }
@@ -0,0 +1,27 @@
1
+ /** 新 Tenant 管理输入与操作归因;身份/actor/来源 origin 不能由正文注入。 */
2
+ import { z } from 'zod';
3
+ import { createHash } from 'node:crypto';
4
+ import { artifactIdSchema, reviewDecisionSchema } from './artifact.js';
5
+ import { accessClassificationSchema, isoDateTimeSchema, sha256Schema, stableUriSchema } from './common.js';
6
+ import { knowledgeActorContextSchema } from './project-intent.js';
7
+ import { materialTenantIdSchema } from './material-context.js';
8
+ import { tenantStorageKey, tenantResourceRefSchema } from './resource-scope.js';
9
+ import { tenantCollectionRequestAuthoritySchema } from './tenant-collection-request.js';
10
+ import { tenantCompositionDraftSchema } from './tenant-rich-document.js';
11
+ import { publicationProfileSchema } from './rich-document.js';
12
+ export const tenantManagementRequestIdSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/);
13
+ export const tenantInvocationAuthoritySchema = tenantCollectionRequestAuthoritySchema;
14
+ export function tenantOperationRef(tenantId, requestId) { return `tenant-operation://${tenantStorageKey(tenantId)}/${createHash('sha256').update(tenantManagementRequestIdSchema.parse(requestId)).digest('hex')}`; }
15
+ export const tenantManagementIntentSchema = z.object({ schemaVersion: z.literal(1), requestId: tenantManagementRequestIdSchema, tenantId: materialTenantIdSchema, ref: stableUriSchema, action: z.string().min(1).max(64), inputHash: sha256Schema, actor: knowledgeActorContextSchema, authority: tenantInvocationAuthoritySchema, receivedAt: isoDateTimeSchema }).strict().superRefine((v, ctx) => { if (v.actor.tenantId !== v.tenantId || v.ref !== tenantOperationRef(v.tenantId, v.requestId))
16
+ ctx.addIssue({ code: 'custom', message: 'Operation retains exact Tenant, actor and request identity' }); });
17
+ const request = { requestId: tenantManagementRequestIdSchema }, version = { expectedVersion: z.number().int().nonnegative().safe() }, refs = z.array(stableUriSchema).max(256);
18
+ export const tenantRawSubmissionSchema = z.object({ ...request, title: z.string().trim().min(1).max(512), summary: z.string().max(1024).default(''), mediaType: z.string().trim().min(1).max(256), access: accessClassificationSchema.default('INTERNAL'), claimedSourceRefs: z.array(stableUriSchema).max(127).default([]) }).strict();
19
+ export const tenantArtifactDraftSchema = z.object({ ...request, ...version, artifactId: artifactIdSchema, kind: z.enum(['RESEARCH', 'KNOWLEDGE', 'INSIGHT']), title: z.string().trim().min(1).max(500), mediaType: z.string().trim().min(1).max(256), access: accessClassificationSchema.default('INTERNAL'), observedAt: isoDateTimeSchema.nullable().default(null), validUntil: isoDateTimeSchema.nullable().default(null), sourceRefs: refs.default([]), evidenceRefs: refs.default([]), relatedRefs: refs.default([]) }).strict();
20
+ export const tenantReviewInputSchema = z.object({ ...request, targetRef: tenantResourceRefSchema, expectedVersion: z.number().int().positive().safe(), decision: reviewDecisionSchema.shape.decision, reviewedAt: isoDateTimeSchema, summary: reviewDecisionSchema.shape.summary, sourceRefs: refs.default([]) }).strict();
21
+ export const tenantPromoteInputSchema = z.object({ ...request, targetRef: tenantResourceRefSchema, expectedVersion: z.number().int().positive().safe(), nextStatus: z.enum(['READY_FOR_REVIEW', 'APPROVED', 'REWORK_REQUIRED', 'REJECTED']), reviewRef: tenantResourceRefSchema.optional() }).strict();
22
+ export const tenantComposeInputSchema = z.object({ ...request, ...version, artifactId: artifactIdSchema, sourceRefs: z.array(stableUriSchema).min(1).max(64), primarySourceRef: stableUriSchema.optional(), title: z.string().trim().min(1).max(500).optional(), outputKind: z.enum(['KNOWLEDGE', 'INSIGHT']).default('KNOWLEDGE'), publicationProfile: publicationProfileSchema.default('KNOWLEDGE_CARD_V1'), validUntil: isoDateTimeSchema.nullable().default(null), composition: tenantCompositionDraftSchema.optional(), sourceDocuments: z.array(z.object({ sourceRef: stableUriSchema, documentRef: stableUriSchema }).strict()).max(64).default([]) }).strict().superRefine((v, ctx) => { if (new Set(v.sourceRefs).size !== v.sourceRefs.length || v.primarySourceRef && !v.sourceRefs.includes(v.primarySourceRef) || new Set(v.sourceDocuments.map(row => row.sourceRef)).size !== v.sourceDocuments.length || v.sourceDocuments.some(row => !v.sourceRefs.includes(row.sourceRef)))
23
+ ctx.addIssue({ code: 'custom', message: 'Source selection must be unique and exact' }); });
24
+ export const tenantReviewDecisionSchema = z.object({ schemaVersion: z.literal(2), format: z.literal('TENANT_REVIEW_DECISION_V1'), targetRef: tenantResourceRefSchema, decision: reviewDecisionSchema.shape.decision, reviewedAt: isoDateTimeSchema, summary: reviewDecisionSchema.shape.summary, sourceRefs: refs }).strict();
25
+ export const tenantShareInputSchema = z.object({ requestId: tenantManagementRequestIdSchema, recipientTenantId: materialTenantIdSchema, matchType: z.enum(['SOURCE', 'REF']), matchValue: stableUriSchema, effect: z.enum(['ALLOW', 'DENY']).default('ALLOW') }).strict().superRefine((v, ctx) => { if (v.effect === 'DENY' && v.matchType !== 'REF')
26
+ ctx.addIssue({ code: 'custom', message: 'DENY requires an exact resource ref' }); });
27
+ export const tenantRevokeShareInputSchema = z.object({ requestId: tenantManagementRequestIdSchema, routeId: z.string().regex(/^[a-f0-9]{64}$/) }).strict();
@@ -0,0 +1,46 @@
1
+ /** Tenant material wire contract. Project selection is explicit; provenance is never an authorization context. */
2
+ import { z } from 'zod';
3
+ import { tenantMaterialLegacyJsonSchema } from './tenant-material-json-schema.js';
4
+ import { stableUriSchema, projectKeySchema, sha256Schema } from './common.js';
5
+ import { rawRetrievalDecisionSchema, rawContentDescriptorSchema, rawMaterialDirectoryItemSchema } from './material-read.js';
6
+ import { materialCapabilitySchema } from './material-capability.js';
7
+ import { materialContextSchema } from './material-context.js';
8
+ import { materialResourceTypeSchema } from './material-carrier.js';
9
+ const context = { projectRef: stableUriSchema.optional() }, limit = z.number().int().min(1).max(20).optional(), after = stableUriSchema.optional(), filters = { resourceType: materialResourceTypeSchema.optional(), sourceProjectKey: projectKeySchema.optional() };
10
+ const ref = z.object({ ...context, ref: stableUriSchema }).strict();
11
+ export const tenantMaterialToolSchemas = {
12
+ knowledge_material_list: z.object({ ...context, after, limit, ...filters, refPrefix: z.string().regex(/^[a-z][a-z0-9+.-]*:\/\/[!-~]*$/i).max(256).optional() }).strict(),
13
+ knowledge_material_describe: ref,
14
+ knowledge_material_read: ref,
15
+ knowledge_material_capabilities: ref,
16
+ knowledge_material_changes: z.object({ ...context, afterSeq: z.number().int().nonnegative().safe().optional(), limit }).strict(),
17
+ knowledge_material_window: z.object(context).strict(),
18
+ knowledge_material_search: z.object({ ...context, query: z.string().min(2).max(200), afterId: z.number().int().nonnegative().safe().optional(), limit, ...filters }).strict(),
19
+ knowledge_material_partitions: z.object({ ...context, afterRouteId: z.string().regex(/^[a-f0-9]{64}$/).optional(), limit }).strict(),
20
+ knowledge_material_partition_page: z.object({ ...context, partition: z.enum(['OWNER']), after, limit }).strict(),
21
+ knowledge_material_route_page: z.object({ ...context, routeId: z.string().regex(/^[a-f0-9]{64}$/), after, limit }).strict(),
22
+ knowledge_material_media: ref.extend({ afterOrdinal: z.number().int().min(-1).safe().optional(), limit, documentRef: stableUriSchema.optional() }).strict(),
23
+ knowledge_material_index_status: z.object({ ...context, afterSourceKey: projectKeySchema.optional(), afterNodeId: z.string().min(1).max(128).optional(), sourceProjectKey: projectKeySchema.optional(), limit }).strict(),
24
+ };
25
+ export const tenantMaterialEnvelopeSchema = z.object({ schemaVersion: z.literal(1), context: materialContextSchema, data: z.unknown() }).strict();
26
+ export const tenantMaterialToolInputSchemas = Object.fromEntries(Object.entries(tenantMaterialToolSchemas).map(([name, schema]) => [name, tenantMaterialLegacyJsonSchema(schema)]));
27
+ const nonnegative = z.number().int().nonnegative().safe(), refPosition = stableUriSchema.nullable(), items = z.array(rawMaterialDirectoryItemSchema).max(100), description = z.object({ item: rawMaterialDirectoryItemSchema.nullable(), byteLength: nonnegative.nullable(), downloadUrl: z.string().max(8192).nullable() }).strict();
28
+ const read = z.object({ item: rawMaterialDirectoryItemSchema.nullable(), byteLength: nonnegative.nullable(), decision: rawRetrievalDecisionSchema, descriptor: rawContentDescriptorSchema.nullable(), delivery: z.enum(['INLINE_BASE64', 'DOWNLOAD', 'UNAVAILABLE']), contentBase64: z.string().max(43692).nullable(), downloadUrl: z.string().max(8192).nullable(), verification: z.enum(['VERIFIED', 'NOT_VERIFIED']) }).strict();
29
+ const route = z.object({ routeId: z.string().regex(/^[a-f0-9]{64}$/), sourceProjectKey: projectKeySchema, matchType: z.enum(['SOURCE_PROJECT', 'SOURCE_SET', 'REF']), matchValue: z.string().min(1).max(4096) }).strict();
30
+ const representation = materialCapabilitySchema.shape.representation;
31
+ export const tenantMaterialDataSchemas = {
32
+ knowledge_material_list: z.object({ items, nextAfter: refPosition }).strict(),
33
+ knowledge_material_describe: description,
34
+ knowledge_material_read: read,
35
+ knowledge_material_capabilities: materialCapabilitySchema.nullable(),
36
+ knowledge_material_changes: z.object({ items: z.array(z.object({ seq: nonnegative, eventType: z.enum(['MATERIAL_REGISTERED', 'MATERIAL_LINEAGE_CHANGED', 'ROUTE_CHANGED', 'PROJECT_SCOPE_CHANGED']), artifactRef: stableUriSchema.nullable(), contentHash: sha256Schema.nullable(), routeId: z.string().regex(/^[a-f0-9]{64}$/).nullable(), createdAt: z.string().datetime() }).strict()).max(100), nextAfter: nonnegative.nullable() }).strict(),
37
+ knowledge_material_window: z.object({ retainedThroughSeq: nonnegative, headSeq: nonnegative }).strict(),
38
+ knowledge_material_search: z.object({ items: z.array(z.object({ item: rawMaterialDirectoryItemSchema, title: z.string().max(512), summary: z.string().max(1024) }).strict()).max(20), nextAfterId: nonnegative.nullable(), indexState: z.enum(['PARTIAL_BACKFILL', 'SYNCED_AS_OF_LAST_OBSERVATION']), configuredSourceCount: nonnegative, sourceCountIsLowerBound: z.boolean(), queryCoverage: z.literal('DESCRIPTIVE_METADATA_V1'), order: z.literal('INDEX_ROWID_ASC') }).strict(),
39
+ knowledge_material_partitions: z.object({ owner: z.literal('OWNER'), items: z.array(route).max(100), nextAfterRouteId: z.string().regex(/^[a-f0-9]{64}$/).nullable() }).strict(),
40
+ knowledge_material_partition_page: z.object({ partition: z.literal('OWNER'), items, nextAfter: refPosition }).strict(),
41
+ knowledge_material_route_page: z.object({ routeState: z.enum(['ACTIVE', 'REVOKED', 'UNBOUND']), items, nextAfter: refPosition }).strict().nullable(),
42
+ knowledge_material_media: z.object({ ref: stableUriSchema, sourceContentHash: sha256Schema, documentRef: stableUriSchema.nullable(), documentContentHash: sha256Schema.nullable(), state: z.enum(['READY', 'MISSING', 'DENIED', 'UNSUPPORTED', 'INTEGRITY_MISMATCH']), items: z.array(z.object({ ordinal: nonnegative, kind: z.enum(['IMAGE', 'VIDEO', 'AUDIO']), sourceUrl: z.string().url(), canonicalUrl: z.string().url().nullable(), availability: z.enum(['MIRRORED', 'REMOTE_ONLY', 'UNAVAILABLE']), mediaRef: stableUriSchema.nullable(), contentHash: sha256Schema.nullable(), mediaType: z.string().nullable(), byteLength: nonnegative.nullable(), state: z.enum(['READY', 'REMOTE_ONLY', 'UNAVAILABLE', 'MISSING', 'DENIED', 'INTEGRITY_MISMATCH']), representation: representation.nullable() }).strict()).max(100), nextAfterOrdinal: nonnegative.nullable() }).strict().nullable(),
43
+ knowledge_material_index_status: z.object({ queryCoverage: z.literal('DESCRIPTIVE_METADATA_V1'), indexedFields: z.tuple([z.literal('TITLE'), z.literal('SUMMARY')]), bodyIndexed: z.literal(false), semanticIndexed: z.literal(false), items: z.array(z.object({ sourceProjectKey: projectKeySchema, nodeId: z.string().min(1).max(128), directoryThroughSeq: nonnegative, searchThroughSeq: nonnegative.nullable(), observedMaxSeq: nonnegative.nullable(), checkpointUpdatedAt: z.string().datetime().nullable(), state: z.enum(['SYNCED_AS_OF_CHECKPOINT', 'PARTIAL_BACKFILL']) }).strict()).max(100), nextAfter: z.object({ sourceProjectKey: projectKeySchema, nodeId: z.string().min(1).max(128) }).strict().nullable() }).strict(),
44
+ };
45
+ export const tenantMaterialToolOutputSchemas = Object.fromEntries(Object.entries(tenantMaterialDataSchemas).map(([name, data]) => [name, tenantMaterialLegacyJsonSchema(tenantMaterialEnvelopeSchema.extend({ data }))]));
46
+ export const tenantMaterialProviderSchema = z.object({ schemaVersion: z.literal(14), provider: z.literal('knowledge-v2'), codeRevision: z.string().min(1).max(256), context: z.literal('TENANT_DEFAULT_PROJECT_EXPLICIT'), scope: z.literal('knowledge.material.read'), mcpEndpoint: stableUriSchema, resource: stableUriSchema, issuer: stableUriSchema, tokenEndpoint: stableUriSchema, revocationEndpoint: stableUriSchema, protocols: z.array(z.string().max(32)).min(1).max(2), tenantReadEnabled: z.boolean(), projectScopeEnabled: z.boolean(), externalTenantParameter: z.literal('external_tenant_id'), materialEndpoint: stableUriSchema, authorizationEndpoint: stableUriSchema, authorizationContextParameters: z.tuple([z.literal('tenant_ref'), z.literal('project_ref')]), tools: z.array(z.string()).max(32), projectScopeManagement: z.literal('knowledge.material.scope'), inlineMaxBytes: z.literal(32768), rawUsagePolicy: z.literal('CONSUMER_DECIDES'), referenceReadMaxBytes: z.literal(16777216) }).strict();
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+ /** Preserve Provider14 discovery bytes; validation still uses the patched Zod schemas. */
3
+ export function tenantMaterialLegacyJsonSchema(schema) {
4
+ return z.toJSONSchema(schema, { target: 'draft-07', override: ({ zodSchema, jsonSchema }) => {
5
+ const def = zodSchema._zod.def;
6
+ if (def.type !== 'number')
7
+ return;
8
+ const last = def.checks?.at(-1)?._zod.def;
9
+ // Zod4.5 .safe() replaced advertised bounds; newer Zod correctly intersects them.
10
+ // Only this frozen discovery envelope retains that historical representation.
11
+ if (last?.check === 'number_format' && 'format' in last && last.format === 'safeint') {
12
+ jsonSchema.minimum = Number.MIN_SAFE_INTEGER;
13
+ jsonSchema.maximum = Number.MAX_SAFE_INTEGER;
14
+ }
15
+ } });
16
+ }
@@ -0,0 +1,13 @@
1
+ /** Bounded PDF text projection, with real page locators; OCR is a distinct capability. */
2
+ import { z } from 'zod';
3
+ import { isoDateTimeSchema, sha256Schema } from './common.js';
4
+ import { materialTenantIdSchema } from './material-context.js';
5
+ import { parseTenantResourceRef, tenantStorageKey } from './resource-scope.js';
6
+ export const TENANT_PDF_TEXT_MEDIA_TYPE = 'application/vnd.native-knowledge.pdf-text+json';
7
+ export const tenantPdfTextSchema = z.object({ schemaVersion: z.literal(1), format: z.literal('TENANT_PDF_TEXT_V1'), tenantId: materialTenantIdSchema, parentEvidenceRef: z.string().max(1024), sourceContentHash: sha256Schema, sourceByteLength: z.number().int().min(5).max(8 * 1024 * 1024), sourceMediaType: z.string().min(1).max(256), sourceUri: z.string().url().max(2048), observedAt: isoDateTimeSchema, extractedAt: isoDateTimeSchema, extractor: z.object({ id: z.literal('native-knowledge.pdf-text'), version: z.literal('1.0.0') }).strict(), pages: z.array(z.object({ page: z.number().int().min(1).max(100), locator: z.string().url().max(2080), text: z.string().max(64000) }).strict()).min(1).max(100), gaps: z.array(z.enum(['TENANT_PDF_OCR_REQUIRED'])).max(1) }).strict().superRefine((v, c) => {
8
+ const p = parseTenantResourceRef(v.parentEvidenceRef);
9
+ if (!p || p.storageKey !== tenantStorageKey(v.tenantId) || p.resourceType !== 'evidence')
10
+ c.addIssue({ code: 'custom', message: 'Exact Tenant PDF parent required' });
11
+ if (v.pages.some((p, i) => p.page !== i + 1 || p.locator !== v.sourceUri + '#page=' + p.page) || v.pages.reduce((n, p) => n + p.text.length, 0) > 2000000 || !v.pages.some(p => p.text.trim()) || v.pages.some(p => !p.text.trim()) !== v.gaps.includes('TENANT_PDF_OCR_REQUIRED'))
12
+ c.addIssue({ code: 'custom', message: 'Page identity, text budget or OCR gap mismatch' });
13
+ });
@@ -0,0 +1,19 @@
1
+ /** Project controls select purpose/context; receipts do not own Tenant assets or lifecycle state. */
2
+ import { z } from 'zod';
3
+ import { sha256Schema, stableUriSchema, isoDateTimeSchema, projectKeySchema } from './common.js';
4
+ import { materialTenantIdSchema, materialProjectScopeInputSchema } from './material-context.js';
5
+ import { knowledgeActorContextSchema } from './project-intent.js';
6
+ import { tenantManagementRequestIdSchema, tenantInvocationAuthoritySchema, tenantOperationRef } from './tenant-management.js';
7
+ import { tenantStorageKey } from './resource-scope.js';
8
+ export const tenantProjectScopeChangeSchema = z.object({ requestId: tenantManagementRequestIdSchema, projectKey: projectKeySchema, expectedRevision: sha256Schema, changes: materialProjectScopeInputSchema }).strict();
9
+ export const tenantProjectLifecycleChangeSchema = z.object({ requestId: tenantManagementRequestIdSchema, projectKey: projectKeySchema, expectedRevision: sha256Schema, state: z.enum(['ACTIVE', 'ARCHIVED']) }).strict();
10
+ export const tenantProjectControlResultSchema = z.discriminatedUnion('type', [
11
+ z.object({ type: z.literal('SCOPE'), changed: z.boolean(), mode: z.enum(['TENANT', 'EXPLICIT']), revision: sha256Schema }).strict(),
12
+ z.object({ type: z.literal('LIFECYCLE'), changed: z.boolean(), state: z.enum(['ACTIVE', 'ARCHIVED']), revision: sha256Schema }).strict(),
13
+ ]);
14
+ export const tenantProjectControlReceiptSchema = z.object({ schemaVersion: z.literal(1), operationRef: stableUriSchema, tenantId: materialTenantIdSchema, projectKey: projectKeySchema, action: z.enum(['SET_SCOPE', 'SET_LIFECYCLE']), inputHash: sha256Schema, actor: knowledgeActorContextSchema, authority: tenantInvocationAuthoritySchema, result: tenantProjectControlResultSchema, committedAt: isoDateTimeSchema }).strict().superRefine((value, ctx) => {
15
+ const prefix = `tenant-operation://${tenantStorageKey(value.tenantId)}/`;
16
+ if (value.actor.tenantId !== value.tenantId || !value.operationRef.startsWith(prefix) || !/^[a-f0-9]{64}$/.test(value.operationRef.slice(prefix.length)) || value.action !== (value.result.type === 'SCOPE' ? 'SET_SCOPE' : 'SET_LIFECYCLE'))
17
+ ctx.addIssue({ code: 'custom', message: 'Project control receipt preserves exact Tenant, action and operation identity' });
18
+ });
19
+ export { tenantOperationRef as tenantProjectOperationRef };
@@ -0,0 +1,26 @@
1
+ /** 原生公共 Reference 候选/receipt 格式3;不改旧 Project release 和 publication wire。 */
2
+ import { z } from 'zod';
3
+ import { createHash } from 'node:crypto';
4
+ import { projectKeySchema, sha256Schema, isoDateTimeSchema } from './common.js';
5
+ import { materialTenantIdSchema } from './material-context.js';
6
+ import { tenantResourceRefSchema, parseTenantResourceRef, tenantStorageKey } from './resource-scope.js';
7
+ import { publicReferenceCatalogueScopeSchema, publicReferenceLegacyReviewSchema } from './public-reference.js';
8
+ export const TENANT_PUBLIC_REFERENCE_LIMITS = { records: 256, sources: 256, totalBytes: 16 * 1024 * 1024, recordBytes: 1024 * 1024 };
9
+ export const tenantPublicReferenceRecordIdentitySchema = z.object({ ref: tenantResourceRefSchema, digest: sha256Schema, contentHash: sha256Schema }).strict();
10
+ const records = z.array(tenantPublicReferenceRecordIdentitySchema).min(1).max(TENANT_PUBLIC_REFERENCE_LIMITS.records).superRefine((rows, ctx) => { if (new Set(rows.map(row => row.ref)).size !== rows.length || rows.some(row => parseTenantResourceRef(row.ref)?.resourceType !== 'reference'))
11
+ ctx.addIssue({ code: 'custom', message: 'Unique exact native Reference versions required' }); });
12
+ export const tenantPublicReferenceReviewSchema = publicReferenceLegacyReviewSchema.safeExtend({ approvedSources: z.array(z.object({ sourceRef: tenantResourceRefSchema.refine(ref => parseTenantResourceRef(ref)?.resourceType === 'evidence'), contentHash: sha256Schema }).strict()).min(1).max(TENANT_PUBLIC_REFERENCE_LIMITS.sources) });
13
+ export const tenantPublicReferencePublishInputSchema = z.object({ schemaVersion: z.literal(3), publicationKey: projectKeySchema, corpusKey: projectKeySchema, records, catalogue: publicReferenceCatalogueScopeSchema, review: tenantPublicReferenceReviewSchema }).strict();
14
+ export const tenantPublicReferencePublicationBasisSchema = z.object({ schemaVersion: z.literal(3), visibility: z.literal('PUBLIC_REFERENCE'), source: z.object({ type: z.literal('TENANT'), tenantId: materialTenantIdSchema }).strict(), corpusKey: projectKeySchema, records, recordsContentHash: sha256Schema, recordsByteLength: z.number().int().positive().max(TENANT_PUBLIC_REFERENCE_LIMITS.totalBytes), recordCount: z.number().int().positive().max(TENANT_PUBLIC_REFERENCE_LIMITS.records), recordTypes: z.array(projectKeySchema).min(1).max(128), sourceCreatedAt: isoDateTimeSchema, catalogue: publicReferenceCatalogueScopeSchema, review: tenantPublicReferenceReviewSchema, publishedAt: isoDateTimeSchema, checkedAt: isoDateTimeSchema, validUntil: isoDateTimeSchema }).strict();
15
+ export function tenantPublicReferencePublicationDigest(raw) { return 'sha256:' + createHash('sha256').update(JSON.stringify(tenantPublicReferencePublicationBasisSchema.parse(raw))).digest('hex'); }
16
+ export const tenantPublicReferencePublicationSchema = tenantPublicReferencePublicationBasisSchema.extend({ publicationId: sha256Schema, publicationRef: z.string().regex(/^public-reference:\/\/[a-f0-9]{64}$/) }).strict().superRefine((v, ctx) => {
17
+ const { publicationId, publicationRef, ...basis } = v, key = tenantStorageKey(v.source.tenantId);
18
+ if (publicationId !== tenantPublicReferencePublicationDigest(basis) || publicationRef !== 'public-reference://' + publicationId.slice(7) || v.records.length !== v.recordCount || v.records.some(row => parseTenantResourceRef(row.ref)?.storageKey !== key))
19
+ ctx.addIssue({ code: 'custom', message: 'Tenant publication identity mismatch' });
20
+ if (v.records.some((row, index) => index > 0 && row.ref <= v.records[index - 1].ref) || new Set(v.recordTypes).size !== v.recordTypes.length || v.review.approvedSources.some(row => parseTenantResourceRef(row.sourceRef)?.storageKey !== key))
21
+ ctx.addIssue({ code: 'custom', message: 'Canonical publication records and same-Tenant sources required' });
22
+ if (Date.parse(v.validUntil) <= Date.parse(v.publishedAt) || Date.parse(v.checkedAt) > Date.parse(v.publishedAt) || Date.parse(v.review.reviewedAt) > Date.parse(v.publishedAt) || Date.parse(v.review.reviewedAt) < Math.max(Date.parse(v.sourceCreatedAt), Date.parse(v.checkedAt)))
23
+ ctx.addIssue({ code: 'custom', message: 'Tenant publication chronology invalid' });
24
+ });
25
+ const directoryPublication = z.object({ ...tenantPublicReferencePublicationBasisSchema.shape }).omit({ review: true, records: true }).extend({ publicationId: sha256Schema, publicationRef: tenantPublicReferencePublicationSchema.shape.publicationRef, reviewedAt: isoDateTimeSchema }).strict();
26
+ export const tenantPublicReferenceDirectorySchema = z.object({ schemaVersion: z.literal(3), contentClass: z.literal('PUBLIC_REFERENCE_DIRECTORY'), coverageProof: z.literal(false), items: z.array(z.object({ publication: directoryPublication, manifestRequired: z.literal(true), availability: z.enum(['NOT_YET_PUBLISHED', 'AVAILABLE', 'STALE']) }).strict()).max(100), nextAfter: sha256Schema.nullable() }).strict();
@@ -0,0 +1,70 @@
1
+ /** Provider 16 的格式 2 资源读取合同;旧 Project/Tenant wire 保持其严格 schema。 */
2
+ import { z } from 'zod';
3
+ import { stableUriSchema, projectKeySchema, sha256Schema, accessClassificationSchema } from './common.js';
4
+ import { materialContextSchema } from './material-context.js';
5
+ import { materialAuthorityPolicySchema, resourceScopeSchema, parseTenantResourceRef, tenantStorageKey } from './resource-scope.js';
6
+ import { tenantResourceOriginSchema } from './tenant-resource.js';
7
+ import { rawRetrievalDecisionSchema, rawContentDescriptorSchema } from './material-read.js';
8
+ import { materialResourceTypeSchema } from './material-carrier.js';
9
+ import { tenantMaterialProviderSchema, tenantMaterialDataSchemas } from './tenant-material-api.js';
10
+ export const resourceOriginSchema = z.union([tenantResourceOriginSchema, z.object({ type: z.literal('LEGACY_PROJECT'), projectKey: projectKeySchema, projectRef: stableUriSchema }).strict()]);
11
+ export const resourceDirectoryItemSchema = z.object({
12
+ ref: stableUriSchema, ownerTenantId: resourceScopeSchema.options[0].shape.tenantId, resourceType: materialResourceTypeSchema,
13
+ origin: resourceOriginSchema, identityHash: sha256Schema, identityKind: z.enum(['CAS_BYTES', 'REFERENCE_RECORD_DIGEST']),
14
+ kind: z.string().min(1).max(128), classification: accessClassificationSchema, mediaType: z.string().min(1).max(256),
15
+ title: z.string().max(512), summary: z.string().max(1024), decision: rawRetrievalDecisionSchema,
16
+ }).strict().superRefine((value, ctx) => {
17
+ if (value.origin.type === 'LEGACY_PROJECT') {
18
+ if (value.origin.projectRef !== `knowledge-project://${value.origin.projectKey}/tenant/${encodeURIComponent(value.ownerTenantId)}` || value.identityKind !== (value.resourceType === 'reference' ? 'REFERENCE_RECORD_DIGEST' : 'CAS_BYTES'))
19
+ ctx.addIssue({ code: 'custom', path: ['origin'], message: 'Legacy origin/identity mismatch' });
20
+ }
21
+ else {
22
+ const ref = parseTenantResourceRef(value.ref), key = tenantStorageKey(value.ownerTenantId);
23
+ if (!ref || ref.storageKey !== key || ref.resourceType !== value.resourceType || value.identityKind !== 'CAS_BYTES')
24
+ ctx.addIssue({ code: 'custom', path: ['ref'], message: 'Native owner/resource identity mismatch' });
25
+ if (value.origin.type === 'TENANT_AUTHOR' && value.origin.actor.tenantId !== value.ownerTenantId)
26
+ ctx.addIssue({ code: 'custom', path: ['origin'], message: 'Author Tenant mismatch' });
27
+ if (value.origin.type === 'TENANT_SOURCE') {
28
+ const prefix = `tenant-source://${key}/${encodeURIComponent(value.origin.sourceId)}`;
29
+ if (value.origin.sourceRef !== prefix || !value.origin.sourceRevisionRef.startsWith(prefix + '/v/') || !/^[a-f0-9]{64}$/.test(value.origin.sourceRevisionRef.slice(prefix.length + 3)))
30
+ ctx.addIssue({ code: 'custom', path: ['origin'], message: 'Source Tenant/revision mismatch' });
31
+ }
32
+ }
33
+ });
34
+ const context = { projectRef: stableUriSchema.optional() }, position = z.string().min(1).max(4096).optional(), limit = z.number().int().min(1).max(100).optional();
35
+ const filters = { resourceType: materialResourceTypeSchema.optional(), sourceProjectKey: projectKeySchema.optional(), refPrefix: z.string().max(256).regex(/^[a-z][a-z0-9+.-]*:\/\/[!-~]*$/i).optional() };
36
+ const ref = z.object({ ...context, ref: stableUriSchema }).strict();
37
+ export const resourceToolSchemas = {
38
+ knowledge_resource_list: z.object({ ...context, after: position, limit, ...filters }).strict(),
39
+ knowledge_resource_describe: ref, knowledge_resource_read: ref, knowledge_resource_capabilities: ref,
40
+ knowledge_resource_search: z.object({ ...context, after: position, limit, ...filters, query: z.string().min(2).max(200) }).strict(),
41
+ knowledge_resource_window: z.object(context).strict(),
42
+ knowledge_resource_changes: z.object({ ...context, after: position, limit }).strict(),
43
+ knowledge_resource_counts: z.object({ ...context, after: position, limit }).strict(),
44
+ knowledge_resource_index_status: z.object(context).strict(),
45
+ knowledge_resource_media: ref.extend({ afterOrdinal: z.number().int().min(-1).safe().optional(), limit, documentRef: stableUriSchema.optional() }).strict(),
46
+ };
47
+ const count = z.number().int().nonnegative().safe(), cursor = z.string().max(4096).nullable(), item = resourceDirectoryItemSchema.nullable(), items = z.array(resourceDirectoryItemSchema).max(100);
48
+ const representation = z.object({ state: z.enum(['READY', 'UNSUPPORTED', 'MISSING', 'INTEGRITY_MISMATCH', 'DENIED']), format: z.enum(['JSON', 'BYTES']), transfer: z.enum(['BUFFERED_JSON', 'VERIFIED_STREAM', 'UNSUPPORTED']), contentHash: sha256Schema.nullable(), mediaType: z.string().min(1).max(256), byteLength: count.nullable(), methods: z.array(z.enum(['GET', 'HEAD', 'POST'])).max(3), range: z.enum(['SINGLE', 'NONE']), verification: z.literal('ON_READ'), verificationChunkBytes: count.nullable() }).strict();
49
+ export const resourceDataSchemas = {
50
+ knowledge_resource_list: z.object({ items, nextAfter: cursor }).strict(),
51
+ knowledge_resource_describe: z.object({ item, byteLength: count.nullable(), downloadUrl: z.string().max(8192).nullable() }).strict(),
52
+ knowledge_resource_read: z.object({ item, byteLength: count.nullable(), decision: rawRetrievalDecisionSchema, descriptor: rawContentDescriptorSchema.nullable(), delivery: z.enum(['INLINE_BASE64', 'DOWNLOAD', 'UNAVAILABLE']), contentBase64: z.string().max(43692).nullable(), downloadUrl: z.string().max(8192).nullable(), verification: z.enum(['VERIFIED', 'NOT_VERIFIED']) }).strict(),
53
+ knowledge_resource_capabilities: z.object({ item, materialAccess: rawRetrievalDecisionSchema, representation: representation.nullable() }).strict(),
54
+ knowledge_resource_search: z.object({ items, nextAfter: cursor, coverage: z.literal('INDEXED_DESCRIPTIONS_ONLY') }).strict(),
55
+ knowledge_resource_window: z.object({ headSeq: count, retainedThroughSeq: count, headCursor: z.string().max(4096), retentionCursor: z.string().max(4096), epoch: z.literal('TENANT_RESOURCE_V1') }).strict(),
56
+ knowledge_resource_changes: z.object({ items: z.array(z.object({ seq: count, eventType: z.string().min(1).max(128), artifactRef: stableUriSchema.nullable(), contentHash: sha256Schema.nullable(), routeId: z.string().max(128).nullable(), createdAt: z.string().datetime({ offset: true }) }).strict()).max(100), nextAfter: cursor }).strict(),
57
+ knowledge_resource_counts: z.object({ owned: count, granted: count, available: count, resourceTypes: z.array(z.object({ resourceType: materialResourceTypeSchema, total: count }).strict()).max(100), nextAfter: cursor, coverage: z.literal('CURRENT_AUTHORIZED_DIRECTORY') }).strict(),
58
+ knowledge_resource_index_status: z.object({ registered: count, indexed: count, unindexed: count, state: z.enum(['SYNCED_AS_OF_LAST_OBSERVATION', 'PARTIAL_BACKFILL']), canonicalInventory: z.literal('NOT_SCANNED'), coverage: z.literal('CURRENT_AUTHORIZED_DIRECTORY') }).strict(),
59
+ knowledge_resource_media: tenantMaterialDataSchemas.knowledge_material_media,
60
+ };
61
+ export const resourceEnvelopeSchema = z.object({ schemaVersion: z.literal(2), context: materialContextSchema, policy: materialAuthorityPolicySchema, data: z.unknown() }).strict().superRefine((value, ctx) => {
62
+ const c = value.context, expected = c.type === 'TENANT' ? `knowledge-tenant://tenant/${encodeURIComponent(c.tenantId)}` : `knowledge-project://${c.projectKey}/tenant/${encodeURIComponent(c.tenantId)}`;
63
+ if ((value.policy === 'TENANT_NATIVE') !== (c.type === 'TENANT') || (c.type === 'TENANT' ? c.tenantRef : c.projectRef) !== expected)
64
+ ctx.addIssue({ code: 'custom', path: ['context'], message: 'Context/policy identity mismatch' });
65
+ });
66
+ export const resourceToolInputSchemas = Object.fromEntries(Object.entries(resourceToolSchemas).map(([name, schema]) => [name, z.toJSONSchema(schema, { target: 'draft-07' })]));
67
+ export const resourceToolOutputSchemas = Object.fromEntries(Object.entries(resourceDataSchemas).map(([name, data]) => [name, z.toJSONSchema(resourceEnvelopeSchema.safeExtend({ data }), { target: 'draft-07' })]));
68
+ export const tenantResourceProviderSchema = tenantMaterialProviderSchema.extend({ schemaVersion: z.literal(16), materialSchemaVersion: z.literal(2), capabilities: z.tuple([z.literal('TENANT_RESOURCE_V1'), z.literal('TENANT_AUTHORITY_V1')]), legacyProviderEndpoint: stableUriSchema });
69
+ export const resourceHttpContractSchema = z.object({ schemaVersion: z.literal(2), context: materialContextSchema, policy: materialAuthorityPolicySchema, capabilities: z.tuple([z.literal('TENANT_RESOURCE_V1'), z.literal('TENANT_AUTHORITY_V1')]), callEndpoint: z.string().min(1).max(8192), fileEndpoint: z.string().min(1).max(8192), tools: z.array(z.string()).max(32), inlineMaxBytes: z.literal(32768), readBufferMaxBytes: z.literal(16777216), fileMethods: z.tuple([z.literal('GET'), z.literal('HEAD'), z.literal('POST')]), verificationChunkBytes: z.literal(65536), requestMaxBytes: z.literal(131072), resultMaxBytes: z.literal(524288) }).strict().superRefine((value, ctx) => { if (!resourceEnvelopeSchema.safeParse({ schemaVersion: 2, context: value.context, policy: value.policy, data: null }).success)
70
+ ctx.addIssue({ code: 'custom', path: ['context'], message: 'Context/policy identity mismatch' }); });
@@ -0,0 +1,118 @@
1
+ /** 租户原生不可变信封保留类型事实;不以项目占位或审核状态判断原文读取。 */
2
+ import { z } from 'zod';
3
+ import { createHash } from 'node:crypto';
4
+ import { accessClassificationSchema, idSchema, isoDateTimeSchema, sha256Schema, stableUriSchema } from './common.js';
5
+ import { artifactManifestSchema } from './artifact.js';
6
+ import { evidenceRefSchema } from './evidence.js';
7
+ import { referenceCorpusSummarySchema } from './reference-record.js';
8
+ import { knowledgeActorContextSchema } from './project-intent.js';
9
+ import { materialResourceTypeSchema } from './material-carrier.js';
10
+ import { materialTenantIdSchema } from './material-context.js';
11
+ import { tenantResourceIdentitySchema, tenantResourceRefs, tenantStorageKey, tenantResourceRefSchema } from './resource-scope.js';
12
+ // 取原有字段验证器,旧 schema/DTO 原样保留;新信封的身份关系在下方集中验证。
13
+ const artifactFacts = z.object({ ...artifactManifestSchema.shape }).omit({ schemaVersion: true, projectKey: true, projectRef: true, ref: true, identityRef: true, contentHash: true, mediaType: true, byteLength: true, access: true, createdAt: true }).extend({ sourceMaterialRefs: z.array(stableUriSchema).max(256).optional() }).strict();
14
+ const evidenceFacts = z.object({ ...evidenceRefSchema.shape }).omit({ schemaVersion: true, projectKey: true, projectRef: true, recoveryProvenance: true, contentHash: true, mediaType: true, byteLength: true, access: true }).extend({ parentEvidenceRef: tenantResourceRefSchema.optional() }).strict();
15
+ const referenceFacts = z.object({ ...referenceCorpusSummarySchema.shape }).omit({ schemaVersion: true, projectKey: true, projectRef: true, recordRef: true, access: true }).strict();
16
+ export const tenantResourceFactsSchema = z.discriminatedUnion('type', [
17
+ z.object({ type: z.literal('ARTIFACT'), metadata: artifactFacts }).strict(),
18
+ z.object({ type: z.literal('EVIDENCE'), metadata: evidenceFacts }).strict(),
19
+ z.object({ type: z.literal('REFERENCE'), metadata: referenceFacts }).strict(),
20
+ z.object({ type: z.literal('CARRIER'), metadata: z.object({ kind: z.string().min(1).max(128), title: z.string().max(512), summary: z.string().max(1024), sourceRefs: z.array(stableUriSchema).min(1).max(128), observedAt: isoDateTimeSchema }).strict() }).strict(),
21
+ ]);
22
+ export const tenantResourceOriginSchema = z.discriminatedUnion('type', [
23
+ z.object({ type: z.literal('TENANT_AUTHOR'), actor: knowledgeActorContextSchema, submittedAt: isoDateTimeSchema }).strict(),
24
+ z.object({ type: z.literal('TENANT_SOURCE'), sourceId: idSchema, sourceRef: stableUriSchema, sourceRevisionRef: stableUriSchema, collector: evidenceRefSchema.shape.collector, runtimeNodeId: idSchema, observedAt: isoDateTimeSchema }).strict(),
25
+ ]);
26
+ export const tenantResourceManifestSchema = z.object({
27
+ schemaVersion: z.literal(2), ownerTenantId: materialTenantIdSchema,
28
+ resourceId: tenantResourceIdentitySchema.shape.resourceId, resourceType: materialResourceTypeSchema,
29
+ version: tenantResourceIdentitySchema.shape.version, ref: stableUriSchema, identityRef: stableUriSchema,
30
+ contentHash: sha256Schema, byteLength: z.number().int().nonnegative().safe(), mediaType: z.string().trim().min(1).max(256),
31
+ access: accessClassificationSchema, createdAt: isoDateTimeSchema,
32
+ origin: tenantResourceOriginSchema, facts: tenantResourceFactsSchema,
33
+ }).strict().superRefine((value, ctx) => {
34
+ const expected = tenantResourceRefs({ tenantId: value.ownerTenantId, resourceType: value.resourceType, resourceId: value.resourceId, version: value.version });
35
+ for (const name of ['ref', 'identityRef'])
36
+ if (value[name] !== expected[name])
37
+ ctx.addIssue({ code: 'custom', path: [name], message: 'Tenant resource identity mismatch' });
38
+ const kind = value.facts.type.toLowerCase();
39
+ if (value.facts.type === 'CARRIER' ? ['artifact', 'evidence', 'reference', 'media'].includes(value.resourceType) : value.resourceType !== kind)
40
+ ctx.addIssue({ code: 'custom', path: ['resourceType'], message: 'Resource type must retain its factual carrier' });
41
+ if (value.origin.type === 'TENANT_AUTHOR') {
42
+ if (value.origin.actor.tenantId !== value.ownerTenantId)
43
+ ctx.addIssue({ code: 'custom', path: ['origin'], message: 'Author must belong to the resource Tenant' });
44
+ }
45
+ else {
46
+ const prefix = `tenant-source://${tenantStorageKey(value.ownerTenantId)}/${encodeURIComponent(value.origin.sourceId)}`;
47
+ if (value.origin.sourceRef !== prefix || !value.origin.sourceRevisionRef.startsWith(prefix + '/v/') || !/^[a-f0-9]{64}$/.test(value.origin.sourceRevisionRef.slice(prefix.length + 3)))
48
+ ctx.addIssue({ code: 'custom', path: ['origin'], message: 'Source must retain exact Tenant/source/revision identity' });
49
+ }
50
+ if (value.facts.type === 'ARTIFACT') {
51
+ const m = value.facts.metadata;
52
+ if (m.artifactId !== value.resourceId || m.version !== value.version)
53
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Artifact identity/version mismatch' });
54
+ if (m.validUntil && m.observedAt && Date.parse(m.validUntil) < Date.parse(m.observedAt))
55
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Invalid Artifact validity interval' });
56
+ if (m.kind === 'SIGNAL_GENERATION' && (m.version !== 1 || m.status !== 'GENERATED') || ['KNOWLEDGE', 'INSIGHT'].includes(m.kind) && m.status === 'GENERATED')
57
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Invalid Artifact lifecycle' });
58
+ const previous = value.version > 1 ? tenantResourceRefs({ tenantId: value.ownerTenantId, resourceType: value.resourceType, resourceId: value.resourceId, version: value.version - 1 }).ref : null;
59
+ if (m.supersedesRef !== previous)
60
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Artifact must retain immediate predecessor' });
61
+ }
62
+ if (value.facts.type === 'EVIDENCE') {
63
+ const m = value.facts.metadata;
64
+ if (m.parentEvidenceRef) {
65
+ const parent = new URL(m.parentEvidenceRef);
66
+ if (parent.hostname !== tenantStorageKey(value.ownerTenantId) || !parent.pathname.startsWith('/evidence/') || m.parentEvidenceRef === value.ref)
67
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Media parent must be another native Tenant Evidence' });
68
+ }
69
+ if (m.evidenceId !== value.resourceId || value.version !== 1 || Boolean(m.sourceSetRef) !== Boolean(m.sourceItemId) || Boolean(m.collectionBatchRef) !== Boolean(m.collectionTaskId))
70
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Evidence identity/bindings mismatch' });
71
+ if (value.origin.type === 'TENANT_SOURCE' && (m.sourceId !== value.origin.sourceId || m.runtimeNodeId !== value.origin.runtimeNodeId || m.observedAt !== value.origin.observedAt || m.sourceRevisionRef !== value.origin.sourceRevisionRef || JSON.stringify(m.collector) !== JSON.stringify(value.origin.collector)))
72
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Evidence must preserve acquisition provenance' });
73
+ }
74
+ if (value.facts.type === 'REFERENCE') {
75
+ const m = value.facts.metadata;
76
+ if (m.version !== value.version || m.effectiveFrom && m.effectiveUntil && Date.parse(m.effectiveUntil) <= Date.parse(m.effectiveFrom))
77
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Reference version/validity mismatch' });
78
+ const previous = value.version > 1 ? tenantResourceRefs({ tenantId: value.ownerTenantId, resourceType: value.resourceType, resourceId: value.resourceId, version: value.version - 1 }).ref : null;
79
+ if (m.supersedesRef !== previous)
80
+ ctx.addIssue({ code: 'custom', path: ['facts'], message: 'Reference must retain immediate predecessor' });
81
+ }
82
+ });
83
+ export const tenantProjectionIntentSchema = z.object({ schemaVersion: z.literal(2), ref: tenantResourceRefSchema, manifestRevision: sha256Schema, createdAt: isoDateTimeSchema }).strict();
84
+ /** 存储、Catalog 与 Identity 共用同一 manifest 身份;undefined 等价于 JSON 省略。 */
85
+ export function tenantResourceManifestRevision(raw) {
86
+ const json = JSON.stringify(tenantResourceManifestSchema.parse(raw));
87
+ if (Buffer.byteLength(json + '\n') > 64 * 1024)
88
+ throw new Error('TENANT_RESOURCE_MANIFEST_TOO_LARGE');
89
+ const canonical = (value) => { if (value === null || typeof value !== 'object')
90
+ return JSON.stringify(value); if (Array.isArray(value))
91
+ return '[' + value.map(canonical).join(',') + ']'; const row = value; return '{' + Object.keys(row).sort().map(key => JSON.stringify(key) + ':' + canonical(row[key])).join(',') + '}'; };
92
+ return 'sha256:' + createHash('sha256').update(canonical(JSON.parse(json))).digest('hex');
93
+ }
94
+ /** Reference 语义摘要与 CAS 字节摘要分开;只正规化集合字段,不改原始 payload 字节。 */
95
+ export function tenantReferenceRecordDigest(tenantId, access, metadata, payload) {
96
+ const parsed = tenantResourceFactsSchema.parse({ type: 'REFERENCE', metadata });
97
+ if (parsed.type !== 'REFERENCE')
98
+ throw new Error('TENANT_REFERENCE_FACTS_REQUIRED');
99
+ const m = parsed.metadata, selectors = m.selectors ? Object.fromEntries(Object.keys(m.selectors).sort().map(key => [key, [...new Set(m.selectors[key])].sort()])) : undefined;
100
+ const basis = { tenantId: materialTenantIdSchema.parse(tenantId), corpusKey: m.corpusKey, recordKey: m.recordKey, recordType: m.recordType, access: accessClassificationSchema.parse(access), authority: m.authority, jurisdiction: m.jurisdiction, ...(selectors ? { selectors } : {}), sourceRefs: [...new Set(m.sourceRefs)].sort(), effectiveFrom: m.effectiveFrom, effectiveUntil: m.effectiveUntil, payload };
101
+ let nodes = 0;
102
+ const canonical = (value, depth = 0) => {
103
+ if (++nodes > 1_000_000 || depth > 64)
104
+ throw new Error('TENANT_REFERENCE_JSON_BUDGET_EXCEEDED');
105
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
106
+ return JSON.stringify(value);
107
+ if (typeof value === 'number' && Number.isFinite(value))
108
+ return JSON.stringify(value);
109
+ if (Array.isArray(value))
110
+ return '[' + value.map(item => canonical(item, depth + 1)).join(',') + ']';
111
+ if (value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype) {
112
+ const row = value;
113
+ return '{' + Object.keys(row).sort().map(key => JSON.stringify(key) + ':' + canonical(row[key], depth + 1)).join(',') + '}';
114
+ }
115
+ throw new Error('TENANT_REFERENCE_JSON_INVALID');
116
+ };
117
+ return 'sha256:' + createHash('sha256').update(canonical(basis)).digest('hex');
118
+ }
@@ -0,0 +1,17 @@
1
+ /** 原生受治理正文引用真实资料类型;Reference/开放载体不被伪装为 Evidence。 */
2
+ import { z } from 'zod';
3
+ import { richDocumentSchema, publicationProfileSchema, validateRichDocumentContent } from './rich-document.js';
4
+ import { materialTenantIdSchema } from './material-context.js';
5
+ import { stableUriSchema } from './common.js';
6
+ export const TENANT_RICH_DOCUMENT_MEDIA_TYPE = 'application/vnd.knowledge-v2.tenant-rich-document+json';
7
+ export const tenantDocumentCitationSchema = z.object({ blockOrdinal: z.number().int().nonnegative(), sourceRefs: z.array(stableUriSchema).min(1).max(32) }).strict().superRefine((value, ctx) => { if (new Set(value.sourceRefs).size !== value.sourceRefs.length)
8
+ ctx.addIssue({ code: 'custom', path: ['sourceRefs'], message: 'Citation refs must be unique' }); });
9
+ export const tenantCompositionDraftSchema = z.object({ ...richDocumentSchema.shape }).pick({ summary: true, byline: true, language: true, blocks: true }).extend({ summary: z.string().trim().min(1).max(4000), byline: richDocumentSchema.shape.byline.optional(), language: richDocumentSchema.shape.language.optional(), blocks: richDocumentSchema.shape.blocks.min(1), citations: z.array(tenantDocumentCitationSchema).min(1).max(10000) }).strict();
10
+ const base = z.object({ ...richDocumentSchema.shape }).omit({ projectKey: true, projectRef: true, format: true, role: true, publicationProfile: true, sourceEvidenceRefs: true, primarySourceEvidenceRef: true, citations: true });
11
+ export const tenantRichDocumentSchema = base.extend({ format: z.literal('TENANT_RICH_DOCUMENT_V1'), tenantId: materialTenantIdSchema, role: z.enum(['KNOWLEDGE', 'INSIGHT']), publicationProfile: publicationProfileSchema, sourceRefs: z.array(stableUriSchema).min(1).max(256), primarySourceRef: stableUriSchema, representationRefs: z.array(stableUriSchema).max(64).default([]), citations: z.array(tenantDocumentCitationSchema).max(10000).default([]) }).strict().superRefine((value, ctx) => {
12
+ // 只把字段适配到共同结构规则;身份、真实载体类型与权限由应用层逐 ref 验证。
13
+ validateRichDocumentContent({ sourceEvidenceRefs: value.sourceRefs, primarySourceEvidenceRef: value.primarySourceRef, mediaEvidenceRefs: value.mediaEvidenceRefs, blocks: value.blocks, citations: value.citations.map(citation => ({ blockOrdinal: citation.blockOrdinal, evidenceRefs: citation.sourceRefs })) }, ctx, { primary: 'primarySourceRef', citation: 'sourceRefs' });
14
+ if (new Set(value.sourceRefs).size !== value.sourceRefs.length)
15
+ ctx.addIssue({ code: 'custom', path: ['sourceRefs'], message: 'Sources must be unique' });
16
+ });
17
+ export const tenantPublicationReadinessSchema = z.object({ schemaVersion: z.literal(2), profile: publicationProfileSchema, ready: z.boolean(), reasons: z.array(z.enum(['ROLE_MISMATCH', 'TITLE_REQUIRED', 'SUMMARY_REQUIRED', 'TEXT_TOO_SHORT', 'STRUCTURE_TOO_THIN', 'SOURCE_REQUIRED', 'CITATION_COVERAGE_REQUIRED', 'SOURCE_MISMATCH', 'MEDIA_UNAVAILABLE'])).max(16), metrics: z.object({ textCharacters: z.number().int().nonnegative(), blockCount: z.number().int().nonnegative(), textBlockCount: z.number().int().nonnegative(), mediaBlockCount: z.number().int().nonnegative(), sourceCount: z.number().int().nonnegative(), mediaEvidenceCount: z.number().int().nonnegative(), citationCount: z.number().int().nonnegative(), citedBlockCount: z.number().int().nonnegative() }).strict() }).strict();
@@ -0,0 +1,33 @@
1
+ /** 项目用途上下文不参与来源获取等价,不是授权 grant 或可执行过滤 DSL。 */
2
+ import { z } from 'zod';
3
+ import { idSchema, stableUriSchema } from './common.js';
4
+ export const tenantSourceBindingContextSchema = z.object({
5
+ schemaVersion: z.literal(1), projectIntentRef: stableUriSchema.nullable().default(null),
6
+ scopeIds: z.array(idSchema).max(256).default([]), sourceFamilies: z.array(idSchema).max(256).default([]), attentionNotes: z.string().max(4000).nullable().default(null),
7
+ }).strict().superRefine((value, ctx) => {
8
+ for (const key of ['scopeIds', 'sourceFamilies'])
9
+ if (new Set(value[key]).size !== value[key].length)
10
+ ctx.addIssue({ code: 'custom', path: [key], message: 'Project demand selectors must be unique' });
11
+ if ((value.scopeIds.length || value.sourceFamilies.length) && !value.projectIntentRef)
12
+ ctx.addIssue({ code: 'custom', path: ['projectIntentRef'], message: 'Scoped demand must retain an exact Project Intent revision' });
13
+ if (value.projectIntentRef && !/^project-intent:\/\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\/[a-f0-9]{64}$/.test(value.projectIntentRef))
14
+ ctx.addIssue({ code: 'custom', path: ['projectIntentRef'], message: 'Exact Project Intent revision required' });
15
+ });
16
+ import { projectKeySchema, isoDateTimeSchema } from './common.js';
17
+ import { knowledgeActorContextSchema, collectionRefreshSchema } from './project-intent.js';
18
+ import { materialTenantIdSchema } from './material-context.js';
19
+ import { tenantSourceIdSchema } from './tenant-source.js';
20
+ import { tenantStorageKey } from './resource-scope.js';
21
+ export const tenantSourceBindingRevisionSchema = z.object({
22
+ schemaVersion: z.literal(1), tenantId: materialTenantIdSchema, projectKey: projectKeySchema, sourceId: tenantSourceIdSchema, revisionRef: stableUriSchema,
23
+ generation: z.number().int().positive().safe(), purpose: z.string().max(4000), refresh: collectionRefreshSchema, enabled: z.boolean(), demandContext: tenantSourceBindingContextSchema,
24
+ origin: z.enum(['MIGRATED_V13', 'EXPLICIT_V14']), actor: knowledgeActorContextSchema.nullable(), changedAt: isoDateTimeSchema.nullable(),
25
+ }).strict().superRefine((v, ctx) => {
26
+ const prefix = `tenant-source://${tenantStorageKey(v.tenantId)}/${v.sourceId}/v/`;
27
+ if (!v.revisionRef.startsWith(prefix) || !/^[a-f0-9]{64}$/.test(v.revisionRef.slice(prefix.length)))
28
+ ctx.addIssue({ code: 'custom', path: ['revisionRef'], message: 'Binding must retain exact Tenant Source identity' });
29
+ if (v.demandContext.projectIntentRef && !v.demandContext.projectIntentRef.startsWith(`project-intent://${v.projectKey}/`))
30
+ ctx.addIssue({ code: 'custom', path: ['demandContext'], message: 'Demand context Project mismatch' });
31
+ if (v.origin === 'MIGRATED_V13' ? (v.actor !== null || v.changedAt !== null) : (!v.actor || v.actor.tenantId !== v.tenantId || !v.changedAt))
32
+ ctx.addIssue({ code: 'custom', path: ['origin'], message: 'Migration cannot invent actor/time; explicit changes require attribution' });
33
+ });
@@ -0,0 +1,9 @@
1
+ /** 显式桥接预检确认获取语义;该请求不授予启停或修改工作归属的权限。 */
2
+ import { z } from 'zod';
3
+ import { projectKeySchema, stableUriSchema } from './common.js';
4
+ import { tenantSourceIdSchema, tenantSourceSemanticsSchema } from './tenant-source.js';
5
+ export const tenantSourceBridgePreviewInputSchema = z.object({
6
+ projectKey: projectKeySchema, legacySourceId: tenantSourceIdSchema, expectedLegacyRevisionRef: stableUriSchema,
7
+ nativeSourceId: tenantSourceIdSchema, expectedNativeRevisionRef: stableUriSchema,
8
+ confirmAcquisitionSemantics: tenantSourceSemanticsSchema, maxResourceBytes: z.number().int().min(1024).max(64 * 1024 * 1024),
9
+ }).strict();
@@ -0,0 +1,26 @@
1
+ /** Host 维护切换的冻结事实;不从回执推断真实进程已退出。 */
2
+ import { z } from 'zod';
3
+ import { createHash } from 'node:crypto';
4
+ import { idSchema, isoDateTimeSchema, stableUriSchema } from './common.js';
5
+ import { projectSourceSnapshotSchema, knowledgeActorContextSchema } from './project-intent.js';
6
+ import { tenantCollectionPlanSchema } from './tenant-collection.js';
7
+ import { tenantSourceBindingRevisionSchema, tenantSourceBindingContextSchema } from './tenant-source-binding.js';
8
+ import { tenantStorageKey } from './resource-scope.js';
9
+ import { materialTenantIdSchema } from './material-context.js';
10
+ export const SOURCE_CUTOVER_FENCE_CAPABILITY = 'SOURCE_CUTOVER_FENCE_V1';
11
+ export const sourceCutoverIdSchema = z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/);
12
+ export const sourceCutoverKey = (id) => createHash('sha256').update(sourceCutoverIdSchema.parse(id)).digest('hex');
13
+ export const sourceCutoverRef = (tenantId, id) => `tenant-source-cutover://${tenantStorageKey(tenantId)}/${sourceCutoverKey(id)}`;
14
+ export const sourceCutoverIntentSchema = z.object({
15
+ schemaVersion: z.literal(1), cutoverId: sourceCutoverIdSchema, cutoverRef: stableUriSchema, tenantId: materialTenantIdSchema, actor: knowledgeActorContextSchema, createdAt: isoDateTimeSchema,
16
+ legacySource: projectSourceSnapshotSchema, plan: tenantCollectionPlanSchema, existingBinding: tenantSourceBindingRevisionSchema.nullable(),
17
+ demandContext: tenantSourceBindingContextSchema,
18
+ }).strict().superRefine((v, ctx) => {
19
+ if (v.cutoverRef !== sourceCutoverRef(v.tenantId, v.cutoverId) || v.actor.tenantId !== v.tenantId || v.legacySource.actor.tenantId !== v.tenantId || v.plan.tenantId !== v.tenantId || v.legacySource.targetId !== v.plan.source.targetId || v.legacySource.access !== v.plan.source.semantics.access || v.demandContext.projectIntentRef !== v.legacySource.projectIntentRef)
20
+ ctx.addIssue({ code: 'custom', message: 'Cutover must retain exact owner, source and Project demand' });
21
+ if (v.existingBinding && (v.existingBinding.tenantId !== v.tenantId || v.existingBinding.projectKey !== v.legacySource.projectKey || v.existingBinding.sourceId !== v.plan.source.sourceId))
22
+ ctx.addIssue({ code: 'custom', path: ['existingBinding'], message: 'Prior binding belongs to another context' });
23
+ });
24
+ export const sourceCutoverStepNameSchema = z.enum(['LEGACY_STOPPED', 'DRAINED', 'ACTIVATED', 'NATIVE_STOPPED', 'NATIVE_DRAINED', 'RESTORE_AUTHORIZED', 'LEGACY_RESTORED', 'ROLLED_BACK']);
25
+ export const sourceCutoverStepSchema = z.object({ schemaVersion: z.literal(1), cutoverRef: stableUriSchema, step: sourceCutoverStepNameSchema, actor: knowledgeActorContextSchema, at: isoDateTimeSchema, details: z.record(z.string().max(64), z.union([z.string().max(4096), z.number().finite(), z.boolean(), z.null()])).refine(v => Object.keys(v).length <= 32) }).strict();
26
+ export const sourceCutoverRuntimeProofSchema = z.object({ nodeId: idSchema, codeRevision: z.string().regex(/^[A-Za-z0-9._-]{7,64}$/), capability: z.literal(SOURCE_CUTOVER_FENCE_CAPABILITY), unmanagedExecutions: z.literal('QUIESCED'), observedAt: isoDateTimeSchema }).strict();