@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,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
|
|
3
|
+
import { currentnessAssessmentSchema } from './currentness.js';
|
|
4
|
+
import { evidenceReviewAttachmentSchema } from './evidence.js';
|
|
5
|
+
export const reviewPacketEvidenceSchema = z.object({
|
|
6
|
+
evidenceRef: stableUriSchema,
|
|
7
|
+
evidenceId: idSchema,
|
|
8
|
+
sourceId: idSchema,
|
|
9
|
+
sourceIds: z.array(idSchema).max(64).default([]),
|
|
10
|
+
projectRef: stableUriSchema.optional(),
|
|
11
|
+
sourceRevisionRef: stableUriSchema.optional(),
|
|
12
|
+
collectionBatchRef: stableUriSchema.optional(),
|
|
13
|
+
collectionTaskId: idSchema.optional(),
|
|
14
|
+
canonicalUri: stableUriSchema.optional(),
|
|
15
|
+
observedAt: isoDateTimeSchema,
|
|
16
|
+
access: accessClassificationSchema,
|
|
17
|
+
contentHash: sha256Schema,
|
|
18
|
+
mediaType: z.string().trim().min(1).max(256).optional(),
|
|
19
|
+
byteLength: z.number().int().nonnegative().optional(),
|
|
20
|
+
currentness: currentnessAssessmentSchema.optional(),
|
|
21
|
+
attachment: evidenceReviewAttachmentSchema,
|
|
22
|
+
}).strict().superRefine((value, ctx) => {
|
|
23
|
+
if (value.attachment.evidenceRef !== value.evidenceRef)
|
|
24
|
+
ctx.addIssue({ code: 'custom', path: ['attachment', 'evidenceRef'], message: 'attachment evidenceRef must match evidence row' });
|
|
25
|
+
if (value.attachment.contentHash !== value.contentHash)
|
|
26
|
+
ctx.addIssue({ code: 'custom', path: ['attachment', 'contentHash'], message: 'attachment contentHash must match evidence row' });
|
|
27
|
+
if (value.mediaType && value.attachment.mediaType !== value.mediaType.toLowerCase())
|
|
28
|
+
ctx.addIssue({ code: 'custom', path: ['attachment', 'mediaType'], message: 'attachment mediaType must match evidence row' });
|
|
29
|
+
if (value.byteLength !== undefined && value.attachment.byteLength !== value.byteLength)
|
|
30
|
+
ctx.addIssue({ code: 'custom', path: ['attachment', 'byteLength'], message: 'attachment byteLength must match evidence row' });
|
|
31
|
+
});
|
|
32
|
+
export const reviewPacketSchema = z.object({
|
|
33
|
+
schemaVersion: schemaVersionSchema,
|
|
34
|
+
packetType: z.literal('KNOWLEDGE_REVIEW_PACKET'),
|
|
35
|
+
projectKey: projectKeySchema,
|
|
36
|
+
projectRef: stableUriSchema.optional(),
|
|
37
|
+
sourceEvent: z.object({
|
|
38
|
+
eventId: idSchema,
|
|
39
|
+
type: z.enum(['SOURCE_CHANGED', 'BOUNDARY_RISK', 'CURRENTNESS_RISK']),
|
|
40
|
+
createdAt: isoDateTimeSchema,
|
|
41
|
+
runtimeNodeId: idSchema.optional(),
|
|
42
|
+
sourceRef: stableUriSchema.optional(),
|
|
43
|
+
sourceRevisionRef: stableUriSchema.optional(),
|
|
44
|
+
collectionBatchRef: stableUriSchema.optional(),
|
|
45
|
+
contentHash: sha256Schema.optional(),
|
|
46
|
+
}).strict(),
|
|
47
|
+
evidence: z.array(reviewPacketEvidenceSchema).max(256),
|
|
48
|
+
reviewPolicy: z.object({
|
|
49
|
+
semanticSynthesisRequired: z.literal(true),
|
|
50
|
+
rawChunkApprovalForbidden: z.literal(true),
|
|
51
|
+
highRiskAutoApprovalForbidden: z.literal(true),
|
|
52
|
+
decisions: z.tuple([z.literal('PASS'), z.literal('REWORK'), z.literal('REJECT')]),
|
|
53
|
+
}).strict(),
|
|
54
|
+
}).strict().superRefine((value, ctx) => {
|
|
55
|
+
if (value.projectRef)
|
|
56
|
+
for (let i = 0; i < value.evidence.length; i += 1) {
|
|
57
|
+
const row = value.evidence[i];
|
|
58
|
+
if (row.projectRef && row.projectRef !== value.projectRef)
|
|
59
|
+
ctx.addIssue({ code: 'custom', path: ['evidence', i, 'projectRef'], message: 'Evidence projectRef must match packet projectRef' });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { accessClassificationSchema, idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
|
|
3
|
+
import { artifactKindSchema } from './artifact.js';
|
|
4
|
+
import { driveRelativePathSchema } from './projection.js';
|
|
5
|
+
export const reviewPlaneStageSchema = z.enum([
|
|
6
|
+
'PROGRAM',
|
|
7
|
+
'INBOX',
|
|
8
|
+
'REVIEWED',
|
|
9
|
+
'USER_APPROVED',
|
|
10
|
+
'PROMOTION_RECEIPTS',
|
|
11
|
+
'GUIDES',
|
|
12
|
+
'REJECTED_SUPERSEDED',
|
|
13
|
+
]);
|
|
14
|
+
export const reviewPlaneWorkspaceSchema = z.object({
|
|
15
|
+
schemaVersion: schemaVersionSchema,
|
|
16
|
+
projectKey: projectKeySchema,
|
|
17
|
+
provider: z.literal('GOOGLE_DRIVE'),
|
|
18
|
+
rootPath: driveRelativePathSchema,
|
|
19
|
+
folders: z.object({
|
|
20
|
+
PROGRAM: driveRelativePathSchema,
|
|
21
|
+
INBOX: driveRelativePathSchema,
|
|
22
|
+
REVIEWED: driveRelativePathSchema,
|
|
23
|
+
USER_APPROVED: driveRelativePathSchema,
|
|
24
|
+
PROMOTION_RECEIPTS: driveRelativePathSchema,
|
|
25
|
+
GUIDES: driveRelativePathSchema.optional(),
|
|
26
|
+
REJECTED_SUPERSEDED: driveRelativePathSchema,
|
|
27
|
+
}).strict(),
|
|
28
|
+
}).strict().superRefine((value, ctx) => {
|
|
29
|
+
const seen = new Set();
|
|
30
|
+
for (const [key, folder] of Object.entries(value.folders)) {
|
|
31
|
+
if (folder === undefined)
|
|
32
|
+
continue;
|
|
33
|
+
if (seen.has(folder))
|
|
34
|
+
ctx.addIssue({ code: 'custom', path: ['folders', key], message: 'review-plane folders must be unique' });
|
|
35
|
+
seen.add(folder);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
export const reviewPlaneApprovalSchema = z.object({
|
|
39
|
+
schemaVersion: schemaVersionSchema,
|
|
40
|
+
approvalId: idSchema,
|
|
41
|
+
projectKey: projectKeySchema,
|
|
42
|
+
sourceProvider: z.literal('GOOGLE_DRIVE'),
|
|
43
|
+
sourcePath: driveRelativePathSchema,
|
|
44
|
+
sourceFileId: z.string().trim().min(1).max(512).nullable(),
|
|
45
|
+
sourceContentHash: sha256Schema,
|
|
46
|
+
sourceArtifactRef: stableUriSchema.nullable(),
|
|
47
|
+
reviewedAt: isoDateTimeSchema,
|
|
48
|
+
approvedAt: isoDateTimeSchema,
|
|
49
|
+
decision: z.literal('PASS'),
|
|
50
|
+
approvalAuthority: z.literal('EXPLICIT_HUMAN'),
|
|
51
|
+
hostDisposition: z.enum(['APPROVED_RESEARCH', 'APPROVED_KNOWLEDGE', 'APPROVED_INSIGHT']),
|
|
52
|
+
targetArtifactId: idSchema,
|
|
53
|
+
targetVersion: z.number().int().positive(),
|
|
54
|
+
targetTitle: z.string().trim().min(1).max(500),
|
|
55
|
+
mediaType: z.string().trim().min(1).max(256),
|
|
56
|
+
observedAt: isoDateTimeSchema.nullable(),
|
|
57
|
+
validUntil: isoDateTimeSchema.nullable(),
|
|
58
|
+
access: accessClassificationSchema,
|
|
59
|
+
evidenceRefs: z.array(stableUriSchema).max(256),
|
|
60
|
+
approvalEvidenceRefs: z.array(stableUriSchema).max(256),
|
|
61
|
+
supersedesRef: stableUriSchema.nullable(),
|
|
62
|
+
}).strict().superRefine((value, ctx) => {
|
|
63
|
+
if (Date.parse(value.approvedAt) < Date.parse(value.reviewedAt))
|
|
64
|
+
ctx.addIssue({ code: 'custom', path: ['approvedAt'], message: 'approvedAt must not precede reviewedAt' });
|
|
65
|
+
if (value.validUntil && value.observedAt && Date.parse(value.validUntil) < Date.parse(value.observedAt))
|
|
66
|
+
ctx.addIssue({ code: 'custom', path: ['validUntil'], message: 'validUntil must not precede observedAt' });
|
|
67
|
+
if (value.targetVersion === 1 && value.supersedesRef !== null)
|
|
68
|
+
ctx.addIssue({ code: 'custom', path: ['supersedesRef'], message: 'version 1 cannot supersede' });
|
|
69
|
+
if (value.targetVersion > 1 && value.supersedesRef === null)
|
|
70
|
+
ctx.addIssue({ code: 'custom', path: ['supersedesRef'], message: 'later version requires exact predecessor ref' });
|
|
71
|
+
});
|
|
72
|
+
export const reviewPlanePromotionReceiptSchema = z.object({
|
|
73
|
+
schemaVersion: schemaVersionSchema,
|
|
74
|
+
projectKey: projectKeySchema,
|
|
75
|
+
approvalId: idSchema,
|
|
76
|
+
sourcePath: driveRelativePathSchema,
|
|
77
|
+
sourceFileId: z.string().trim().min(1).max(512).nullable(),
|
|
78
|
+
sourceContentHash: sha256Schema,
|
|
79
|
+
promotedAt: isoDateTimeSchema,
|
|
80
|
+
targetKind: artifactKindSchema,
|
|
81
|
+
draftRef: stableUriSchema,
|
|
82
|
+
readyRef: stableUriSchema,
|
|
83
|
+
reviewRef: stableUriSchema,
|
|
84
|
+
approvedRef: stableUriSchema,
|
|
85
|
+
approvedContentHash: sha256Schema,
|
|
86
|
+
exactBytesVerified: z.literal(true),
|
|
87
|
+
humanApprovalVerified: z.literal(true),
|
|
88
|
+
automaticPromotion: z.literal(false),
|
|
89
|
+
}).strict();
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { idSchema, isoDateTimeSchema, projectKeySchema, schemaVersionSchema, sha256Schema, stableUriSchema } from './common.js';
|
|
3
|
+
export const RICH_DOCUMENT_MEDIA_TYPE = 'application/vnd.knowledge-v2.rich-document+json';
|
|
4
|
+
export const richDocumentRoleSchema = z.enum(['SOURCE_MIRROR', 'KNOWLEDGE', 'INSIGHT']);
|
|
5
|
+
export const publicationProfileSchema = z.enum(['HUMAN_ARTICLE_V1', 'KNOWLEDGE_CARD_V1']);
|
|
6
|
+
export const publicationReadinessReasonSchema = z.enum(['ROLE_MISMATCH', 'TITLE_REQUIRED', 'SUMMARY_REQUIRED', 'TEXT_TOO_SHORT', 'STRUCTURE_TOO_THIN', 'SOURCE_EVIDENCE_REQUIRED', 'CITATION_COVERAGE_REQUIRED', 'EVIDENCE_MISMATCH', 'MEDIA_UNAVAILABLE']);
|
|
7
|
+
export const richDocumentMediaAvailabilitySchema = z.enum(['MIRRORED', 'REMOTE_ONLY', 'UNAVAILABLE']);
|
|
8
|
+
const ordinalSchema = z.number().int().nonnegative().max(100_000);
|
|
9
|
+
const humanText = (max) => z.string().max(max);
|
|
10
|
+
const httpUrlSchema = z.string().url().max(2048).superRefine((value, ctx) => { const protocol = new URL(value).protocol; if (!['http:', 'https:'].includes(protocol))
|
|
11
|
+
ctx.addIssue({ code: 'custom', message: 'rich document media/source URL must use HTTP(S)' }); });
|
|
12
|
+
const richInlineSchema = z.object({ text: humanText(64_000), href: httpUrlSchema.nullable() }).strict();
|
|
13
|
+
const inlines = z.array(richInlineSchema).max(10_000).optional();
|
|
14
|
+
const textBlockBase = { ordinal: ordinalSchema };
|
|
15
|
+
export const richHeadingBlockSchema = z.object({ ...textBlockBase, kind: z.literal('HEADING'), level: z.number().int().min(1).max(6), text: humanText(16_000), inlines }).strict();
|
|
16
|
+
export const richParagraphBlockSchema = z.object({ ...textBlockBase, kind: z.literal('PARAGRAPH'), text: humanText(64_000), inlines }).strict();
|
|
17
|
+
export const richListItemBlockSchema = z.object({ ...textBlockBase, kind: z.literal('LIST_ITEM'), text: humanText(32_000), ordered: z.boolean(), level: z.number().int().min(0).max(16), index: z.number().int().positive().nullable(), inlines }).strict();
|
|
18
|
+
export const richQuoteBlockSchema = z.object({ ...textBlockBase, kind: z.literal('QUOTE'), text: humanText(64_000), inlines }).strict();
|
|
19
|
+
export const richCodeBlockSchema = z.object({ ...textBlockBase, kind: z.literal('CODE'), text: humanText(128_000), language: z.string().trim().min(1).max(128).nullable() }).strict();
|
|
20
|
+
export const richDividerBlockSchema = z.object({ ...textBlockBase, kind: z.literal('DIVIDER') }).strict();
|
|
21
|
+
export const richTableBlockSchema = z.object({ ...textBlockBase, kind: z.literal('TABLE'), rows: z.array(z.object({ cells: z.array(z.object({ text: humanText(16_000), header: z.boolean() }).strict()).max(128) }).strict()).min(1).max(1000) }).strict();
|
|
22
|
+
const mediaBase = {
|
|
23
|
+
ordinal: ordinalSchema,
|
|
24
|
+
sourceUrl: httpUrlSchema,
|
|
25
|
+
canonicalUrl: httpUrlSchema.nullable(),
|
|
26
|
+
availability: richDocumentMediaAvailabilitySchema,
|
|
27
|
+
mediaRef: stableUriSchema.nullable(),
|
|
28
|
+
evidenceRef: stableUriSchema.nullable(),
|
|
29
|
+
contentHash: sha256Schema.nullable(),
|
|
30
|
+
mediaType: z.string().trim().min(1).max(256).nullable(),
|
|
31
|
+
byteLength: z.number().int().nonnegative().nullable(),
|
|
32
|
+
caption: humanText(16_000).nullable(),
|
|
33
|
+
};
|
|
34
|
+
function mediaConsistency(value, ctx) {
|
|
35
|
+
const mirrored = Boolean(value.mediaRef && value.evidenceRef && value.contentHash && value.mediaType && value.byteLength !== null);
|
|
36
|
+
if (value.availability === 'MIRRORED' && !mirrored)
|
|
37
|
+
ctx.addIssue({ code: 'custom', message: 'MIRRORED media requires EvidenceRef, hash, mediaType and byteLength' });
|
|
38
|
+
if (value.availability !== 'MIRRORED' && (value.mediaRef || value.evidenceRef || value.contentHash || value.mediaType || value.byteLength !== null))
|
|
39
|
+
ctx.addIssue({ code: 'custom', message: 'non-MIRRORED media cannot claim mirrored Evidence' });
|
|
40
|
+
}
|
|
41
|
+
export const richImageBlockSchema = z.object({ ...mediaBase, kind: z.literal('IMAGE'), alt: humanText(4000).nullable(), width: z.number().int().positive().max(100_000).nullable(), height: z.number().int().positive().max(100_000).nullable() }).strict().superRefine(mediaConsistency);
|
|
42
|
+
export const richVideoBlockSchema = z.object({ ...mediaBase, kind: z.literal('VIDEO'), title: humanText(4000).nullable(), posterUrl: httpUrlSchema.nullable(), posterEvidenceRef: stableUriSchema.nullable() }).strict().superRefine(mediaConsistency);
|
|
43
|
+
export const richAudioBlockSchema = z.object({ ...mediaBase, kind: z.literal('AUDIO'), title: humanText(4000).nullable() }).strict().superRefine(mediaConsistency);
|
|
44
|
+
export const richEmbedBlockSchema = z.object({ ...textBlockBase, kind: z.literal('EMBED'), sourceUrl: httpUrlSchema, provider: z.string().trim().min(1).max(128).nullable(), title: humanText(4000).nullable(), caption: humanText(16_000).nullable() }).strict();
|
|
45
|
+
export const richDocumentBlockSchema = z.discriminatedUnion('kind', [
|
|
46
|
+
richHeadingBlockSchema, richParagraphBlockSchema, richListItemBlockSchema, richQuoteBlockSchema, richCodeBlockSchema,
|
|
47
|
+
richImageBlockSchema, richVideoBlockSchema, richAudioBlockSchema, richEmbedBlockSchema, richDividerBlockSchema, richTableBlockSchema,
|
|
48
|
+
]);
|
|
49
|
+
export const richDocumentCitationSchema = z.object({
|
|
50
|
+
blockOrdinal: ordinalSchema,
|
|
51
|
+
evidenceRefs: z.array(stableUriSchema).min(1).max(32),
|
|
52
|
+
}).strict().superRefine((value, ctx) => { if (new Set(value.evidenceRefs).size !== value.evidenceRefs.length)
|
|
53
|
+
ctx.addIssue({ code: 'custom', path: ['evidenceRefs'], message: 'citation EvidenceRefs must be unique' }); });
|
|
54
|
+
export const richCompositionDraftSchema = z.object({
|
|
55
|
+
summary: humanText(4000),
|
|
56
|
+
byline: humanText(1000).nullable().optional(),
|
|
57
|
+
language: z.string().trim().min(1).max(64).nullable().optional(),
|
|
58
|
+
blocks: z.array(richDocumentBlockSchema).min(1).max(10_000),
|
|
59
|
+
citations: z.array(richDocumentCitationSchema).min(1).max(10_000),
|
|
60
|
+
}).strict();
|
|
61
|
+
export function validateRichDocumentContent(value, ctx, names = { primary: 'primarySourceEvidenceRef', citation: 'evidenceRefs' }) {
|
|
62
|
+
if (!value.sourceEvidenceRefs.includes(value.primarySourceEvidenceRef))
|
|
63
|
+
ctx.addIssue({ code: 'custom', path: [names.primary], message: 'primary source must be included in sourceEvidenceRefs' });
|
|
64
|
+
for (let index = 0; index < value.blocks.length; index += 1)
|
|
65
|
+
if (value.blocks[index].ordinal !== index)
|
|
66
|
+
ctx.addIssue({ code: 'custom', path: ['blocks', index, 'ordinal'], message: 'block ordinals must be contiguous and equal array position' });
|
|
67
|
+
for (let index = 0; index < value.blocks.length; index += 1) {
|
|
68
|
+
const block = value.blocks[index];
|
|
69
|
+
if ('inlines' in block && block.inlines && block.inlines.map(part => part.text).join('') !== block.text)
|
|
70
|
+
ctx.addIssue({ code: 'custom', path: ['blocks', index, 'inlines'], message: 'inline text must equal block text' });
|
|
71
|
+
}
|
|
72
|
+
const claimed = new Set(value.mediaEvidenceRefs);
|
|
73
|
+
if (claimed.size !== value.mediaEvidenceRefs.length)
|
|
74
|
+
ctx.addIssue({ code: 'custom', path: ['mediaEvidenceRefs'], message: 'mediaEvidenceRefs must be unique' });
|
|
75
|
+
for (let index = 0; index < value.blocks.length; index += 1) {
|
|
76
|
+
const block = value.blocks[index];
|
|
77
|
+
if (['IMAGE', 'VIDEO', 'AUDIO'].includes(block.kind)) {
|
|
78
|
+
const media = block;
|
|
79
|
+
if (media.evidenceRef && !claimed.has(media.evidenceRef))
|
|
80
|
+
ctx.addIssue({ code: 'custom', path: ['blocks', index, 'evidenceRef'], message: 'mirrored media EvidenceRef must be listed in mediaEvidenceRefs' });
|
|
81
|
+
if (media.kind === 'VIDEO' && media.posterEvidenceRef && !claimed.has(media.posterEvidenceRef))
|
|
82
|
+
ctx.addIssue({ code: 'custom', path: ['blocks', index, 'posterEvidenceRef'], message: 'video poster EvidenceRef must be listed in mediaEvidenceRefs' });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const citationOrdinals = new Set(), allowedCitationRefs = new Set([...value.sourceEvidenceRefs, ...value.mediaEvidenceRefs]);
|
|
86
|
+
for (let index = 0; index < value.citations.length; index += 1) {
|
|
87
|
+
const citation = value.citations[index];
|
|
88
|
+
if (citation.blockOrdinal >= value.blocks.length)
|
|
89
|
+
ctx.addIssue({ code: 'custom', path: ['citations', index, 'blockOrdinal'], message: 'citation block ordinal must identify an existing block' });
|
|
90
|
+
if (citationOrdinals.has(citation.blockOrdinal))
|
|
91
|
+
ctx.addIssue({ code: 'custom', path: ['citations', index, 'blockOrdinal'], message: 'only one citation record is allowed per block' });
|
|
92
|
+
citationOrdinals.add(citation.blockOrdinal);
|
|
93
|
+
for (const ref of citation.evidenceRefs)
|
|
94
|
+
if (!allowedCitationRefs.has(ref))
|
|
95
|
+
ctx.addIssue({ code: 'custom', path: ['citations', index, names.citation], message: 'citation EvidenceRef must be a source or media EvidenceRef' });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export const richDocumentSchema = z.object({
|
|
99
|
+
schemaVersion: schemaVersionSchema,
|
|
100
|
+
format: z.literal('RICH_DOCUMENT_V1'),
|
|
101
|
+
role: richDocumentRoleSchema,
|
|
102
|
+
publicationProfile: publicationProfileSchema.nullable().default(null),
|
|
103
|
+
projectKey: projectKeySchema,
|
|
104
|
+
projectRef: stableUriSchema,
|
|
105
|
+
sourceEvidenceRefs: z.array(stableUriSchema).min(1).max(256),
|
|
106
|
+
primarySourceEvidenceRef: stableUriSchema,
|
|
107
|
+
sourceContentHash: sha256Schema,
|
|
108
|
+
sourceUri: stableUriSchema,
|
|
109
|
+
sourceMediaType: z.string().trim().min(1).max(256),
|
|
110
|
+
sourceByteLength: z.number().int().nonnegative(),
|
|
111
|
+
title: humanText(1000).nullable(),
|
|
112
|
+
byline: humanText(1000).nullable(),
|
|
113
|
+
publishedAt: isoDateTimeSchema.nullable(),
|
|
114
|
+
language: z.string().trim().min(1).max(64).nullable(),
|
|
115
|
+
topics: z.array(z.string().trim().min(1).max(160)).max(32).optional(),
|
|
116
|
+
summary: humanText(4000).nullable(),
|
|
117
|
+
textContent: humanText(2_000_000),
|
|
118
|
+
blocks: z.array(richDocumentBlockSchema).max(10_000),
|
|
119
|
+
mediaEvidenceRefs: z.array(stableUriSchema).max(512),
|
|
120
|
+
citations: z.array(richDocumentCitationSchema).max(10_000).default([]),
|
|
121
|
+
extractedAt: isoDateTimeSchema,
|
|
122
|
+
extractor: z.object({ id: idSchema, version: z.string().trim().min(1).max(128) }).strict(),
|
|
123
|
+
}).strict().superRefine((value, ctx) => {
|
|
124
|
+
if (value.role === 'SOURCE_MIRROR' && value.publicationProfile !== null)
|
|
125
|
+
ctx.addIssue({ code: 'custom', path: ['publicationProfile'], message: 'source mirror cannot claim a publication profile' });
|
|
126
|
+
if (value.role !== 'SOURCE_MIRROR' && value.publicationProfile === null)
|
|
127
|
+
ctx.addIssue({ code: 'custom', path: ['publicationProfile'], message: 'published Knowledge/Insight requires a publication profile' });
|
|
128
|
+
try {
|
|
129
|
+
const u = new URL(value.projectRef);
|
|
130
|
+
if (u.protocol !== 'knowledge-project:' || u.hostname.toLowerCase() !== value.projectKey.toLowerCase())
|
|
131
|
+
ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'projectRef project must match rich document project' });
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
ctx.addIssue({ code: 'custom', path: ['projectRef'], message: 'invalid projectRef' });
|
|
135
|
+
}
|
|
136
|
+
validateRichDocumentContent(value, ctx);
|
|
137
|
+
});
|
|
138
|
+
export const richSourceDocumentManifestSchema = z.object({
|
|
139
|
+
schemaVersion: schemaVersionSchema,
|
|
140
|
+
projectKey: projectKeySchema,
|
|
141
|
+
documentRef: stableUriSchema,
|
|
142
|
+
sourceEvidenceRef: stableUriSchema,
|
|
143
|
+
sourceContentHash: sha256Schema,
|
|
144
|
+
documentContentHash: sha256Schema,
|
|
145
|
+
documentByteLength: z.number().int().positive(),
|
|
146
|
+
extractedAt: isoDateTimeSchema,
|
|
147
|
+
extractor: z.object({ id: idSchema, version: z.string().trim().min(1).max(128) }).strict(),
|
|
148
|
+
}).strict().superRefine((value, ctx) => { try {
|
|
149
|
+
const u = new URL(value.documentRef);
|
|
150
|
+
if (u.protocol !== 'rich-document:' || u.hostname.toLowerCase() !== value.projectKey.toLowerCase() || !/^\/[a-f0-9]{64}\/[a-f0-9]{64}$/.test(u.pathname))
|
|
151
|
+
ctx.addIssue({ code: 'custom', path: ['documentRef'], message: 'invalid Rich Source Document identity' });
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
ctx.addIssue({ code: 'custom', path: ['documentRef'], message: 'invalid Rich Source Document identity' });
|
|
155
|
+
} });
|
|
156
|
+
export const richSourceDocumentPointerSchema = z.object({
|
|
157
|
+
schemaVersion: schemaVersionSchema,
|
|
158
|
+
projectKey: projectKeySchema,
|
|
159
|
+
sourceEvidenceRef: stableUriSchema,
|
|
160
|
+
documentRef: stableUriSchema,
|
|
161
|
+
documentContentHash: sha256Schema,
|
|
162
|
+
manifestRelativePath: z.string().trim().min(1).max(4096).refine(value => !value.startsWith('/') && !value.split('/').includes('..'), 'manifestRelativePath must remain project-relative'),
|
|
163
|
+
updatedAt: isoDateTimeSchema,
|
|
164
|
+
}).strict();
|
|
165
|
+
export const publicationReadinessReportSchema = z.object({
|
|
166
|
+
schemaVersion: schemaVersionSchema,
|
|
167
|
+
profile: publicationProfileSchema,
|
|
168
|
+
ready: z.boolean(),
|
|
169
|
+
reasons: z.array(publicationReadinessReasonSchema).max(16),
|
|
170
|
+
metrics: z.object({ textCharacters: z.number().int().nonnegative(), blockCount: z.number().int().nonnegative(), textBlockCount: z.number().int().nonnegative(), mediaBlockCount: z.number().int().nonnegative(), sourceEvidenceCount: z.number().int().nonnegative(), citationCount: z.number().int().nonnegative(), citedBlockCount: z.number().int().nonnegative() }).strict(),
|
|
171
|
+
}).strict();
|
|
172
|
+
export const richMediaLinkSchema = z.object({
|
|
173
|
+
schemaVersion: schemaVersionSchema,
|
|
174
|
+
projectKey: projectKeySchema,
|
|
175
|
+
mediaRef: stableUriSchema,
|
|
176
|
+
sourceEvidenceRef: stableUriSchema,
|
|
177
|
+
mediaEvidenceRef: stableUriSchema,
|
|
178
|
+
contentHash: sha256Schema,
|
|
179
|
+
mediaType: z.string().trim().min(1).max(256),
|
|
180
|
+
byteLength: z.number().int().nonnegative(),
|
|
181
|
+
sourceUrl: httpUrlSchema,
|
|
182
|
+
canonicalUrl: httpUrlSchema,
|
|
183
|
+
linkedAt: isoDateTimeSchema,
|
|
184
|
+
}).strict();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Release admission contract. Intermediate, unreleased wave snapshots are not rollback targets. */
|
|
2
|
+
export const tenantCoreReadCapabilities = [
|
|
3
|
+
'TENANT_RESOURCE_V1', 'TENANT_SOURCE_V1', 'TENANT_SOURCE_DOCUMENT_V2', 'TENANT_WORKFLOW_V1',
|
|
4
|
+
'TENANT_ROUTES_V1', 'TENANT_CURRENTNESS_V2', 'TENANT_PUBLIC_REFERENCE_V1',
|
|
5
|
+
'TENANT_PROJECT_CONTEXT_V1', 'TENANT_OAUTH_MANAGEMENT_V1', 'TENANT_RUNTIME_STATUS_V1',
|
|
6
|
+
];
|
|
7
|
+
export const tenantCoreWriteCapabilities = ['TENANT_COLLECTION_V2', 'TENANT_ADMISSION_STOP_V1', 'TENANT_ADMISSION_V2'];
|
|
8
|
+
export function runtimeCompatibility(component) {
|
|
9
|
+
return { schemaVersion: 1, component, identityReadMinimum: 11, identityReadMaximum: 21,
|
|
10
|
+
capabilities: component === 'core' ? [...tenantCoreReadCapabilities, ...tenantCoreWriteCapabilities, 'TENANT_NATIVE_CLIENT_AUTH_V1'] : [...tenantCoreReadCapabilities, 'TENANT_NATIVE_CLIENT_AUTH_V1'],
|
|
11
|
+
tenantProviders: component === 'core' ? [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] : [], requiredCoreProviders: component === 'admin-web' ? [20] : [],
|
|
12
|
+
budgetReadVersions: component === 'core' ? [2] : [], budgetMigrationVersions: component === 'core' ? [1] : [] };
|
|
13
|
+
}
|
|
14
|
+
/** Conservative published compatibility floor: native Identity >=12 requires the complete current Tenant reader. */
|
|
15
|
+
export function assertRuntimeFormatCompatibility(raw, facts, component, mode) {
|
|
16
|
+
const fail = (code) => { throw new Error('RUNTIME_COMPATIBILITY_' + code); };
|
|
17
|
+
if (!['core', 'admin-web'].includes(component) || !['INSTALL', 'START', 'ROLLBACK', 'RECOVERY'].includes(mode))
|
|
18
|
+
fail('CONTEXT_INVALID');
|
|
19
|
+
if (!Number.isSafeInteger(facts.identityVersion) || facts.identityVersion < 11 || facts.identityVersion > 21)
|
|
20
|
+
fail('IDENTITY_UNSUPPORTED');
|
|
21
|
+
if (facts.budgetVersion !== null && ![1, 2].includes(facts.budgetVersion))
|
|
22
|
+
fail('BUDGET_UNSUPPORTED');
|
|
23
|
+
if (raw === undefined) {
|
|
24
|
+
if (facts.identityVersion >= 12 || facts.budgetVersion !== null)
|
|
25
|
+
fail('DECLARATION_REQUIRED');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw))
|
|
29
|
+
fail('DECLARATION_INVALID');
|
|
30
|
+
const value = raw, expected = runtimeCompatibility(component), keys = Object.keys(expected);
|
|
31
|
+
if (Object.keys(value).length !== keys.length || Object.keys(value).some(key => !keys.includes(key)) || value.schemaVersion !== 1 || value.component !== component)
|
|
32
|
+
fail('DECLARATION_INVALID');
|
|
33
|
+
if (!Number.isSafeInteger(value.identityReadMinimum) || !Number.isSafeInteger(value.identityReadMaximum) || value.identityReadMinimum < 11 || value.identityReadMaximum > 21 || value.identityReadMinimum > value.identityReadMaximum)
|
|
34
|
+
fail('DECLARATION_INVALID');
|
|
35
|
+
for (const key of ['capabilities', 'tenantProviders', 'requiredCoreProviders', 'budgetReadVersions', 'budgetMigrationVersions']) {
|
|
36
|
+
const list = value[key], allowed = expected[key];
|
|
37
|
+
if (!Array.isArray(list) || new Set(list).size !== list.length || list.some(item => !allowed.includes(item)))
|
|
38
|
+
fail('DECLARATION_INVALID');
|
|
39
|
+
}
|
|
40
|
+
if (facts.identityVersion < value.identityReadMinimum || facts.identityVersion > value.identityReadMaximum)
|
|
41
|
+
fail('IDENTITY_UNSUPPORTED');
|
|
42
|
+
if (facts.identityVersion >= 12 && expected.capabilities.some(cap => (facts.identityVersion >= 21 || cap !== 'TENANT_NATIVE_CLIENT_AUTH_V1') && !value.capabilities.includes(cap)))
|
|
43
|
+
fail('CAPABILITY_REQUIRED');
|
|
44
|
+
// New Provider21/22 are advertised, but historical Identity >=12 does not imply the new opt-in writer.
|
|
45
|
+
if (facts.identityVersion >= 12 && ['tenantProviders', 'requiredCoreProviders'].some(key => (key === 'tenantProviders' && component === 'core' ? [16, 17, 18, 19, 20] : expected[key]).some(version => !value[key].includes(version))))
|
|
46
|
+
fail('PROVIDER_REQUIRED');
|
|
47
|
+
if (facts.identityVersion >= 21 && component === 'core' && !value.tenantProviders.includes(26))
|
|
48
|
+
fail('PROVIDER_REQUIRED');
|
|
49
|
+
if (component === 'core' && facts.budgetVersion !== null && !value.capabilities.includes('TENANT_ADMISSION_V2'))
|
|
50
|
+
fail('CAPABILITY_REQUIRED');
|
|
51
|
+
if (component === 'core' && facts.budgetVersion !== null && !value.budgetReadVersions.includes(facts.budgetVersion)) {
|
|
52
|
+
if (mode !== 'INSTALL' || !value.budgetMigrationVersions.includes(facts.budgetVersion))
|
|
53
|
+
fail('BUDGET_MIGRATION_REQUIRED');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { idSchema, isoDateTimeSchema, schemaVersionSchema, sha256Schema, stableUriSchema, } from './common.js';
|
|
3
|
+
export const runtimeCapabilitySchema = z.string().trim().min(1).max(128);
|
|
4
|
+
export const runtimeNodeSchema = z.object({
|
|
5
|
+
schemaVersion: schemaVersionSchema,
|
|
6
|
+
nodeId: idSchema,
|
|
7
|
+
capabilities: z.array(runtimeCapabilitySchema).max(128),
|
|
8
|
+
codeRevision: z.string().trim().min(7).max(64),
|
|
9
|
+
observedAt: isoDateTimeSchema,
|
|
10
|
+
}).strict();
|
|
11
|
+
export const workAssignmentSchema = z.object({
|
|
12
|
+
schemaVersion: schemaVersionSchema,
|
|
13
|
+
assignmentId: idSchema,
|
|
14
|
+
workloadId: idSchema,
|
|
15
|
+
runtimeNodeId: idSchema,
|
|
16
|
+
projectRef: stableUriSchema.optional(),
|
|
17
|
+
revision: z.number().int().positive(),
|
|
18
|
+
enabled: z.boolean(),
|
|
19
|
+
effectiveFrom: isoDateTimeSchema.optional(),
|
|
20
|
+
effectiveUntil: isoDateTimeSchema.optional(),
|
|
21
|
+
}).strict().superRefine((value, ctx) => {
|
|
22
|
+
if (value.effectiveFrom && value.effectiveUntil && Date.parse(value.effectiveUntil) <= Date.parse(value.effectiveFrom)) {
|
|
23
|
+
ctx.addIssue({
|
|
24
|
+
code: 'custom',
|
|
25
|
+
path: ['effectiveUntil'],
|
|
26
|
+
message: 'effectiveUntil must be after effectiveFrom',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
export const runStatusSchema = z.enum(['SUCCEEDED', 'FAILED', 'PARTIAL', 'SKIPPED', 'BLOCKED']);
|
|
31
|
+
/** Fixed numeric diagnostics only; not a response body, URL or new finality authority. */
|
|
32
|
+
export function nativeAcquisitionHttpStatus(errorCode, value) {
|
|
33
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 100 || value > 599)
|
|
34
|
+
return undefined;
|
|
35
|
+
const terminal = errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' && (value < 200 || value >= 300 && value < 500 && ![401, 403, 429].includes(value));
|
|
36
|
+
const controlled = errorCode === 'TENANT_SOURCE_ACCESS_CONTROLLED' && [401, 403].includes(value);
|
|
37
|
+
return terminal || controlled ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
export const runReceiptSchema = z.object({
|
|
40
|
+
schemaVersion: schemaVersionSchema,
|
|
41
|
+
runId: idSchema,
|
|
42
|
+
workloadId: idSchema,
|
|
43
|
+
runtimeNodeId: idSchema,
|
|
44
|
+
projectRef: stableUriSchema.optional(),
|
|
45
|
+
projectIntentRef: stableUriSchema.optional(),
|
|
46
|
+
collectionBatchRef: stableUriSchema.optional(),
|
|
47
|
+
assignmentRevision: z.number().int().positive(),
|
|
48
|
+
codeRevision: z.string().trim().min(7).max(64),
|
|
49
|
+
startedAt: isoDateTimeSchema,
|
|
50
|
+
finishedAt: isoDateTimeSchema,
|
|
51
|
+
status: runStatusSchema,
|
|
52
|
+
inputFingerprint: sha256Schema.optional(),
|
|
53
|
+
outputFingerprint: sha256Schema.optional(),
|
|
54
|
+
evidenceCount: z.number().int().nonnegative(),
|
|
55
|
+
observationCount: z.number().int().nonnegative(),
|
|
56
|
+
errorCode: z.string().trim().min(1).max(128).optional(),
|
|
57
|
+
acquisitionHttpStatus: z.number().int().min(100).max(599).optional(),
|
|
58
|
+
}).strict().superRefine((value, ctx) => {
|
|
59
|
+
if (Date.parse(value.finishedAt) < Date.parse(value.startedAt)) {
|
|
60
|
+
ctx.addIssue({ code: 'custom', path: ['finishedAt'], message: 'finishedAt must not precede startedAt' });
|
|
61
|
+
}
|
|
62
|
+
if (value.status === 'FAILED' && !value.errorCode) {
|
|
63
|
+
ctx.addIssue({ code: 'custom', path: ['errorCode'], message: 'FAILED runs require errorCode' });
|
|
64
|
+
}
|
|
65
|
+
if (value.acquisitionHttpStatus !== undefined && (!value.collectionBatchRef || nativeAcquisitionHttpStatus(value.errorCode, value.acquisitionHttpStatus) === undefined || (value.errorCode === 'TENANT_SOURCE_HTTP_TERMINAL' ? value.status !== 'FAILED' : value.status !== 'BLOCKED'))) {
|
|
66
|
+
ctx.addIssue({ code: 'custom', path: ['acquisitionHttpStatus'], message: 'HTTP diagnostic must match the native acquisition failure class' });
|
|
67
|
+
}
|
|
68
|
+
});
|