@native-knowledge/cli 0.1.0-rc.1 → 0.1.0-rc.3

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 (94) hide show
  1. package/LICENSE +202 -21
  2. package/NOTICE +29 -0
  3. package/dist/agent-entry-cli.js +90 -0
  4. package/dist/auth-commands.js +102 -0
  5. package/dist/cli.js +106 -4874
  6. package/dist/commands.js +41 -0
  7. package/dist/download.js +45 -0
  8. package/dist/errors.js +7 -0
  9. package/dist/input.js +89 -0
  10. package/dist/internal/contracts/artifact.js +103 -0
  11. package/dist/internal/contracts/collection-control.js +69 -0
  12. package/dist/internal/contracts/collection.js +174 -0
  13. package/dist/internal/contracts/common.js +16 -0
  14. package/dist/internal/contracts/currentness.js +221 -0
  15. package/dist/internal/contracts/event.js +35 -0
  16. package/dist/internal/contracts/evidence.js +171 -0
  17. package/dist/internal/contracts/index.js +55 -0
  18. package/dist/internal/contracts/material-capability.js +8 -0
  19. package/dist/internal/contracts/material-carrier.js +15 -0
  20. package/dist/internal/contracts/material-context.js +9 -0
  21. package/dist/internal/contracts/material-read.js +12 -0
  22. package/dist/internal/contracts/observation.js +39 -0
  23. package/dist/internal/contracts/persistence.js +239 -0
  24. package/dist/internal/contracts/project-intent.js +121 -0
  25. package/dist/internal/contracts/projection.js +194 -0
  26. package/dist/internal/contracts/public-reference.js +87 -0
  27. package/dist/internal/contracts/reference-record.js +55 -0
  28. package/dist/internal/contracts/remote-file.js +159 -0
  29. package/dist/internal/contracts/resource-scope.js +38 -0
  30. package/dist/internal/contracts/resource-transfer.js +11 -0
  31. package/dist/internal/contracts/review-packet.js +61 -0
  32. package/dist/internal/contracts/review-plane.js +89 -0
  33. package/dist/internal/contracts/rich-document.js +184 -0
  34. package/dist/internal/contracts/runtime-compatibility.js +55 -0
  35. package/dist/internal/contracts/runtime.js +68 -0
  36. package/dist/internal/contracts/scheduler.js +256 -0
  37. package/dist/internal/contracts/signal.js +77 -0
  38. package/dist/internal/contracts/source-governance.js +194 -0
  39. package/dist/internal/contracts/source.js +32 -0
  40. package/dist/internal/contracts/tenant-budget.js +34 -0
  41. package/dist/internal/contracts/tenant-collection-request.js +58 -0
  42. package/dist/internal/contracts/tenant-collection.js +66 -0
  43. package/dist/internal/contracts/tenant-currentness.js +26 -0
  44. package/dist/internal/contracts/tenant-management.js +27 -0
  45. package/dist/internal/contracts/tenant-material-api.js +46 -0
  46. package/dist/internal/contracts/tenant-material-json-schema.js +16 -0
  47. package/dist/internal/contracts/tenant-pdf-text.js +13 -0
  48. package/dist/internal/contracts/tenant-project-control.js +19 -0
  49. package/dist/internal/contracts/tenant-public-reference.js +26 -0
  50. package/dist/internal/contracts/tenant-resource-api.js +70 -0
  51. package/dist/internal/contracts/tenant-resource.js +118 -0
  52. package/dist/internal/contracts/tenant-rich-document.js +17 -0
  53. package/dist/internal/contracts/tenant-source-binding.js +33 -0
  54. package/dist/internal/contracts/tenant-source-bridge.js +9 -0
  55. package/dist/internal/contracts/tenant-source-cutover.js +26 -0
  56. package/dist/internal/contracts/tenant-source-document.js +38 -0
  57. package/dist/internal/contracts/tenant-source-lease.js +5 -0
  58. package/dist/internal/contracts/tenant-source.js +83 -0
  59. package/dist/internal/contracts/tenant-web-collection.js +118 -0
  60. package/dist/internal/contracts/tenant-web-content-plan.js +39 -0
  61. package/dist/internal/contracts/tenant-web-content-result.js +15 -0
  62. package/dist/internal/contracts/tenant-web-content.js +16 -0
  63. package/dist/internal/contracts/tenant-web-crawl.js +55 -0
  64. package/dist/internal/contracts/tenant-web-dynamic.js +68 -0
  65. package/dist/internal/contracts/tenant-web-results.js +41 -0
  66. package/dist/internal/contracts/tenant-web-workflow.js +29 -0
  67. package/dist/internal/serving-contracts/agent-catalog.js +56 -0
  68. package/dist/internal/serving-contracts/agent-entry.js +57 -0
  69. package/dist/internal/serving-contracts/agent-guidance-shapes.js +12 -0
  70. package/dist/internal/serving-contracts/agent-input-hints.js +104 -0
  71. package/dist/internal/serving-contracts/body-search.js +16 -0
  72. package/dist/internal/serving-contracts/consumer.js +45 -0
  73. package/dist/internal/serving-contracts/core.js +76 -0
  74. package/dist/internal/serving-contracts/http.js +64 -0
  75. package/dist/internal/serving-contracts/index.js +25 -0
  76. package/dist/internal/serving-contracts/management.js +15 -0
  77. package/dist/internal/serving-contracts/material-capabilities.js +14 -0
  78. package/dist/internal/serving-contracts/material-index-status.js +5 -0
  79. package/dist/internal/serving-contracts/mcp.js +137 -0
  80. package/dist/internal/serving-contracts/tenant-governance.js +13 -0
  81. package/dist/internal/serving-contracts/tenant-management.js +89 -0
  82. package/dist/internal/serving-contracts/tenant-project.js +39 -0
  83. package/dist/internal/serving-contracts/tenant-runtime.js +37 -0
  84. package/dist/internal/serving-contracts/tenant-session.js +8 -0
  85. package/dist/internal/serving-contracts/tenant-web-content.js +20 -0
  86. package/dist/internal/serving-contracts/tenant-web-crawl.js +13 -0
  87. package/dist/internal/serving-contracts/tenant-web-dynamic.js +20 -0
  88. package/dist/internal/serving-contracts/tenant-web-workflow.js +36 -0
  89. package/dist/internal/serving-contracts/tenant-web.js +53 -0
  90. package/dist/internal/serving-contracts/tenant-workspace.js +28 -0
  91. package/dist/internal/serving-contracts/workflow.js +37 -0
  92. package/dist/profile-store.js +142 -0
  93. package/package.json +7 -17
  94. package/README.md +0 -28
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ import { accessClassificationSchema, idSchema, projectKeySchema, schemaVersionSchema, stableUriSchema, } from './common.js';
3
+ export const sourceKindSchema = z.enum([
4
+ 'WEB',
5
+ 'API',
6
+ 'FILE',
7
+ 'REPOSITORY',
8
+ 'FEED',
9
+ 'SOCIAL',
10
+ 'BROWSER',
11
+ 'MANUAL',
12
+ 'OTHER',
13
+ ]);
14
+ export const sourceDefinitionSchema = z.object({
15
+ schemaVersion: schemaVersionSchema,
16
+ sourceId: idSchema,
17
+ projectKey: projectKeySchema.optional(),
18
+ kind: sourceKindSchema,
19
+ authority: z.string().trim().min(1).max(256),
20
+ access: accessClassificationSchema,
21
+ canonicalUri: stableUriSchema.optional(),
22
+ refreshPolicy: z.string().trim().min(1).max(256),
23
+ enabled: z.boolean(),
24
+ tags: z.array(z.string().trim().min(1).max(128)).max(64).default([]),
25
+ }).strict();
26
+ export const collectorDefinitionSchema = z.object({
27
+ schemaVersion: schemaVersionSchema,
28
+ collectorId: idSchema,
29
+ version: z.string().trim().min(1).max(128),
30
+ supportedSourceKinds: z.array(sourceKindSchema).min(1),
31
+ capabilities: z.array(z.string().trim().min(1).max(128)).max(64),
32
+ }).strict();
@@ -0,0 +1,34 @@
1
+ /** Host-authored admission accounting, independent of business authorization and receipt schemas. */
2
+ import { z } from 'zod';
3
+ import { createHash } from 'node:crypto';
4
+ import { materialTenantIdSchema } from './material-context.js';
5
+ import { isoDateTimeSchema } from './common.js';
6
+ import { parseTenantCollectionBatchRef } from './tenant-collection.js';
7
+ import { tenantStorageKey } from './resource-scope.js';
8
+ const positive = (max) => z.number().int().min(1).max(max);
9
+ export const tenantBudgetPolicySchema = z.object({ tenantId: materialTenantIdSchema, maxInFlight: positive(32), maxWorkUnits: positive(1_000_000), maxRuntimeMs: positive(86_400_000), weight: positive(8), paused: z.boolean() }).strict();
10
+ export const tenantBudgetConfigSchema = z.object({ schemaVersion: z.literal(1), windowSeconds: z.number().int().min(60).max(86400), maxInFlight: positive(128), maxWaitRounds: positive(2048), tenants: z.array(tenantBudgetPolicySchema).min(1).max(128) }).strict().superRefine((value, ctx) => {
11
+ if (new Set(value.tenants.map(t => t.tenantId)).size !== value.tenants.length || value.tenants.some(t => t.maxInFlight > value.maxInFlight) || value.tenants.reduce((sum, t) => sum + t.weight, 0) > value.maxWaitRounds)
12
+ ctx.addIssue({ code: 'custom', message: 'Unique Tenants, Host limits and weighted waiting bound are required' });
13
+ });
14
+ export const tenantBudgetCandidateSchema = z.object({ key: z.string().regex(/^[a-f0-9]{64}$/), tenantId: materialTenantIdSchema, workUnits: positive(100_000), runtimeMs: z.number().int().min(1000).max(7_200_000) }).strict();
15
+ export const tenantBudgetReservationSchema = tenantBudgetCandidateSchema.extend({ admissionId: z.string().uuid(), nodeId: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/), startedAt: isoDateTimeSchema, expiresAt: isoDateTimeSchema, windowStart: isoDateTimeSchema }).strict();
16
+ export const tenantBudgetReasonSchema = z.enum(['READY', 'TENANT_UNCONFIGURED', 'PAUSED', 'HOST_IN_FLIGHT', 'TENANT_IN_FLIGHT', 'WORK_BUDGET', 'TIME_BUDGET', 'WORK_ACTIVE']);
17
+ /** 两小时是整个准入的硬上限;短任务保留结算余量,合法的上限任务不因此被排除。 */
18
+ export function tenantLegacyAdmissionRuntimeMs(timeoutSeconds) {
19
+ if (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 30 || timeoutSeconds > 7200)
20
+ throw new Error('TENANT_LEGACY_TIMEOUT_INVALID');
21
+ return Math.min(7_200_000, timeoutSeconds * 1000 + 2000);
22
+ }
23
+ export function tenantCollectionAdmissionKey(tenantId, batchRef) {
24
+ const batch = parseTenantCollectionBatchRef(batchRef);
25
+ if (!batch || batch.storageKey !== tenantStorageKey(tenantId))
26
+ throw new Error('TENANT_BUDGET_WORK_SCOPE_INVALID');
27
+ return createHash('sha256').update(JSON.stringify(['TENANT_COLLECTION_ADMISSION_V1', tenantId, batchRef])).digest('hex');
28
+ }
29
+ export function tenantLegacyAdmissionKey(tenantId, workloadId) {
30
+ materialTenantIdSchema.parse(tenantId);
31
+ if (typeof workloadId !== 'string' || workloadId.length > 256 || !/^project:[A-Za-z0-9][A-Za-z0-9._-]{0,127}:.+$/.test(workloadId))
32
+ throw new Error('TENANT_BUDGET_WORK_SCOPE_INVALID');
33
+ return createHash('sha256').update(JSON.stringify(['TENANT_LEGACY_ADMISSION_V1', tenantId, workloadId])).digest('hex');
34
+ }
@@ -0,0 +1,58 @@
1
+ /** 请求归因与 project-blind 批次分离;只保存委托身份,不保存可使用的 token。 */
2
+ import { createHash } from 'node:crypto';
3
+ import { z } from 'zod';
4
+ import { idSchema, isoDateTimeSchema, stableUriSchema, sha256Schema } from './common.js';
5
+ import { resourceScopeSchema, tenantStorageKey, parseTenantResourceRef, tenantResourceRefSchema } from './resource-scope.js';
6
+ import { materialTenantIdSchema } from './material-context.js';
7
+ import { knowledgeActorContextSchema } from './project-intent.js';
8
+ import { tenantCollectionPlanSchema, tenantCollectionCycleSchema, tenantCollectionBatchIdentity, tenantCapturedEvidenceIdentity } from './tenant-collection.js';
9
+ export const tenantCollectionRequestAuthoritySchema = z.discriminatedUnion('type', [
10
+ z.object({ type: z.literal('HOST') }).strict(),
11
+ z.object({ type: z.literal('OAUTH'), authorityVersion: z.literal(2), clientId: idSchema, grantId: idSchema, resource: stableUriSchema }).strict(),
12
+ ]);
13
+ export function tenantCollectionRequestKey(requestId) { return createHash('sha256').update(tenantCollectionCycleSchema.parse(requestId)).digest('hex'); }
14
+ export function tenantCollectionRequestRef(tenantId, requestId) { return `tenant-collection-request://${tenantStorageKey(tenantId)}/${tenantCollectionRequestKey(requestId)}`; }
15
+ export const tenantCollectionRequestSchema = z.object({
16
+ schemaVersion: z.literal(2), tenantId: materialTenantIdSchema, requestId: tenantCollectionCycleSchema, requestRef: stableUriSchema,
17
+ context: resourceScopeSchema, actor: knowledgeActorContextSchema, authority: tenantCollectionRequestAuthoritySchema,
18
+ plan: tenantCollectionPlanSchema, trigger: z.enum(['MANUAL', 'DUE']), cycleId: tenantCollectionCycleSchema, batchId: z.string().regex(/^batch_[a-f0-9]{64}$/), batchRef: stableUriSchema, requestedAt: isoDateTimeSchema,
19
+ }).strict().superRefine((value, ctx) => {
20
+ const expected = tenantCollectionBatchIdentity(value.tenantId, value.plan.revision, value.trigger, value.cycleId);
21
+ if (value.context.tenantId !== value.tenantId || value.actor.tenantId !== value.tenantId || value.plan.tenantId !== value.tenantId || value.requestRef !== tenantCollectionRequestRef(value.tenantId, value.requestId))
22
+ ctx.addIssue({ code: 'custom', path: ['context'], message: 'Request must retain exact Tenant/context/request identity' });
23
+ if (value.batchId !== expected.batchId || value.batchRef !== expected.batchRef)
24
+ ctx.addIssue({ code: 'custom', path: ['batchRef'], message: 'Request cannot retarget a different frozen batch' });
25
+ if (value.trigger === 'DUE' && value.authority.type !== 'HOST')
26
+ ctx.addIssue({ code: 'custom', path: ['trigger'], message: 'Only an explicit Host dispatcher may request DUE' });
27
+ });
28
+ const completionV1 = z.object({
29
+ schemaVersion: z.literal(1), tenantId: materialTenantIdSchema, requestRef: stableUriSchema, batchRef: stableUriSchema,
30
+ phase: z.enum(['RAW_REGISTERED', 'DERIVED_REGISTERED']), evidenceRef: stableUriSchema, contentHash: sha256Schema,
31
+ runId: idSchema, runtimeNodeId: idSchema, completedAt: isoDateTimeSchema,
32
+ }).strict();
33
+ export const tenantCollectionCompletionSchema = z.union([completionV1, completionV1.extend({ schemaVersion: z.literal(2), phase: z.literal('DERIVED_REGISTERED'), documentRef: tenantResourceRefSchema }).strict()]);
34
+ export function assertTenantCollectionCompletion(request, raw) {
35
+ const completion = tenantCollectionCompletionSchema.parse(raw), expected = tenantCapturedEvidenceIdentity({ tenantId: request.tenantId, batchRef: request.batchRef, sourceRevisionRef: request.plan.source.revisionRef });
36
+ const expectedPhase = request.plan.source.semantics.representation.format === 'RAW' ? 'RAW_REGISTERED' : 'DERIVED_REGISTERED';
37
+ if (completion.tenantId !== request.tenantId || completion.requestRef !== request.requestRef || completion.batchRef !== request.batchRef || completion.evidenceRef !== expected.ref || completion.phase !== expectedPhase)
38
+ throw new Error('TENANT_COLLECTION_COMPLETION_MISMATCH');
39
+ if (expectedPhase === 'DERIVED_REGISTERED') {
40
+ const document = completion.schemaVersion === 2 ? parseTenantResourceRef(completion.documentRef) : null;
41
+ if (!document || document.storageKey !== tenantStorageKey(request.tenantId) || document.resourceType !== 'source_document')
42
+ throw new Error('TENANT_COLLECTION_DERIVATION_UNPROVEN');
43
+ }
44
+ else if (completion.schemaVersion !== 1)
45
+ throw new Error('TENANT_COLLECTION_COMPLETION_MISMATCH');
46
+ return completion;
47
+ }
48
+ /** Admission retirement is not acquisition failure or business completion. */
49
+ export const TENANT_REQUEST_ADMISSION_STOP_CAPABILITY = 'TENANT_REQUEST_ADMISSION_STOP_V1';
50
+ export const tenantCollectionAdmissionStopSchema = z.object({ schemaVersion: z.literal(1), tenantId: materialTenantIdSchema, requestRef: stableUriSchema, requestHash: sha256Schema, reason: z.enum(['GRANT_REVOKED', 'DELEGATION_SUPERSEDED']), stoppedAt: isoDateTimeSchema }).strict();
51
+ /** Acquisition outcome only: a failed request is neither completed nor delegation-retired. */
52
+ export const tenantCollectionFailureSchema = z.object({
53
+ schemaVersion: z.literal(1), tenantId: materialTenantIdSchema, requestRef: stableUriSchema, requestHash: sha256Schema, batchRef: stableUriSchema,
54
+ runId: idSchema, runtimeNodeId: idSchema, receiptHash: sha256Schema, failedAt: isoDateTimeSchema, errorCode: z.literal('TENANT_SOURCE_HTTP_TERMINAL'),
55
+ }).strict();
56
+ export function isTenantCollectionTerminalFailure(request, receipt) {
57
+ return receipt.status === 'FAILED' && receipt.errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' && receipt.workloadId === request.plan.workloadId && receipt.collectionBatchRef === request.batchRef && receipt.inputFingerprint === request.plan.revision && receipt.projectRef === undefined && receipt.projectIntentRef === undefined && receipt.evidenceCount === 0 && receipt.observationCount === 0 && Date.parse(receipt.finishedAt) >= Date.parse(receipt.startedAt) && request.plan.source.semantics.target.kind === 'URL' && request.plan.source.semantics.credential.type === 'PUBLIC';
58
+ }
@@ -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' }); });