@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.
- package/LICENSE +202 -21
- package/NOTICE +29 -0
- package/README.md +26 -38
- package/dist/{types/sdk/agent-entry.d.ts → agent-entry.d.ts} +3 -3
- package/dist/agent-entry.js +153 -0
- package/dist/{types/sdk/agent-guidance-error.d.ts → agent-guidance-error.d.ts} +1 -1
- package/dist/agent-guidance-error.js +46 -0
- package/dist/auth.d.ts +124 -0
- package/dist/auth.js +165 -0
- package/dist/{types/sdk/index.d.ts → index.d.ts} +15 -14
- package/dist/index.js +159 -6570
- package/dist/internal/contracts/artifact.js +103 -0
- package/dist/internal/contracts/collection-control.js +69 -0
- package/dist/internal/contracts/collection.js +174 -0
- package/dist/internal/contracts/common.js +16 -0
- package/dist/internal/contracts/currentness.js +221 -0
- package/dist/internal/contracts/event.js +35 -0
- package/dist/internal/contracts/evidence.js +171 -0
- package/dist/{types → internal}/contracts/index.d.ts +0 -1
- package/dist/internal/contracts/index.js +54 -0
- package/dist/internal/contracts/material-capability.js +8 -0
- package/dist/internal/contracts/material-carrier.js +15 -0
- package/dist/internal/contracts/material-context.js +9 -0
- package/dist/internal/contracts/material-read.js +12 -0
- package/dist/internal/contracts/observation.js +39 -0
- package/dist/internal/contracts/persistence.js +239 -0
- package/dist/internal/contracts/project-intent.js +121 -0
- package/dist/internal/contracts/projection.js +194 -0
- package/dist/internal/contracts/public-reference.js +87 -0
- package/dist/internal/contracts/reference-record.js +55 -0
- package/dist/internal/contracts/remote-file.js +159 -0
- package/dist/internal/contracts/resource-scope.js +38 -0
- package/dist/internal/contracts/review-packet.js +61 -0
- package/dist/internal/contracts/review-plane.js +89 -0
- package/dist/internal/contracts/rich-document.js +184 -0
- package/dist/internal/contracts/runtime-compatibility.js +55 -0
- package/dist/internal/contracts/runtime.js +68 -0
- package/dist/internal/contracts/scheduler.js +256 -0
- package/dist/internal/contracts/signal.js +77 -0
- package/dist/internal/contracts/source-governance.js +194 -0
- package/dist/internal/contracts/source.js +32 -0
- package/dist/internal/contracts/tenant-budget.js +34 -0
- package/dist/internal/contracts/tenant-collection-request.js +58 -0
- package/dist/internal/contracts/tenant-collection.js +66 -0
- package/dist/internal/contracts/tenant-currentness.js +26 -0
- package/dist/internal/contracts/tenant-management.js +27 -0
- package/dist/internal/contracts/tenant-material-api.js +46 -0
- package/dist/internal/contracts/tenant-material-json-schema.js +16 -0
- package/dist/internal/contracts/tenant-pdf-text.js +13 -0
- package/dist/internal/contracts/tenant-project-control.js +19 -0
- package/dist/internal/contracts/tenant-public-reference.js +26 -0
- package/dist/internal/contracts/tenant-resource-api.js +70 -0
- package/dist/internal/contracts/tenant-resource.js +118 -0
- package/dist/internal/contracts/tenant-rich-document.js +17 -0
- package/dist/internal/contracts/tenant-source-binding.js +33 -0
- package/dist/internal/contracts/tenant-source-bridge.js +9 -0
- package/dist/internal/contracts/tenant-source-cutover.js +26 -0
- package/dist/internal/contracts/tenant-source-document.js +38 -0
- package/dist/internal/contracts/tenant-source-lease.js +5 -0
- package/dist/internal/contracts/tenant-source.js +83 -0
- package/dist/internal/contracts/tenant-web-collection.js +118 -0
- package/dist/internal/contracts/tenant-web-content-plan.js +39 -0
- package/dist/internal/contracts/tenant-web-content-result.js +15 -0
- package/dist/internal/contracts/tenant-web-content.js +16 -0
- package/dist/internal/contracts/tenant-web-crawl.js +55 -0
- package/dist/internal/contracts/tenant-web-dynamic.js +68 -0
- package/dist/internal/contracts/tenant-web-results.js +41 -0
- package/dist/internal/contracts/tenant-web-workflow.js +29 -0
- package/dist/internal/serving-contracts/agent-catalog.js +56 -0
- package/dist/internal/serving-contracts/agent-entry.js +57 -0
- package/dist/internal/serving-contracts/agent-guidance-shapes.js +12 -0
- package/dist/internal/serving-contracts/agent-input-hints.js +104 -0
- package/dist/internal/serving-contracts/body-search.js +16 -0
- package/dist/internal/serving-contracts/consumer.js +45 -0
- package/dist/internal/serving-contracts/core.js +76 -0
- package/dist/internal/serving-contracts/http.js +64 -0
- package/dist/{types → internal}/serving-contracts/index.d.ts +1 -0
- package/dist/internal/serving-contracts/index.js +25 -0
- package/dist/internal/serving-contracts/management.js +15 -0
- package/dist/internal/serving-contracts/material-capabilities.js +14 -0
- package/dist/internal/serving-contracts/material-index-status.js +5 -0
- package/dist/internal/serving-contracts/mcp.js +140 -0
- package/dist/internal/serving-contracts/tenant-governance.js +13 -0
- package/dist/internal/serving-contracts/tenant-management.js +89 -0
- package/dist/internal/serving-contracts/tenant-project.js +39 -0
- package/dist/internal/serving-contracts/tenant-runtime.js +37 -0
- package/dist/internal/serving-contracts/tenant-session.d.ts +87 -0
- package/dist/internal/serving-contracts/tenant-session.js +8 -0
- package/dist/internal/serving-contracts/tenant-web-content.js +20 -0
- package/dist/internal/serving-contracts/tenant-web-crawl.js +13 -0
- package/dist/internal/serving-contracts/tenant-web-dynamic.js +20 -0
- package/dist/internal/serving-contracts/tenant-web-workflow.js +36 -0
- package/dist/internal/serving-contracts/tenant-web.js +53 -0
- package/dist/internal/serving-contracts/tenant-workspace.js +28 -0
- package/dist/internal/serving-contracts/workflow.js +37 -0
- package/dist/material-stream.js +29 -0
- package/dist/{types/sdk/oauth.d.ts → oauth.d.ts} +18 -18
- package/dist/oauth.js +539 -0
- package/dist/{types/sdk/resource.d.ts → resource.d.ts} +5 -5
- package/dist/resource.js +123 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +3 -0
- package/dist/tenant-client.d.ts +169 -0
- package/dist/tenant-client.js +58 -0
- package/dist/{types/sdk/tenant-management.d.ts → tenant-management.d.ts} +6 -6
- package/dist/tenant-management.js +149 -0
- package/dist/{types/sdk/tenant-project.d.ts → tenant-project.d.ts} +1 -1
- package/dist/tenant-project.js +63 -0
- package/dist/{types/sdk/tenant-runtime.d.ts → tenant-runtime.d.ts} +1 -1
- package/dist/tenant-runtime.js +17 -0
- package/dist/{types/sdk/tenant-web-content.d.ts → tenant-web-content.d.ts} +1 -1
- package/dist/tenant-web-content.js +70 -0
- package/dist/{types/sdk/tenant-web-crawl.d.ts → tenant-web-crawl.d.ts} +1 -1
- package/dist/tenant-web-crawl.js +39 -0
- package/dist/{types/sdk/tenant-web-dynamic.d.ts → tenant-web-dynamic.d.ts} +1 -1
- package/dist/tenant-web-dynamic.js +39 -0
- package/dist/{types/sdk/tenant-web-workflow.d.ts → tenant-web-workflow.d.ts} +1 -1
- package/dist/tenant-web-workflow.js +63 -0
- package/dist/{types/sdk/tenant-web.d.ts → tenant-web.d.ts} +1 -1
- package/dist/tenant-web.js +39 -0
- package/dist/{types/sdk/tenant-workspace.d.ts → tenant-workspace.d.ts} +1 -1
- package/dist/tenant-workspace.js +21 -0
- package/dist/token-provider.d.ts +14 -0
- package/dist/token-provider.js +21 -0
- package/package.json +27 -15
- package/dist/types/contracts/resource-transfer.d.ts +0 -24
- package/dist/types/sdk/resource-integrity.d.ts +0 -21
- package/dist/types/sdk/transport.d.ts +0 -16
- /package/dist/{types → internal}/contracts/artifact.d.ts +0 -0
- /package/dist/{types → internal}/contracts/collection-control.d.ts +0 -0
- /package/dist/{types → internal}/contracts/collection.d.ts +0 -0
- /package/dist/{types → internal}/contracts/common.d.ts +0 -0
- /package/dist/{types → internal}/contracts/currentness.d.ts +0 -0
- /package/dist/{types → internal}/contracts/event.d.ts +0 -0
- /package/dist/{types → internal}/contracts/evidence.d.ts +0 -0
- /package/dist/{types → internal}/contracts/material-capability.d.ts +0 -0
- /package/dist/{types → internal}/contracts/material-carrier.d.ts +0 -0
- /package/dist/{types → internal}/contracts/material-context.d.ts +0 -0
- /package/dist/{types → internal}/contracts/material-read.d.ts +0 -0
- /package/dist/{types → internal}/contracts/observation.d.ts +0 -0
- /package/dist/{types → internal}/contracts/persistence.d.ts +0 -0
- /package/dist/{types → internal}/contracts/project-intent.d.ts +0 -0
- /package/dist/{types → internal}/contracts/projection.d.ts +0 -0
- /package/dist/{types → internal}/contracts/public-reference.d.ts +0 -0
- /package/dist/{types → internal}/contracts/reference-record.d.ts +0 -0
- /package/dist/{types → internal}/contracts/remote-file.d.ts +0 -0
- /package/dist/{types → internal}/contracts/resource-scope.d.ts +0 -0
- /package/dist/{types → internal}/contracts/review-packet.d.ts +0 -0
- /package/dist/{types → internal}/contracts/review-plane.d.ts +0 -0
- /package/dist/{types → internal}/contracts/rich-document.d.ts +0 -0
- /package/dist/{types → internal}/contracts/runtime-compatibility.d.ts +0 -0
- /package/dist/{types → internal}/contracts/runtime.d.ts +0 -0
- /package/dist/{types → internal}/contracts/scheduler.d.ts +0 -0
- /package/dist/{types → internal}/contracts/signal.d.ts +0 -0
- /package/dist/{types → internal}/contracts/source-governance.d.ts +0 -0
- /package/dist/{types → internal}/contracts/source.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-budget.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-collection-request.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-collection.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-currentness.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-management.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-material-api.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-pdf-text.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-project-control.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-public-reference.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-resource-api.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-resource.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-rich-document.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-source-binding.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-source-bridge.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-source-cutover.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-source-document.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-source-lease.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-source.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-collection.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-content-plan.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-content-result.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-content.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-crawl.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-dynamic.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-results.d.ts +0 -0
- /package/dist/{types → internal}/contracts/tenant-web-workflow.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/agent-catalog.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/agent-entry.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/agent-guidance-shapes.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/agent-input-hints.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/body-search.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/consumer.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/core.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/http.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/management.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/material-capabilities.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/material-index-status.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/mcp.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-governance.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-management.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-project.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-runtime.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-web-content.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-web-crawl.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-web-dynamic.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-web-workflow.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-web.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/tenant-workspace.d.ts +0 -0
- /package/dist/{types → internal}/serving-contracts/workflow.d.ts +0 -0
- /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();
|