@plasius/asset-contracts 0.4.5 → 0.5.0
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/CHANGELOG.md +40 -0
- package/README.md +96 -1
- package/dist/index.cjs +1130 -954
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +1108 -935
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0008-package-owned-issue-tracking.md +62 -0
- package/docs/adrs/adr-0009-model-resource-manifest-qualification.md +74 -0
- package/docs/adrs/index.md +2 -0
- package/docs/tdrs/index.md +1 -0
- package/docs/tdrs/tdr-0004-model-resource-manifest-qualification.md +70 -0
- package/package.json +4 -3
- package/src/index.ts +1 -0
- package/src/model-resource-manifest.ts +198 -0
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
SUPPORTED_ADDITIVE_WEBGPU_MATRIX_POLICIES,
|
|
7
7
|
SUPPORTED_STABLE_WEBGPU_MATRIX_POLICIES,
|
|
8
8
|
canonicalizeGpuContract as canonicalizeGpuContract3,
|
|
9
|
-
computeSha256,
|
|
9
|
+
computeSha256 as computeSha2562,
|
|
10
10
|
parseGpuInterfaceManifest,
|
|
11
11
|
parseJsonBytes,
|
|
12
12
|
parseModelGpuCompatibilityDescriptor,
|
|
@@ -160,14 +160,14 @@ function assertRecord(value, fieldName) {
|
|
|
160
160
|
}
|
|
161
161
|
return value;
|
|
162
162
|
}
|
|
163
|
-
function assertAllowedKeys(
|
|
164
|
-
const unexpected = Object.keys(
|
|
163
|
+
function assertAllowedKeys(record2, allowedKeys, fieldName) {
|
|
164
|
+
const unexpected = Object.keys(record2).find((key) => !allowedKeys.includes(key));
|
|
165
165
|
if (unexpected !== void 0) {
|
|
166
166
|
throw new Error(`${fieldName} contains unexpected field ${unexpected}.`);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
function assertContractVersion(
|
|
170
|
-
if (
|
|
169
|
+
function assertContractVersion(record2, fieldName) {
|
|
170
|
+
if (record2.contractVersion !== void 0 && record2.contractVersion !== MODEL_RESOLUTION_CONTRACT_VERSION) {
|
|
171
171
|
throw new Error(`${fieldName} contractVersion is not supported.`);
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -290,22 +290,22 @@ function requiredVector3(value, fieldName, positive = false) {
|
|
|
290
290
|
];
|
|
291
291
|
}
|
|
292
292
|
function createBounds(value, fieldName) {
|
|
293
|
-
const
|
|
294
|
-
assertAllowedKeys(
|
|
295
|
-
const min = requiredVector3(
|
|
296
|
-
const max = requiredVector3(
|
|
293
|
+
const record2 = assertRecord(value, fieldName);
|
|
294
|
+
assertAllowedKeys(record2, ["min", "max"], fieldName);
|
|
295
|
+
const min = requiredVector3(record2.min, `${fieldName}.min`);
|
|
296
|
+
const max = requiredVector3(record2.max, `${fieldName}.max`);
|
|
297
297
|
if (min.some((axis, index) => axis >= max[index])) {
|
|
298
298
|
throw new Error(`${fieldName} min axes must be lower than max axes.`);
|
|
299
299
|
}
|
|
300
300
|
return { min, max };
|
|
301
301
|
}
|
|
302
302
|
function createDimensions(value, fieldName) {
|
|
303
|
-
const
|
|
304
|
-
assertAllowedKeys(
|
|
303
|
+
const record2 = assertRecord(value, fieldName);
|
|
304
|
+
assertAllowedKeys(record2, ["width", "height", "depth"], fieldName);
|
|
305
305
|
return {
|
|
306
|
-
width: requiredFiniteNumber(
|
|
307
|
-
height: requiredFiniteNumber(
|
|
308
|
-
depth: requiredFiniteNumber(
|
|
306
|
+
width: requiredFiniteNumber(record2.width, `${fieldName}.width`, Number.EPSILON),
|
|
307
|
+
height: requiredFiniteNumber(record2.height, `${fieldName}.height`, Number.EPSILON),
|
|
308
|
+
depth: requiredFiniteNumber(record2.depth, `${fieldName}.depth`, Number.EPSILON)
|
|
309
309
|
};
|
|
310
310
|
}
|
|
311
311
|
function assertModelUri(value, fieldName) {
|
|
@@ -389,8 +389,8 @@ function opaqueSourceAssetId(value, fieldName = "ModelProvenance.sourceAssetId")
|
|
|
389
389
|
return value;
|
|
390
390
|
}
|
|
391
391
|
function createHardConstraints(value) {
|
|
392
|
-
const
|
|
393
|
-
assertAllowedKeys(
|
|
392
|
+
const record2 = value === void 0 ? {} : assertRecord(value, "ModelRequestSpec.hardConstraints");
|
|
393
|
+
assertAllowedKeys(record2, [
|
|
394
394
|
"boundsMetres",
|
|
395
395
|
"dimensionsMetres",
|
|
396
396
|
"maxTriangles",
|
|
@@ -412,62 +412,62 @@ function createHardConstraints(value) {
|
|
|
412
412
|
collision: "optional",
|
|
413
413
|
partition: "allowed"
|
|
414
414
|
};
|
|
415
|
-
if (
|
|
416
|
-
result.boundsMetres = createBounds(
|
|
415
|
+
if (record2.boundsMetres !== void 0) {
|
|
416
|
+
result.boundsMetres = createBounds(record2.boundsMetres, "ModelRequestSpec.hardConstraints.boundsMetres");
|
|
417
417
|
}
|
|
418
|
-
if (
|
|
419
|
-
result.dimensionsMetres = createDimensions(
|
|
418
|
+
if (record2.dimensionsMetres !== void 0) {
|
|
419
|
+
result.dimensionsMetres = createDimensions(record2.dimensionsMetres, "ModelRequestSpec.hardConstraints.dimensionsMetres");
|
|
420
420
|
}
|
|
421
|
-
if (
|
|
421
|
+
if (record2.maxTriangles !== void 0) {
|
|
422
422
|
result.maxTriangles = requiredInteger(
|
|
423
|
-
|
|
423
|
+
record2.maxTriangles,
|
|
424
424
|
"ModelRequestSpec.hardConstraints.maxTriangles",
|
|
425
425
|
1,
|
|
426
426
|
STATIC_WORLD_V1_MODEL_POLICY.maxTriangles
|
|
427
427
|
);
|
|
428
428
|
}
|
|
429
|
-
if (
|
|
429
|
+
if (record2.maxBytes !== void 0) {
|
|
430
430
|
result.maxBytes = requiredInteger(
|
|
431
|
-
|
|
431
|
+
record2.maxBytes,
|
|
432
432
|
"ModelRequestSpec.hardConstraints.maxBytes",
|
|
433
433
|
1,
|
|
434
434
|
STATIC_WORLD_V1_MODEL_POLICY.maxBytes
|
|
435
435
|
);
|
|
436
436
|
}
|
|
437
|
-
if (
|
|
437
|
+
if (record2.maxTextureBytes !== void 0) {
|
|
438
438
|
result.maxTextureBytes = requiredInteger(
|
|
439
|
-
|
|
439
|
+
record2.maxTextureBytes,
|
|
440
440
|
"ModelRequestSpec.hardConstraints.maxTextureBytes",
|
|
441
441
|
1,
|
|
442
442
|
STATIC_WORLD_V1_MODEL_POLICY.maxTextureBytes
|
|
443
443
|
);
|
|
444
444
|
}
|
|
445
|
-
if (
|
|
445
|
+
if (record2.maxTextureDimensionPx !== void 0) {
|
|
446
446
|
result.maxTextureDimensionPx = requiredInteger(
|
|
447
|
-
|
|
447
|
+
record2.maxTextureDimensionPx,
|
|
448
448
|
"ModelRequestSpec.hardConstraints.maxTextureDimensionPx",
|
|
449
449
|
1,
|
|
450
450
|
STATIC_WORLD_V1_MODEL_POLICY.maxTextureDimensionPx
|
|
451
451
|
);
|
|
452
452
|
}
|
|
453
|
-
if (
|
|
453
|
+
if (record2.maxPartitionCellMetres !== void 0) {
|
|
454
454
|
result.maxPartitionCellMetres = requiredFiniteNumber(
|
|
455
|
-
|
|
455
|
+
record2.maxPartitionCellMetres,
|
|
456
456
|
"ModelRequestSpec.hardConstraints.maxPartitionCellMetres",
|
|
457
457
|
Number.EPSILON,
|
|
458
458
|
STATIC_WORLD_V1_MODEL_POLICY.maxPartitionCellMetres
|
|
459
459
|
);
|
|
460
460
|
}
|
|
461
|
-
if (
|
|
462
|
-
result.lod = requiredEnum(
|
|
461
|
+
if (record2.lod !== void 0) {
|
|
462
|
+
result.lod = requiredEnum(record2.lod, ["required", "optional", "forbidden"], "ModelRequestSpec.hardConstraints.lod");
|
|
463
463
|
}
|
|
464
|
-
if (
|
|
465
|
-
result.collision = requiredEnum(
|
|
464
|
+
if (record2.collision !== void 0) {
|
|
465
|
+
result.collision = requiredEnum(record2.collision, ["required", "optional", "forbidden"], "ModelRequestSpec.hardConstraints.collision");
|
|
466
466
|
}
|
|
467
|
-
if (
|
|
468
|
-
result.partition = requiredEnum(
|
|
467
|
+
if (record2.partition !== void 0) {
|
|
468
|
+
result.partition = requiredEnum(record2.partition, ["single", "allowed", "required"], "ModelRequestSpec.hardConstraints.partition");
|
|
469
469
|
}
|
|
470
|
-
if (
|
|
470
|
+
if (record2.maxTextureBytes === void 0 && result.maxBytes !== void 0) {
|
|
471
471
|
result.maxTextureBytes = Math.min(result.maxTextureBytes ?? result.maxBytes, result.maxBytes);
|
|
472
472
|
}
|
|
473
473
|
if (result.maxBytes !== void 0 && result.maxTextureBytes !== void 0 && result.maxTextureBytes > result.maxBytes) {
|
|
@@ -492,8 +492,8 @@ function createSoftPreferences(value) {
|
|
|
492
492
|
if (value === void 0) {
|
|
493
493
|
return {};
|
|
494
494
|
}
|
|
495
|
-
const
|
|
496
|
-
assertAllowedKeys(
|
|
495
|
+
const record2 = assertRecord(value, "ModelRequestSpec.softPreferences");
|
|
496
|
+
assertAllowedKeys(record2, [
|
|
497
497
|
"category",
|
|
498
498
|
"style",
|
|
499
499
|
"materials",
|
|
@@ -504,22 +504,22 @@ function createSoftPreferences(value) {
|
|
|
504
504
|
], "ModelRequestSpec.softPreferences");
|
|
505
505
|
const result = {};
|
|
506
506
|
for (const field of ["category", "style", "era", "condition"]) {
|
|
507
|
-
const normalized = optionalString(
|
|
507
|
+
const normalized = optionalString(record2[field], `ModelRequestSpec.softPreferences.${field}`, 80);
|
|
508
508
|
if (normalized !== void 0) {
|
|
509
509
|
assertNoDirectUrl(normalized, `ModelRequestSpec.softPreferences.${field}`);
|
|
510
510
|
result[field] = normalized;
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
513
|
for (const field of ["materials", "colors", "tags"]) {
|
|
514
|
-
if (
|
|
515
|
-
result[field] = stringList(
|
|
514
|
+
if (record2[field] !== void 0) {
|
|
515
|
+
result[field] = stringList(record2[field], `ModelRequestSpec.softPreferences.${field}`);
|
|
516
516
|
}
|
|
517
517
|
}
|
|
518
518
|
return result;
|
|
519
519
|
}
|
|
520
520
|
function createModelRequestSpec(input) {
|
|
521
|
-
const
|
|
522
|
-
assertAllowedKeys(
|
|
521
|
+
const record2 = assertRecord(input, "ModelRequestSpec");
|
|
522
|
+
assertAllowedKeys(record2, [
|
|
523
523
|
"contractVersion",
|
|
524
524
|
"policyProfileId",
|
|
525
525
|
"query",
|
|
@@ -530,21 +530,21 @@ function createModelRequestSpec(input) {
|
|
|
530
530
|
"softPreferences",
|
|
531
531
|
"exclusions"
|
|
532
532
|
], "ModelRequestSpec");
|
|
533
|
-
assertContractVersion(
|
|
534
|
-
if (
|
|
533
|
+
assertContractVersion(record2, "ModelRequestSpec");
|
|
534
|
+
if (record2.policyProfileId !== void 0 && record2.policyProfileId !== STATIC_WORLD_V1_MODEL_POLICY.id) {
|
|
535
535
|
throw new Error("ModelRequestSpec.policyProfileId must be static-world-v1.");
|
|
536
536
|
}
|
|
537
|
-
const query = requiredString(
|
|
537
|
+
const query = requiredString(record2.query, "ModelRequestSpec.query", MAX_QUERY_LENGTH);
|
|
538
538
|
assertNoDirectUrl(query, "ModelRequestSpec.query");
|
|
539
539
|
const revision = requiredInteger(
|
|
540
|
-
|
|
540
|
+
record2.revision,
|
|
541
541
|
`ModelRequestSpec revision (maximum ${MODEL_REQUEST_MAX_REVISION})`,
|
|
542
542
|
0,
|
|
543
543
|
MODEL_REQUEST_MAX_REVISION
|
|
544
544
|
);
|
|
545
545
|
let locale;
|
|
546
|
-
if (
|
|
547
|
-
const requestedLocale = requiredString(
|
|
546
|
+
if (record2.locale !== void 0) {
|
|
547
|
+
const requestedLocale = requiredString(record2.locale, "ModelRequestSpec.locale", 64);
|
|
548
548
|
try {
|
|
549
549
|
[locale] = Intl.getCanonicalLocales(requestedLocale);
|
|
550
550
|
} catch {
|
|
@@ -554,7 +554,7 @@ function createModelRequestSpec(input) {
|
|
|
554
554
|
throw new Error("ModelRequestSpec.locale must be a valid BCP 47 locale.");
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
|
-
const rankerId =
|
|
557
|
+
const rankerId = record2.rankerId === void 0 ? void 0 : requiredToken(record2.rankerId, "ModelRequestSpec.rankerId");
|
|
558
558
|
const result = {
|
|
559
559
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
560
560
|
policyProfileId: STATIC_WORLD_V1_MODEL_POLICY.id,
|
|
@@ -562,9 +562,9 @@ function createModelRequestSpec(input) {
|
|
|
562
562
|
revision,
|
|
563
563
|
...locale === void 0 ? {} : { locale },
|
|
564
564
|
...rankerId === void 0 ? {} : { rankerId },
|
|
565
|
-
hardConstraints: createHardConstraints(
|
|
566
|
-
softPreferences: createSoftPreferences(
|
|
567
|
-
exclusions:
|
|
565
|
+
hardConstraints: createHardConstraints(record2.hardConstraints),
|
|
566
|
+
softPreferences: createSoftPreferences(record2.softPreferences),
|
|
567
|
+
exclusions: record2.exclusions === void 0 ? [] : stringList(record2.exclusions, "ModelRequestSpec.exclusions")
|
|
568
568
|
};
|
|
569
569
|
return deepFreeze(result);
|
|
570
570
|
}
|
|
@@ -602,8 +602,8 @@ function classifyModelMatchAssurance(score, hardConstraintPass, evidenceMode, ex
|
|
|
602
602
|
return MODEL_MATCH_ASSURANCE_ORDER[rawAssurance] <= MODEL_MATCH_ASSURANCE_ORDER[assuranceCeiling] ? rawAssurance : assuranceCeiling;
|
|
603
603
|
}
|
|
604
604
|
function createRankerRef(value) {
|
|
605
|
-
const
|
|
606
|
-
assertAllowedKeys(
|
|
605
|
+
const record2 = assertRecord(value, "ModelMatchAssessment.ranker");
|
|
606
|
+
assertAllowedKeys(record2, [
|
|
607
607
|
"id",
|
|
608
608
|
"version",
|
|
609
609
|
"calibrationId",
|
|
@@ -612,25 +612,25 @@ function createRankerRef(value) {
|
|
|
612
612
|
"assuranceCeiling"
|
|
613
613
|
], "ModelMatchAssessment.ranker");
|
|
614
614
|
return {
|
|
615
|
-
id: requiredToken(
|
|
616
|
-
version: assetVersion(
|
|
617
|
-
calibrationId: requiredToken(
|
|
618
|
-
calibrationVersion: assetVersion(
|
|
615
|
+
id: requiredToken(record2.id, "ModelMatchAssessment.ranker.id"),
|
|
616
|
+
version: assetVersion(record2.version, "ModelMatchAssessment.ranker.version"),
|
|
617
|
+
calibrationId: requiredToken(record2.calibrationId, "ModelMatchAssessment.ranker.calibrationId"),
|
|
618
|
+
calibrationVersion: assetVersion(record2.calibrationVersion, "ModelMatchAssessment.ranker.calibrationVersion"),
|
|
619
619
|
evidenceMode: requiredEnum(
|
|
620
|
-
|
|
620
|
+
record2.evidenceMode,
|
|
621
621
|
MODEL_RANKER_EVIDENCE_MODES,
|
|
622
622
|
"ModelMatchAssessment.ranker.evidenceMode"
|
|
623
623
|
),
|
|
624
624
|
assuranceCeiling: requiredEnum(
|
|
625
|
-
|
|
625
|
+
record2.assuranceCeiling,
|
|
626
626
|
MODEL_MATCH_ASSURANCE_BANDS,
|
|
627
627
|
"ModelMatchAssessment.ranker.assuranceCeiling"
|
|
628
628
|
)
|
|
629
629
|
};
|
|
630
630
|
}
|
|
631
631
|
function createModelMatchAssessment(input) {
|
|
632
|
-
const
|
|
633
|
-
assertAllowedKeys(
|
|
632
|
+
const record2 = assertRecord(input, "ModelMatchAssessment");
|
|
633
|
+
assertAllowedKeys(record2, [
|
|
634
634
|
"contractVersion",
|
|
635
635
|
"score",
|
|
636
636
|
"assurance",
|
|
@@ -643,19 +643,19 @@ function createModelMatchAssessment(input) {
|
|
|
643
643
|
"candidateId",
|
|
644
644
|
"candidateContentHash"
|
|
645
645
|
], "ModelMatchAssessment");
|
|
646
|
-
assertContractVersion(
|
|
647
|
-
const score = requiredFiniteNumber(
|
|
648
|
-
const hardConstraintPass = requiredBoolean(
|
|
649
|
-
const exactMatch = requiredBoolean(
|
|
650
|
-
const ranker = createRankerRef(
|
|
651
|
-
const request = createModelRequestSpec(
|
|
646
|
+
assertContractVersion(record2, "ModelMatchAssessment");
|
|
647
|
+
const score = requiredFiniteNumber(record2.score, "ModelMatchAssessment.score", 0, 1);
|
|
648
|
+
const hardConstraintPass = requiredBoolean(record2.hardConstraintPass, "ModelMatchAssessment.hardConstraintPass");
|
|
649
|
+
const exactMatch = requiredBoolean(record2.exactMatch, "ModelMatchAssessment.exactMatch");
|
|
650
|
+
const ranker = createRankerRef(record2.ranker);
|
|
651
|
+
const request = createModelRequestSpec(record2.request);
|
|
652
652
|
if (request.rankerId !== void 0 && request.rankerId !== ranker.id) {
|
|
653
653
|
throw new Error("ModelMatchAssessment ranker.id must match the caller-selected request rankerId.");
|
|
654
654
|
}
|
|
655
|
-
if (
|
|
656
|
-
requiredEnum(
|
|
655
|
+
if (record2.assurance !== void 0) {
|
|
656
|
+
requiredEnum(record2.assurance, MODEL_MATCH_ASSURANCE_BANDS, "ModelMatchAssessment.assurance");
|
|
657
657
|
}
|
|
658
|
-
let reasonCodes2 = stringList(
|
|
658
|
+
let reasonCodes2 = stringList(record2.reasonCodes, "ModelMatchAssessment.reasonCodes", {
|
|
659
659
|
tokens: true
|
|
660
660
|
});
|
|
661
661
|
if (!hardConstraintPass && reasonCodes2.length === 0) {
|
|
@@ -688,18 +688,18 @@ function createModelMatchAssessment(input) {
|
|
|
688
688
|
exactMatch,
|
|
689
689
|
reasonCodes: reasonCodes2,
|
|
690
690
|
ranker,
|
|
691
|
-
fidelityWarnings: stringList(
|
|
691
|
+
fidelityWarnings: stringList(record2.fidelityWarnings ?? [], "ModelMatchAssessment.fidelityWarnings"),
|
|
692
692
|
request,
|
|
693
|
-
candidateId: requiredToken(
|
|
693
|
+
candidateId: requiredToken(record2.candidateId, "ModelMatchAssessment.candidateId"),
|
|
694
694
|
candidateContentHash: requiredSha256(
|
|
695
|
-
|
|
695
|
+
record2.candidateContentHash,
|
|
696
696
|
"ModelMatchAssessment.candidateContentHash"
|
|
697
697
|
)
|
|
698
698
|
});
|
|
699
699
|
}
|
|
700
700
|
function createModelTechnicalProfile(input) {
|
|
701
|
-
const
|
|
702
|
-
assertAllowedKeys(
|
|
701
|
+
const record2 = assertRecord(input, "ModelTechnicalProfile");
|
|
702
|
+
assertAllowedKeys(record2, [
|
|
703
703
|
"boundsMetres",
|
|
704
704
|
"dimensionsMetres",
|
|
705
705
|
"triangleCount",
|
|
@@ -711,22 +711,22 @@ function createModelTechnicalProfile(input) {
|
|
|
711
711
|
"partitionCount",
|
|
712
712
|
"partitionCellMetres"
|
|
713
713
|
], "ModelTechnicalProfile");
|
|
714
|
-
const boundsMetres = createBounds(
|
|
715
|
-
const dimensionsMetres = createDimensions(
|
|
714
|
+
const boundsMetres = createBounds(record2.boundsMetres, "ModelTechnicalProfile.boundsMetres");
|
|
715
|
+
const dimensionsMetres = createDimensions(record2.dimensionsMetres, "ModelTechnicalProfile.dimensionsMetres");
|
|
716
716
|
const derivedDimensions = boundsMetres.max.map((axis, index) => axis - boundsMetres.min[index]);
|
|
717
717
|
const suppliedDimensions = [dimensionsMetres.width, dimensionsMetres.height, dimensionsMetres.depth];
|
|
718
718
|
if (derivedDimensions.some((axis, index) => Math.abs(axis - suppliedDimensions[index]) > Math.max(1e-6, Math.abs(axis) * 1e-6))) {
|
|
719
719
|
throw new Error("ModelTechnicalProfile dimensionsMetres must match boundsMetres.");
|
|
720
720
|
}
|
|
721
|
-
const byteLength = requiredInteger(
|
|
721
|
+
const byteLength = requiredInteger(record2.byteLength, "ModelTechnicalProfile.byteLength", 1, Number.MAX_SAFE_INTEGER);
|
|
722
722
|
const textureByteLength = requiredInteger(
|
|
723
|
-
|
|
723
|
+
record2.textureByteLength,
|
|
724
724
|
"ModelTechnicalProfile.textureByteLength",
|
|
725
725
|
0,
|
|
726
726
|
Number.MAX_SAFE_INTEGER
|
|
727
727
|
);
|
|
728
728
|
const maxTextureDimensionPx = requiredInteger(
|
|
729
|
-
|
|
729
|
+
record2.maxTextureDimensionPx,
|
|
730
730
|
"ModelTechnicalProfile.maxTextureDimensionPx",
|
|
731
731
|
0,
|
|
732
732
|
32768
|
|
@@ -742,15 +742,15 @@ function createModelTechnicalProfile(input) {
|
|
|
742
742
|
return deepFreeze({
|
|
743
743
|
boundsMetres,
|
|
744
744
|
dimensionsMetres,
|
|
745
|
-
triangleCount: requiredInteger(
|
|
745
|
+
triangleCount: requiredInteger(record2.triangleCount, "ModelTechnicalProfile.triangleCount", 1, 1e9),
|
|
746
746
|
byteLength,
|
|
747
747
|
textureByteLength,
|
|
748
748
|
maxTextureDimensionPx,
|
|
749
|
-
lodCount: requiredInteger(
|
|
750
|
-
hasCollision: requiredBoolean(
|
|
751
|
-
partitionCount: requiredInteger(
|
|
749
|
+
lodCount: requiredInteger(record2.lodCount, "ModelTechnicalProfile.lodCount", 1, 4),
|
|
750
|
+
hasCollision: requiredBoolean(record2.hasCollision, "ModelTechnicalProfile.hasCollision"),
|
|
751
|
+
partitionCount: requiredInteger(record2.partitionCount, "ModelTechnicalProfile.partitionCount", 1, 1e4),
|
|
752
752
|
partitionCellMetres: requiredFiniteNumber(
|
|
753
|
-
|
|
753
|
+
record2.partitionCellMetres,
|
|
754
754
|
"ModelTechnicalProfile.partitionCellMetres",
|
|
755
755
|
Number.EPSILON,
|
|
756
756
|
1e6
|
|
@@ -758,19 +758,19 @@ function createModelTechnicalProfile(input) {
|
|
|
758
758
|
});
|
|
759
759
|
}
|
|
760
760
|
function createAttribution(value) {
|
|
761
|
-
const
|
|
762
|
-
assertAllowedKeys(
|
|
763
|
-
const sourcePageUri =
|
|
761
|
+
const record2 = assertRecord(value, "ModelRightsAssessment.attribution");
|
|
762
|
+
assertAllowedKeys(record2, ["title", "creator", "notice", "sourcePageUri"], "ModelRightsAssessment.attribution");
|
|
763
|
+
const sourcePageUri = record2.sourcePageUri === void 0 ? void 0 : assertPublicPageUri(record2.sourcePageUri, "ModelRightsAssessment.attribution.sourcePageUri");
|
|
764
764
|
return {
|
|
765
|
-
title: requiredString(
|
|
766
|
-
creator: requiredString(
|
|
767
|
-
notice: requiredString(
|
|
765
|
+
title: requiredString(record2.title, "ModelRightsAssessment.attribution.title", 256),
|
|
766
|
+
creator: requiredString(record2.creator, "ModelRightsAssessment.attribution.creator", 256),
|
|
767
|
+
notice: requiredString(record2.notice, "ModelRightsAssessment.attribution.notice", 512),
|
|
768
768
|
...sourcePageUri === void 0 ? {} : { sourcePageUri }
|
|
769
769
|
};
|
|
770
770
|
}
|
|
771
771
|
function createModelRightsAssessment(input) {
|
|
772
|
-
const
|
|
773
|
-
assertAllowedKeys(
|
|
772
|
+
const record2 = assertRecord(input, "ModelRightsAssessment");
|
|
773
|
+
assertAllowedKeys(record2, [
|
|
774
774
|
"decisionId",
|
|
775
775
|
"decisionToken",
|
|
776
776
|
"policyId",
|
|
@@ -784,8 +784,8 @@ function createModelRightsAssessment(input) {
|
|
|
784
784
|
"attribution",
|
|
785
785
|
"reviewedAt"
|
|
786
786
|
], "ModelRightsAssessment");
|
|
787
|
-
const status = requiredEnum(
|
|
788
|
-
const attribution =
|
|
787
|
+
const status = requiredEnum(record2.status, MODEL_RIGHTS_STATUSES, "ModelRightsAssessment.status");
|
|
788
|
+
const attribution = record2.attribution === void 0 ? void 0 : createAttribution(record2.attribution);
|
|
789
789
|
if (status === "attribution-required" && attribution === void 0) {
|
|
790
790
|
throw new Error("ModelRightsAssessment attribution is required for attribution-required rights.");
|
|
791
791
|
}
|
|
@@ -793,29 +793,29 @@ function createModelRightsAssessment(input) {
|
|
|
793
793
|
throw new Error("ModelRightsAssessment attribution-required rights require a public attribution sourcePageUri.");
|
|
794
794
|
}
|
|
795
795
|
return deepFreeze({
|
|
796
|
-
decisionId: requiredToken(
|
|
796
|
+
decisionId: requiredToken(record2.decisionId, "ModelRightsAssessment.decisionId"),
|
|
797
797
|
decisionToken: requiredAttestationToken(
|
|
798
|
-
|
|
798
|
+
record2.decisionToken,
|
|
799
799
|
"ModelRightsAssessment.decisionToken"
|
|
800
800
|
),
|
|
801
|
-
policyId: requiredToken(
|
|
802
|
-
policyVersion: assetVersion(
|
|
803
|
-
sourceId: requiredToken(
|
|
804
|
-
sourceAssetId: opaqueSourceAssetId(
|
|
801
|
+
policyId: requiredToken(record2.policyId, "ModelRightsAssessment.policyId"),
|
|
802
|
+
policyVersion: assetVersion(record2.policyVersion, "ModelRightsAssessment.policyVersion"),
|
|
803
|
+
sourceId: requiredToken(record2.sourceId, "ModelRightsAssessment.sourceId"),
|
|
804
|
+
sourceAssetId: opaqueSourceAssetId(record2.sourceAssetId, "ModelRightsAssessment.sourceAssetId"),
|
|
805
805
|
sourceContentHash: requiredSha256(
|
|
806
|
-
|
|
806
|
+
record2.sourceContentHash,
|
|
807
807
|
"ModelRightsAssessment.sourceContentHash"
|
|
808
808
|
),
|
|
809
809
|
status,
|
|
810
|
-
licenseId: requiredToken(
|
|
811
|
-
evidencePageUri: assertPublicPageUri(
|
|
810
|
+
licenseId: requiredToken(record2.licenseId, "ModelRightsAssessment.licenseId"),
|
|
811
|
+
evidencePageUri: assertPublicPageUri(record2.evidencePageUri, "ModelRightsAssessment.evidencePageUri"),
|
|
812
812
|
...attribution === void 0 ? {} : { attribution },
|
|
813
|
-
reviewedAt: requiredTimestamp(
|
|
813
|
+
reviewedAt: requiredTimestamp(record2.reviewedAt, "ModelRightsAssessment.reviewedAt")
|
|
814
814
|
});
|
|
815
815
|
}
|
|
816
816
|
function createModelProvenance(input) {
|
|
817
|
-
const
|
|
818
|
-
assertAllowedKeys(
|
|
817
|
+
const record2 = assertRecord(input, "ModelProvenance");
|
|
818
|
+
assertAllowedKeys(record2, [
|
|
819
819
|
"kind",
|
|
820
820
|
"sourceId",
|
|
821
821
|
"sourceAssetId",
|
|
@@ -823,9 +823,9 @@ function createModelProvenance(input) {
|
|
|
823
823
|
"contentHash",
|
|
824
824
|
"capturedAt"
|
|
825
825
|
], "ModelProvenance");
|
|
826
|
-
const kind = requiredEnum(
|
|
827
|
-
const sourceAssetId = opaqueSourceAssetId(
|
|
828
|
-
const sourcePageUri =
|
|
826
|
+
const kind = requiredEnum(record2.kind, MODEL_PROVENANCE_KINDS, "ModelProvenance kind");
|
|
827
|
+
const sourceAssetId = opaqueSourceAssetId(record2.sourceAssetId);
|
|
828
|
+
const sourcePageUri = record2.sourcePageUri === void 0 ? void 0 : assertPublicPageUri(record2.sourcePageUri, "ModelProvenance.sourcePageUri");
|
|
829
829
|
if (kind === "provider" && sourcePageUri === void 0) {
|
|
830
830
|
throw new Error("ModelProvenance provider provenance requires sourcePageUri.");
|
|
831
831
|
}
|
|
@@ -834,16 +834,16 @@ function createModelProvenance(input) {
|
|
|
834
834
|
}
|
|
835
835
|
return deepFreeze({
|
|
836
836
|
kind,
|
|
837
|
-
sourceId: requiredToken(
|
|
837
|
+
sourceId: requiredToken(record2.sourceId, "ModelProvenance.sourceId"),
|
|
838
838
|
sourceAssetId,
|
|
839
839
|
...sourcePageUri === void 0 ? {} : { sourcePageUri },
|
|
840
|
-
contentHash: requiredSha256(
|
|
841
|
-
capturedAt: requiredTimestamp(
|
|
840
|
+
contentHash: requiredSha256(record2.contentHash, "ModelProvenance.contentHash"),
|
|
841
|
+
capturedAt: requiredTimestamp(record2.capturedAt, "ModelProvenance.capturedAt")
|
|
842
842
|
});
|
|
843
843
|
}
|
|
844
844
|
function createModelAssetRef(input) {
|
|
845
|
-
const
|
|
846
|
-
assertAllowedKeys(
|
|
845
|
+
const record2 = assertRecord(input, "ModelAssetRef");
|
|
846
|
+
assertAllowedKeys(record2, [
|
|
847
847
|
"contractVersion",
|
|
848
848
|
"assetId",
|
|
849
849
|
"version",
|
|
@@ -851,10 +851,10 @@ function createModelAssetRef(input) {
|
|
|
851
851
|
"contentHash",
|
|
852
852
|
"runtimeManifestUri"
|
|
853
853
|
], "ModelAssetRef");
|
|
854
|
-
assertContractVersion(
|
|
855
|
-
const validatedAssetId = assetId(
|
|
856
|
-
const validatedVersion = assertImmutableAssetVersion(
|
|
857
|
-
const runtimeManifestUri = assertModelUri(
|
|
854
|
+
assertContractVersion(record2, "ModelAssetRef");
|
|
855
|
+
const validatedAssetId = assetId(record2.assetId, "ModelAssetRef.assetId");
|
|
856
|
+
const validatedVersion = assertImmutableAssetVersion(record2.version);
|
|
857
|
+
const runtimeManifestUri = assertModelUri(record2.runtimeManifestUri, "ModelAssetRef.runtimeManifestUri");
|
|
858
858
|
const expectedManifestUri = `mcp://models/catalog/${validatedAssetId}/versions/${validatedVersion}/manifest`;
|
|
859
859
|
if (runtimeManifestUri !== expectedManifestUri) {
|
|
860
860
|
throw new Error(
|
|
@@ -865,21 +865,21 @@ function createModelAssetRef(input) {
|
|
|
865
865
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
866
866
|
assetId: validatedAssetId,
|
|
867
867
|
version: validatedVersion,
|
|
868
|
-
kind: requiredEnum(
|
|
869
|
-
contentHash: requiredSha256(
|
|
868
|
+
kind: requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelAssetRef.kind"),
|
|
869
|
+
contentHash: requiredSha256(record2.contentHash, "ModelAssetRef.contentHash"),
|
|
870
870
|
runtimeManifestUri
|
|
871
871
|
});
|
|
872
872
|
}
|
|
873
873
|
function createCandidateAssetRef(value) {
|
|
874
|
-
const
|
|
875
|
-
const disposition = requiredEnum(
|
|
874
|
+
const record2 = assertRecord(value, "ModelCandidate.assetRef");
|
|
875
|
+
const disposition = requiredEnum(record2.disposition, ["existing", "proposed"], "ModelCandidate.assetRef.disposition");
|
|
876
876
|
if (disposition === "existing") {
|
|
877
|
-
assertAllowedKeys(
|
|
878
|
-
const asset = createModelAssetRef(
|
|
879
|
-
if (
|
|
877
|
+
assertAllowedKeys(record2, ["disposition", "asset", "kind", "contentHash"], "ModelCandidate.assetRef");
|
|
878
|
+
const asset = createModelAssetRef(record2.asset);
|
|
879
|
+
if (record2.kind !== void 0 && record2.kind !== asset.kind) {
|
|
880
880
|
throw new Error("ModelCandidate existing assetRef kind must match asset.");
|
|
881
881
|
}
|
|
882
|
-
if (
|
|
882
|
+
if (record2.contentHash !== void 0 && record2.contentHash !== asset.contentHash) {
|
|
883
883
|
throw new Error("ModelCandidate existing assetRef contentHash must match asset.");
|
|
884
884
|
}
|
|
885
885
|
return {
|
|
@@ -889,27 +889,27 @@ function createCandidateAssetRef(value) {
|
|
|
889
889
|
asset
|
|
890
890
|
};
|
|
891
891
|
}
|
|
892
|
-
assertAllowedKeys(
|
|
892
|
+
assertAllowedKeys(record2, ["disposition", "proposalId", "kind", "contentHash"], "ModelCandidate.assetRef");
|
|
893
893
|
return {
|
|
894
894
|
disposition,
|
|
895
|
-
proposalId: requiredToken(
|
|
896
|
-
kind: requiredEnum(
|
|
897
|
-
contentHash: requiredSha256(
|
|
895
|
+
proposalId: requiredToken(record2.proposalId, "ModelCandidate.assetRef.proposalId"),
|
|
896
|
+
kind: requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelCandidate.assetRef.kind"),
|
|
897
|
+
contentHash: requiredSha256(record2.contentHash, "ModelCandidate.assetRef.contentHash")
|
|
898
898
|
};
|
|
899
899
|
}
|
|
900
900
|
function createConfirmationView(value, index) {
|
|
901
901
|
const fieldName = `ModelCandidate.views[${index}]`;
|
|
902
|
-
const
|
|
903
|
-
assertAllowedKeys(
|
|
904
|
-
const width = requiredInteger(
|
|
905
|
-
const height = requiredInteger(
|
|
906
|
-
if (
|
|
902
|
+
const record2 = assertRecord(value, fieldName);
|
|
903
|
+
assertAllowedKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
904
|
+
const width = requiredInteger(record2.width, `${fieldName}.width`, MODEL_CONFIRMATION_VIEW_SIZE_PX, MODEL_CONFIRMATION_VIEW_SIZE_PX);
|
|
905
|
+
const height = requiredInteger(record2.height, `${fieldName}.height`, MODEL_CONFIRMATION_VIEW_SIZE_PX, MODEL_CONFIRMATION_VIEW_SIZE_PX);
|
|
906
|
+
if (record2.contentType !== "image/png") {
|
|
907
907
|
throw new Error(`${fieldName}.contentType must be image/png.`);
|
|
908
908
|
}
|
|
909
909
|
return {
|
|
910
|
-
kind: requiredEnum(
|
|
911
|
-
imageUri: assertModelUri(
|
|
912
|
-
sha256: requiredSha256(
|
|
910
|
+
kind: requiredEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`),
|
|
911
|
+
imageUri: assertModelUri(record2.imageUri, `${fieldName}.imageUri`),
|
|
912
|
+
sha256: requiredSha256(record2.sha256, `${fieldName}.sha256`),
|
|
913
913
|
contentType: "image/png",
|
|
914
914
|
width,
|
|
915
915
|
height
|
|
@@ -943,8 +943,8 @@ function createConfirmationViews(value, resolutionId, candidateId) {
|
|
|
943
943
|
return [ordered[0], ordered[1], ordered[2], ordered[3]];
|
|
944
944
|
}
|
|
945
945
|
function createRenderEvidence(value, manifest, views) {
|
|
946
|
-
const
|
|
947
|
-
assertAllowedKeys(
|
|
946
|
+
const record2 = assertRecord(value, "ModelCandidate.renderEvidence");
|
|
947
|
+
assertAllowedKeys(record2, [
|
|
948
948
|
"renderId",
|
|
949
949
|
"rendererId",
|
|
950
950
|
"rendererVersion",
|
|
@@ -956,35 +956,35 @@ function createRenderEvidence(value, manifest, views) {
|
|
|
956
956
|
"renderedAt",
|
|
957
957
|
"attestationToken"
|
|
958
958
|
], "ModelCandidate.renderEvidence");
|
|
959
|
-
if (
|
|
959
|
+
if (record2.processingManifestId !== manifest.manifestId) {
|
|
960
960
|
throw new Error("ModelCandidate.renderEvidence.processingManifestId must match processingManifest.");
|
|
961
961
|
}
|
|
962
|
-
if (
|
|
962
|
+
if (record2.sourceContentHash !== manifest.lods[0]?.resource.sha256) {
|
|
963
963
|
throw new Error("ModelCandidate.renderEvidence.sourceContentHash must match canonical LOD0.");
|
|
964
964
|
}
|
|
965
|
-
if (!Array.isArray(
|
|
965
|
+
if (!Array.isArray(record2.viewSha256s) || record2.viewSha256s.length !== 4) {
|
|
966
966
|
throw new Error("ModelCandidate.renderEvidence.viewSha256s must contain four ordered hashes.");
|
|
967
967
|
}
|
|
968
|
-
const viewSha256s =
|
|
968
|
+
const viewSha256s = record2.viewSha256s.map((digest, index) => requiredSha256(digest, `ModelCandidate.renderEvidence.viewSha256s[${index}]`));
|
|
969
969
|
if (viewSha256s.some((digest, index) => digest !== views[index]?.sha256)) {
|
|
970
970
|
throw new Error("ModelCandidate.renderEvidence view hashes must match the candidate view pack.");
|
|
971
971
|
}
|
|
972
|
-
const renderedAt = requiredTimestamp(
|
|
972
|
+
const renderedAt = requiredTimestamp(record2.renderedAt, "ModelCandidate.renderEvidence.renderedAt");
|
|
973
973
|
if (Date.parse(renderedAt) < Date.parse(manifest.processedAt)) {
|
|
974
974
|
throw new Error("ModelCandidate.renderEvidence.renderedAt must not precede processing.");
|
|
975
975
|
}
|
|
976
976
|
return {
|
|
977
|
-
renderId: requiredToken(
|
|
978
|
-
rendererId: requiredToken(
|
|
979
|
-
rendererVersion: assetVersion(
|
|
980
|
-
settingsId: requiredToken(
|
|
981
|
-
settingsVersion: assetVersion(
|
|
977
|
+
renderId: requiredToken(record2.renderId, "ModelCandidate.renderEvidence.renderId"),
|
|
978
|
+
rendererId: requiredToken(record2.rendererId, "ModelCandidate.renderEvidence.rendererId"),
|
|
979
|
+
rendererVersion: assetVersion(record2.rendererVersion, "ModelCandidate.renderEvidence.rendererVersion"),
|
|
980
|
+
settingsId: requiredToken(record2.settingsId, "ModelCandidate.renderEvidence.settingsId"),
|
|
981
|
+
settingsVersion: assetVersion(record2.settingsVersion, "ModelCandidate.renderEvidence.settingsVersion"),
|
|
982
982
|
processingManifestId: manifest.manifestId,
|
|
983
983
|
sourceContentHash: manifest.contentHash,
|
|
984
984
|
viewSha256s: [viewSha256s[0], viewSha256s[1], viewSha256s[2], viewSha256s[3]],
|
|
985
985
|
renderedAt,
|
|
986
986
|
attestationToken: requiredAttestationToken(
|
|
987
|
-
|
|
987
|
+
record2.attestationToken,
|
|
988
988
|
"ModelCandidate.renderEvidence.attestationToken"
|
|
989
989
|
)
|
|
990
990
|
};
|
|
@@ -995,8 +995,8 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
995
995
|
}
|
|
996
996
|
const gates = value.map((item, index) => {
|
|
997
997
|
const fieldName = `ModelCandidate.hardGates[${index}]`;
|
|
998
|
-
const
|
|
999
|
-
assertAllowedKeys(
|
|
998
|
+
const record2 = assertRecord(item, fieldName);
|
|
999
|
+
assertAllowedKeys(record2, [
|
|
1000
1000
|
"kind",
|
|
1001
1001
|
"outcome",
|
|
1002
1002
|
"validatorId",
|
|
@@ -1006,26 +1006,26 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1006
1006
|
"evaluatedAt",
|
|
1007
1007
|
"attestationToken"
|
|
1008
1008
|
], fieldName);
|
|
1009
|
-
const kind = requiredEnum(
|
|
1010
|
-
const subjectContentHash = requiredSha256(
|
|
1009
|
+
const kind = requiredEnum(record2.kind, MODEL_CANDIDATE_HARD_GATE_KINDS, `${fieldName}.kind`);
|
|
1010
|
+
const subjectContentHash = requiredSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`);
|
|
1011
1011
|
const expectedHash = kind === "malware-scan" ? provenance.contentHash : manifest.closureHash;
|
|
1012
1012
|
if (subjectContentHash !== expectedHash) {
|
|
1013
1013
|
throw new Error(`${fieldName}.subjectContentHash must match the gate subject.`);
|
|
1014
1014
|
}
|
|
1015
|
-
const outcome = requiredEnum(
|
|
1016
|
-
const reasonCodes2 = stringList(
|
|
1015
|
+
const outcome = requiredEnum(record2.outcome, ["passed", "blocked"], `${fieldName}.outcome`);
|
|
1016
|
+
const reasonCodes2 = stringList(record2.reasonCodes ?? [], `${fieldName}.reasonCodes`, { tokens: true });
|
|
1017
1017
|
if (outcome === "blocked" && reasonCodes2.length === 0) {
|
|
1018
1018
|
throw new Error(`${fieldName}.reasonCodes must explain a blocked hard gate.`);
|
|
1019
1019
|
}
|
|
1020
1020
|
return {
|
|
1021
1021
|
kind,
|
|
1022
1022
|
outcome,
|
|
1023
|
-
validatorId: requiredToken(
|
|
1024
|
-
validatorVersion: assetVersion(
|
|
1023
|
+
validatorId: requiredToken(record2.validatorId, `${fieldName}.validatorId`),
|
|
1024
|
+
validatorVersion: assetVersion(record2.validatorVersion, `${fieldName}.validatorVersion`),
|
|
1025
1025
|
subjectContentHash,
|
|
1026
1026
|
reasonCodes: reasonCodes2,
|
|
1027
|
-
evaluatedAt: requiredTimestamp(
|
|
1028
|
-
attestationToken: requiredAttestationToken(
|
|
1027
|
+
evaluatedAt: requiredTimestamp(record2.evaluatedAt, `${fieldName}.evaluatedAt`),
|
|
1028
|
+
attestationToken: requiredAttestationToken(record2.attestationToken, `${fieldName}.attestationToken`)
|
|
1029
1029
|
};
|
|
1030
1030
|
});
|
|
1031
1031
|
if (new Set(gates.map((gate) => gate.kind)).size !== MODEL_CANDIDATE_HARD_GATE_KINDS.length) {
|
|
@@ -1038,8 +1038,8 @@ function createCandidateHardGates(value, provenance, manifest) {
|
|
|
1038
1038
|
return MODEL_CANDIDATE_HARD_GATE_KINDS.map((kind) => byKind.get(kind));
|
|
1039
1039
|
}
|
|
1040
1040
|
function createModelCandidate(input) {
|
|
1041
|
-
const
|
|
1042
|
-
assertAllowedKeys(
|
|
1041
|
+
const record2 = assertRecord(input, "ModelCandidate");
|
|
1042
|
+
assertAllowedKeys(record2, [
|
|
1043
1043
|
"contractVersion",
|
|
1044
1044
|
"resolutionId",
|
|
1045
1045
|
"candidateId",
|
|
@@ -1055,24 +1055,24 @@ function createModelCandidate(input) {
|
|
|
1055
1055
|
"confirmationToken",
|
|
1056
1056
|
"confirmationRequired"
|
|
1057
1057
|
], "ModelCandidate");
|
|
1058
|
-
assertContractVersion(
|
|
1059
|
-
if (
|
|
1058
|
+
assertContractVersion(record2, "ModelCandidate");
|
|
1059
|
+
if (record2.confirmationRequired !== void 0 && record2.confirmationRequired !== true) {
|
|
1060
1060
|
throw new Error("ModelCandidate confirmationRequired must always be true.");
|
|
1061
1061
|
}
|
|
1062
|
-
if (typeof
|
|
1062
|
+
if (typeof record2.confirmationToken !== "string" || !CONFIRMATION_TOKEN_PATTERN.test(record2.confirmationToken)) {
|
|
1063
1063
|
throw new Error("ModelCandidate confirmationToken must be a bounded opaque token.");
|
|
1064
1064
|
}
|
|
1065
|
-
const resolutionId = requiredModelPathSegment(
|
|
1066
|
-
const candidateId = requiredModelPathSegment(
|
|
1067
|
-
const assetRef = createCandidateAssetRef(
|
|
1068
|
-
const technicalProfile = createModelTechnicalProfile(
|
|
1069
|
-
const processingManifest = createModelProcessingManifest(
|
|
1065
|
+
const resolutionId = requiredModelPathSegment(record2.resolutionId, "ModelCandidate.resolutionId");
|
|
1066
|
+
const candidateId = requiredModelPathSegment(record2.candidateId, "ModelCandidate.candidateId");
|
|
1067
|
+
const assetRef = createCandidateAssetRef(record2.assetRef);
|
|
1068
|
+
const technicalProfile = createModelTechnicalProfile(record2.technicalProfile);
|
|
1069
|
+
const processingManifest = createModelProcessingManifest(record2.processingManifest);
|
|
1070
1070
|
if (processingManifest.resolutionId !== resolutionId || processingManifest.candidateId !== candidateId || processingManifest.kind !== assetRef.kind || processingManifest.contentHash !== assetRef.contentHash || JSON.stringify(processingManifest.technicalProfile) !== JSON.stringify(technicalProfile)) {
|
|
1071
1071
|
throw new Error(
|
|
1072
1072
|
"ModelCandidate processingManifest must match candidateId, asset kind/contentHash, and technicalProfile."
|
|
1073
1073
|
);
|
|
1074
1074
|
}
|
|
1075
|
-
const match = createModelMatchAssessment(
|
|
1075
|
+
const match = createModelMatchAssessment(record2.match);
|
|
1076
1076
|
if (match.candidateId !== candidateId || match.candidateContentHash !== assetRef.contentHash) {
|
|
1077
1077
|
throw new Error("ModelCandidate match assessment must bind candidateId and candidate contentHash.");
|
|
1078
1078
|
}
|
|
@@ -1092,11 +1092,11 @@ function createModelCandidate(input) {
|
|
|
1092
1092
|
"ModelCandidate match reasonCodes must include every failed hard constraint."
|
|
1093
1093
|
);
|
|
1094
1094
|
}
|
|
1095
|
-
const provenance = createModelProvenance(
|
|
1095
|
+
const provenance = createModelProvenance(record2.provenance);
|
|
1096
1096
|
if (provenance.contentHash !== processingManifest.converter.sourceContentHash) {
|
|
1097
1097
|
throw new Error("ModelCandidate provenance contentHash must match converter sourceContentHash.");
|
|
1098
1098
|
}
|
|
1099
|
-
const rights = createModelRightsAssessment(
|
|
1099
|
+
const rights = createModelRightsAssessment(record2.rights);
|
|
1100
1100
|
if (rights.sourceId !== provenance.sourceId || rights.sourceAssetId !== provenance.sourceAssetId || rights.sourceContentHash !== provenance.contentHash) {
|
|
1101
1101
|
throw new Error("ModelCandidate rights decision must bind the exact provenance source and content hash.");
|
|
1102
1102
|
}
|
|
@@ -1108,9 +1108,9 @@ function createModelCandidate(input) {
|
|
|
1108
1108
|
if (processingManifest.fidelityGate.outcome === "blocked" && !match.reasonCodes.includes("fidelity-blocked")) {
|
|
1109
1109
|
throw new Error("ModelCandidate blocked fidelity must be recorded in match reasonCodes.");
|
|
1110
1110
|
}
|
|
1111
|
-
const views = createConfirmationViews(
|
|
1112
|
-
const renderEvidence = createRenderEvidence(
|
|
1113
|
-
const hardGates = createCandidateHardGates(
|
|
1111
|
+
const views = createConfirmationViews(record2.views, resolutionId, candidateId);
|
|
1112
|
+
const renderEvidence = createRenderEvidence(record2.renderEvidence, processingManifest, views);
|
|
1113
|
+
const hardGates = createCandidateHardGates(record2.hardGates, provenance, processingManifest);
|
|
1114
1114
|
return deepFreeze({
|
|
1115
1115
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1116
1116
|
resolutionId,
|
|
@@ -1124,7 +1124,7 @@ function createModelCandidate(input) {
|
|
|
1124
1124
|
views,
|
|
1125
1125
|
renderEvidence,
|
|
1126
1126
|
hardGates,
|
|
1127
|
-
confirmationToken:
|
|
1127
|
+
confirmationToken: record2.confirmationToken,
|
|
1128
1128
|
confirmationRequired: true
|
|
1129
1129
|
});
|
|
1130
1130
|
}
|
|
@@ -1136,8 +1136,8 @@ function isModelCandidateConfirmable(candidate) {
|
|
|
1136
1136
|
return candidate.confirmationRequired && candidate.match.hardConstraintPass && candidate.match.assurance !== "none" && (candidate.rights.status === "allowed" || candidate.rights.status === "attribution-required") && !blockingEvidence && candidate.processingManifest.fidelityGate.outcome !== "blocked" && candidate.hardGates.length === MODEL_CANDIDATE_HARD_GATE_KINDS.length && candidate.hardGates.every((gate) => gate.outcome === "passed") && candidate.views.length === MODEL_CONFIRMATION_VIEW_KINDS.length;
|
|
1137
1137
|
}
|
|
1138
1138
|
function createModelCandidateConfirmation(input, expectedCandidate, expectedResolutionId) {
|
|
1139
|
-
const
|
|
1140
|
-
assertAllowedKeys(
|
|
1139
|
+
const record2 = assertRecord(input, "ModelCandidateConfirmation");
|
|
1140
|
+
assertAllowedKeys(record2, [
|
|
1141
1141
|
"contractVersion",
|
|
1142
1142
|
"confirmationId",
|
|
1143
1143
|
"resolutionId",
|
|
@@ -1148,7 +1148,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1148
1148
|
"confirmedAt",
|
|
1149
1149
|
"semanticRiskAccepted"
|
|
1150
1150
|
], "ModelCandidateConfirmation");
|
|
1151
|
-
assertContractVersion(
|
|
1151
|
+
assertContractVersion(record2, "ModelCandidateConfirmation");
|
|
1152
1152
|
const candidate = createModelCandidate(expectedCandidate);
|
|
1153
1153
|
const resolutionId = requiredModelPathSegment(
|
|
1154
1154
|
expectedResolutionId,
|
|
@@ -1159,19 +1159,19 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1159
1159
|
"ModelCandidateConfirmation expected candidate must belong to the expected resolutionId."
|
|
1160
1160
|
);
|
|
1161
1161
|
}
|
|
1162
|
-
if (
|
|
1162
|
+
if (record2.resolutionId !== resolutionId) {
|
|
1163
1163
|
throw new Error("ModelCandidateConfirmation.resolutionId must match the expected resolutionId.");
|
|
1164
1164
|
}
|
|
1165
|
-
if (
|
|
1165
|
+
if (record2.candidateId !== candidate.candidateId) {
|
|
1166
1166
|
throw new Error("ModelCandidateConfirmation.candidateId must match the confirmed candidate.");
|
|
1167
1167
|
}
|
|
1168
|
-
if (typeof
|
|
1168
|
+
if (typeof record2.confirmationToken !== "string" || !CONFIRMATION_TOKEN_PATTERN.test(record2.confirmationToken) || record2.confirmationToken !== candidate.confirmationToken) {
|
|
1169
1169
|
throw new Error("ModelCandidateConfirmation.confirmationToken must match the signed candidate token.");
|
|
1170
1170
|
}
|
|
1171
|
-
if (!Array.isArray(
|
|
1171
|
+
if (!Array.isArray(record2.viewSha256s) || record2.viewSha256s.length !== 4) {
|
|
1172
1172
|
throw new Error("ModelCandidateConfirmation.viewSha256s must contain the ordered four candidate view hashes.");
|
|
1173
1173
|
}
|
|
1174
|
-
const viewSha256s =
|
|
1174
|
+
const viewSha256s = record2.viewSha256s.map((value, index) => requiredSha256(value, `ModelCandidateConfirmation.viewSha256s[${index}]`));
|
|
1175
1175
|
const expectedHashes = candidate.views.map((view) => view.sha256);
|
|
1176
1176
|
if (viewSha256s.some((digest, index) => digest !== expectedHashes[index])) {
|
|
1177
1177
|
throw new Error("ModelCandidateConfirmation view hashes must match the ordered candidate views.");
|
|
@@ -1180,7 +1180,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1180
1180
|
throw new Error("ModelCandidateConfirmation candidate is not confirmable because a hard review gate is blocking.");
|
|
1181
1181
|
}
|
|
1182
1182
|
const semanticRiskAccepted = requiredBoolean(
|
|
1183
|
-
|
|
1183
|
+
record2.semanticRiskAccepted,
|
|
1184
1184
|
"ModelCandidateConfirmation.semanticRiskAccepted"
|
|
1185
1185
|
);
|
|
1186
1186
|
if (candidate.match.assurance === "low" && !semanticRiskAccepted) {
|
|
@@ -1189,10 +1189,10 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1189
1189
|
if (candidate.match.assurance === "high" && semanticRiskAccepted) {
|
|
1190
1190
|
throw new Error("ModelCandidateConfirmation high assurance must not record a semantic risk override.");
|
|
1191
1191
|
}
|
|
1192
|
-
const confirmedBy = requiredString(
|
|
1192
|
+
const confirmedBy = requiredString(record2.confirmedBy, "ModelCandidateConfirmation.confirmedBy", 256);
|
|
1193
1193
|
assertNoDirectUrl(confirmedBy, "ModelCandidateConfirmation.confirmedBy");
|
|
1194
1194
|
const confirmedAt = requiredTimestamp(
|
|
1195
|
-
|
|
1195
|
+
record2.confirmedAt,
|
|
1196
1196
|
"ModelCandidateConfirmation.confirmedAt"
|
|
1197
1197
|
);
|
|
1198
1198
|
const latestEvidenceTime = Math.max(
|
|
@@ -1210,7 +1210,7 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1210
1210
|
}
|
|
1211
1211
|
return deepFreeze({
|
|
1212
1212
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1213
|
-
confirmationId: requiredToken(
|
|
1213
|
+
confirmationId: requiredToken(record2.confirmationId, "ModelCandidateConfirmation.confirmationId"),
|
|
1214
1214
|
resolutionId,
|
|
1215
1215
|
candidateId: candidate.candidateId,
|
|
1216
1216
|
confirmationToken: candidate.confirmationToken,
|
|
@@ -1221,33 +1221,33 @@ function createModelCandidateConfirmation(input, expectedCandidate, expectedReso
|
|
|
1221
1221
|
});
|
|
1222
1222
|
}
|
|
1223
1223
|
function createModelResourceRef(input) {
|
|
1224
|
-
const
|
|
1225
|
-
assertAllowedKeys(
|
|
1226
|
-
const
|
|
1227
|
-
if (!CONTENT_TYPE_PATTERN.test(
|
|
1224
|
+
const record2 = assertRecord(input, "ModelResourceRef");
|
|
1225
|
+
assertAllowedKeys(record2, ["uri", "byteLength", "sha256", "contentType"], "ModelResourceRef");
|
|
1226
|
+
const contentType2 = requiredString(record2.contentType, "ModelResourceRef.contentType", 128);
|
|
1227
|
+
if (!CONTENT_TYPE_PATTERN.test(contentType2)) {
|
|
1228
1228
|
throw new Error("ModelResourceRef.contentType must be a valid media type.");
|
|
1229
1229
|
}
|
|
1230
1230
|
return deepFreeze({
|
|
1231
|
-
uri: assertModelUri(
|
|
1232
|
-
byteLength: requiredInteger(
|
|
1233
|
-
sha256: requiredSha256(
|
|
1234
|
-
contentType
|
|
1231
|
+
uri: assertModelUri(record2.uri, "ModelResourceRef.uri"),
|
|
1232
|
+
byteLength: requiredInteger(record2.byteLength, "ModelResourceRef.byteLength", 1, Number.MAX_SAFE_INTEGER),
|
|
1233
|
+
sha256: requiredSha256(record2.sha256, "ModelResourceRef.sha256"),
|
|
1234
|
+
contentType: contentType2
|
|
1235
1235
|
});
|
|
1236
1236
|
}
|
|
1237
1237
|
function createLodRecord(value, index) {
|
|
1238
1238
|
const fieldName = `ModelProcessingManifest.lods[${index}]`;
|
|
1239
|
-
const
|
|
1240
|
-
assertAllowedKeys(
|
|
1241
|
-
const resource = createModelResourceRef(
|
|
1239
|
+
const record2 = assertRecord(value, fieldName);
|
|
1240
|
+
assertAllowedKeys(record2, ["level", "resource", "triangleCount", "geometricErrorMetres"], fieldName);
|
|
1241
|
+
const resource = createModelResourceRef(record2.resource);
|
|
1242
1242
|
if (resource.contentType !== "model/gltf-binary") {
|
|
1243
1243
|
throw new Error(`${fieldName}.resource.contentType must be model/gltf-binary for canonical GLB output.`);
|
|
1244
1244
|
}
|
|
1245
1245
|
return {
|
|
1246
|
-
level: requiredInteger(
|
|
1246
|
+
level: requiredInteger(record2.level, `${fieldName}.level`, 0, 3),
|
|
1247
1247
|
resource,
|
|
1248
|
-
triangleCount: requiredInteger(
|
|
1248
|
+
triangleCount: requiredInteger(record2.triangleCount, `${fieldName}.triangleCount`, 1, 1e9),
|
|
1249
1249
|
geometricErrorMetres: requiredFiniteNumber(
|
|
1250
|
-
|
|
1250
|
+
record2.geometricErrorMetres,
|
|
1251
1251
|
`${fieldName}.geometricErrorMetres`,
|
|
1252
1252
|
0,
|
|
1253
1253
|
1e6
|
|
@@ -1255,40 +1255,40 @@ function createLodRecord(value, index) {
|
|
|
1255
1255
|
};
|
|
1256
1256
|
}
|
|
1257
1257
|
function createCollisionRecord(value) {
|
|
1258
|
-
const
|
|
1259
|
-
assertAllowedKeys(
|
|
1258
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.collision");
|
|
1259
|
+
assertAllowedKeys(record2, ["kind", "resource"], "ModelProcessingManifest.collision");
|
|
1260
1260
|
const kind = requiredEnum(
|
|
1261
|
-
|
|
1261
|
+
record2.kind,
|
|
1262
1262
|
["none", "box", "convex-hull", "triangle-mesh", "compound"],
|
|
1263
1263
|
"ModelProcessingManifest.collision.kind"
|
|
1264
1264
|
);
|
|
1265
1265
|
if (kind === "none") {
|
|
1266
|
-
if (
|
|
1266
|
+
if (record2.resource !== void 0) {
|
|
1267
1267
|
throw new Error("ModelProcessingManifest collision kind none must not include a resource.");
|
|
1268
1268
|
}
|
|
1269
1269
|
return { kind };
|
|
1270
1270
|
}
|
|
1271
|
-
if (
|
|
1271
|
+
if (record2.resource === void 0) {
|
|
1272
1272
|
throw new Error("ModelProcessingManifest non-empty collision kinds require a resource.");
|
|
1273
1273
|
}
|
|
1274
|
-
const resource = createModelResourceRef(
|
|
1274
|
+
const resource = createModelResourceRef(record2.resource);
|
|
1275
1275
|
if (resource.contentType !== "model/gltf-binary") {
|
|
1276
1276
|
throw new Error("ModelProcessingManifest collision resources must be model/gltf-binary GLBs.");
|
|
1277
1277
|
}
|
|
1278
1278
|
return { kind, resource };
|
|
1279
1279
|
}
|
|
1280
1280
|
function createTransform(value, fieldName) {
|
|
1281
|
-
const
|
|
1282
|
-
assertAllowedKeys(
|
|
1283
|
-
const translationMetres = requiredVector3(
|
|
1284
|
-
if (!Array.isArray(
|
|
1281
|
+
const record2 = assertRecord(value, fieldName);
|
|
1282
|
+
assertAllowedKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
1283
|
+
const translationMetres = requiredVector3(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
1284
|
+
if (!Array.isArray(record2.rotationQuaternion) || record2.rotationQuaternion.length !== 4) {
|
|
1285
1285
|
throw new Error(`${fieldName}.rotationQuaternion must contain exactly four numbers.`);
|
|
1286
1286
|
}
|
|
1287
1287
|
const rotationQuaternion = [
|
|
1288
|
-
requiredFiniteNumber(
|
|
1289
|
-
requiredFiniteNumber(
|
|
1290
|
-
requiredFiniteNumber(
|
|
1291
|
-
requiredFiniteNumber(
|
|
1288
|
+
requiredFiniteNumber(record2.rotationQuaternion[0], `${fieldName}.rotationQuaternion[0]`, -1, 1),
|
|
1289
|
+
requiredFiniteNumber(record2.rotationQuaternion[1], `${fieldName}.rotationQuaternion[1]`, -1, 1),
|
|
1290
|
+
requiredFiniteNumber(record2.rotationQuaternion[2], `${fieldName}.rotationQuaternion[2]`, -1, 1),
|
|
1291
|
+
requiredFiniteNumber(record2.rotationQuaternion[3], `${fieldName}.rotationQuaternion[3]`, -1, 1)
|
|
1292
1292
|
];
|
|
1293
1293
|
const magnitude = Math.hypot(...rotationQuaternion);
|
|
1294
1294
|
if (Math.abs(magnitude - 1) > 1e-4) {
|
|
@@ -1297,31 +1297,31 @@ function createTransform(value, fieldName) {
|
|
|
1297
1297
|
return {
|
|
1298
1298
|
translationMetres,
|
|
1299
1299
|
rotationQuaternion,
|
|
1300
|
-
scale: requiredVector3(
|
|
1300
|
+
scale: requiredVector3(record2.scale, `${fieldName}.scale`, true)
|
|
1301
1301
|
};
|
|
1302
1302
|
}
|
|
1303
1303
|
function createAssemblyChildAssetRef(value, resolutionId, candidateId, fieldName) {
|
|
1304
|
-
const
|
|
1305
|
-
if (
|
|
1304
|
+
const record2 = assertRecord(value, fieldName);
|
|
1305
|
+
if (record2.disposition !== "staged-derived") {
|
|
1306
1306
|
const catalogRef = createModelAssetRef(value);
|
|
1307
1307
|
if (catalogRef.kind !== "leaf") {
|
|
1308
1308
|
throw new Error("ModelProcessingManifest v1 assembly child assetRef values must be leaf assets.");
|
|
1309
1309
|
}
|
|
1310
1310
|
return catalogRef;
|
|
1311
1311
|
}
|
|
1312
|
-
assertAllowedKeys(
|
|
1312
|
+
assertAllowedKeys(record2, [
|
|
1313
1313
|
"disposition",
|
|
1314
1314
|
"derivedId",
|
|
1315
1315
|
"kind",
|
|
1316
1316
|
"contentHash",
|
|
1317
1317
|
"processingManifestUri"
|
|
1318
1318
|
], fieldName);
|
|
1319
|
-
if (
|
|
1319
|
+
if (record2.kind !== "leaf") {
|
|
1320
1320
|
throw new Error("ModelProcessingManifest staged-derived child assetRef kind must be leaf.");
|
|
1321
1321
|
}
|
|
1322
|
-
const derivedId = requiredModelPathSegment(
|
|
1322
|
+
const derivedId = requiredModelPathSegment(record2.derivedId, `${fieldName}.derivedId`);
|
|
1323
1323
|
const processingManifestUri = assertModelUri(
|
|
1324
|
-
|
|
1324
|
+
record2.processingManifestUri,
|
|
1325
1325
|
`${fieldName}.processingManifestUri`
|
|
1326
1326
|
);
|
|
1327
1327
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/children/${derivedId}/manifest`;
|
|
@@ -1332,18 +1332,18 @@ function createAssemblyChildAssetRef(value, resolutionId, candidateId, fieldName
|
|
|
1332
1332
|
disposition: "staged-derived",
|
|
1333
1333
|
derivedId,
|
|
1334
1334
|
kind: "leaf",
|
|
1335
|
-
contentHash: requiredSha256(
|
|
1335
|
+
contentHash: requiredSha256(record2.contentHash, `${fieldName}.contentHash`),
|
|
1336
1336
|
processingManifestUri
|
|
1337
1337
|
};
|
|
1338
1338
|
}
|
|
1339
1339
|
function createAssemblyChild(value, index, resolutionId, candidateId) {
|
|
1340
1340
|
const fieldName = `ModelProcessingManifest.children[${index}]`;
|
|
1341
|
-
const
|
|
1342
|
-
assertAllowedKeys(
|
|
1343
|
-
const instanceId = requiredToken(
|
|
1344
|
-
const parentInstanceId =
|
|
1341
|
+
const record2 = assertRecord(value, fieldName);
|
|
1342
|
+
assertAllowedKeys(record2, ["instanceId", "parentInstanceId", "assetRef", "transform"], fieldName);
|
|
1343
|
+
const instanceId = requiredToken(record2.instanceId, `${fieldName}.instanceId`);
|
|
1344
|
+
const parentInstanceId = record2.parentInstanceId === void 0 ? void 0 : requiredToken(record2.parentInstanceId, `${fieldName}.parentInstanceId`);
|
|
1345
1345
|
const childAssetRef = createAssemblyChildAssetRef(
|
|
1346
|
-
|
|
1346
|
+
record2.assetRef,
|
|
1347
1347
|
resolutionId,
|
|
1348
1348
|
candidateId,
|
|
1349
1349
|
`${fieldName}.assetRef`
|
|
@@ -1352,21 +1352,21 @@ function createAssemblyChild(value, index, resolutionId, candidateId) {
|
|
|
1352
1352
|
instanceId,
|
|
1353
1353
|
...parentInstanceId === void 0 ? {} : { parentInstanceId },
|
|
1354
1354
|
assetRef: childAssetRef,
|
|
1355
|
-
transform: createTransform(
|
|
1355
|
+
transform: createTransform(record2.transform, `${fieldName}.transform`)
|
|
1356
1356
|
};
|
|
1357
1357
|
}
|
|
1358
1358
|
function createDiagnostic(value, fieldName) {
|
|
1359
|
-
const
|
|
1360
|
-
assertAllowedKeys(
|
|
1359
|
+
const record2 = assertRecord(value, fieldName);
|
|
1360
|
+
assertAllowedKeys(record2, ["severity", "code", "message"], fieldName);
|
|
1361
1361
|
return {
|
|
1362
|
-
severity: requiredEnum(
|
|
1363
|
-
code: requiredToken(
|
|
1364
|
-
message: requiredString(
|
|
1362
|
+
severity: requiredEnum(record2.severity, ["info", "warning", "blocking"], `${fieldName}.severity`),
|
|
1363
|
+
code: requiredToken(record2.code, `${fieldName}.code`),
|
|
1364
|
+
message: requiredString(record2.message, `${fieldName}.message`, 1024)
|
|
1365
1365
|
};
|
|
1366
1366
|
}
|
|
1367
1367
|
function createConverterEvidence(value) {
|
|
1368
|
-
const
|
|
1369
|
-
assertAllowedKeys(
|
|
1368
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.converter");
|
|
1369
|
+
assertAllowedKeys(record2, [
|
|
1370
1370
|
"id",
|
|
1371
1371
|
"version",
|
|
1372
1372
|
"sourceFormat",
|
|
@@ -1376,42 +1376,42 @@ function createConverterEvidence(value) {
|
|
|
1376
1376
|
"diagnostics",
|
|
1377
1377
|
"losses"
|
|
1378
1378
|
], "ModelProcessingManifest.converter");
|
|
1379
|
-
if (!Array.isArray(
|
|
1379
|
+
if (!Array.isArray(record2.diagnostics) || record2.diagnostics.length > 100) {
|
|
1380
1380
|
throw new Error("ModelProcessingManifest.converter.diagnostics must be a bounded array.");
|
|
1381
1381
|
}
|
|
1382
|
-
if (!Array.isArray(
|
|
1382
|
+
if (!Array.isArray(record2.losses) || record2.losses.length > 100) {
|
|
1383
1383
|
throw new Error("ModelProcessingManifest.converter.losses must be a bounded array.");
|
|
1384
1384
|
}
|
|
1385
1385
|
return {
|
|
1386
|
-
id: requiredToken(
|
|
1387
|
-
version: assetVersion(
|
|
1388
|
-
sourceFormat: requiredToken(
|
|
1389
|
-
targetFormat: requiredToken(
|
|
1390
|
-
sourceContentHash: requiredSha256(
|
|
1391
|
-
outputContentHash: requiredSha256(
|
|
1392
|
-
diagnostics:
|
|
1393
|
-
losses:
|
|
1386
|
+
id: requiredToken(record2.id, "ModelProcessingManifest.converter.id"),
|
|
1387
|
+
version: assetVersion(record2.version, "ModelProcessingManifest.converter.version"),
|
|
1388
|
+
sourceFormat: requiredToken(record2.sourceFormat, "ModelProcessingManifest.converter.sourceFormat"),
|
|
1389
|
+
targetFormat: requiredToken(record2.targetFormat, "ModelProcessingManifest.converter.targetFormat"),
|
|
1390
|
+
sourceContentHash: requiredSha256(record2.sourceContentHash, "ModelProcessingManifest.converter.sourceContentHash"),
|
|
1391
|
+
outputContentHash: requiredSha256(record2.outputContentHash, "ModelProcessingManifest.converter.outputContentHash"),
|
|
1392
|
+
diagnostics: record2.diagnostics.map((item, index) => createDiagnostic(item, `ModelProcessingManifest.converter.diagnostics[${index}]`)),
|
|
1393
|
+
losses: record2.losses.map((item, index) => createDiagnostic(item, `ModelProcessingManifest.converter.losses[${index}]`))
|
|
1394
1394
|
};
|
|
1395
1395
|
}
|
|
1396
1396
|
function createFidelityEvidence(value, index) {
|
|
1397
1397
|
const fieldName = `ModelProcessingManifest.fidelityEvidence[${index}]`;
|
|
1398
|
-
const
|
|
1399
|
-
assertAllowedKeys(
|
|
1400
|
-
const evidenceResource =
|
|
1398
|
+
const record2 = assertRecord(value, fieldName);
|
|
1399
|
+
assertAllowedKeys(record2, ["aspect", "outcome", "message", "evidenceResource"], fieldName);
|
|
1400
|
+
const evidenceResource = record2.evidenceResource === void 0 ? void 0 : createModelResourceRef(record2.evidenceResource);
|
|
1401
1401
|
return {
|
|
1402
1402
|
aspect: requiredEnum(
|
|
1403
|
-
|
|
1403
|
+
record2.aspect,
|
|
1404
1404
|
["geometry", "materials", "textures", "rigging", "animation", "metadata"],
|
|
1405
1405
|
`${fieldName}.aspect`
|
|
1406
1406
|
),
|
|
1407
|
-
outcome: requiredEnum(
|
|
1408
|
-
message: requiredString(
|
|
1407
|
+
outcome: requiredEnum(record2.outcome, ["preserved", "approximated", "lost"], `${fieldName}.outcome`),
|
|
1408
|
+
message: requiredString(record2.message, `${fieldName}.message`, 1024),
|
|
1409
1409
|
...evidenceResource === void 0 ? {} : { evidenceResource }
|
|
1410
1410
|
};
|
|
1411
1411
|
}
|
|
1412
1412
|
function createCollisionPolicyEvidence(value, collision) {
|
|
1413
|
-
const
|
|
1414
|
-
assertAllowedKeys(
|
|
1413
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.collisionPolicy");
|
|
1414
|
+
assertAllowedKeys(record2, [
|
|
1415
1415
|
"profileId",
|
|
1416
1416
|
"profileVersion",
|
|
1417
1417
|
"disposition",
|
|
@@ -1419,7 +1419,7 @@ function createCollisionPolicyEvidence(value, collision) {
|
|
|
1419
1419
|
"decisionToken"
|
|
1420
1420
|
], "ModelProcessingManifest.collisionPolicy");
|
|
1421
1421
|
const disposition = requiredEnum(
|
|
1422
|
-
|
|
1422
|
+
record2.disposition,
|
|
1423
1423
|
["proxy-required", "none-allowed"],
|
|
1424
1424
|
"ModelProcessingManifest.collisionPolicy.disposition"
|
|
1425
1425
|
);
|
|
@@ -1427,22 +1427,22 @@ function createCollisionPolicyEvidence(value, collision) {
|
|
|
1427
1427
|
throw new Error("ModelProcessingManifest collisionPolicy must explicitly authorize collision none or require a proxy.");
|
|
1428
1428
|
}
|
|
1429
1429
|
return {
|
|
1430
|
-
profileId: requiredToken(
|
|
1430
|
+
profileId: requiredToken(record2.profileId, "ModelProcessingManifest.collisionPolicy.profileId"),
|
|
1431
1431
|
profileVersion: assetVersion(
|
|
1432
|
-
|
|
1432
|
+
record2.profileVersion,
|
|
1433
1433
|
"ModelProcessingManifest.collisionPolicy.profileVersion"
|
|
1434
1434
|
),
|
|
1435
1435
|
disposition,
|
|
1436
|
-
category: requiredToken(
|
|
1436
|
+
category: requiredToken(record2.category, "ModelProcessingManifest.collisionPolicy.category"),
|
|
1437
1437
|
decisionToken: requiredAttestationToken(
|
|
1438
|
-
|
|
1438
|
+
record2.decisionToken,
|
|
1439
1439
|
"ModelProcessingManifest.collisionPolicy.decisionToken"
|
|
1440
1440
|
)
|
|
1441
1441
|
};
|
|
1442
1442
|
}
|
|
1443
1443
|
function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
1444
|
-
const
|
|
1445
|
-
assertAllowedKeys(
|
|
1444
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.fidelityGate");
|
|
1445
|
+
assertAllowedKeys(record2, [
|
|
1446
1446
|
"profileId",
|
|
1447
1447
|
"profileVersion",
|
|
1448
1448
|
"outcome",
|
|
@@ -1451,7 +1451,7 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1451
1451
|
"decisionToken"
|
|
1452
1452
|
], "ModelProcessingManifest.fidelityGate");
|
|
1453
1453
|
const expectedAspects = ["geometry", "materials", "textures"];
|
|
1454
|
-
if (!Array.isArray(
|
|
1454
|
+
if (!Array.isArray(record2.requiredAspects) || record2.requiredAspects.length !== expectedAspects.length || record2.requiredAspects.some((aspect, index) => aspect !== expectedAspects[index])) {
|
|
1455
1455
|
throw new Error("ModelProcessingManifest.fidelityGate.requiredAspects must be geometry, materials, textures.");
|
|
1456
1456
|
}
|
|
1457
1457
|
const byAspect = new Map(fidelityEvidence.map((evidence) => [evidence.aspect, evidence]));
|
|
@@ -1469,7 +1469,7 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1469
1469
|
) || hasWarningLoss;
|
|
1470
1470
|
const derivedOutcome = hasBlockingConverterEvidence || geometryLost ? "blocked" : hasUnresolvedFidelity ? "low-only" : "passed";
|
|
1471
1471
|
const outcome = requiredEnum(
|
|
1472
|
-
|
|
1472
|
+
record2.outcome,
|
|
1473
1473
|
["passed", "low-only", "blocked"],
|
|
1474
1474
|
"ModelProcessingManifest.fidelityGate.outcome"
|
|
1475
1475
|
);
|
|
@@ -1477,13 +1477,13 @@ function createFidelityGateEvidence(value, fidelityEvidence, converter) {
|
|
|
1477
1477
|
throw new Error("ModelProcessingManifest fidelityGate outcome must match converter and fidelity evidence.");
|
|
1478
1478
|
}
|
|
1479
1479
|
return {
|
|
1480
|
-
profileId: requiredToken(
|
|
1481
|
-
profileVersion: assetVersion(
|
|
1480
|
+
profileId: requiredToken(record2.profileId, "ModelProcessingManifest.fidelityGate.profileId"),
|
|
1481
|
+
profileVersion: assetVersion(record2.profileVersion, "ModelProcessingManifest.fidelityGate.profileVersion"),
|
|
1482
1482
|
outcome,
|
|
1483
1483
|
requiredAspects: expectedAspects,
|
|
1484
|
-
evaluatedAt: requiredTimestamp(
|
|
1484
|
+
evaluatedAt: requiredTimestamp(record2.evaluatedAt, "ModelProcessingManifest.fidelityGate.evaluatedAt"),
|
|
1485
1485
|
decisionToken: requiredAttestationToken(
|
|
1486
|
-
|
|
1486
|
+
record2.decisionToken,
|
|
1487
1487
|
"ModelProcessingManifest.fidelityGate.decisionToken"
|
|
1488
1488
|
)
|
|
1489
1489
|
};
|
|
@@ -1492,15 +1492,15 @@ function assertCoordinateSystem(value) {
|
|
|
1492
1492
|
if (value === void 0) {
|
|
1493
1493
|
return;
|
|
1494
1494
|
}
|
|
1495
|
-
const
|
|
1496
|
-
assertAllowedKeys(
|
|
1495
|
+
const record2 = assertRecord(value, "ModelProcessingManifest.coordinateSystem");
|
|
1496
|
+
assertAllowedKeys(record2, [
|
|
1497
1497
|
"unit",
|
|
1498
1498
|
"upAxis",
|
|
1499
1499
|
"forwardAxis",
|
|
1500
1500
|
"origin",
|
|
1501
1501
|
"outwardFaceWinding"
|
|
1502
1502
|
], "ModelProcessingManifest.coordinateSystem");
|
|
1503
|
-
if (
|
|
1503
|
+
if (record2.unit !== CANONICAL_MODEL_COORDINATE_SYSTEM.unit || record2.upAxis !== CANONICAL_MODEL_COORDINATE_SYSTEM.upAxis || record2.forwardAxis !== CANONICAL_MODEL_COORDINATE_SYSTEM.forwardAxis || record2.origin !== CANONICAL_MODEL_COORDINATE_SYSTEM.origin || record2.outwardFaceWinding !== CANONICAL_MODEL_COORDINATE_SYSTEM.outwardFaceWinding) {
|
|
1504
1504
|
throw new Error(
|
|
1505
1505
|
"ModelProcessingManifest.coordinateSystem must be metres, Y-up, -Z forward, floor-centred, and counter-clockwise outward."
|
|
1506
1506
|
);
|
|
@@ -1544,8 +1544,8 @@ function assertAssemblyHierarchy(children) {
|
|
|
1544
1544
|
}
|
|
1545
1545
|
}
|
|
1546
1546
|
function createModelProcessingManifest(input) {
|
|
1547
|
-
const
|
|
1548
|
-
assertAllowedKeys(
|
|
1547
|
+
const record2 = assertRecord(input, "ModelProcessingManifest");
|
|
1548
|
+
assertAllowedKeys(record2, [
|
|
1549
1549
|
"contractVersion",
|
|
1550
1550
|
"manifestId",
|
|
1551
1551
|
"resolutionId",
|
|
@@ -1564,23 +1564,23 @@ function createModelProcessingManifest(input) {
|
|
|
1564
1564
|
"fidelityGate",
|
|
1565
1565
|
"processedAt"
|
|
1566
1566
|
], "ModelProcessingManifest");
|
|
1567
|
-
assertContractVersion(
|
|
1567
|
+
assertContractVersion(record2, "ModelProcessingManifest");
|
|
1568
1568
|
const resolutionId = requiredModelPathSegment(
|
|
1569
|
-
|
|
1569
|
+
record2.resolutionId,
|
|
1570
1570
|
"ModelProcessingManifest.resolutionId"
|
|
1571
1571
|
);
|
|
1572
1572
|
const candidateId = requiredModelPathSegment(
|
|
1573
|
-
|
|
1573
|
+
record2.candidateId,
|
|
1574
1574
|
"ModelProcessingManifest.candidateId"
|
|
1575
1575
|
);
|
|
1576
|
-
assertCoordinateSystem(
|
|
1577
|
-
const kind = requiredEnum(
|
|
1578
|
-
const contentHash = requiredSha256(
|
|
1579
|
-
const closureHash = requiredSha256(
|
|
1576
|
+
assertCoordinateSystem(record2.coordinateSystem);
|
|
1577
|
+
const kind = requiredEnum(record2.kind, MODEL_ASSET_KINDS, "ModelProcessingManifest.kind");
|
|
1578
|
+
const contentHash = requiredSha256(record2.contentHash, "ModelProcessingManifest.contentHash");
|
|
1579
|
+
const closureHash = requiredSha256(record2.closureHash, "ModelProcessingManifest.closureHash");
|
|
1580
1580
|
if (kind === "leaf" && closureHash !== contentHash) {
|
|
1581
1581
|
throw new Error("ModelProcessingManifest leaf closureHash must match contentHash.");
|
|
1582
1582
|
}
|
|
1583
|
-
const technicalProfile = createModelTechnicalProfile(
|
|
1583
|
+
const technicalProfile = createModelTechnicalProfile(record2.technicalProfile);
|
|
1584
1584
|
const [minimumX, minimumY, minimumZ] = technicalProfile.boundsMetres.min;
|
|
1585
1585
|
const [maximumX, , maximumZ] = technicalProfile.boundsMetres.max;
|
|
1586
1586
|
if (Math.abs(minimumY) > MODEL_CANONICAL_ORIGIN_TOLERANCE_METRES || Math.abs((minimumX + maximumX) / 2) > MODEL_CANONICAL_ORIGIN_TOLERANCE_METRES || Math.abs((minimumZ + maximumZ) / 2) > MODEL_CANONICAL_ORIGIN_TOLERANCE_METRES) {
|
|
@@ -1588,10 +1588,10 @@ function createModelProcessingManifest(input) {
|
|
|
1588
1588
|
"ModelProcessingManifest floor-centred origin requires minY and the X/Z bounds centre to be zero."
|
|
1589
1589
|
);
|
|
1590
1590
|
}
|
|
1591
|
-
if (!Array.isArray(
|
|
1591
|
+
if (!Array.isArray(record2.lods) || record2.lods.length < 1 || record2.lods.length > 4) {
|
|
1592
1592
|
throw new Error("ModelProcessingManifest.lods must contain one to four adaptive levels beginning with LOD0.");
|
|
1593
1593
|
}
|
|
1594
|
-
const lods =
|
|
1594
|
+
const lods = record2.lods.map(createLodRecord);
|
|
1595
1595
|
for (const [index, lod] of lods.entries()) {
|
|
1596
1596
|
if (lod.level !== index) {
|
|
1597
1597
|
throw new Error("ModelProcessingManifest LOD levels must be contiguous and begin with LOD0.");
|
|
@@ -1637,8 +1637,8 @@ function createModelProcessingManifest(input) {
|
|
|
1637
1637
|
`ModelProcessingManifest.lods[${index}].resource`
|
|
1638
1638
|
);
|
|
1639
1639
|
}
|
|
1640
|
-
const collision = createCollisionRecord(
|
|
1641
|
-
const collisionPolicy = createCollisionPolicyEvidence(
|
|
1640
|
+
const collision = createCollisionRecord(record2.collision);
|
|
1641
|
+
const collisionPolicy = createCollisionPolicyEvidence(record2.collisionPolicy, collision);
|
|
1642
1642
|
if (technicalProfile.hasCollision !== (collision.kind !== "none")) {
|
|
1643
1643
|
throw new Error("ModelProcessingManifest technicalProfile.hasCollision must match collision evidence.");
|
|
1644
1644
|
}
|
|
@@ -1653,10 +1653,10 @@ function createModelProcessingManifest(input) {
|
|
|
1653
1653
|
"ModelProcessingManifest.collision.resource"
|
|
1654
1654
|
);
|
|
1655
1655
|
}
|
|
1656
|
-
if (!Array.isArray(
|
|
1656
|
+
if (!Array.isArray(record2.children) || record2.children.length > 256) {
|
|
1657
1657
|
throw new Error("ModelProcessingManifest.children must be a bounded array.");
|
|
1658
1658
|
}
|
|
1659
|
-
const children =
|
|
1659
|
+
const children = record2.children.map((child, index) => createAssemblyChild(child, index, resolutionId, candidateId));
|
|
1660
1660
|
if (kind === "leaf" && children.length !== 0) {
|
|
1661
1661
|
throw new Error("ModelProcessingManifest leaf models must not contain assembly children.");
|
|
1662
1662
|
}
|
|
@@ -1670,17 +1670,17 @@ function createModelProcessingManifest(input) {
|
|
|
1670
1670
|
throw new Error("ModelProcessingManifest assembly instanceId values must be unique.");
|
|
1671
1671
|
}
|
|
1672
1672
|
assertAssemblyHierarchy(children);
|
|
1673
|
-
const converter = createConverterEvidence(
|
|
1673
|
+
const converter = createConverterEvidence(record2.converter);
|
|
1674
1674
|
if (converter.targetFormat !== "glb") {
|
|
1675
1675
|
throw new Error("ModelProcessingManifest converter.targetFormat must be glb.");
|
|
1676
1676
|
}
|
|
1677
1677
|
if (converter.outputContentHash !== contentHash) {
|
|
1678
1678
|
throw new Error("ModelProcessingManifest converter outputContentHash must match contentHash.");
|
|
1679
1679
|
}
|
|
1680
|
-
if (!Array.isArray(
|
|
1680
|
+
if (!Array.isArray(record2.fidelityEvidence) || record2.fidelityEvidence.length > 100) {
|
|
1681
1681
|
throw new Error("ModelProcessingManifest.fidelityEvidence must be a bounded array.");
|
|
1682
1682
|
}
|
|
1683
|
-
const fidelityEvidence =
|
|
1683
|
+
const fidelityEvidence = record2.fidelityEvidence.map(createFidelityEvidence);
|
|
1684
1684
|
if (new Set(fidelityEvidence.map((evidence) => evidence.aspect)).size !== fidelityEvidence.length) {
|
|
1685
1685
|
throw new Error("ModelProcessingManifest fidelity evidence aspects must be unique.");
|
|
1686
1686
|
}
|
|
@@ -1694,10 +1694,10 @@ function createModelProcessingManifest(input) {
|
|
|
1694
1694
|
);
|
|
1695
1695
|
}
|
|
1696
1696
|
}
|
|
1697
|
-
const fidelityGate = createFidelityGateEvidence(
|
|
1697
|
+
const fidelityGate = createFidelityGateEvidence(record2.fidelityGate, fidelityEvidence, converter);
|
|
1698
1698
|
return deepFreeze({
|
|
1699
1699
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1700
|
-
manifestId: requiredToken(
|
|
1700
|
+
manifestId: requiredToken(record2.manifestId, "ModelProcessingManifest.manifestId"),
|
|
1701
1701
|
resolutionId,
|
|
1702
1702
|
candidateId,
|
|
1703
1703
|
kind,
|
|
@@ -1712,7 +1712,7 @@ function createModelProcessingManifest(input) {
|
|
|
1712
1712
|
converter,
|
|
1713
1713
|
fidelityEvidence,
|
|
1714
1714
|
fidelityGate,
|
|
1715
|
-
processedAt: requiredTimestamp(
|
|
1715
|
+
processedAt: requiredTimestamp(record2.processedAt, "ModelProcessingManifest.processedAt")
|
|
1716
1716
|
});
|
|
1717
1717
|
}
|
|
1718
1718
|
function modelCandidateEquivalent(left, right) {
|
|
@@ -1731,8 +1731,8 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1731
1731
|
if (candidate.assetRef.disposition !== "proposed") {
|
|
1732
1732
|
throw new Error("ModelPromotionReceipt is only valid for a staged proposed candidate.");
|
|
1733
1733
|
}
|
|
1734
|
-
const
|
|
1735
|
-
assertAllowedKeys(
|
|
1734
|
+
const record2 = assertRecord(input, "ModelPromotionReceipt");
|
|
1735
|
+
assertAllowedKeys(record2, [
|
|
1736
1736
|
"contractVersion",
|
|
1737
1737
|
"promotionId",
|
|
1738
1738
|
"resolutionId",
|
|
@@ -1746,17 +1746,17 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1746
1746
|
"promotedAt",
|
|
1747
1747
|
"publicationToken"
|
|
1748
1748
|
], "ModelPromotionReceipt");
|
|
1749
|
-
assertContractVersion(
|
|
1750
|
-
if (
|
|
1749
|
+
assertContractVersion(record2, "ModelPromotionReceipt");
|
|
1750
|
+
if (record2.resolutionId !== resolutionId || record2.candidateId !== candidate.candidateId || record2.proposalId !== candidate.assetRef.proposalId || record2.confirmationId !== confirmation.confirmationId || record2.processingManifestId !== candidate.processingManifest.manifestId || record2.processingContentHash !== candidate.processingManifest.contentHash || record2.closureHash !== candidate.processingManifest.closureHash) {
|
|
1751
1751
|
throw new Error("ModelPromotionReceipt must bind the exact proposal, confirmation, manifest, and closure.");
|
|
1752
1752
|
}
|
|
1753
|
-
const receiptAssetRef = createModelAssetRef(
|
|
1753
|
+
const receiptAssetRef = createModelAssetRef(record2.finalAssetRef);
|
|
1754
1754
|
if (JSON.stringify(receiptAssetRef) !== JSON.stringify(finalAssetRef)) {
|
|
1755
1755
|
throw new Error("ModelPromotionReceipt.finalAssetRef must match the completed finalAssetRef.");
|
|
1756
1756
|
}
|
|
1757
1757
|
return deepFreeze({
|
|
1758
1758
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1759
|
-
promotionId: requiredToken(
|
|
1759
|
+
promotionId: requiredToken(record2.promotionId, "ModelPromotionReceipt.promotionId"),
|
|
1760
1760
|
resolutionId,
|
|
1761
1761
|
candidateId: candidate.candidateId,
|
|
1762
1762
|
proposalId: candidate.assetRef.proposalId,
|
|
@@ -1765,9 +1765,9 @@ function createModelPromotionReceipt(input, resolutionId, candidate, confirmatio
|
|
|
1765
1765
|
processingContentHash: candidate.processingManifest.contentHash,
|
|
1766
1766
|
closureHash: candidate.processingManifest.closureHash,
|
|
1767
1767
|
finalAssetRef: receiptAssetRef,
|
|
1768
|
-
promotedAt: requiredTimestamp(
|
|
1768
|
+
promotedAt: requiredTimestamp(record2.promotedAt, "ModelPromotionReceipt.promotedAt"),
|
|
1769
1769
|
publicationToken: requiredAttestationToken(
|
|
1770
|
-
|
|
1770
|
+
record2.publicationToken,
|
|
1771
1771
|
"ModelPromotionReceipt.publicationToken"
|
|
1772
1772
|
)
|
|
1773
1773
|
});
|
|
@@ -1828,8 +1828,8 @@ function isModelResolutionState(value) {
|
|
|
1828
1828
|
return typeof value === "string" && MODEL_RESOLUTION_STATES.includes(value);
|
|
1829
1829
|
}
|
|
1830
1830
|
function createModelResolution(input) {
|
|
1831
|
-
const
|
|
1832
|
-
assertAllowedKeys(
|
|
1831
|
+
const record2 = assertRecord(input, "ModelResolution");
|
|
1832
|
+
assertAllowedKeys(record2, [
|
|
1833
1833
|
"contractVersion",
|
|
1834
1834
|
"resolutionId",
|
|
1835
1835
|
"request",
|
|
@@ -1845,19 +1845,19 @@ function createModelResolution(input) {
|
|
|
1845
1845
|
"createdAt",
|
|
1846
1846
|
"updatedAt"
|
|
1847
1847
|
], "ModelResolution");
|
|
1848
|
-
assertContractVersion(
|
|
1849
|
-
const resolutionId = requiredModelPathSegment(
|
|
1850
|
-
const request = createModelRequestSpec(
|
|
1851
|
-
const createdAt = requiredTimestamp(
|
|
1852
|
-
const updatedAt = requiredTimestamp(
|
|
1848
|
+
assertContractVersion(record2, "ModelResolution");
|
|
1849
|
+
const resolutionId = requiredModelPathSegment(record2.resolutionId, "ModelResolution.resolutionId");
|
|
1850
|
+
const request = createModelRequestSpec(record2.request);
|
|
1851
|
+
const createdAt = requiredTimestamp(record2.createdAt, "ModelResolution.createdAt");
|
|
1852
|
+
const updatedAt = requiredTimestamp(record2.updatedAt, "ModelResolution.updatedAt");
|
|
1853
1853
|
if (Date.parse(updatedAt) < Date.parse(createdAt)) {
|
|
1854
1854
|
throw new Error("ModelResolution.updatedAt must not precede createdAt.");
|
|
1855
1855
|
}
|
|
1856
|
-
const state = requiredEnum(
|
|
1857
|
-
if (!Array.isArray(
|
|
1856
|
+
const state = requiredEnum(record2.state, MODEL_RESOLUTION_STATES, "ModelResolution.state");
|
|
1857
|
+
if (!Array.isArray(record2.candidates) || record2.candidates.length > MAX_CANDIDATES) {
|
|
1858
1858
|
throw new Error(`ModelResolution.candidates must contain at most ${MAX_CANDIDATES} items.`);
|
|
1859
1859
|
}
|
|
1860
|
-
const candidates =
|
|
1860
|
+
const candidates = record2.candidates.map(createModelCandidate);
|
|
1861
1861
|
if (new Set(candidates.map((candidate) => candidate.candidateId)).size !== candidates.length) {
|
|
1862
1862
|
throw new Error("ModelResolution candidateId values must be unique.");
|
|
1863
1863
|
}
|
|
@@ -1891,8 +1891,8 @@ function createModelResolution(input) {
|
|
|
1891
1891
|
}
|
|
1892
1892
|
}
|
|
1893
1893
|
let bestCandidate;
|
|
1894
|
-
if (
|
|
1895
|
-
const requestedBest = createModelCandidate(
|
|
1894
|
+
if (record2.bestCandidate !== void 0) {
|
|
1895
|
+
const requestedBest = createModelCandidate(record2.bestCandidate);
|
|
1896
1896
|
bestCandidate = candidates.find((candidate) => candidate.candidateId === requestedBest.candidateId);
|
|
1897
1897
|
if (bestCandidate === void 0 || !modelCandidateEquivalent(bestCandidate, requestedBest)) {
|
|
1898
1898
|
throw new Error("ModelResolution.bestCandidate must exactly match a candidate.");
|
|
@@ -1907,11 +1907,11 @@ function createModelResolution(input) {
|
|
|
1907
1907
|
);
|
|
1908
1908
|
}
|
|
1909
1909
|
let confirmation;
|
|
1910
|
-
if (
|
|
1910
|
+
if (record2.confirmation !== void 0) {
|
|
1911
1911
|
if (bestCandidate === void 0) {
|
|
1912
1912
|
throw new Error("ModelResolution confirmation requires bestCandidate.");
|
|
1913
1913
|
}
|
|
1914
|
-
confirmation = createModelCandidateConfirmation(
|
|
1914
|
+
confirmation = createModelCandidateConfirmation(record2.confirmation, bestCandidate, resolutionId);
|
|
1915
1915
|
if (Date.parse(confirmation.confirmedAt) < Date.parse(createdAt) || Date.parse(confirmation.confirmedAt) > Date.parse(updatedAt)) {
|
|
1916
1916
|
throw new Error("ModelResolution confirmation.confirmedAt must fall between createdAt and updatedAt.");
|
|
1917
1917
|
}
|
|
@@ -1925,15 +1925,15 @@ function createModelResolution(input) {
|
|
|
1925
1925
|
if (confirmation !== void 0 && !["promoting", "completed", "failed", "cancelled"].includes(state)) {
|
|
1926
1926
|
throw new Error(`ModelResolution confirmation evidence is not allowed in ${state} state.`);
|
|
1927
1927
|
}
|
|
1928
|
-
const refinementQuestions =
|
|
1929
|
-
const stateReasonCode =
|
|
1928
|
+
const refinementQuestions = record2.refinementQuestions === void 0 ? [] : stringList(record2.refinementQuestions, "ModelResolution.refinementQuestions", { maxItems: 3 });
|
|
1929
|
+
const stateReasonCode = record2.stateReasonCode === void 0 ? void 0 : requiredToken(record2.stateReasonCode, "ModelResolution.stateReasonCode");
|
|
1930
1930
|
if (["failed", "cancelled", "unresolved"].includes(state) && stateReasonCode === void 0) {
|
|
1931
1931
|
throw new Error(`ModelResolution ${state} state requires stateReasonCode.`);
|
|
1932
1932
|
}
|
|
1933
1933
|
if (state === "awaiting-confirmation" && bestCandidate?.match.assurance === "low" && refinementQuestions.length === 0) {
|
|
1934
1934
|
throw new Error("ModelResolution low-assurance bestCandidate requires refinementQuestions.");
|
|
1935
1935
|
}
|
|
1936
|
-
const finalAssetRef =
|
|
1936
|
+
const finalAssetRef = record2.finalAssetRef === void 0 ? void 0 : createModelAssetRef(record2.finalAssetRef);
|
|
1937
1937
|
if (state === "completed" && finalAssetRef === void 0) {
|
|
1938
1938
|
throw new Error("ModelResolution completed state requires finalAssetRef.");
|
|
1939
1939
|
}
|
|
@@ -1944,12 +1944,12 @@ function createModelResolution(input) {
|
|
|
1944
1944
|
throw new Error("ModelResolution finalAssetRef must match bestCandidate kind, hash, and immutable identity.");
|
|
1945
1945
|
}
|
|
1946
1946
|
let promotionReceipt;
|
|
1947
|
-
if (
|
|
1947
|
+
if (record2.promotionReceipt !== void 0) {
|
|
1948
1948
|
if (state !== "completed" || bestCandidate === void 0 || confirmation === void 0 || finalAssetRef === void 0) {
|
|
1949
1949
|
throw new Error("ModelResolution promotionReceipt is only allowed for a completed staged promotion.");
|
|
1950
1950
|
}
|
|
1951
1951
|
promotionReceipt = createModelPromotionReceipt(
|
|
1952
|
-
|
|
1952
|
+
record2.promotionReceipt,
|
|
1953
1953
|
resolutionId,
|
|
1954
1954
|
bestCandidate,
|
|
1955
1955
|
confirmation,
|
|
@@ -1969,7 +1969,7 @@ function createModelResolution(input) {
|
|
|
1969
1969
|
contractVersion: MODEL_RESOLUTION_CONTRACT_VERSION,
|
|
1970
1970
|
resolutionId,
|
|
1971
1971
|
request,
|
|
1972
|
-
attempts: requiredInteger(
|
|
1972
|
+
attempts: requiredInteger(record2.attempts, "ModelResolution.attempts", 1, MAX_ATTEMPTS),
|
|
1973
1973
|
state,
|
|
1974
1974
|
candidates,
|
|
1975
1975
|
...bestCandidate === void 0 ? {} : { bestCandidate },
|
|
@@ -1991,15 +1991,15 @@ function createGeneratorSeed(value, fieldName) {
|
|
|
1991
1991
|
return seed;
|
|
1992
1992
|
}
|
|
1993
1993
|
function createModelGeneratorRequest(input) {
|
|
1994
|
-
const
|
|
1995
|
-
assertAllowedKeys(
|
|
1994
|
+
const record2 = assertRecord(input, "ModelGeneratorRequest");
|
|
1995
|
+
assertAllowedKeys(record2, [
|
|
1996
1996
|
"generationId",
|
|
1997
1997
|
"request",
|
|
1998
1998
|
"budgets",
|
|
1999
1999
|
"seed",
|
|
2000
2000
|
"deadline"
|
|
2001
2001
|
], "ModelGeneratorRequest");
|
|
2002
|
-
const budgets = assertRecord(
|
|
2002
|
+
const budgets = assertRecord(record2.budgets, "ModelGeneratorRequest.budgets");
|
|
2003
2003
|
assertAllowedKeys(budgets, [
|
|
2004
2004
|
"maxDurationMs",
|
|
2005
2005
|
"maxTriangles",
|
|
@@ -2007,7 +2007,7 @@ function createModelGeneratorRequest(input) {
|
|
|
2007
2007
|
"maxTextureBytes",
|
|
2008
2008
|
"maxTextureDimensionPx"
|
|
2009
2009
|
], "ModelGeneratorRequest.budgets");
|
|
2010
|
-
const seed = createGeneratorSeed(
|
|
2010
|
+
const seed = createGeneratorSeed(record2.seed, "ModelGeneratorRequest.seed");
|
|
2011
2011
|
const maxBytes = requiredInteger(
|
|
2012
2012
|
budgets.maxBytes,
|
|
2013
2013
|
"ModelGeneratorRequest budget maxBytes",
|
|
@@ -2024,8 +2024,8 @@ function createModelGeneratorRequest(input) {
|
|
|
2024
2024
|
throw new Error("ModelGeneratorRequest maxTextureBytes must not exceed maxBytes.");
|
|
2025
2025
|
}
|
|
2026
2026
|
return deepFreeze({
|
|
2027
|
-
generationId: requiredModelPathSegment(
|
|
2028
|
-
request: createModelRequestSpec(
|
|
2027
|
+
generationId: requiredModelPathSegment(record2.generationId, "ModelGeneratorRequest.generationId"),
|
|
2028
|
+
request: createModelRequestSpec(record2.request),
|
|
2029
2029
|
budgets: {
|
|
2030
2030
|
maxDurationMs: requiredInteger(budgets.maxDurationMs, "ModelGeneratorRequest budget maxDurationMs", 1, 3e5),
|
|
2031
2031
|
maxTriangles: requiredInteger(budgets.maxTriangles, "ModelGeneratorRequest budget maxTriangles", 1, 1e7),
|
|
@@ -2034,7 +2034,7 @@ function createModelGeneratorRequest(input) {
|
|
|
2034
2034
|
maxTextureDimensionPx: requiredInteger(budgets.maxTextureDimensionPx, "ModelGeneratorRequest budget maxTextureDimensionPx", 1, 32768)
|
|
2035
2035
|
},
|
|
2036
2036
|
seed,
|
|
2037
|
-
deadline: requiredTimestamp(
|
|
2037
|
+
deadline: requiredTimestamp(record2.deadline, "ModelGeneratorRequest.deadline")
|
|
2038
2038
|
});
|
|
2039
2039
|
}
|
|
2040
2040
|
var MODEL_GENERATOR_ARTIFACT_ROLES = [
|
|
@@ -2072,8 +2072,8 @@ function assertGeneratorRetryability(reasonCode2, retryable) {
|
|
|
2072
2072
|
}
|
|
2073
2073
|
function createModelGeneratorArtifact(value, index) {
|
|
2074
2074
|
const fieldName = `ModelGeneratorResult.sourceBundle.artifacts[${index}]`;
|
|
2075
|
-
const
|
|
2076
|
-
assertAllowedKeys(
|
|
2075
|
+
const record2 = assertRecord(value, fieldName);
|
|
2076
|
+
assertAllowedKeys(record2, [
|
|
2077
2077
|
"uri",
|
|
2078
2078
|
"byteLength",
|
|
2079
2079
|
"sha256",
|
|
@@ -2083,12 +2083,12 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2083
2083
|
"maxTextureDimensionPx"
|
|
2084
2084
|
], fieldName);
|
|
2085
2085
|
const resource = createModelResourceRef({
|
|
2086
|
-
uri:
|
|
2087
|
-
byteLength:
|
|
2088
|
-
sha256:
|
|
2089
|
-
contentType:
|
|
2086
|
+
uri: record2.uri,
|
|
2087
|
+
byteLength: record2.byteLength,
|
|
2088
|
+
sha256: record2.sha256,
|
|
2089
|
+
contentType: record2.contentType
|
|
2090
2090
|
});
|
|
2091
|
-
const role = requiredEnum(
|
|
2091
|
+
const role = requiredEnum(record2.role, MODEL_GENERATOR_ARTIFACT_ROLES, `${fieldName}.role`);
|
|
2092
2092
|
const compatibleContentTypes = {
|
|
2093
2093
|
"model-entrypoint": ["model/gltf-binary", "model/gltf+json"],
|
|
2094
2094
|
"model-dependency": ["model/gltf-binary", "model/gltf+json"],
|
|
@@ -2100,13 +2100,13 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2100
2100
|
throw new Error(`${fieldName}.contentType is not compatible with artifact role ${role}.`);
|
|
2101
2101
|
}
|
|
2102
2102
|
const textureByteLength = requiredInteger(
|
|
2103
|
-
|
|
2103
|
+
record2.textureByteLength,
|
|
2104
2104
|
`${fieldName}.textureByteLength`,
|
|
2105
2105
|
0,
|
|
2106
2106
|
resource.byteLength
|
|
2107
2107
|
);
|
|
2108
2108
|
const maxTextureDimensionPx = requiredInteger(
|
|
2109
|
-
|
|
2109
|
+
record2.maxTextureDimensionPx,
|
|
2110
2110
|
`${fieldName}.maxTextureDimensionPx`,
|
|
2111
2111
|
0,
|
|
2112
2112
|
32768
|
|
@@ -2123,8 +2123,8 @@ function createModelGeneratorArtifact(value, index) {
|
|
|
2123
2123
|
return { ...resource, role, textureByteLength, maxTextureDimensionPx };
|
|
2124
2124
|
}
|
|
2125
2125
|
function createModelGeneratorUsage(value) {
|
|
2126
|
-
const
|
|
2127
|
-
assertAllowedKeys(
|
|
2126
|
+
const record2 = assertRecord(value, "ModelGeneratorResult.sourceBundle.usage");
|
|
2127
|
+
assertAllowedKeys(record2, [
|
|
2128
2128
|
"durationMs",
|
|
2129
2129
|
"triangleCount",
|
|
2130
2130
|
"byteLength",
|
|
@@ -2132,13 +2132,13 @@ function createModelGeneratorUsage(value) {
|
|
|
2132
2132
|
"maxTextureDimensionPx"
|
|
2133
2133
|
], "ModelGeneratorResult.sourceBundle.usage");
|
|
2134
2134
|
const textureByteLength = requiredInteger(
|
|
2135
|
-
|
|
2135
|
+
record2.textureByteLength,
|
|
2136
2136
|
"ModelGeneratorResult.sourceBundle.usage.textureByteLength",
|
|
2137
2137
|
0,
|
|
2138
2138
|
Number.MAX_SAFE_INTEGER
|
|
2139
2139
|
);
|
|
2140
2140
|
const maxTextureDimensionPx = requiredInteger(
|
|
2141
|
-
|
|
2141
|
+
record2.maxTextureDimensionPx,
|
|
2142
2142
|
"ModelGeneratorResult.sourceBundle.usage.maxTextureDimensionPx",
|
|
2143
2143
|
0,
|
|
2144
2144
|
32768
|
|
@@ -2147,30 +2147,30 @@ function createModelGeneratorUsage(value) {
|
|
|
2147
2147
|
throw new Error("ModelGeneratorResult texture usage bytes and dimensions must both be zero or both positive.");
|
|
2148
2148
|
}
|
|
2149
2149
|
return {
|
|
2150
|
-
durationMs: requiredInteger(
|
|
2151
|
-
triangleCount: requiredInteger(
|
|
2152
|
-
byteLength: requiredInteger(
|
|
2150
|
+
durationMs: requiredInteger(record2.durationMs, "ModelGeneratorResult.sourceBundle.usage.durationMs", 0, 3e5),
|
|
2151
|
+
triangleCount: requiredInteger(record2.triangleCount, "ModelGeneratorResult.sourceBundle.usage.triangleCount", 1, 1e7),
|
|
2152
|
+
byteLength: requiredInteger(record2.byteLength, "ModelGeneratorResult.sourceBundle.usage.byteLength", 1, Number.MAX_SAFE_INTEGER),
|
|
2153
2153
|
textureByteLength,
|
|
2154
2154
|
maxTextureDimensionPx
|
|
2155
2155
|
};
|
|
2156
2156
|
}
|
|
2157
|
-
function createGeneratorTerminalEvidence(
|
|
2158
|
-
const generationId = requiredModelPathSegment(
|
|
2157
|
+
function createGeneratorTerminalEvidence(record2, expected) {
|
|
2158
|
+
const generationId = requiredModelPathSegment(record2.generationId, "ModelGeneratorResult.generationId");
|
|
2159
2159
|
if (generationId !== expected.generationId) {
|
|
2160
2160
|
throw new Error("ModelGeneratorResult.generationId must match the generator request.");
|
|
2161
2161
|
}
|
|
2162
|
-
const diagnosticId =
|
|
2162
|
+
const diagnosticId = record2.diagnosticId === void 0 ? void 0 : requiredToken(record2.diagnosticId, "ModelGeneratorResult.diagnosticId");
|
|
2163
2163
|
return {
|
|
2164
2164
|
generationId,
|
|
2165
|
-
retryable: requiredBoolean(
|
|
2166
|
-
occurredAt: requiredTimestamp(
|
|
2165
|
+
retryable: requiredBoolean(record2.retryable, "ModelGeneratorResult.retryable"),
|
|
2166
|
+
occurredAt: requiredTimestamp(record2.occurredAt, "ModelGeneratorResult.occurredAt"),
|
|
2167
2167
|
...diagnosticId === void 0 ? {} : { diagnosticId }
|
|
2168
2168
|
};
|
|
2169
2169
|
}
|
|
2170
2170
|
function createModelGeneratorResult(input, expectedRequest) {
|
|
2171
2171
|
const expected = createModelGeneratorRequest(expectedRequest);
|
|
2172
|
-
const
|
|
2173
|
-
const status = requiredEnum(
|
|
2172
|
+
const record2 = assertRecord(input, "ModelGeneratorResult");
|
|
2173
|
+
const status = requiredEnum(record2.status, [
|
|
2174
2174
|
"disabled",
|
|
2175
2175
|
"generated",
|
|
2176
2176
|
"unavailable",
|
|
@@ -2178,13 +2178,13 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2178
2178
|
"cancelled",
|
|
2179
2179
|
"budget-exceeded"
|
|
2180
2180
|
], "ModelGeneratorResult.status");
|
|
2181
|
-
const generationId = requiredModelPathSegment(
|
|
2181
|
+
const generationId = requiredModelPathSegment(record2.generationId, "ModelGeneratorResult.generationId");
|
|
2182
2182
|
if (generationId !== expected.generationId) {
|
|
2183
2183
|
throw new Error("ModelGeneratorResult.generationId must match the generator request.");
|
|
2184
2184
|
}
|
|
2185
2185
|
if (status === "disabled") {
|
|
2186
|
-
assertAllowedKeys(
|
|
2187
|
-
if (
|
|
2186
|
+
assertAllowedKeys(record2, ["status", "generationId", "reasonCode"], "ModelGeneratorResult");
|
|
2187
|
+
if (record2.reasonCode !== MODEL_GENERATOR_DISABLED_REASON_CODE) {
|
|
2188
2188
|
throw new Error(
|
|
2189
2189
|
`ModelGeneratorResult.reasonCode must be ${MODEL_GENERATOR_DISABLED_REASON_CODE}.`
|
|
2190
2190
|
);
|
|
@@ -2196,8 +2196,8 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2196
2196
|
});
|
|
2197
2197
|
}
|
|
2198
2198
|
if (status === "generated") {
|
|
2199
|
-
assertAllowedKeys(
|
|
2200
|
-
const sourceBundle = assertRecord(
|
|
2199
|
+
assertAllowedKeys(record2, ["status", "generationId", "sourceBundle"], "ModelGeneratorResult");
|
|
2200
|
+
const sourceBundle = assertRecord(record2.sourceBundle, "ModelGeneratorResult.sourceBundle");
|
|
2201
2201
|
assertAllowedKeys(sourceBundle, [
|
|
2202
2202
|
"bundleId",
|
|
2203
2203
|
"generationId",
|
|
@@ -2297,14 +2297,14 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2297
2297
|
"diagnosticId"
|
|
2298
2298
|
];
|
|
2299
2299
|
if (status === "budget-exceeded") {
|
|
2300
|
-
assertAllowedKeys(
|
|
2301
|
-
if (
|
|
2300
|
+
assertAllowedKeys(record2, [...terminalAllowedKeys, "violations"], "ModelGeneratorResult");
|
|
2301
|
+
if (record2.reasonCode !== "generator-budget-exceeded" || record2.retryable !== false) {
|
|
2302
2302
|
throw new Error("ModelGeneratorResult budget-exceeded must use its fixed reasonCode and retryable false.");
|
|
2303
2303
|
}
|
|
2304
|
-
if (!Array.isArray(
|
|
2304
|
+
if (!Array.isArray(record2.violations) || record2.violations.length < 1 || record2.violations.length > 5) {
|
|
2305
2305
|
throw new Error("ModelGeneratorResult budget violations must contain one to five entries.");
|
|
2306
2306
|
}
|
|
2307
|
-
const violations =
|
|
2307
|
+
const violations = record2.violations.map((value, index) => {
|
|
2308
2308
|
const fieldName = `ModelGeneratorResult.violations[${index}]`;
|
|
2309
2309
|
const violation = assertRecord(value, fieldName);
|
|
2310
2310
|
assertAllowedKeys(violation, ["budget", "limit", "observed"], fieldName);
|
|
@@ -2319,7 +2319,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2319
2319
|
if (new Set(violations.map((violation) => violation.budget)).size !== violations.length) {
|
|
2320
2320
|
throw new Error("ModelGeneratorResult budget violation names must be unique.");
|
|
2321
2321
|
}
|
|
2322
|
-
const terminal2 = createGeneratorTerminalEvidence(
|
|
2322
|
+
const terminal2 = createGeneratorTerminalEvidence(record2, expected);
|
|
2323
2323
|
return deepFreeze({
|
|
2324
2324
|
status,
|
|
2325
2325
|
...terminal2,
|
|
@@ -2328,10 +2328,10 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2328
2328
|
violations
|
|
2329
2329
|
});
|
|
2330
2330
|
}
|
|
2331
|
-
assertAllowedKeys(
|
|
2332
|
-
const terminal = createGeneratorTerminalEvidence(
|
|
2331
|
+
assertAllowedKeys(record2, terminalAllowedKeys, "ModelGeneratorResult");
|
|
2332
|
+
const terminal = createGeneratorTerminalEvidence(record2, expected);
|
|
2333
2333
|
if (status === "unavailable") {
|
|
2334
|
-
const reasonCode3 = requiredEnum(
|
|
2334
|
+
const reasonCode3 = requiredEnum(record2.reasonCode, [
|
|
2335
2335
|
"capacity-unavailable",
|
|
2336
2336
|
"provider-unavailable",
|
|
2337
2337
|
"authentication-required",
|
|
@@ -2342,7 +2342,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2342
2342
|
return deepFreeze({ status, ...terminal, reasonCode: reasonCode3 });
|
|
2343
2343
|
}
|
|
2344
2344
|
if (status === "failed") {
|
|
2345
|
-
const reasonCode3 = requiredEnum(
|
|
2345
|
+
const reasonCode3 = requiredEnum(record2.reasonCode, [
|
|
2346
2346
|
"provider-failed",
|
|
2347
2347
|
"invalid-generator-output",
|
|
2348
2348
|
"internal-failure"
|
|
@@ -2350,7 +2350,7 @@ function createModelGeneratorResult(input, expectedRequest) {
|
|
|
2350
2350
|
assertGeneratorRetryability(reasonCode3, terminal.retryable);
|
|
2351
2351
|
return deepFreeze({ status, ...terminal, reasonCode: reasonCode3 });
|
|
2352
2352
|
}
|
|
2353
|
-
const reasonCode2 = requiredEnum(
|
|
2353
|
+
const reasonCode2 = requiredEnum(record2.reasonCode, [
|
|
2354
2354
|
"caller-aborted",
|
|
2355
2355
|
"deadline-exceeded",
|
|
2356
2356
|
"superseded",
|
|
@@ -2699,9 +2699,9 @@ function asRecord(value, fieldName) {
|
|
|
2699
2699
|
}
|
|
2700
2700
|
return value;
|
|
2701
2701
|
}
|
|
2702
|
-
function exactKeys(
|
|
2702
|
+
function exactKeys(record2, allowed, fieldName) {
|
|
2703
2703
|
const set = new Set(allowed);
|
|
2704
|
-
const unsupported = Object.keys(
|
|
2704
|
+
const unsupported = Object.keys(record2).find((key) => !set.has(key));
|
|
2705
2705
|
if (unsupported !== void 0) throw new Error(`${fieldName} contains an unsupported field.`);
|
|
2706
2706
|
}
|
|
2707
2707
|
function asDenseArray(value, fieldName, minimum, maximum) {
|
|
@@ -2732,9 +2732,9 @@ function assertDataOnlyTree(value, fieldName, seen = /* @__PURE__ */ new WeakSet
|
|
|
2732
2732
|
assertDataOnlyTree(descriptor.value, `${fieldName}[${index}]`, seen, depth + 1);
|
|
2733
2733
|
}
|
|
2734
2734
|
} else {
|
|
2735
|
-
const
|
|
2736
|
-
for (const key of Object.keys(
|
|
2737
|
-
const descriptor = Object.getOwnPropertyDescriptor(
|
|
2735
|
+
const record2 = asRecord(value, fieldName);
|
|
2736
|
+
for (const key of Object.keys(record2)) {
|
|
2737
|
+
const descriptor = Object.getOwnPropertyDescriptor(record2, key);
|
|
2738
2738
|
assertDataOnlyTree(descriptor.value, `${fieldName}.${key}`, seen, depth + 1);
|
|
2739
2739
|
}
|
|
2740
2740
|
}
|
|
@@ -2819,10 +2819,10 @@ function requireVector(value, fieldName) {
|
|
|
2819
2819
|
];
|
|
2820
2820
|
}
|
|
2821
2821
|
function createBounds2(value, fieldName) {
|
|
2822
|
-
const
|
|
2823
|
-
exactKeys(
|
|
2824
|
-
const min = requireVector(
|
|
2825
|
-
const max = requireVector(
|
|
2822
|
+
const record2 = asRecord(value, fieldName);
|
|
2823
|
+
exactKeys(record2, ["min", "max"], fieldName);
|
|
2824
|
+
const min = requireVector(record2.min, `${fieldName}.min`);
|
|
2825
|
+
const max = requireVector(record2.max, `${fieldName}.max`);
|
|
2826
2826
|
if (min.some((coordinate, index) => coordinate >= max[index])) throw new Error(`${fieldName} minimum values must be below maximum values.`);
|
|
2827
2827
|
return { min, max };
|
|
2828
2828
|
}
|
|
@@ -2830,18 +2830,18 @@ function approximatelyEqual(left, right) {
|
|
|
2830
2830
|
return Math.abs(left - right) <= Math.max(1e-6, Math.abs(left) * 1e-6, Math.abs(right) * 1e-6);
|
|
2831
2831
|
}
|
|
2832
2832
|
function createHashAttestation(value, expectedDomain, expectedDigest, fieldName) {
|
|
2833
|
-
const
|
|
2834
|
-
exactKeys(
|
|
2835
|
-
if (
|
|
2833
|
+
const record2 = asRecord(value, fieldName);
|
|
2834
|
+
exactKeys(record2, ["algorithm", "domain", "digest", "attestationToken"], fieldName);
|
|
2835
|
+
if (record2.algorithm !== "sha256" || record2.domain !== expectedDomain) {
|
|
2836
2836
|
throw new Error(`${fieldName} must use the governed SHA-256 domain.`);
|
|
2837
2837
|
}
|
|
2838
|
-
const digest = requireSha256(
|
|
2838
|
+
const digest = requireSha256(record2.digest, `${fieldName}.digest`);
|
|
2839
2839
|
if (digest !== expectedDigest) throw new Error(`${fieldName}.digest must match its bound contract hash.`);
|
|
2840
2840
|
return {
|
|
2841
2841
|
algorithm: "sha256",
|
|
2842
2842
|
domain: expectedDomain,
|
|
2843
2843
|
digest,
|
|
2844
|
-
attestationToken: requireAttestation(
|
|
2844
|
+
attestationToken: requireAttestation(record2.attestationToken, `${fieldName}.attestationToken`)
|
|
2845
2845
|
};
|
|
2846
2846
|
}
|
|
2847
2847
|
function canonicalizePvoxHashProjectionV1(value) {
|
|
@@ -2898,19 +2898,19 @@ function assertFloorCentredBounds(bounds, fieldName) {
|
|
|
2898
2898
|
}
|
|
2899
2899
|
}
|
|
2900
2900
|
function createDimensions2(value, fieldName) {
|
|
2901
|
-
const
|
|
2902
|
-
exactKeys(
|
|
2901
|
+
const record2 = asRecord(value, fieldName);
|
|
2902
|
+
exactKeys(record2, ["width", "height", "depth"], fieldName);
|
|
2903
2903
|
return {
|
|
2904
|
-
width: requireNumber(
|
|
2905
|
-
height: requireNumber(
|
|
2906
|
-
depth: requireNumber(
|
|
2904
|
+
width: requireNumber(record2.width, `${fieldName}.width`, Number.EPSILON, 1e9),
|
|
2905
|
+
height: requireNumber(record2.height, `${fieldName}.height`, Number.EPSILON, 1e9),
|
|
2906
|
+
depth: requireNumber(record2.depth, `${fieldName}.depth`, Number.EPSILON, 1e9)
|
|
2907
2907
|
};
|
|
2908
2908
|
}
|
|
2909
2909
|
function assertCoordinateSystem2(value, fieldName) {
|
|
2910
|
-
const
|
|
2911
|
-
exactKeys(
|
|
2910
|
+
const record2 = asRecord(value, fieldName);
|
|
2911
|
+
exactKeys(record2, ["unit", "upAxis", "forwardAxis", "origin", "outwardFaceWinding"], fieldName);
|
|
2912
2912
|
for (const [key, expected] of Object.entries(CANONICAL_MODEL_COORDINATE_SYSTEM)) {
|
|
2913
|
-
if (
|
|
2913
|
+
if (record2[key] !== expected) throw new Error(`${fieldName} must use the canonical model coordinate system.`);
|
|
2914
2914
|
}
|
|
2915
2915
|
}
|
|
2916
2916
|
function parsePvoxResourceScope(uri) {
|
|
@@ -2935,20 +2935,20 @@ function parsePvoxResourceScope(uri) {
|
|
|
2935
2935
|
throw new Error("PVOX artifact URI must be an authenticated canonical PVOX MCP resource.");
|
|
2936
2936
|
}
|
|
2937
2937
|
function createResource(value) {
|
|
2938
|
-
const
|
|
2939
|
-
exactKeys(
|
|
2940
|
-
const uri = requireString(
|
|
2938
|
+
const record2 = asRecord(value, "PvoxAssetManifestV1.artifact");
|
|
2939
|
+
exactKeys(record2, ["uri", "byteLength", "sha256", "contentType", "authenticated"], "PvoxAssetManifestV1.artifact");
|
|
2940
|
+
const uri = requireString(record2.uri, "PvoxAssetManifestV1.artifact.uri", 512);
|
|
2941
2941
|
if (uri.includes("%") || uri.includes("?") || uri.includes("#") || uri.includes("..") || DIRECT_URL.test(uri)) {
|
|
2942
2942
|
throw new Error("PvoxAssetManifestV1.artifact.uri must be an authenticated canonical MCP resource.");
|
|
2943
2943
|
}
|
|
2944
2944
|
const scope = parsePvoxResourceScope(uri);
|
|
2945
|
-
const sha2562 = requireSha256(
|
|
2945
|
+
const sha2562 = requireSha256(record2.sha256, "PvoxAssetManifestV1.artifact.sha256");
|
|
2946
2946
|
if (scope.sha256 !== sha2562) throw new Error("PvoxAssetManifestV1.artifact URI hash must match sha256.");
|
|
2947
|
-
if (
|
|
2948
|
-
if (
|
|
2947
|
+
if (record2.contentType !== PVOX_CONTENT_TYPE) throw new Error("PvoxAssetManifestV1.artifact.contentType must be the PVOX content type.");
|
|
2948
|
+
if (record2.authenticated !== true) throw new Error("PvoxAssetManifestV1.artifact must be authenticated.");
|
|
2949
2949
|
return {
|
|
2950
2950
|
uri,
|
|
2951
|
-
byteLength: requireInteger(
|
|
2951
|
+
byteLength: requireInteger(record2.byteLength, "PvoxAssetManifestV1.artifact.byteLength", 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes),
|
|
2952
2952
|
sha256: sha2562,
|
|
2953
2953
|
contentType: PVOX_CONTENT_TYPE,
|
|
2954
2954
|
authenticated: true
|
|
@@ -2956,15 +2956,15 @@ function createResource(value) {
|
|
|
2956
2956
|
}
|
|
2957
2957
|
function createPage(value, index) {
|
|
2958
2958
|
const fieldName = `PvoxAssetManifestV1.pages[${index}]`;
|
|
2959
|
-
const
|
|
2960
|
-
const pageKind = requireEnum(
|
|
2961
|
-
exactKeys(
|
|
2962
|
-
const byteLength = requireInteger(
|
|
2963
|
-
const decodedByteLength = requireInteger(
|
|
2959
|
+
const record2 = asRecord(value, fieldName);
|
|
2960
|
+
const pageKind = requireEnum(record2.pageKind, PVOX_PAGE_KINDS, `${fieldName}.pageKind`);
|
|
2961
|
+
exactKeys(record2, ["pageIndex", "pageKind", "byteOffset", "byteLength", "decodedByteLength", "sha256", "spatialKey"], fieldName);
|
|
2962
|
+
const byteLength = requireInteger(record2.byteLength, `${fieldName}.byteLength`, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES);
|
|
2963
|
+
const decodedByteLength = requireInteger(record2.decodedByteLength, `${fieldName}.decodedByteLength`, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES);
|
|
2964
2964
|
if (decodedByteLength !== byteLength) throw new Error(`${fieldName} decodedByteLength must equal byteLength because PVOX pages are never compressed.`);
|
|
2965
2965
|
let spatialKey;
|
|
2966
|
-
if (
|
|
2967
|
-
const spatialRecord = asRecord(
|
|
2966
|
+
if (record2.spatialKey !== void 0) {
|
|
2967
|
+
const spatialRecord = asRecord(record2.spatialKey, `${fieldName}.spatialKey`);
|
|
2968
2968
|
exactKeys(spatialRecord, ["lodLevel", "partitionIndex", "hierarchyDepth", "minimumMortonCode", "maximumMortonCode"], `${fieldName}.spatialKey`);
|
|
2969
2969
|
const minimumMortonCode = requireString(spatialRecord.minimumMortonCode, `${fieldName}.spatialKey.minimumMortonCode`, 16);
|
|
2970
2970
|
const maximumMortonCode = requireString(spatialRecord.maximumMortonCode, `${fieldName}.spatialKey.maximumMortonCode`, 16);
|
|
@@ -2984,19 +2984,19 @@ function createPage(value, index) {
|
|
|
2984
2984
|
throw new Error(`${fieldName} render/collision field pages require spatialKey; metadata/LOD-structure pages forbid it.`);
|
|
2985
2985
|
}
|
|
2986
2986
|
return {
|
|
2987
|
-
pageIndex: requireInteger(
|
|
2987
|
+
pageIndex: requireInteger(record2.pageIndex, `${fieldName}.pageIndex`, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1),
|
|
2988
2988
|
pageKind,
|
|
2989
|
-
byteOffset: requireInteger(
|
|
2989
|
+
byteOffset: requireInteger(record2.byteOffset, `${fieldName}.byteOffset`, 0, PVOX_DEFAULT_LIMITS.maximumArtifactBytes - 1),
|
|
2990
2990
|
byteLength,
|
|
2991
2991
|
decodedByteLength,
|
|
2992
|
-
sha256: requireSha256(
|
|
2992
|
+
sha256: requireSha256(record2.sha256, `${fieldName}.sha256`),
|
|
2993
2993
|
...spatialKey === void 0 ? {} : { spatialKey }
|
|
2994
2994
|
};
|
|
2995
2995
|
}
|
|
2996
2996
|
function createLod(value, index, pageCount) {
|
|
2997
2997
|
const fieldName = `PvoxAssetManifestV1.lods[${index}]`;
|
|
2998
|
-
const
|
|
2999
|
-
exactKeys(
|
|
2998
|
+
const record2 = asRecord(value, fieldName);
|
|
2999
|
+
exactKeys(record2, [
|
|
3000
3000
|
"level",
|
|
3001
3001
|
"firstPageIndex",
|
|
3002
3002
|
"pageCount",
|
|
@@ -3010,30 +3010,30 @@ function createLod(value, index, pageCount) {
|
|
|
3010
3010
|
"p95DeltaE2000",
|
|
3011
3011
|
"normalizedMaterialError"
|
|
3012
3012
|
], fieldName);
|
|
3013
|
-
const firstPageIndex = requireInteger(
|
|
3014
|
-
const lodPageCount = requireInteger(
|
|
3013
|
+
const firstPageIndex = requireInteger(record2.firstPageIndex, `${fieldName}.firstPageIndex`, 0, pageCount - 1);
|
|
3014
|
+
const lodPageCount = requireInteger(record2.pageCount, `${fieldName}.pageCount`, 1, pageCount);
|
|
3015
3015
|
if (firstPageIndex + lodPageCount > pageCount) throw new Error(`${fieldName} page range exceeds the PVOX page set.`);
|
|
3016
|
-
const maximumSurfaceErrorMetres = requireNumber(
|
|
3017
|
-
const p99SurfaceErrorMetres = requireNumber(
|
|
3016
|
+
const maximumSurfaceErrorMetres = requireNumber(record2.maximumSurfaceErrorMetres, `${fieldName}.maximumSurfaceErrorMetres`, 0, 1e6);
|
|
3017
|
+
const p99SurfaceErrorMetres = requireNumber(record2.p99SurfaceErrorMetres, `${fieldName}.p99SurfaceErrorMetres`, 0, maximumSurfaceErrorMetres);
|
|
3018
3018
|
return {
|
|
3019
|
-
level: requireInteger(
|
|
3019
|
+
level: requireInteger(record2.level, `${fieldName}.level`, 0, 3),
|
|
3020
3020
|
firstPageIndex,
|
|
3021
3021
|
pageCount: lodPageCount,
|
|
3022
|
-
brickCount: requireInteger(
|
|
3023
|
-
cellSizeMetres: requireNumber(
|
|
3022
|
+
brickCount: requireInteger(record2.brickCount, `${fieldName}.brickCount`, 1, PVOX_DEFAULT_LIMITS.maximumBricks),
|
|
3023
|
+
cellSizeMetres: requireNumber(record2.cellSizeMetres, `${fieldName}.cellSizeMetres`, Number.EPSILON, 1e6),
|
|
3024
3024
|
maximumSurfaceErrorMetres,
|
|
3025
3025
|
p99SurfaceErrorMetres,
|
|
3026
|
-
silhouetteIou: requireNumber(
|
|
3027
|
-
maximumContourDisplacementPx: requireNumber(
|
|
3028
|
-
renderedSsim: requireNumber(
|
|
3029
|
-
p95DeltaE2000: requireNumber(
|
|
3030
|
-
normalizedMaterialError: requireNumber(
|
|
3026
|
+
silhouetteIou: requireNumber(record2.silhouetteIou, `${fieldName}.silhouetteIou`, 0, 1),
|
|
3027
|
+
maximumContourDisplacementPx: requireNumber(record2.maximumContourDisplacementPx, `${fieldName}.maximumContourDisplacementPx`, 0, 1e6),
|
|
3028
|
+
renderedSsim: requireNumber(record2.renderedSsim, `${fieldName}.renderedSsim`, 0, 1),
|
|
3029
|
+
p95DeltaE2000: requireNumber(record2.p95DeltaE2000, `${fieldName}.p95DeltaE2000`, 0, 100),
|
|
3030
|
+
normalizedMaterialError: requireNumber(record2.normalizedMaterialError, `${fieldName}.normalizedMaterialError`, 0, 1)
|
|
3031
3031
|
};
|
|
3032
3032
|
}
|
|
3033
3033
|
function createSourceIngestionLimitsV2(value) {
|
|
3034
3034
|
const fieldName = "ModelRequestSpecV2.sourceIngestionLimits";
|
|
3035
|
-
const
|
|
3036
|
-
exactKeys(
|
|
3035
|
+
const record2 = asRecord(value, fieldName);
|
|
3036
|
+
exactKeys(record2, [
|
|
3037
3037
|
"maximumDownloadBytes",
|
|
3038
3038
|
"maximumExpandedBytes",
|
|
3039
3039
|
"maximumArchiveEntries",
|
|
@@ -3042,12 +3042,12 @@ function createSourceIngestionLimitsV2(value) {
|
|
|
3042
3042
|
"maximumTextureDimensionPx"
|
|
3043
3043
|
], fieldName);
|
|
3044
3044
|
const limits = {
|
|
3045
|
-
maximumDownloadBytes: requireInteger(
|
|
3046
|
-
maximumExpandedBytes: requireInteger(
|
|
3047
|
-
maximumArchiveEntries: requireInteger(
|
|
3048
|
-
maximumSourceFiles: requireInteger(
|
|
3049
|
-
maximumDecodedTextureBytes: requireInteger(
|
|
3050
|
-
maximumTextureDimensionPx: requireInteger(
|
|
3045
|
+
maximumDownloadBytes: requireInteger(record2.maximumDownloadBytes, `${fieldName}.maximumDownloadBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumDownloadBytes),
|
|
3046
|
+
maximumExpandedBytes: requireInteger(record2.maximumExpandedBytes, `${fieldName}.maximumExpandedBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumExpandedBytes),
|
|
3047
|
+
maximumArchiveEntries: requireInteger(record2.maximumArchiveEntries, `${fieldName}.maximumArchiveEntries`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumArchiveEntries),
|
|
3048
|
+
maximumSourceFiles: requireInteger(record2.maximumSourceFiles, `${fieldName}.maximumSourceFiles`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumSourceFiles),
|
|
3049
|
+
maximumDecodedTextureBytes: requireInteger(record2.maximumDecodedTextureBytes, `${fieldName}.maximumDecodedTextureBytes`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumDecodedTextureBytes),
|
|
3050
|
+
maximumTextureDimensionPx: requireInteger(record2.maximumTextureDimensionPx, `${fieldName}.maximumTextureDimensionPx`, 1, PVOX_DEFAULT_SOURCE_INGESTION_LIMITS.maximumTextureDimensionPx)
|
|
3051
3051
|
};
|
|
3052
3052
|
if (limits.maximumExpandedBytes < limits.maximumDownloadBytes) {
|
|
3053
3053
|
throw new Error(`${fieldName}.maximumExpandedBytes must cover maximumDownloadBytes.`);
|
|
@@ -3062,8 +3062,8 @@ function createSourceIngestionLimitsV2(value) {
|
|
|
3062
3062
|
}
|
|
3063
3063
|
function createPvoxRuntimeLimitsV1(value) {
|
|
3064
3064
|
const fieldName = "ModelRequestSpecV2.pvoxRuntimeProfile.limits";
|
|
3065
|
-
const
|
|
3066
|
-
exactKeys(
|
|
3065
|
+
const record2 = asRecord(value, fieldName);
|
|
3066
|
+
exactKeys(record2, [
|
|
3067
3067
|
"maximumArtifactBytes",
|
|
3068
3068
|
"maximumPages",
|
|
3069
3069
|
"maximumHierarchyDepth",
|
|
@@ -3084,24 +3084,24 @@ function createPvoxRuntimeLimitsV1(value) {
|
|
|
3084
3084
|
"maximumGpuResidentBytes"
|
|
3085
3085
|
], fieldName);
|
|
3086
3086
|
const limits = {
|
|
3087
|
-
maximumArtifactBytes: requireInteger(
|
|
3088
|
-
maximumPages: requireInteger(
|
|
3089
|
-
maximumHierarchyDepth: requireInteger(
|
|
3090
|
-
maximumHierarchyNodes: requireInteger(
|
|
3091
|
-
maximumBricks: requireInteger(
|
|
3092
|
-
maximumLogicalVoxels: requireInteger(
|
|
3093
|
-
maximumEncodedSurfaceSamples: requireInteger(
|
|
3094
|
-
maximumSurfaceProperties: requireInteger(
|
|
3095
|
-
maximumPhysicalPaletteRecords: requireInteger(
|
|
3096
|
-
maximumPhysicalEvidenceEntries: requireInteger(
|
|
3097
|
-
maximumMaterialRegions: requireInteger(
|
|
3098
|
-
maximumInteriorLayers: requireInteger(
|
|
3099
|
-
maximumMassPropertyRecords: requireInteger(
|
|
3100
|
-
maximumBondRecords: requireInteger(
|
|
3101
|
-
maximumPartitions: requireInteger(
|
|
3102
|
-
maximumLodCount: requireInteger(
|
|
3103
|
-
maximumCpuResidentBytes: requireInteger(
|
|
3104
|
-
maximumGpuResidentBytes: requireInteger(
|
|
3087
|
+
maximumArtifactBytes: requireInteger(record2.maximumArtifactBytes, `${fieldName}.maximumArtifactBytes`, 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes),
|
|
3088
|
+
maximumPages: requireInteger(record2.maximumPages, `${fieldName}.maximumPages`, 1, PVOX_DEFAULT_LIMITS.maximumPages),
|
|
3089
|
+
maximumHierarchyDepth: requireInteger(record2.maximumHierarchyDepth, `${fieldName}.maximumHierarchyDepth`, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth),
|
|
3090
|
+
maximumHierarchyNodes: requireInteger(record2.maximumHierarchyNodes, `${fieldName}.maximumHierarchyNodes`, 1, PVOX_DEFAULT_LIMITS.maximumHierarchyNodes),
|
|
3091
|
+
maximumBricks: requireInteger(record2.maximumBricks, `${fieldName}.maximumBricks`, 1, PVOX_DEFAULT_LIMITS.maximumBricks),
|
|
3092
|
+
maximumLogicalVoxels: requireInteger(record2.maximumLogicalVoxels, `${fieldName}.maximumLogicalVoxels`, 1, PVOX_DEFAULT_LIMITS.maximumLogicalVoxels),
|
|
3093
|
+
maximumEncodedSurfaceSamples: requireInteger(record2.maximumEncodedSurfaceSamples, `${fieldName}.maximumEncodedSurfaceSamples`, 1, PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples),
|
|
3094
|
+
maximumSurfaceProperties: requireInteger(record2.maximumSurfaceProperties, `${fieldName}.maximumSurfaceProperties`, 1, PVOX_DEFAULT_LIMITS.maximumSurfaceProperties),
|
|
3095
|
+
maximumPhysicalPaletteRecords: requireInteger(record2.maximumPhysicalPaletteRecords, `${fieldName}.maximumPhysicalPaletteRecords`, 1, PVOX_DEFAULT_LIMITS.maximumPhysicalProperties),
|
|
3096
|
+
maximumPhysicalEvidenceEntries: requireInteger(record2.maximumPhysicalEvidenceEntries, `${fieldName}.maximumPhysicalEvidenceEntries`, 1, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries),
|
|
3097
|
+
maximumMaterialRegions: requireInteger(record2.maximumMaterialRegions, `${fieldName}.maximumMaterialRegions`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions),
|
|
3098
|
+
maximumInteriorLayers: requireInteger(record2.maximumInteriorLayers, `${fieldName}.maximumInteriorLayers`, 1, PVOX_DEFAULT_LIMITS.maximumInteriorLayers),
|
|
3099
|
+
maximumMassPropertyRecords: requireInteger(record2.maximumMassPropertyRecords, `${fieldName}.maximumMassPropertyRecords`, 1, PVOX_DEFAULT_LIMITS.maximumMassPropertyRecords),
|
|
3100
|
+
maximumBondRecords: requireInteger(record2.maximumBondRecords, `${fieldName}.maximumBondRecords`, 0, PVOX_DEFAULT_LIMITS.maximumBondRecords),
|
|
3101
|
+
maximumPartitions: requireInteger(record2.maximumPartitions, `${fieldName}.maximumPartitions`, 1, PVOX_DEFAULT_LIMITS.maximumPartitions),
|
|
3102
|
+
maximumLodCount: requireInteger(record2.maximumLodCount, `${fieldName}.maximumLodCount`, 1, PVOX_DEFAULT_LIMITS.maximumLodCount),
|
|
3103
|
+
maximumCpuResidentBytes: requireInteger(record2.maximumCpuResidentBytes, `${fieldName}.maximumCpuResidentBytes`, 1, PVOX_DEFAULT_LIMITS.maximumCpuResidentBytes),
|
|
3104
|
+
maximumGpuResidentBytes: requireInteger(record2.maximumGpuResidentBytes, `${fieldName}.maximumGpuResidentBytes`, 1, PVOX_DEFAULT_LIMITS.maximumGpuResidentBytes)
|
|
3105
3105
|
};
|
|
3106
3106
|
if (limits.maximumArtifactBytes > limits.maximumPages * PVOX_PAGE_SIZE_BYTES) {
|
|
3107
3107
|
throw new Error(`${fieldName}.maximumPages cannot contain maximumArtifactBytes.`);
|
|
@@ -3116,9 +3116,9 @@ function createPvoxRuntimeLimitsV1(value) {
|
|
|
3116
3116
|
}
|
|
3117
3117
|
function createPvoxRuntimeRequestProfileV1(value) {
|
|
3118
3118
|
const fieldName = "ModelRequestSpecV2.pvoxRuntimeProfile";
|
|
3119
|
-
const
|
|
3120
|
-
exactKeys(
|
|
3121
|
-
const requestedCapabilityEntries = asDenseArray(
|
|
3119
|
+
const record2 = asRecord(value, fieldName);
|
|
3120
|
+
exactKeys(record2, ["profileId", "fidelityProfileId", "capabilityProfileId", "geometryMode", "requiredCapabilities", "limits"], fieldName);
|
|
3121
|
+
const requestedCapabilityEntries = asDenseArray(record2.requiredCapabilities, `${fieldName}.requiredCapabilities`, 1, PVOX_CAPABILITIES.length);
|
|
3122
3122
|
const requiredCapabilities = requestedCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `${fieldName}.requiredCapabilities[${index}]`));
|
|
3123
3123
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) {
|
|
3124
3124
|
throw new Error(`${fieldName}.requiredCapabilities must not contain duplicates.`);
|
|
@@ -3126,7 +3126,7 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3126
3126
|
if (!requiredCapabilities.includes("rendering")) {
|
|
3127
3127
|
throw new Error(`${fieldName}.requiredCapabilities must include rendering.`);
|
|
3128
3128
|
}
|
|
3129
|
-
const capabilityProfileId = requireEnum(
|
|
3129
|
+
const capabilityProfileId = requireEnum(record2.capabilityProfileId, PVOX_RUNTIME_CAPABILITY_PROFILE_IDS, `${fieldName}.capabilityProfileId`);
|
|
3130
3130
|
const profileRequirements = {
|
|
3131
3131
|
"static-render-v1": ["rendering"],
|
|
3132
3132
|
"world-editable-v1": ["rendering", "collision", "destruction", "thermal", "moisture", "fluid-boundary"],
|
|
@@ -3135,24 +3135,24 @@ function createPvoxRuntimeRequestProfileV1(value) {
|
|
|
3135
3135
|
if (profileRequirements[capabilityProfileId].some((capability) => !requiredCapabilities.includes(capability))) {
|
|
3136
3136
|
throw new Error(`${fieldName}.requiredCapabilities do not satisfy capabilityProfileId.`);
|
|
3137
3137
|
}
|
|
3138
|
-
const limits = createPvoxRuntimeLimitsV1(
|
|
3138
|
+
const limits = createPvoxRuntimeLimitsV1(record2.limits);
|
|
3139
3139
|
if (capabilityProfileId === "world-editable-v1" && limits.maximumPhysicalEvidenceEntries < limits.maximumMaterialRegions * PVOX_PHYSICAL_PROPERTIES.length) {
|
|
3140
3140
|
throw new Error(`${fieldName}.limits must budget a complete physical-property matrix for every allowed material region.`);
|
|
3141
3141
|
}
|
|
3142
3142
|
return {
|
|
3143
|
-
profileId: requireToken(
|
|
3144
|
-
fidelityProfileId: requireEnum(
|
|
3143
|
+
profileId: requireToken(record2.profileId, `${fieldName}.profileId`),
|
|
3144
|
+
fidelityProfileId: requireEnum(record2.fidelityProfileId, PVOX_FIDELITY_PROFILE_IDS, `${fieldName}.fidelityProfileId`),
|
|
3145
3145
|
capabilityProfileId,
|
|
3146
|
-
geometryMode: requireEnum(
|
|
3146
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed", "auto"], `${fieldName}.geometryMode`),
|
|
3147
3147
|
requiredCapabilities,
|
|
3148
3148
|
limits
|
|
3149
3149
|
};
|
|
3150
3150
|
}
|
|
3151
3151
|
function createModelRequestSpecV2(input) {
|
|
3152
3152
|
const fieldName = "ModelRequestSpecV2";
|
|
3153
|
-
const
|
|
3154
|
-
assertDataOnlyTree(
|
|
3155
|
-
exactKeys(
|
|
3153
|
+
const record2 = asRecord(input, fieldName);
|
|
3154
|
+
assertDataOnlyTree(record2, fieldName);
|
|
3155
|
+
exactKeys(record2, [
|
|
3156
3156
|
"contractVersion",
|
|
3157
3157
|
"policyProfileId",
|
|
3158
3158
|
"requestSemanticProfileHash",
|
|
@@ -3166,26 +3166,26 @@ function createModelRequestSpecV2(input) {
|
|
|
3166
3166
|
"sourceIngestionLimits",
|
|
3167
3167
|
"pvoxRuntimeProfile"
|
|
3168
3168
|
], fieldName);
|
|
3169
|
-
if (
|
|
3169
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) {
|
|
3170
3170
|
throw new Error(`${fieldName}.contractVersion is unsupported.`);
|
|
3171
3171
|
}
|
|
3172
|
-
if (
|
|
3172
|
+
if (record2.policyProfileId !== PVOX_MODEL_REQUEST_POLICY_ID) {
|
|
3173
3173
|
throw new Error(`${fieldName}.policyProfileId must be ${PVOX_MODEL_REQUEST_POLICY_ID}.`);
|
|
3174
3174
|
}
|
|
3175
|
-
const hardConstraintsRecord = asRecord(
|
|
3175
|
+
const hardConstraintsRecord = asRecord(record2.hardConstraints ?? {}, `${fieldName}.hardConstraints`);
|
|
3176
3176
|
exactKeys(hardConstraintsRecord, ["boundsMetres", "dimensionsMetres", "collision", "partition"], `${fieldName}.hardConstraints`);
|
|
3177
|
-
const softPreferencesRecord = asRecord(
|
|
3177
|
+
const softPreferencesRecord = asRecord(record2.softPreferences ?? {}, `${fieldName}.softPreferences`);
|
|
3178
3178
|
for (const field of ["materials", "colors", "tags"]) {
|
|
3179
3179
|
if (softPreferencesRecord[field] !== void 0) {
|
|
3180
3180
|
asDenseArray(softPreferencesRecord[field], `${fieldName}.softPreferences.${field}`, 0, 32);
|
|
3181
3181
|
}
|
|
3182
3182
|
}
|
|
3183
|
-
const exclusions = asDenseArray(
|
|
3183
|
+
const exclusions = asDenseArray(record2.exclusions ?? [], `${fieldName}.exclusions`, 0, 32);
|
|
3184
3184
|
const normalizedV1 = createModelRequestSpec({
|
|
3185
|
-
query:
|
|
3186
|
-
revision:
|
|
3187
|
-
...
|
|
3188
|
-
...
|
|
3185
|
+
query: record2.query,
|
|
3186
|
+
revision: record2.revision,
|
|
3187
|
+
...record2.locale === void 0 ? {} : { locale: record2.locale },
|
|
3188
|
+
...record2.rankerId === void 0 ? {} : { rankerId: record2.rankerId },
|
|
3189
3189
|
hardConstraints: hardConstraintsRecord,
|
|
3190
3190
|
softPreferences: softPreferencesRecord,
|
|
3191
3191
|
exclusions
|
|
@@ -3199,7 +3199,7 @@ function createModelRequestSpecV2(input) {
|
|
|
3199
3199
|
return deepFreeze2({
|
|
3200
3200
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
3201
3201
|
policyProfileId: PVOX_MODEL_REQUEST_POLICY_ID,
|
|
3202
|
-
requestSemanticProfileHash: requireSha256(
|
|
3202
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, `${fieldName}.requestSemanticProfileHash`),
|
|
3203
3203
|
query: normalizedV1.query,
|
|
3204
3204
|
revision: normalizedV1.revision,
|
|
3205
3205
|
...normalizedV1.locale === void 0 ? {} : { locale: normalizedV1.locale },
|
|
@@ -3207,13 +3207,13 @@ function createModelRequestSpecV2(input) {
|
|
|
3207
3207
|
hardConstraints,
|
|
3208
3208
|
softPreferences: normalizedV1.softPreferences,
|
|
3209
3209
|
exclusions: normalizedV1.exclusions,
|
|
3210
|
-
sourceIngestionLimits: createSourceIngestionLimitsV2(
|
|
3211
|
-
pvoxRuntimeProfile: createPvoxRuntimeRequestProfileV1(
|
|
3210
|
+
sourceIngestionLimits: createSourceIngestionLimitsV2(record2.sourceIngestionLimits),
|
|
3211
|
+
pvoxRuntimeProfile: createPvoxRuntimeRequestProfileV1(record2.pvoxRuntimeProfile)
|
|
3212
3212
|
});
|
|
3213
3213
|
}
|
|
3214
3214
|
function createPvoxAssetManifestV1(input) {
|
|
3215
|
-
const
|
|
3216
|
-
exactKeys(
|
|
3215
|
+
const record2 = asRecord(input, "PvoxAssetManifestV1");
|
|
3216
|
+
exactKeys(record2, [
|
|
3217
3217
|
"manifestVersion",
|
|
3218
3218
|
"representation",
|
|
3219
3219
|
"formatVersion",
|
|
@@ -3244,23 +3244,23 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3244
3244
|
"pages",
|
|
3245
3245
|
"lods"
|
|
3246
3246
|
], "PvoxAssetManifestV1");
|
|
3247
|
-
if (
|
|
3248
|
-
if (
|
|
3249
|
-
const formatVersion = asRecord(
|
|
3247
|
+
if (record2.manifestVersion !== PVOX_ASSET_MANIFEST_VERSION) throw new Error("PvoxAssetManifestV1.manifestVersion is unsupported.");
|
|
3248
|
+
if (record2.representation !== "pvox") throw new Error("PvoxAssetManifestV1.representation must be pvox.");
|
|
3249
|
+
const formatVersion = asRecord(record2.formatVersion, "PvoxAssetManifestV1.formatVersion");
|
|
3250
3250
|
exactKeys(formatVersion, ["major", "minor"], "PvoxAssetManifestV1.formatVersion");
|
|
3251
3251
|
if (formatVersion.major !== PVOX_FORMAT_VERSION.major || formatVersion.minor !== PVOX_FORMAT_VERSION.minor) throw new Error("PvoxAssetManifestV1.formatVersion is unsupported.");
|
|
3252
|
-
if (
|
|
3252
|
+
if (record2.contentType !== PVOX_CONTENT_TYPE || record2.fileExtension !== PVOX_FILE_EXTENSION || record2.magic !== PVOX_MAGIC) {
|
|
3253
3253
|
throw new Error("PvoxAssetManifestV1 must declare the canonical PVOX content type, extension, and magic.");
|
|
3254
3254
|
}
|
|
3255
|
-
if (
|
|
3255
|
+
if (record2.brickEdgeVoxels !== PVOX_BRICK_EDGE_VOXELS || record2.pageSizeBytes !== PVOX_PAGE_SIZE_BYTES) {
|
|
3256
3256
|
throw new Error("PvoxAssetManifestV1 must use fixed 8-cubed bricks and 64-KiB pages.");
|
|
3257
3257
|
}
|
|
3258
|
-
assertCoordinateSystem2(
|
|
3259
|
-
const boundsMetres = createBounds2(
|
|
3258
|
+
assertCoordinateSystem2(record2.coordinateSystem, "PvoxAssetManifestV1.coordinateSystem");
|
|
3259
|
+
const boundsMetres = createBounds2(record2.boundsMetres, "PvoxAssetManifestV1.boundsMetres");
|
|
3260
3260
|
assertFloorCentredBounds(boundsMetres, "PvoxAssetManifestV1.boundsMetres");
|
|
3261
|
-
const artifact = createResource(
|
|
3261
|
+
const artifact = createResource(record2.artifact);
|
|
3262
3262
|
if (artifact.byteLength % PVOX_PAGE_SIZE_BYTES !== 0) throw new Error("PvoxAssetManifestV1 artifact byteLength must contain whole 64-KiB pages.");
|
|
3263
|
-
const pageEntries = asDenseArray(
|
|
3263
|
+
const pageEntries = asDenseArray(record2.pages, "PvoxAssetManifestV1.pages", 1, PVOX_DEFAULT_LIMITS.maximumPages);
|
|
3264
3264
|
const pages = pageEntries.map(createPage);
|
|
3265
3265
|
let expectedOffset = 0;
|
|
3266
3266
|
const previousSpatialMaximumByScope = /* @__PURE__ */ new Map();
|
|
@@ -3281,7 +3281,7 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3281
3281
|
expectedOffset += current.byteLength;
|
|
3282
3282
|
}
|
|
3283
3283
|
if (expectedOffset !== artifact.byteLength) throw new Error("PvoxAssetManifestV1 pages must exactly cover the artifact bytes.");
|
|
3284
|
-
const lodEntries = asDenseArray(
|
|
3284
|
+
const lodEntries = asDenseArray(record2.lods, "PvoxAssetManifestV1.lods", 1, 4);
|
|
3285
3285
|
const lods = lodEntries.map((entry, index) => createLod(entry, index, pages.length));
|
|
3286
3286
|
for (const [index, current] of lods.entries()) {
|
|
3287
3287
|
if (current.level !== index) throw new Error("PvoxAssetManifestV1 LOD levels must be contiguous from LOD0.");
|
|
@@ -3291,14 +3291,14 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3291
3291
|
throw new Error("PvoxAssetManifestV1 LOD page ranges must reference render-field pages in the matching LOD scope.");
|
|
3292
3292
|
}
|
|
3293
3293
|
}
|
|
3294
|
-
const sourceContentHash = requireSha256(
|
|
3295
|
-
const canonicalDocumentHash = requireSha256(
|
|
3296
|
-
const compilationInputHash = requireSha256(
|
|
3297
|
-
const runtimeRequestProfileHash = requireSha256(
|
|
3294
|
+
const sourceContentHash = requireSha256(record2.sourceContentHash, "PvoxAssetManifestV1.sourceContentHash");
|
|
3295
|
+
const canonicalDocumentHash = requireSha256(record2.canonicalDocumentHash, "PvoxAssetManifestV1.canonicalDocumentHash");
|
|
3296
|
+
const compilationInputHash = requireSha256(record2.compilationInputHash, "PvoxAssetManifestV1.compilationInputHash");
|
|
3297
|
+
const runtimeRequestProfileHash = requireSha256(record2.runtimeRequestProfileHash, "PvoxAssetManifestV1.runtimeRequestProfileHash");
|
|
3298
3298
|
if ((/* @__PURE__ */ new Set([sourceContentHash, canonicalDocumentHash, compilationInputHash, runtimeRequestProfileHash])).size !== 4) {
|
|
3299
3299
|
throw new Error("PvoxAssetManifestV1 source, canonical document, compilation input, and runtime request profile hashes must be distinct attestations.");
|
|
3300
3300
|
}
|
|
3301
|
-
if (
|
|
3301
|
+
if (record2.headerByteLength !== PVOX_HEADER_BYTE_LENGTH || record2.directoryEntryByteLength !== PVOX_DIRECTORY_ENTRY_BYTE_LENGTH || record2.maximumRunsPerBrick !== PVOX_MAX_RUNS_PER_BRICK || record2.maximumLocalSamplesPerBrick !== PVOX_MAX_LOCAL_SAMPLES_PER_BRICK || record2.maximumEncodedBrickPayloadBytes !== PVOX_MAX_ENCODED_BRICK_PAYLOAD_BYTES) {
|
|
3302
3302
|
throw new Error("PvoxAssetManifestV1 must declare the fixed PVOX 1.0 layout and brick codec limits.");
|
|
3303
3303
|
}
|
|
3304
3304
|
return deepFreeze2({
|
|
@@ -3310,19 +3310,19 @@ function createPvoxAssetManifestV1(input) {
|
|
|
3310
3310
|
magic: PVOX_MAGIC,
|
|
3311
3311
|
brickEdgeVoxels: PVOX_BRICK_EDGE_VOXELS,
|
|
3312
3312
|
pageSizeBytes: PVOX_PAGE_SIZE_BYTES,
|
|
3313
|
-
geometryMode: requireEnum(
|
|
3313
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed"], "PvoxAssetManifestV1.geometryMode"),
|
|
3314
3314
|
sourceContentHash,
|
|
3315
3315
|
canonicalDocumentHash,
|
|
3316
|
-
rootHash: requireSha256(
|
|
3317
|
-
directoryHash: requireSha256(
|
|
3318
|
-
pageSetHash: requireSha256(
|
|
3319
|
-
binaryClosureHash: requireSha256(
|
|
3320
|
-
validationEvidenceHash: requireSha256(
|
|
3316
|
+
rootHash: requireSha256(record2.rootHash, "PvoxAssetManifestV1.rootHash"),
|
|
3317
|
+
directoryHash: requireSha256(record2.directoryHash, "PvoxAssetManifestV1.directoryHash"),
|
|
3318
|
+
pageSetHash: requireSha256(record2.pageSetHash, "PvoxAssetManifestV1.pageSetHash"),
|
|
3319
|
+
binaryClosureHash: requireSha256(record2.binaryClosureHash, "PvoxAssetManifestV1.binaryClosureHash"),
|
|
3320
|
+
validationEvidenceHash: requireSha256(record2.validationEvidenceHash, "PvoxAssetManifestV1.validationEvidenceHash"),
|
|
3321
3321
|
compilationInputHash,
|
|
3322
3322
|
runtimeRequestProfileHash,
|
|
3323
3323
|
headerByteLength: PVOX_HEADER_BYTE_LENGTH,
|
|
3324
3324
|
directoryEntryByteLength: PVOX_DIRECTORY_ENTRY_BYTE_LENGTH,
|
|
3325
|
-
sectionCount: requireInteger(
|
|
3325
|
+
sectionCount: requireInteger(record2.sectionCount, "PvoxAssetManifestV1.sectionCount", PVOX_MIN_STATIC_SECTION_COUNT, PVOX_MAX_SECTIONS),
|
|
3326
3326
|
maximumRunsPerBrick: PVOX_MAX_RUNS_PER_BRICK,
|
|
3327
3327
|
maximumLocalSamplesPerBrick: PVOX_MAX_LOCAL_SAMPLES_PER_BRICK,
|
|
3328
3328
|
maximumEncodedBrickPayloadBytes: PVOX_MAX_ENCODED_BRICK_PAYLOAD_BYTES,
|
|
@@ -3337,8 +3337,8 @@ function createPvoxAssetManifest(input) {
|
|
|
3337
3337
|
return createPvoxAssetManifestV1(input);
|
|
3338
3338
|
}
|
|
3339
3339
|
function createVoxelTechnicalProfile(input) {
|
|
3340
|
-
const
|
|
3341
|
-
exactKeys(
|
|
3340
|
+
const record2 = asRecord(input, "VoxelTechnicalProfile");
|
|
3341
|
+
exactKeys(record2, [
|
|
3342
3342
|
"boundsMetres",
|
|
3343
3343
|
"dimensionsMetres",
|
|
3344
3344
|
"geometryMode",
|
|
@@ -3370,49 +3370,49 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3370
3370
|
"maximumPartitionExtentMetres",
|
|
3371
3371
|
"maximumPartitionDiagonalMetres"
|
|
3372
3372
|
], "VoxelTechnicalProfile");
|
|
3373
|
-
const boundsMetres = createBounds2(
|
|
3373
|
+
const boundsMetres = createBounds2(record2.boundsMetres, "VoxelTechnicalProfile.boundsMetres");
|
|
3374
3374
|
assertFloorCentredBounds(boundsMetres, "VoxelTechnicalProfile.boundsMetres");
|
|
3375
|
-
const dimensionsMetres = createDimensions2(
|
|
3375
|
+
const dimensionsMetres = createDimensions2(record2.dimensionsMetres, "VoxelTechnicalProfile.dimensionsMetres");
|
|
3376
3376
|
const derived = boundsMetres.max.map((coordinate, index) => coordinate - boundsMetres.min[index]);
|
|
3377
3377
|
const supplied = [dimensionsMetres.width, dimensionsMetres.height, dimensionsMetres.depth];
|
|
3378
3378
|
if (derived.some((coordinate, index) => Math.abs(coordinate - supplied[index]) > Math.max(1e-6, Math.abs(coordinate) * 1e-6))) {
|
|
3379
3379
|
throw new Error("VoxelTechnicalProfile dimensionsMetres must match boundsMetres.");
|
|
3380
3380
|
}
|
|
3381
|
-
const artifactByteLength = requireInteger(
|
|
3382
|
-
const pageCount = requireInteger(
|
|
3381
|
+
const artifactByteLength = requireInteger(record2.artifactByteLength, "VoxelTechnicalProfile.artifactByteLength", 1, PVOX_DEFAULT_LIMITS.maximumArtifactBytes);
|
|
3382
|
+
const pageCount = requireInteger(record2.pageCount, "VoxelTechnicalProfile.pageCount", 1, PVOX_DEFAULT_LIMITS.maximumPages);
|
|
3383
3383
|
if (artifactByteLength !== pageCount * PVOX_PAGE_SIZE_BYTES) {
|
|
3384
3384
|
throw new Error("VoxelTechnicalProfile artifactByteLength must equal pageCount multiplied by the fixed PVOX page size.");
|
|
3385
3385
|
}
|
|
3386
|
-
const brickCount = requireInteger(
|
|
3387
|
-
const logicalVoxelCapacity = requireInteger(
|
|
3386
|
+
const brickCount = requireInteger(record2.brickCount, "VoxelTechnicalProfile.brickCount", 1, PVOX_DEFAULT_LIMITS.maximumBricks);
|
|
3387
|
+
const logicalVoxelCapacity = requireInteger(record2.logicalVoxelCapacity, "VoxelTechnicalProfile.logicalVoxelCapacity", 1, PVOX_DEFAULT_LIMITS.maximumLogicalVoxels);
|
|
3388
3388
|
if (logicalVoxelCapacity !== brickCount * PVOX_BRICK_EDGE_VOXELS ** 3) {
|
|
3389
3389
|
throw new Error("VoxelTechnicalProfile logicalVoxelCapacity must equal brickCount multiplied by 512 voxels per brick.");
|
|
3390
3390
|
}
|
|
3391
|
-
const encodedSurfaceSampleCount = requireInteger(
|
|
3391
|
+
const encodedSurfaceSampleCount = requireInteger(record2.encodedSurfaceSampleCount, "VoxelTechnicalProfile.encodedSurfaceSampleCount", 1, PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples);
|
|
3392
3392
|
if (encodedSurfaceSampleCount > logicalVoxelCapacity) {
|
|
3393
3393
|
throw new Error("VoxelTechnicalProfile encodedSurfaceSampleCount cannot exceed logicalVoxelCapacity.");
|
|
3394
3394
|
}
|
|
3395
|
-
const surfacePropertyCount = requireInteger(
|
|
3396
|
-
const sectionCount = requireInteger(
|
|
3397
|
-
const hierarchyDepth = requireInteger(
|
|
3398
|
-
const hierarchyNodeCount = requireInteger(
|
|
3399
|
-
const lodCount = requireInteger(
|
|
3400
|
-
const partitionCount = requireInteger(
|
|
3401
|
-
const levelSpanRecordCount = requireInteger(
|
|
3402
|
-
const physicalPaletteRecordCount = requireInteger(
|
|
3403
|
-
const physicalEvidenceRecordCount = requireInteger(
|
|
3404
|
-
const materialRegionCount = requireInteger(
|
|
3405
|
-
const interiorLayerRecordCount = requireInteger(
|
|
3406
|
-
const massPropertyRecordCount = requireInteger(
|
|
3407
|
-
const bondRecordCount = requireInteger(
|
|
3408
|
-
const hasCollision = requireBoolean(
|
|
3409
|
-
const collisionHierarchyNodeCount = requireInteger(
|
|
3410
|
-
const collisionBrickCount = requireInteger(
|
|
3411
|
-
const collisionLogicalVoxelCapacity = requireInteger(
|
|
3395
|
+
const surfacePropertyCount = requireInteger(record2.surfacePropertyCount, "VoxelTechnicalProfile.surfacePropertyCount", 1, PVOX_DEFAULT_LIMITS.maximumSurfaceProperties);
|
|
3396
|
+
const sectionCount = requireInteger(record2.sectionCount, "VoxelTechnicalProfile.sectionCount", PVOX_MIN_STATIC_SECTION_COUNT, PVOX_MAX_SECTIONS);
|
|
3397
|
+
const hierarchyDepth = requireInteger(record2.hierarchyDepth, "VoxelTechnicalProfile.hierarchyDepth", 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth);
|
|
3398
|
+
const hierarchyNodeCount = requireInteger(record2.hierarchyNodeCount, "VoxelTechnicalProfile.hierarchyNodeCount", 1, PVOX_DEFAULT_LIMITS.maximumHierarchyNodes);
|
|
3399
|
+
const lodCount = requireInteger(record2.lodCount, "VoxelTechnicalProfile.lodCount", 1, PVOX_DEFAULT_LIMITS.maximumLodCount);
|
|
3400
|
+
const partitionCount = requireInteger(record2.partitionCount, "VoxelTechnicalProfile.partitionCount", 1, PVOX_DEFAULT_LIMITS.maximumPartitions);
|
|
3401
|
+
const levelSpanRecordCount = requireInteger(record2.levelSpanRecordCount, "VoxelTechnicalProfile.levelSpanRecordCount", partitionCount * lodCount, partitionCount * lodCount * (hierarchyDepth + 1));
|
|
3402
|
+
const physicalPaletteRecordCount = requireInteger(record2.physicalPaletteRecordCount, "VoxelTechnicalProfile.physicalPaletteRecordCount", 1, PVOX_DEFAULT_LIMITS.maximumPhysicalProperties);
|
|
3403
|
+
const physicalEvidenceRecordCount = requireInteger(record2.physicalEvidenceRecordCount, "VoxelTechnicalProfile.physicalEvidenceRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
3404
|
+
const materialRegionCount = requireInteger(record2.materialRegionCount, "VoxelTechnicalProfile.materialRegionCount", 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3405
|
+
const interiorLayerRecordCount = requireInteger(record2.interiorLayerRecordCount, "VoxelTechnicalProfile.interiorLayerRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumInteriorLayers);
|
|
3406
|
+
const massPropertyRecordCount = requireInteger(record2.massPropertyRecordCount, "VoxelTechnicalProfile.massPropertyRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumMassPropertyRecords);
|
|
3407
|
+
const bondRecordCount = requireInteger(record2.bondRecordCount, "VoxelTechnicalProfile.bondRecordCount", 0, PVOX_DEFAULT_LIMITS.maximumBondRecords);
|
|
3408
|
+
const hasCollision = requireBoolean(record2.hasCollision, "VoxelTechnicalProfile.hasCollision");
|
|
3409
|
+
const collisionHierarchyNodeCount = requireInteger(record2.collisionHierarchyNodeCount, "VoxelTechnicalProfile.collisionHierarchyNodeCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumHierarchyNodes : 0);
|
|
3410
|
+
const collisionBrickCount = requireInteger(record2.collisionBrickCount, "VoxelTechnicalProfile.collisionBrickCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumBricks : 0);
|
|
3411
|
+
const collisionLogicalVoxelCapacity = requireInteger(record2.collisionLogicalVoxelCapacity, "VoxelTechnicalProfile.collisionLogicalVoxelCapacity", hasCollision ? PVOX_BRICK_EDGE_VOXELS ** 3 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumLogicalVoxels : 0);
|
|
3412
3412
|
if (collisionLogicalVoxelCapacity !== collisionBrickCount * PVOX_BRICK_EDGE_VOXELS ** 3) {
|
|
3413
3413
|
throw new Error("VoxelTechnicalProfile collisionLogicalVoxelCapacity must equal collisionBrickCount multiplied by 512.");
|
|
3414
3414
|
}
|
|
3415
|
-
const collisionEncodedSurfaceSampleCount = requireInteger(
|
|
3415
|
+
const collisionEncodedSurfaceSampleCount = requireInteger(record2.collisionEncodedSurfaceSampleCount, "VoxelTechnicalProfile.collisionEncodedSurfaceSampleCount", hasCollision ? 1 : 0, hasCollision ? PVOX_DEFAULT_LIMITS.maximumEncodedSurfaceSamples : 0);
|
|
3416
3416
|
if (collisionEncodedSurfaceSampleCount > collisionLogicalVoxelCapacity) {
|
|
3417
3417
|
throw new Error("VoxelTechnicalProfile collision encoded sample count cannot exceed its logical voxel capacity.");
|
|
3418
3418
|
}
|
|
@@ -3422,15 +3422,15 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3422
3422
|
}
|
|
3423
3423
|
const maximumDimensionMetres = Math.max(...supplied);
|
|
3424
3424
|
const fullDiagonalMetres = Math.hypot(...supplied);
|
|
3425
|
-
const maximumPartitionExtentMetres = requireNumber(
|
|
3426
|
-
const maximumPartitionDiagonalMetres = requireNumber(
|
|
3425
|
+
const maximumPartitionExtentMetres = requireNumber(record2.maximumPartitionExtentMetres, "VoxelTechnicalProfile.maximumPartitionExtentMetres", Number.EPSILON, maximumDimensionMetres + Math.max(1e-9, maximumDimensionMetres * 1e-9));
|
|
3426
|
+
const maximumPartitionDiagonalMetres = requireNumber(record2.maximumPartitionDiagonalMetres, "VoxelTechnicalProfile.maximumPartitionDiagonalMetres", maximumPartitionExtentMetres, fullDiagonalMetres + Math.max(1e-9, fullDiagonalMetres * 1e-9));
|
|
3427
3427
|
if (partitionCount === 1 && (!approximatelyEqual(maximumPartitionExtentMetres, maximumDimensionMetres) || !approximatelyEqual(maximumPartitionDiagonalMetres, fullDiagonalMetres))) {
|
|
3428
3428
|
throw new Error("VoxelTechnicalProfile single-partition extent and diagonal must cover the complete bounds.");
|
|
3429
3429
|
}
|
|
3430
3430
|
const profile = {
|
|
3431
3431
|
boundsMetres,
|
|
3432
3432
|
dimensionsMetres,
|
|
3433
|
-
geometryMode: requireEnum(
|
|
3433
|
+
geometryMode: requireEnum(record2.geometryMode, ["solid", "shell", "mixed"], "VoxelTechnicalProfile.geometryMode"),
|
|
3434
3434
|
artifactByteLength,
|
|
3435
3435
|
pageCount,
|
|
3436
3436
|
brickCount,
|
|
@@ -3452,8 +3452,8 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3452
3452
|
collisionBrickCount,
|
|
3453
3453
|
collisionLogicalVoxelCapacity,
|
|
3454
3454
|
collisionEncodedSurfaceSampleCount,
|
|
3455
|
-
cpuResidentByteLength: requireInteger(
|
|
3456
|
-
gpuResidentByteLength: requireInteger(
|
|
3455
|
+
cpuResidentByteLength: requireInteger(record2.cpuResidentByteLength, "VoxelTechnicalProfile.cpuResidentByteLength", 1, PVOX_DEFAULT_LIMITS.maximumCpuResidentBytes),
|
|
3456
|
+
gpuResidentByteLength: requireInteger(record2.gpuResidentByteLength, "VoxelTechnicalProfile.gpuResidentByteLength", 1, PVOX_DEFAULT_LIMITS.maximumGpuResidentBytes),
|
|
3457
3457
|
hasCollision,
|
|
3458
3458
|
partitionCount,
|
|
3459
3459
|
maximumPartitionExtentMetres,
|
|
@@ -3466,21 +3466,21 @@ function createVoxelTechnicalProfile(input) {
|
|
|
3466
3466
|
return deepFreeze2(profile);
|
|
3467
3467
|
}
|
|
3468
3468
|
function createVoxelCapabilityAssessment(input) {
|
|
3469
|
-
const
|
|
3470
|
-
exactKeys(
|
|
3471
|
-
const status = requireEnum(
|
|
3472
|
-
const reasonCodes2 = requireReasonCodes(
|
|
3469
|
+
const record2 = asRecord(input, "VoxelCapabilityAssessment");
|
|
3470
|
+
exactKeys(record2, ["capability", "status", "evaluatorId", "evaluatorVersion", "subjectContentHash", "evidenceHash", "reasonCodes", "evaluatedAt"], "VoxelCapabilityAssessment");
|
|
3471
|
+
const status = requireEnum(record2.status, ["supported", "unsupported", "not-applicable"], "VoxelCapabilityAssessment.status");
|
|
3472
|
+
const reasonCodes2 = requireReasonCodes(record2.reasonCodes, "VoxelCapabilityAssessment.reasonCodes");
|
|
3473
3473
|
if (status === "supported" && reasonCodes2.length !== 0) throw new Error("Supported capability evidence must not contain failure reason codes.");
|
|
3474
3474
|
if (status !== "supported" && reasonCodes2.length === 0) throw new Error("Unavailable capability evidence requires a reason code.");
|
|
3475
3475
|
return deepFreeze2({
|
|
3476
|
-
capability: requireEnum(
|
|
3476
|
+
capability: requireEnum(record2.capability, PVOX_CAPABILITIES, "VoxelCapabilityAssessment.capability"),
|
|
3477
3477
|
status,
|
|
3478
|
-
evaluatorId: requireToken(
|
|
3479
|
-
evaluatorVersion: assertImmutableAssetVersion(
|
|
3480
|
-
subjectContentHash: requireSha256(
|
|
3481
|
-
evidenceHash: requireSha256(
|
|
3478
|
+
evaluatorId: requireToken(record2.evaluatorId, "VoxelCapabilityAssessment.evaluatorId"),
|
|
3479
|
+
evaluatorVersion: assertImmutableAssetVersion(record2.evaluatorVersion),
|
|
3480
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "VoxelCapabilityAssessment.subjectContentHash"),
|
|
3481
|
+
evidenceHash: requireSha256(record2.evidenceHash, "VoxelCapabilityAssessment.evidenceHash"),
|
|
3482
3482
|
reasonCodes: reasonCodes2,
|
|
3483
|
-
evaluatedAt: requireTimestamp(
|
|
3483
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "VoxelCapabilityAssessment.evaluatedAt")
|
|
3484
3484
|
});
|
|
3485
3485
|
}
|
|
3486
3486
|
var PVOX_PHYSICAL_PROPERTY_POLICIES = deepFreeze2({
|
|
@@ -3505,42 +3505,42 @@ var PVOX_PHYSICAL_PROPERTY_POLICIES = deepFreeze2({
|
|
|
3505
3505
|
"interior-thickness": { unit: "m", minimum: Number.EPSILON, maximum: 1e6 }
|
|
3506
3506
|
});
|
|
3507
3507
|
function createPhysicalPropertyEvidence(input) {
|
|
3508
|
-
const
|
|
3509
|
-
exactKeys(
|
|
3510
|
-
const property = requireEnum(
|
|
3508
|
+
const record2 = asRecord(input, "PhysicalPropertyEvidence");
|
|
3509
|
+
exactKeys(record2, ["property", "value", "unit", "provenance", "confidence", "subjectContentHash", "regionId", "materialId", "policyId", "policyVersion", "evidenceHash", "reviewedBy", "reviewedAt", "reviewToken"], "PhysicalPropertyEvidence");
|
|
3510
|
+
const property = requireEnum(record2.property, PVOX_PHYSICAL_PROPERTIES, "PhysicalPropertyEvidence.property");
|
|
3511
3511
|
const policy = PVOX_PHYSICAL_PROPERTY_POLICIES[property];
|
|
3512
|
-
if (
|
|
3512
|
+
if (record2.policyId !== PVOX_PHYSICAL_PROPERTY_POLICY_ID || record2.policyVersion !== PVOX_PHYSICAL_PROPERTY_POLICY_VERSION) {
|
|
3513
3513
|
throw new Error("PhysicalPropertyEvidence must use the governed immutable physical-property policy.");
|
|
3514
3514
|
}
|
|
3515
|
-
assertImmutableAssetVersion(
|
|
3516
|
-
if (
|
|
3517
|
-
const provenance = requireEnum(
|
|
3518
|
-
const reviewedBy =
|
|
3519
|
-
const reviewedAt =
|
|
3520
|
-
const reviewToken =
|
|
3515
|
+
assertImmutableAssetVersion(record2.policyVersion);
|
|
3516
|
+
if (record2.unit !== policy.unit) throw new Error("PhysicalPropertyEvidence unit must match its governed property policy.");
|
|
3517
|
+
const provenance = requireEnum(record2.provenance, ["source", "authored", "derived", "inferred", "default"], "PhysicalPropertyEvidence.provenance");
|
|
3518
|
+
const reviewedBy = record2.reviewedBy === void 0 ? void 0 : requireToken(record2.reviewedBy, "PhysicalPropertyEvidence.reviewedBy");
|
|
3519
|
+
const reviewedAt = record2.reviewedAt === void 0 ? void 0 : requireTimestamp(record2.reviewedAt, "PhysicalPropertyEvidence.reviewedAt");
|
|
3520
|
+
const reviewToken = record2.reviewToken === void 0 ? void 0 : requireAttestation(record2.reviewToken, "PhysicalPropertyEvidence.reviewToken");
|
|
3521
3521
|
if (new Set([reviewedBy, reviewedAt, reviewToken].map((entry) => entry === void 0)).size !== 1) throw new Error("PhysicalPropertyEvidence signed review identity, timestamp, and token must be supplied together.");
|
|
3522
3522
|
if (provenance === "inferred" && reviewedBy === void 0) throw new Error("Inferred PhysicalPropertyEvidence requires human review.");
|
|
3523
3523
|
return deepFreeze2({
|
|
3524
3524
|
property,
|
|
3525
|
-
value: requireNumber(
|
|
3525
|
+
value: requireNumber(record2.value, "PhysicalPropertyEvidence.value", policy.minimum, policy.maximum),
|
|
3526
3526
|
unit: policy.unit,
|
|
3527
3527
|
provenance,
|
|
3528
|
-
confidence: requireNumber(
|
|
3529
|
-
subjectContentHash: requireSha256(
|
|
3530
|
-
regionId: requireToken(
|
|
3531
|
-
materialId: requireToken(
|
|
3528
|
+
confidence: requireNumber(record2.confidence, "PhysicalPropertyEvidence.confidence", 0, 1),
|
|
3529
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "PhysicalPropertyEvidence.subjectContentHash"),
|
|
3530
|
+
regionId: requireToken(record2.regionId, "PhysicalPropertyEvidence.regionId"),
|
|
3531
|
+
materialId: requireToken(record2.materialId, "PhysicalPropertyEvidence.materialId"),
|
|
3532
3532
|
policyId: PVOX_PHYSICAL_PROPERTY_POLICY_ID,
|
|
3533
3533
|
policyVersion: PVOX_PHYSICAL_PROPERTY_POLICY_VERSION,
|
|
3534
|
-
evidenceHash: requireSha256(
|
|
3534
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PhysicalPropertyEvidence.evidenceHash"),
|
|
3535
3535
|
...reviewedBy === void 0 ? {} : { reviewedBy, reviewedAt, reviewToken }
|
|
3536
3536
|
});
|
|
3537
3537
|
}
|
|
3538
3538
|
function createPvoxPhysicalRegionInventoryV1(input) {
|
|
3539
3539
|
const fieldName = "PvoxPhysicalRegionInventoryV1";
|
|
3540
|
-
const
|
|
3541
|
-
exactKeys(
|
|
3542
|
-
if (
|
|
3543
|
-
const entriesInput = asDenseArray(
|
|
3540
|
+
const record2 = asRecord(input, fieldName);
|
|
3541
|
+
exactKeys(record2, ["inventoryVersion", "subjectContentHash", "inventoryHash", "inventoryHashAttestation", "validationEvidenceHash", "entries"], fieldName);
|
|
3542
|
+
if (record2.inventoryVersion !== PVOX_PHYSICAL_REGION_INVENTORY_VERSION) throw new Error(`${fieldName}.inventoryVersion is unsupported.`);
|
|
3543
|
+
const entriesInput = asDenseArray(record2.entries, `${fieldName}.entries`, 1, PVOX_DEFAULT_LIMITS.maximumMaterialRegions);
|
|
3544
3544
|
const entries = entriesInput.map((entry, index) => {
|
|
3545
3545
|
const entryName = `${fieldName}.entries[${index}]`;
|
|
3546
3546
|
const item = asRecord(entry, entryName);
|
|
@@ -3555,23 +3555,23 @@ function createPvoxPhysicalRegionInventoryV1(input) {
|
|
|
3555
3555
|
if (entries.some((entry, index) => entry.regionIndex !== index)) throw new Error(`${fieldName} regionIndex values must be contiguous and canonically ordered.`);
|
|
3556
3556
|
const pairs = entries.map(({ regionId, materialId }) => `${regionId}\0${materialId}`);
|
|
3557
3557
|
if (new Set(pairs).size !== pairs.length) throw new Error(`${fieldName} region/material pairs must be unique.`);
|
|
3558
|
-
const inventoryHash = requireSha256(
|
|
3558
|
+
const inventoryHash = requireSha256(record2.inventoryHash, `${fieldName}.inventoryHash`);
|
|
3559
3559
|
return deepFreeze2({
|
|
3560
3560
|
inventoryVersion: PVOX_PHYSICAL_REGION_INVENTORY_VERSION,
|
|
3561
|
-
subjectContentHash: requireSha256(
|
|
3561
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`),
|
|
3562
3562
|
inventoryHash,
|
|
3563
|
-
inventoryHashAttestation: createHashAttestation(
|
|
3564
|
-
validationEvidenceHash: requireSha256(
|
|
3563
|
+
inventoryHashAttestation: createHashAttestation(record2.inventoryHashAttestation, PVOX_PHYSICAL_INVENTORY_HASH_DOMAIN, inventoryHash, `${fieldName}.inventoryHashAttestation`),
|
|
3564
|
+
validationEvidenceHash: requireSha256(record2.validationEvidenceHash, `${fieldName}.validationEvidenceHash`),
|
|
3565
3565
|
entries
|
|
3566
3566
|
});
|
|
3567
3567
|
}
|
|
3568
3568
|
function createReviewedStructuralEvidence(input, expectedKind, expectedSubjectHash, expectedInventoryHash, expectedRecordCount, fieldName) {
|
|
3569
|
-
const
|
|
3570
|
-
exactKeys(
|
|
3571
|
-
if (
|
|
3572
|
-
const subjectContentHash = requireSha256(
|
|
3573
|
-
const physicalInventoryHash = requireSha256(
|
|
3574
|
-
const recordCount = requireInteger(
|
|
3569
|
+
const record2 = asRecord(input, fieldName);
|
|
3570
|
+
exactKeys(record2, ["kind", "subjectContentHash", "physicalInventoryHash", "recordCount", "evidenceHash", "reviewerId", "reviewedAt", "reviewToken"], fieldName);
|
|
3571
|
+
if (record2.kind !== expectedKind) throw new Error(`${fieldName}.kind must be ${expectedKind}.`);
|
|
3572
|
+
const subjectContentHash = requireSha256(record2.subjectContentHash, `${fieldName}.subjectContentHash`);
|
|
3573
|
+
const physicalInventoryHash = requireSha256(record2.physicalInventoryHash, `${fieldName}.physicalInventoryHash`);
|
|
3574
|
+
const recordCount = requireInteger(record2.recordCount, `${fieldName}.recordCount`, 0, PVOX_DEFAULT_LIMITS.maximumBondRecords);
|
|
3575
3575
|
if (subjectContentHash !== expectedSubjectHash || physicalInventoryHash !== expectedInventoryHash || recordCount !== expectedRecordCount) {
|
|
3576
3576
|
throw new Error(`${fieldName} must bind the exact PVOX closure, physical inventory, and binary record count.`);
|
|
3577
3577
|
}
|
|
@@ -3580,15 +3580,15 @@ function createReviewedStructuralEvidence(input, expectedKind, expectedSubjectHa
|
|
|
3580
3580
|
subjectContentHash,
|
|
3581
3581
|
physicalInventoryHash,
|
|
3582
3582
|
recordCount,
|
|
3583
|
-
evidenceHash: requireSha256(
|
|
3584
|
-
reviewerId: requireToken(
|
|
3585
|
-
reviewedAt: requireTimestamp(
|
|
3586
|
-
reviewToken: requireAttestation(
|
|
3583
|
+
evidenceHash: requireSha256(record2.evidenceHash, `${fieldName}.evidenceHash`),
|
|
3584
|
+
reviewerId: requireToken(record2.reviewerId, `${fieldName}.reviewerId`),
|
|
3585
|
+
reviewedAt: requireTimestamp(record2.reviewedAt, `${fieldName}.reviewedAt`),
|
|
3586
|
+
reviewToken: requireAttestation(record2.reviewToken, `${fieldName}.reviewToken`)
|
|
3587
3587
|
});
|
|
3588
3588
|
}
|
|
3589
3589
|
function createPvoxFidelityEvidence(input) {
|
|
3590
|
-
const
|
|
3591
|
-
exactKeys(
|
|
3590
|
+
const record2 = asRecord(input, "PvoxFidelityEvidence");
|
|
3591
|
+
exactKeys(record2, [
|
|
3592
3592
|
"profileId",
|
|
3593
3593
|
"surfaceProfileId",
|
|
3594
3594
|
"profileVersion",
|
|
@@ -3615,8 +3615,8 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3615
3615
|
"evaluatedAt",
|
|
3616
3616
|
"decisionToken"
|
|
3617
3617
|
], "PvoxFidelityEvidence");
|
|
3618
|
-
const profileId = requireEnum(
|
|
3619
|
-
const surfaceProfileId = requireEnum(
|
|
3618
|
+
const profileId = requireEnum(record2.profileId, PVOX_FIDELITY_PROFILE_IDS, "PvoxFidelityEvidence.profileId");
|
|
3619
|
+
const surfaceProfileId = requireEnum(record2.surfaceProfileId, [
|
|
3620
3620
|
"precision-hero-v1",
|
|
3621
3621
|
"props-furniture-v1",
|
|
3622
3622
|
"rocks-organic-shells-v1",
|
|
@@ -3625,12 +3625,12 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3625
3625
|
if (profileId !== "deformable-v1" && profileId !== surfaceProfileId) {
|
|
3626
3626
|
throw new Error("PvoxFidelityEvidence surfaceProfileId must match its governed profileId.");
|
|
3627
3627
|
}
|
|
3628
|
-
if (
|
|
3628
|
+
if (record2.profileVersion !== PVOX_FIDELITY_PROFILE_VERSION) {
|
|
3629
3629
|
throw new Error("PvoxFidelityEvidence.profileVersion must use the governed profile version.");
|
|
3630
3630
|
}
|
|
3631
|
-
assertImmutableAssetVersion(
|
|
3632
|
-
const evaluatedDiagonalMetres = requireNumber(
|
|
3633
|
-
const lod0CellSizeMetres = requireNumber(
|
|
3631
|
+
assertImmutableAssetVersion(record2.profileVersion);
|
|
3632
|
+
const evaluatedDiagonalMetres = requireNumber(record2.evaluatedDiagonalMetres, "PvoxFidelityEvidence.evaluatedDiagonalMetres", Number.EPSILON, 1e9);
|
|
3633
|
+
const lod0CellSizeMetres = requireNumber(record2.lod0CellSizeMetres, "PvoxFidelityEvidence.lod0CellSizeMetres", Number.EPSILON, 1e6);
|
|
3634
3634
|
const profileFormula = {
|
|
3635
3635
|
"precision-hero-v1": [25e-5, 1e-4],
|
|
3636
3636
|
"props-furniture-v1": [5e-4, 25e-5],
|
|
@@ -3640,16 +3640,16 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3640
3640
|
const [absoluteFloor, diagonalFactor] = profileFormula[surfaceProfileId];
|
|
3641
3641
|
const governedP99Ceiling = Math.max(absoluteFloor, evaluatedDiagonalMetres * diagonalFactor);
|
|
3642
3642
|
const governedMaximumCeiling = Math.min(3 * governedP99Ceiling, 1.5 * lod0CellSizeMetres);
|
|
3643
|
-
const suppliedP99Ceiling = requireNumber(
|
|
3644
|
-
const suppliedMaximumCeiling = requireNumber(
|
|
3643
|
+
const suppliedP99Ceiling = requireNumber(record2.p99AllowedSurfaceErrorMetres, "PvoxFidelityEvidence.p99AllowedSurfaceErrorMetres", 0, 1e6);
|
|
3644
|
+
const suppliedMaximumCeiling = requireNumber(record2.maximumAllowedSurfaceErrorMetres, "PvoxFidelityEvidence.maximumAllowedSurfaceErrorMetres", 0, 1e6);
|
|
3645
3645
|
if (!approximatelyEqual(suppliedP99Ceiling, governedP99Ceiling) || !approximatelyEqual(suppliedMaximumCeiling, governedMaximumCeiling)) {
|
|
3646
3646
|
throw new Error("PvoxFidelityEvidence surface ceilings must match the governed profile formula and LOD0 cell limit.");
|
|
3647
3647
|
}
|
|
3648
|
-
const minimumSilhouetteIou = requireNumber(
|
|
3649
|
-
const maximumAllowedContourDisplacementPx = requireNumber(
|
|
3650
|
-
const minimumRenderedSsim = requireNumber(
|
|
3651
|
-
const maximumP95DeltaE2000 = requireNumber(
|
|
3652
|
-
const maximumNormalizedMaterialError = requireNumber(
|
|
3648
|
+
const minimumSilhouetteIou = requireNumber(record2.minimumSilhouetteIou, "PvoxFidelityEvidence.minimumSilhouetteIou", 0, 1);
|
|
3649
|
+
const maximumAllowedContourDisplacementPx = requireNumber(record2.maximumAllowedContourDisplacementPx, "PvoxFidelityEvidence.maximumAllowedContourDisplacementPx", 0, 1e6);
|
|
3650
|
+
const minimumRenderedSsim = requireNumber(record2.minimumRenderedSsim, "PvoxFidelityEvidence.minimumRenderedSsim", 0, 1);
|
|
3651
|
+
const maximumP95DeltaE2000 = requireNumber(record2.maximumP95DeltaE2000, "PvoxFidelityEvidence.maximumP95DeltaE2000", 0, 100);
|
|
3652
|
+
const maximumNormalizedMaterialError = requireNumber(record2.maximumNormalizedMaterialError, "PvoxFidelityEvidence.maximumNormalizedMaterialError", 0, 1);
|
|
3653
3653
|
if (!approximatelyEqual(minimumSilhouetteIou, 0.995) || !approximatelyEqual(maximumAllowedContourDisplacementPx, 0.5) || !approximatelyEqual(minimumRenderedSsim, 0.98) || !approximatelyEqual(maximumP95DeltaE2000, 3) || !approximatelyEqual(maximumNormalizedMaterialError, 1 / 255)) {
|
|
3654
3654
|
throw new Error("PvoxFidelityEvidence must use the governed silhouette, contour, render, colour, and material thresholds.");
|
|
3655
3655
|
}
|
|
@@ -3657,38 +3657,38 @@ function createPvoxFidelityEvidence(input) {
|
|
|
3657
3657
|
profileId,
|
|
3658
3658
|
surfaceProfileId,
|
|
3659
3659
|
profileVersion: PVOX_FIDELITY_PROFILE_VERSION,
|
|
3660
|
-
canonicalDocumentHash: requireSha256(
|
|
3661
|
-
pvoxBinaryClosureHash: requireSha256(
|
|
3660
|
+
canonicalDocumentHash: requireSha256(record2.canonicalDocumentHash, "PvoxFidelityEvidence.canonicalDocumentHash"),
|
|
3661
|
+
pvoxBinaryClosureHash: requireSha256(record2.pvoxBinaryClosureHash, "PvoxFidelityEvidence.pvoxBinaryClosureHash"),
|
|
3662
3662
|
evaluatedDiagonalMetres,
|
|
3663
3663
|
lod0CellSizeMetres,
|
|
3664
3664
|
maximumAllowedSurfaceErrorMetres: governedMaximumCeiling,
|
|
3665
|
-
maximumSurfaceErrorMetres: requireNumber(
|
|
3665
|
+
maximumSurfaceErrorMetres: requireNumber(record2.maximumSurfaceErrorMetres, "PvoxFidelityEvidence.maximumSurfaceErrorMetres", 0, 1e6),
|
|
3666
3666
|
p99AllowedSurfaceErrorMetres: governedP99Ceiling,
|
|
3667
|
-
p99SurfaceErrorMetres: requireNumber(
|
|
3667
|
+
p99SurfaceErrorMetres: requireNumber(record2.p99SurfaceErrorMetres, "PvoxFidelityEvidence.p99SurfaceErrorMetres", 0, 1e6),
|
|
3668
3668
|
minimumSilhouetteIou,
|
|
3669
|
-
silhouetteIou: requireNumber(
|
|
3669
|
+
silhouetteIou: requireNumber(record2.silhouetteIou, "PvoxFidelityEvidence.silhouetteIou", 0, 1),
|
|
3670
3670
|
maximumAllowedContourDisplacementPx,
|
|
3671
|
-
maximumContourDisplacementPx: requireNumber(
|
|
3671
|
+
maximumContourDisplacementPx: requireNumber(record2.maximumContourDisplacementPx, "PvoxFidelityEvidence.maximumContourDisplacementPx", 0, 1e6),
|
|
3672
3672
|
minimumRenderedSsim,
|
|
3673
|
-
renderedSsim: requireNumber(
|
|
3673
|
+
renderedSsim: requireNumber(record2.renderedSsim, "PvoxFidelityEvidence.renderedSsim", 0, 1),
|
|
3674
3674
|
maximumP95DeltaE2000,
|
|
3675
|
-
p95DeltaE2000: requireNumber(
|
|
3675
|
+
p95DeltaE2000: requireNumber(record2.p95DeltaE2000, "PvoxFidelityEvidence.p95DeltaE2000", 0, 100),
|
|
3676
3676
|
maximumNormalizedMaterialError,
|
|
3677
|
-
normalizedMaterialError: requireNumber(
|
|
3678
|
-
evidenceHash: requireSha256(
|
|
3679
|
-
evaluatedAt: requireTimestamp(
|
|
3680
|
-
decisionToken: requireAttestation(
|
|
3677
|
+
normalizedMaterialError: requireNumber(record2.normalizedMaterialError, "PvoxFidelityEvidence.normalizedMaterialError", 0, 1),
|
|
3678
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PvoxFidelityEvidence.evidenceHash"),
|
|
3679
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "PvoxFidelityEvidence.evaluatedAt"),
|
|
3680
|
+
decisionToken: requireAttestation(record2.decisionToken, "PvoxFidelityEvidence.decisionToken")
|
|
3681
3681
|
};
|
|
3682
3682
|
const passed = output.maximumSurfaceErrorMetres <= output.maximumAllowedSurfaceErrorMetres && output.p99SurfaceErrorMetres <= output.p99AllowedSurfaceErrorMetres && output.silhouetteIou >= output.minimumSilhouetteIou && output.maximumContourDisplacementPx <= output.maximumAllowedContourDisplacementPx && output.renderedSsim >= output.minimumRenderedSsim && output.p95DeltaE2000 <= output.maximumP95DeltaE2000 && output.normalizedMaterialError <= output.maximumNormalizedMaterialError;
|
|
3683
|
-
const outcome = requireEnum(
|
|
3683
|
+
const outcome = requireEnum(record2.outcome, ["passed", "blocked"], "PvoxFidelityEvidence.outcome");
|
|
3684
3684
|
if (outcome !== (passed ? "passed" : "blocked")) throw new Error("PvoxFidelityEvidence outcome must match its measured fidelity thresholds.");
|
|
3685
3685
|
return deepFreeze2({ ...output, outcome });
|
|
3686
3686
|
}
|
|
3687
3687
|
function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
3688
3688
|
const fieldName = "PvoxEditJournalCurrentState";
|
|
3689
|
-
const
|
|
3690
|
-
exactKeys(
|
|
3691
|
-
const pageEntries = asDenseArray(
|
|
3689
|
+
const record2 = asRecord(input, fieldName);
|
|
3690
|
+
exactKeys(record2, ["baseContentHash", "basePageSetHash", "placementId", "gridVersion", "revision", "rootHash", "latestJournalHash", "pageHashes"], fieldName);
|
|
3691
|
+
const pageEntries = asDenseArray(record2.pageHashes, `${fieldName}.pageHashes`, baseManifest.pages.length, baseManifest.pages.length);
|
|
3692
3692
|
const pageHashes = pageEntries.map((entry, index) => {
|
|
3693
3693
|
const entryName = `${fieldName}.pageHashes[${index}]`;
|
|
3694
3694
|
const page = asRecord(entry, entryName);
|
|
@@ -3698,38 +3698,38 @@ function createPvoxEditJournalCurrentState(input, baseManifest) {
|
|
|
3698
3698
|
return { pageIndex, sha256: requireSha256(page.sha256, `${entryName}.sha256`) };
|
|
3699
3699
|
});
|
|
3700
3700
|
return deepFreeze2({
|
|
3701
|
-
baseContentHash: requireSha256(
|
|
3702
|
-
basePageSetHash: requireSha256(
|
|
3703
|
-
placementId: requireToken(
|
|
3704
|
-
gridVersion: requireToken(
|
|
3705
|
-
revision: requireInteger(
|
|
3706
|
-
rootHash: requireSha256(
|
|
3707
|
-
latestJournalHash: requireSha256(
|
|
3701
|
+
baseContentHash: requireSha256(record2.baseContentHash, `${fieldName}.baseContentHash`),
|
|
3702
|
+
basePageSetHash: requireSha256(record2.basePageSetHash, `${fieldName}.basePageSetHash`),
|
|
3703
|
+
placementId: requireToken(record2.placementId, `${fieldName}.placementId`),
|
|
3704
|
+
gridVersion: requireToken(record2.gridVersion, `${fieldName}.gridVersion`),
|
|
3705
|
+
revision: requireInteger(record2.revision, `${fieldName}.revision`, 1, Number.MAX_SAFE_INTEGER),
|
|
3706
|
+
rootHash: requireSha256(record2.rootHash, `${fieldName}.rootHash`),
|
|
3707
|
+
latestJournalHash: requireSha256(record2.latestJournalHash, `${fieldName}.latestJournalHash`),
|
|
3708
3708
|
pageHashes
|
|
3709
3709
|
});
|
|
3710
3710
|
}
|
|
3711
3711
|
function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
3712
|
-
const
|
|
3713
|
-
exactKeys(
|
|
3714
|
-
if (
|
|
3712
|
+
const record2 = asRecord(input, "PvoxEditJournal");
|
|
3713
|
+
exactKeys(record2, ["journalVersion", "baseContentHash", "basePageSetHash", "placementId", "gridVersion", "operationId", "expectedRevision", "resultingRevision", "previousJournalHash", "expectedRootHash", "resultingRootHash", "resultingRootHashAttestation", "patches", "dirtyBoundsMetres", "massDeltaKg", "journalHash", "journalHashAttestation", "recordedAt"], "PvoxEditJournal");
|
|
3714
|
+
if (record2.journalVersion !== PVOX_EDIT_JOURNAL_VERSION) throw new Error("PvoxEditJournal.journalVersion is unsupported.");
|
|
3715
3715
|
const baseManifest = createPvoxAssetManifestV1(baseManifestInput);
|
|
3716
|
-
const baseContentHash = requireSha256(
|
|
3717
|
-
const basePageSetHash = requireSha256(
|
|
3716
|
+
const baseContentHash = requireSha256(record2.baseContentHash, "PvoxEditJournal.baseContentHash");
|
|
3717
|
+
const basePageSetHash = requireSha256(record2.basePageSetHash, "PvoxEditJournal.basePageSetHash");
|
|
3718
3718
|
if (baseContentHash !== baseManifest.artifact.sha256 || basePageSetHash !== baseManifest.pageSetHash) {
|
|
3719
3719
|
throw new Error("PvoxEditJournal must bind the exact base PVOX artifact and page inventory.");
|
|
3720
3720
|
}
|
|
3721
|
-
const expectedRevision = requireInteger(
|
|
3722
|
-
const resultingRevision = requireInteger(
|
|
3721
|
+
const expectedRevision = requireInteger(record2.expectedRevision, "PvoxEditJournal.expectedRevision", 0, Number.MAX_SAFE_INTEGER - 1);
|
|
3722
|
+
const resultingRevision = requireInteger(record2.resultingRevision, "PvoxEditJournal.resultingRevision", 1, Number.MAX_SAFE_INTEGER);
|
|
3723
3723
|
if (resultingRevision !== expectedRevision + 1) throw new Error("PvoxEditJournal revisions must be sequential.");
|
|
3724
|
-
const previousJournalHash = requireSha256(
|
|
3724
|
+
const previousJournalHash = requireSha256(record2.previousJournalHash, "PvoxEditJournal.previousJournalHash");
|
|
3725
3725
|
if (expectedRevision === 0 !== (previousJournalHash === PVOX_EDIT_JOURNAL_GENESIS_HASH)) {
|
|
3726
3726
|
throw new Error("PvoxEditJournal genesis revision must use only the canonical genesis previousJournalHash.");
|
|
3727
3727
|
}
|
|
3728
|
-
const expectedRootHash = requireSha256(
|
|
3729
|
-
const resultingRootHash = requireSha256(
|
|
3728
|
+
const expectedRootHash = requireSha256(record2.expectedRootHash, "PvoxEditJournal.expectedRootHash");
|
|
3729
|
+
const resultingRootHash = requireSha256(record2.resultingRootHash, "PvoxEditJournal.resultingRootHash");
|
|
3730
3730
|
if (expectedRootHash === resultingRootHash) throw new Error("PvoxEditJournal copy-on-write operation must change the overlay root hash.");
|
|
3731
|
-
const placementId = requireToken(
|
|
3732
|
-
const gridVersion = requireToken(
|
|
3731
|
+
const placementId = requireToken(record2.placementId, "PvoxEditJournal.placementId");
|
|
3732
|
+
const gridVersion = requireToken(record2.gridVersion, "PvoxEditJournal.gridVersion");
|
|
3733
3733
|
let currentState;
|
|
3734
3734
|
if (expectedRevision === 0) {
|
|
3735
3735
|
if (currentStateInput !== void 0) throw new Error("PvoxEditJournal genesis must not supply a non-genesis current state.");
|
|
@@ -3741,7 +3741,7 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
3741
3741
|
throw new Error("PvoxEditJournal non-genesis CAS subject must match the authenticated current placement state.");
|
|
3742
3742
|
}
|
|
3743
3743
|
}
|
|
3744
|
-
const patchEntries = asDenseArray(
|
|
3744
|
+
const patchEntries = asDenseArray(record2.patches, "PvoxEditJournal.patches", 1, PVOX_MAX_EDIT_PATCHES);
|
|
3745
3745
|
const patches = patchEntries.map((entry, index) => {
|
|
3746
3746
|
const fieldName = `PvoxEditJournal.patches[${index}]`;
|
|
3747
3747
|
const patch = asRecord(entry, fieldName);
|
|
@@ -3793,26 +3793,26 @@ function createPvoxEditJournal(input, baseManifestInput, currentStateInput) {
|
|
|
3793
3793
|
if (new Set(patches.map(({ expectedPageIndex }) => expectedPageIndex)).size !== patches.length) {
|
|
3794
3794
|
throw new Error("PvoxEditJournal permits only one coherent patch transition per page.");
|
|
3795
3795
|
}
|
|
3796
|
-
const journalHash = requireSha256(
|
|
3796
|
+
const journalHash = requireSha256(record2.journalHash, "PvoxEditJournal.journalHash");
|
|
3797
3797
|
return deepFreeze2({
|
|
3798
3798
|
journalVersion: PVOX_EDIT_JOURNAL_VERSION,
|
|
3799
3799
|
baseContentHash,
|
|
3800
3800
|
basePageSetHash,
|
|
3801
3801
|
placementId,
|
|
3802
3802
|
gridVersion,
|
|
3803
|
-
operationId: requireToken(
|
|
3803
|
+
operationId: requireToken(record2.operationId, "PvoxEditJournal.operationId"),
|
|
3804
3804
|
expectedRevision,
|
|
3805
3805
|
resultingRevision,
|
|
3806
3806
|
previousJournalHash,
|
|
3807
3807
|
expectedRootHash,
|
|
3808
3808
|
resultingRootHash,
|
|
3809
|
-
resultingRootHashAttestation: createHashAttestation(
|
|
3809
|
+
resultingRootHashAttestation: createHashAttestation(record2.resultingRootHashAttestation, PVOX_EDIT_OVERLAY_ROOT_HASH_DOMAIN, resultingRootHash, "PvoxEditJournal.resultingRootHashAttestation"),
|
|
3810
3810
|
patches,
|
|
3811
|
-
dirtyBoundsMetres: createBounds2(
|
|
3812
|
-
massDeltaKg: requireNumber(
|
|
3811
|
+
dirtyBoundsMetres: createBounds2(record2.dirtyBoundsMetres, "PvoxEditJournal.dirtyBoundsMetres"),
|
|
3812
|
+
massDeltaKg: requireNumber(record2.massDeltaKg, "PvoxEditJournal.massDeltaKg", -Number.MAX_VALUE, Number.MAX_VALUE),
|
|
3813
3813
|
journalHash,
|
|
3814
|
-
journalHashAttestation: createHashAttestation(
|
|
3815
|
-
recordedAt: requireTimestamp(
|
|
3814
|
+
journalHashAttestation: createHashAttestation(record2.journalHashAttestation, PVOX_EDIT_JOURNAL_HASH_DOMAIN, journalHash, "PvoxEditJournal.journalHashAttestation"),
|
|
3815
|
+
recordedAt: requireTimestamp(record2.recordedAt, "PvoxEditJournal.recordedAt")
|
|
3816
3816
|
});
|
|
3817
3817
|
}
|
|
3818
3818
|
function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
@@ -3853,55 +3853,55 @@ function createPvoxEditJournalChain(input, baseManifestInput) {
|
|
|
3853
3853
|
return deepFreeze2(journals);
|
|
3854
3854
|
}
|
|
3855
3855
|
function createCompilerEvidence(value) {
|
|
3856
|
-
const
|
|
3857
|
-
exactKeys(
|
|
3856
|
+
const record2 = asRecord(value, "ModelProcessingManifestV2.converter");
|
|
3857
|
+
exactKeys(record2, ["compilerId", "compilerVersion", "sourceFormat", "sourceContentHash", "canonicalDocumentHash", "compilationInputHash", "runtimeRequestProfileHash", "outputContentHash", "evidenceHash", "compiledAt"], "ModelProcessingManifestV2.converter");
|
|
3858
3858
|
return {
|
|
3859
|
-
compilerId: requireToken(
|
|
3860
|
-
compilerVersion: assertImmutableAssetVersion(
|
|
3861
|
-
sourceFormat: requireToken(
|
|
3862
|
-
sourceContentHash: requireSha256(
|
|
3863
|
-
canonicalDocumentHash: requireSha256(
|
|
3864
|
-
compilationInputHash: requireSha256(
|
|
3865
|
-
runtimeRequestProfileHash: requireSha256(
|
|
3866
|
-
outputContentHash: requireSha256(
|
|
3867
|
-
evidenceHash: requireSha256(
|
|
3868
|
-
compiledAt: requireTimestamp(
|
|
3859
|
+
compilerId: requireToken(record2.compilerId, "ModelProcessingManifestV2.converter.compilerId"),
|
|
3860
|
+
compilerVersion: assertImmutableAssetVersion(record2.compilerVersion),
|
|
3861
|
+
sourceFormat: requireToken(record2.sourceFormat, "ModelProcessingManifestV2.converter.sourceFormat"),
|
|
3862
|
+
sourceContentHash: requireSha256(record2.sourceContentHash, "ModelProcessingManifestV2.converter.sourceContentHash"),
|
|
3863
|
+
canonicalDocumentHash: requireSha256(record2.canonicalDocumentHash, "ModelProcessingManifestV2.converter.canonicalDocumentHash"),
|
|
3864
|
+
compilationInputHash: requireSha256(record2.compilationInputHash, "ModelProcessingManifestV2.converter.compilationInputHash"),
|
|
3865
|
+
runtimeRequestProfileHash: requireSha256(record2.runtimeRequestProfileHash, "ModelProcessingManifestV2.converter.runtimeRequestProfileHash"),
|
|
3866
|
+
outputContentHash: requireSha256(record2.outputContentHash, "ModelProcessingManifestV2.converter.outputContentHash"),
|
|
3867
|
+
evidenceHash: requireSha256(record2.evidenceHash, "ModelProcessingManifestV2.converter.evidenceHash"),
|
|
3868
|
+
compiledAt: requireTimestamp(record2.compiledAt, "ModelProcessingManifestV2.converter.compiledAt")
|
|
3869
3869
|
};
|
|
3870
3870
|
}
|
|
3871
3871
|
function createPvoxGateEvidence(input) {
|
|
3872
|
-
const
|
|
3873
|
-
exactKeys(
|
|
3874
|
-
const outcome = requireEnum(
|
|
3875
|
-
const reasonCodes2 = requireReasonCodes(
|
|
3872
|
+
const record2 = asRecord(input, "PvoxGateEvidence");
|
|
3873
|
+
exactKeys(record2, ["kind", "outcome", "validatorId", "validatorVersion", "subjectContentHash", "evidenceHash", "reasonCodes", "evaluatedAt", "attestationToken"], "PvoxGateEvidence");
|
|
3874
|
+
const outcome = requireEnum(record2.outcome, ["passed", "blocked"], "PvoxGateEvidence.outcome");
|
|
3875
|
+
const reasonCodes2 = requireReasonCodes(record2.reasonCodes, "PvoxGateEvidence.reasonCodes");
|
|
3876
3876
|
if (outcome === "blocked" && reasonCodes2.length === 0) throw new Error("Blocked PvoxGateEvidence requires a reason code.");
|
|
3877
3877
|
return deepFreeze2({
|
|
3878
|
-
kind: requireEnum(
|
|
3878
|
+
kind: requireEnum(record2.kind, PVOX_NON_OVERRIDABLE_GATE_KINDS, "PvoxGateEvidence.kind"),
|
|
3879
3879
|
outcome,
|
|
3880
|
-
validatorId: requireToken(
|
|
3881
|
-
validatorVersion: assertImmutableAssetVersion(
|
|
3882
|
-
subjectContentHash: requireSha256(
|
|
3883
|
-
evidenceHash: requireSha256(
|
|
3880
|
+
validatorId: requireToken(record2.validatorId, "PvoxGateEvidence.validatorId"),
|
|
3881
|
+
validatorVersion: assertImmutableAssetVersion(record2.validatorVersion),
|
|
3882
|
+
subjectContentHash: requireSha256(record2.subjectContentHash, "PvoxGateEvidence.subjectContentHash"),
|
|
3883
|
+
evidenceHash: requireSha256(record2.evidenceHash, "PvoxGateEvidence.evidenceHash"),
|
|
3884
3884
|
reasonCodes: reasonCodes2,
|
|
3885
|
-
evaluatedAt: requireTimestamp(
|
|
3886
|
-
attestationToken: requireAttestation(
|
|
3885
|
+
evaluatedAt: requireTimestamp(record2.evaluatedAt, "PvoxGateEvidence.evaluatedAt"),
|
|
3886
|
+
attestationToken: requireAttestation(record2.attestationToken, "PvoxGateEvidence.attestationToken")
|
|
3887
3887
|
});
|
|
3888
3888
|
}
|
|
3889
3889
|
function createTransform2(value, fieldName) {
|
|
3890
|
-
const
|
|
3891
|
-
exactKeys(
|
|
3892
|
-
const translationMetres = requireVector(
|
|
3893
|
-
const rotationEntries = asDenseArray(
|
|
3890
|
+
const record2 = asRecord(value, fieldName);
|
|
3891
|
+
exactKeys(record2, ["translationMetres", "rotationQuaternion", "scale"], fieldName);
|
|
3892
|
+
const translationMetres = requireVector(record2.translationMetres, `${fieldName}.translationMetres`);
|
|
3893
|
+
const rotationEntries = asDenseArray(record2.rotationQuaternion, `${fieldName}.rotationQuaternion`, 4, 4);
|
|
3894
3894
|
const rotationQuaternion = rotationEntries.map((entry, index) => requireNumber(entry, `${fieldName}.rotationQuaternion[${index}]`, -1, 1));
|
|
3895
3895
|
if (Math.abs(Math.hypot(...rotationQuaternion) - 1) > 1e-4) throw new Error(`${fieldName}.rotationQuaternion must be normalized.`);
|
|
3896
|
-
const scale = requireVector(
|
|
3896
|
+
const scale = requireVector(record2.scale, `${fieldName}.scale`);
|
|
3897
3897
|
if (scale.some((entry) => entry <= 0)) throw new Error(`${fieldName}.scale values must be positive.`);
|
|
3898
3898
|
return { translationMetres, rotationQuaternion, scale };
|
|
3899
3899
|
}
|
|
3900
3900
|
function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
3901
3901
|
const fieldName = `ModelProcessingManifestV2.children[${index}]`;
|
|
3902
|
-
const
|
|
3903
|
-
exactKeys(
|
|
3904
|
-
const childRef = asRecord(
|
|
3902
|
+
const record2 = asRecord(value, fieldName);
|
|
3903
|
+
exactKeys(record2, ["instanceId", "parentInstanceId", "assetRef", "transform"], fieldName);
|
|
3904
|
+
const childRef = asRecord(record2.assetRef, `${fieldName}.assetRef`);
|
|
3905
3905
|
let assetRef;
|
|
3906
3906
|
if (childRef.disposition === "staged-derived") {
|
|
3907
3907
|
exactKeys(childRef, ["disposition", "representation", "derivedId", "kind", "contentHash", "binaryClosureHash", "processingClosureHash", "processingManifestId", "processingManifestUri"], `${fieldName}.assetRef`);
|
|
@@ -3938,12 +3938,12 @@ function createAssemblyChild2(value, index, resolutionId, candidateId) {
|
|
|
3938
3938
|
processingManifestUri: expectedUri
|
|
3939
3939
|
};
|
|
3940
3940
|
}
|
|
3941
|
-
const parentInstanceId =
|
|
3941
|
+
const parentInstanceId = record2.parentInstanceId === void 0 ? void 0 : requireToken(record2.parentInstanceId, `${fieldName}.parentInstanceId`);
|
|
3942
3942
|
return {
|
|
3943
|
-
instanceId: requireToken(
|
|
3943
|
+
instanceId: requireToken(record2.instanceId, `${fieldName}.instanceId`),
|
|
3944
3944
|
...parentInstanceId === void 0 ? {} : { parentInstanceId },
|
|
3945
3945
|
assetRef,
|
|
3946
|
-
transform: createTransform2(
|
|
3946
|
+
transform: createTransform2(record2.transform, `${fieldName}.transform`)
|
|
3947
3947
|
};
|
|
3948
3948
|
}
|
|
3949
3949
|
function assertAssembly(children) {
|
|
@@ -3985,8 +3985,8 @@ var NON_DEFAULT_CRITICAL_PROPERTIES = /* @__PURE__ */ new Set([
|
|
|
3985
3985
|
"melting-temperature"
|
|
3986
3986
|
]);
|
|
3987
3987
|
function createModelProcessingManifestV2(input) {
|
|
3988
|
-
const
|
|
3989
|
-
exactKeys(
|
|
3988
|
+
const record2 = asRecord(input, "ModelProcessingManifestV2");
|
|
3989
|
+
exactKeys(record2, [
|
|
3990
3990
|
"contractVersion",
|
|
3991
3991
|
"representation",
|
|
3992
3992
|
"manifestId",
|
|
@@ -4023,24 +4023,24 @@ function createModelProcessingManifestV2(input) {
|
|
|
4023
4023
|
"children",
|
|
4024
4024
|
"processedAt"
|
|
4025
4025
|
], "ModelProcessingManifestV2");
|
|
4026
|
-
if (
|
|
4027
|
-
if (
|
|
4028
|
-
const resolutionId = requirePathSegment(
|
|
4029
|
-
const candidateId = requirePathSegment(
|
|
4030
|
-
assertCoordinateSystem2(
|
|
4031
|
-
const pvox = createPvoxAssetManifestV1(
|
|
4026
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelProcessingManifestV2.contractVersion is unsupported.");
|
|
4027
|
+
if (record2.representation !== "pvox") throw new Error("ModelProcessingManifestV2.representation must be pvox.");
|
|
4028
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelProcessingManifestV2.resolutionId");
|
|
4029
|
+
const candidateId = requirePathSegment(record2.candidateId, "ModelProcessingManifestV2.candidateId");
|
|
4030
|
+
assertCoordinateSystem2(record2.coordinateSystem, "ModelProcessingManifestV2.coordinateSystem");
|
|
4031
|
+
const pvox = createPvoxAssetManifestV1(record2.pvox);
|
|
4032
4032
|
const resourceScope = parsePvoxResourceScope(pvox.artifact.uri);
|
|
4033
4033
|
if (resourceScope.kind === "resolution" && (resourceScope.resolutionId !== resolutionId || resourceScope.candidateId !== candidateId)) {
|
|
4034
4034
|
throw new Error("ModelProcessingManifestV2 PVOX resource namespace must match resolutionId and candidateId.");
|
|
4035
4035
|
}
|
|
4036
|
-
const technicalProfile = createVoxelTechnicalProfile(
|
|
4037
|
-
const contentHash = requireSha256(
|
|
4038
|
-
const binaryClosureHash = requireSha256(
|
|
4039
|
-
const processingClosureHash = requireSha256(
|
|
4040
|
-
const assemblyClosureHash = requireSha256(
|
|
4041
|
-
const requestSemanticProfileHash = requireSha256(
|
|
4042
|
-
const compilationInputHash = requireSha256(
|
|
4043
|
-
const runtimeRequestProfileHash = requireSha256(
|
|
4036
|
+
const technicalProfile = createVoxelTechnicalProfile(record2.technicalProfile);
|
|
4037
|
+
const contentHash = requireSha256(record2.contentHash, "ModelProcessingManifestV2.contentHash");
|
|
4038
|
+
const binaryClosureHash = requireSha256(record2.binaryClosureHash, "ModelProcessingManifestV2.binaryClosureHash");
|
|
4039
|
+
const processingClosureHash = requireSha256(record2.processingClosureHash, "ModelProcessingManifestV2.processingClosureHash");
|
|
4040
|
+
const assemblyClosureHash = requireSha256(record2.assemblyClosureHash, "ModelProcessingManifestV2.assemblyClosureHash");
|
|
4041
|
+
const requestSemanticProfileHash = requireSha256(record2.requestSemanticProfileHash, "ModelProcessingManifestV2.requestSemanticProfileHash");
|
|
4042
|
+
const compilationInputHash = requireSha256(record2.compilationInputHash, "ModelProcessingManifestV2.compilationInputHash");
|
|
4043
|
+
const runtimeRequestProfileHash = requireSha256(record2.runtimeRequestProfileHash, "ModelProcessingManifestV2.runtimeRequestProfileHash");
|
|
4044
4044
|
if (contentHash !== pvox.artifact.sha256) throw new Error("ModelProcessingManifestV2 contentHash must match the PVOX artifact.");
|
|
4045
4045
|
if (binaryClosureHash !== pvox.binaryClosureHash) throw new Error("ModelProcessingManifestV2 binaryClosureHash must match the PVOX binary closure.");
|
|
4046
4046
|
if (compilationInputHash !== pvox.compilationInputHash || runtimeRequestProfileHash !== pvox.runtimeRequestProfileHash) {
|
|
@@ -4060,20 +4060,20 @@ function createModelProcessingManifestV2(input) {
|
|
|
4060
4060
|
if (technicalProfile.maximumPartitionExtentMetres > maximumGridExtentMetres && !approximatelyEqual(technicalProfile.maximumPartitionExtentMetres, maximumGridExtentMetres)) {
|
|
4061
4061
|
throw new Error("ModelProcessingManifestV2 partition extent exceeds the LOD0 cell and hierarchy grid capacity; use smaller fidelity-driven partitions.");
|
|
4062
4062
|
}
|
|
4063
|
-
const capabilityEntries = asDenseArray(
|
|
4063
|
+
const capabilityEntries = asDenseArray(record2.capabilities, "ModelProcessingManifestV2.capabilities", PVOX_CAPABILITIES.length, PVOX_CAPABILITIES.length);
|
|
4064
4064
|
const capabilities = capabilityEntries.map(createVoxelCapabilityAssessment);
|
|
4065
4065
|
const byCapability = new Map(capabilities.map((entry) => [entry.capability, entry]));
|
|
4066
4066
|
if (byCapability.size !== PVOX_CAPABILITIES.length || PVOX_CAPABILITIES.some((name) => !byCapability.has(name))) throw new Error("ModelProcessingManifestV2 must include every PVOX capability exactly once.");
|
|
4067
4067
|
const orderedCapabilities = PVOX_CAPABILITIES.map((name) => byCapability.get(name));
|
|
4068
4068
|
if (orderedCapabilities.some((entry) => entry.subjectContentHash !== binaryClosureHash)) throw new Error("ModelProcessingManifestV2 capability evidence must bind the binaryClosureHash.");
|
|
4069
|
-
const capabilityEvidenceSetHash = requireSha256(
|
|
4069
|
+
const capabilityEvidenceSetHash = requireSha256(record2.capabilityEvidenceSetHash, "ModelProcessingManifestV2.capabilityEvidenceSetHash");
|
|
4070
4070
|
const capabilityEvidenceSetAttestation = createHashAttestation(
|
|
4071
|
-
|
|
4071
|
+
record2.capabilityEvidenceSetAttestation,
|
|
4072
4072
|
PVOX_CAPABILITY_SET_HASH_DOMAIN,
|
|
4073
4073
|
capabilityEvidenceSetHash,
|
|
4074
4074
|
"ModelProcessingManifestV2.capabilityEvidenceSetAttestation"
|
|
4075
4075
|
);
|
|
4076
|
-
const requiredCapabilityEntries = asDenseArray(
|
|
4076
|
+
const requiredCapabilityEntries = asDenseArray(record2.requiredCapabilities, "ModelProcessingManifestV2.requiredCapabilities", 1, PVOX_CAPABILITIES.length);
|
|
4077
4077
|
const requiredCapabilities = requiredCapabilityEntries.map((entry, index) => requireEnum(entry, PVOX_CAPABILITIES, `ModelProcessingManifestV2.requiredCapabilities[${index}]`));
|
|
4078
4078
|
if (new Set(requiredCapabilities).size !== requiredCapabilities.length) throw new Error("ModelProcessingManifestV2.requiredCapabilities must be unique.");
|
|
4079
4079
|
if (!requiredCapabilities.includes("rendering")) throw new Error("ModelProcessingManifestV2 rendering capability is always required.");
|
|
@@ -4081,11 +4081,11 @@ function createModelProcessingManifestV2(input) {
|
|
|
4081
4081
|
if (technicalProfile.hasCollision && collisionPages.every(({ spatialKey }) => spatialKey?.lodLevel !== 0)) {
|
|
4082
4082
|
throw new Error("ModelProcessingManifestV2 supported collision requires LOD0 collision-field evidence.");
|
|
4083
4083
|
}
|
|
4084
|
-
const physicalRegionInventory = createPvoxPhysicalRegionInventoryV1(
|
|
4084
|
+
const physicalRegionInventory = createPvoxPhysicalRegionInventoryV1(record2.physicalRegionInventory);
|
|
4085
4085
|
if (physicalRegionInventory.subjectContentHash !== binaryClosureHash || physicalRegionInventory.validationEvidenceHash !== pvox.validationEvidenceHash || physicalRegionInventory.entries.length !== technicalProfile.materialRegionCount || physicalRegionInventory.entries.some(({ physicalPaletteIndex }) => physicalPaletteIndex >= technicalProfile.physicalPaletteRecordCount)) {
|
|
4086
4086
|
throw new Error("ModelProcessingManifestV2 authoritative physical region inventory must bind the PVOX validator, closure, region count, and palette records.");
|
|
4087
4087
|
}
|
|
4088
|
-
const physicalEntries = asDenseArray(
|
|
4088
|
+
const physicalEntries = asDenseArray(record2.physicalProperties, "ModelProcessingManifestV2.physicalProperties", 0, PVOX_DEFAULT_LIMITS.maximumPhysicalEvidenceEntries);
|
|
4089
4089
|
const physicalProperties = physicalEntries.map(createPhysicalPropertyEvidence);
|
|
4090
4090
|
const byRegionProperty = new Map(physicalProperties.map((entry) => [`${entry.regionId}\0${entry.materialId}\0${entry.property}`, entry]));
|
|
4091
4091
|
if (byRegionProperty.size !== physicalProperties.length) throw new Error("ModelProcessingManifestV2 physical properties must be unique per region, material, and property.");
|
|
@@ -4125,15 +4125,15 @@ function createModelProcessingManifestV2(input) {
|
|
|
4125
4125
|
if (physicalProperties.length !== technicalProfile.physicalEvidenceRecordCount) {
|
|
4126
4126
|
throw new Error("ModelProcessingManifestV2 authoritative material-region evidence count must match the PVOX PEVI record count.");
|
|
4127
4127
|
}
|
|
4128
|
-
const physicalEvidenceHash = requireSha256(
|
|
4128
|
+
const physicalEvidenceHash = requireSha256(record2.physicalEvidenceHash, "ModelProcessingManifestV2.physicalEvidenceHash");
|
|
4129
4129
|
const physicalEvidenceAttestation = createHashAttestation(
|
|
4130
|
-
|
|
4130
|
+
record2.physicalEvidenceAttestation,
|
|
4131
4131
|
PVOX_PHYSICAL_EVIDENCE_HASH_DOMAIN,
|
|
4132
4132
|
physicalEvidenceHash,
|
|
4133
4133
|
"ModelProcessingManifestV2.physicalEvidenceAttestation"
|
|
4134
4134
|
);
|
|
4135
4135
|
const massPropertiesEvidence = createReviewedStructuralEvidence(
|
|
4136
|
-
|
|
4136
|
+
record2.massPropertiesEvidence,
|
|
4137
4137
|
"mass-properties",
|
|
4138
4138
|
binaryClosureHash,
|
|
4139
4139
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4141,7 +4141,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4141
4141
|
"ModelProcessingManifestV2.massPropertiesEvidence"
|
|
4142
4142
|
);
|
|
4143
4143
|
const bondGraphEvidence = createReviewedStructuralEvidence(
|
|
4144
|
-
|
|
4144
|
+
record2.bondGraphEvidence,
|
|
4145
4145
|
"bond-graph",
|
|
4146
4146
|
binaryClosureHash,
|
|
4147
4147
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4149,7 +4149,7 @@ function createModelProcessingManifestV2(input) {
|
|
|
4149
4149
|
"ModelProcessingManifestV2.bondGraphEvidence"
|
|
4150
4150
|
);
|
|
4151
4151
|
const interiorLayerEvidence = createReviewedStructuralEvidence(
|
|
4152
|
-
|
|
4152
|
+
record2.interiorLayerEvidence,
|
|
4153
4153
|
"interior-layers",
|
|
4154
4154
|
binaryClosureHash,
|
|
4155
4155
|
physicalRegionInventory.inventoryHash,
|
|
@@ -4159,9 +4159,9 @@ function createModelProcessingManifestV2(input) {
|
|
|
4159
4159
|
if (byCapability.get("destruction")?.status === "supported" && (technicalProfile.massPropertyRecordCount === 0 || technicalProfile.bondRecordCount === 0 || technicalProfile.interiorLayerRecordCount === 0)) {
|
|
4160
4160
|
throw new Error("ModelProcessingManifestV2 destruction capability requires reviewed mass, bond-graph, and interior-layer records.");
|
|
4161
4161
|
}
|
|
4162
|
-
const converter = createCompilerEvidence(
|
|
4162
|
+
const converter = createCompilerEvidence(record2.converter);
|
|
4163
4163
|
if (converter.sourceContentHash !== pvox.sourceContentHash || converter.canonicalDocumentHash !== pvox.canonicalDocumentHash || converter.compilationInputHash !== compilationInputHash || converter.runtimeRequestProfileHash !== runtimeRequestProfileHash || converter.outputContentHash !== contentHash) throw new Error("ModelProcessingManifestV2 converter hashes must match source, canonical document, compilation input, runtime request profile, and PVOX outputContentHash.");
|
|
4164
|
-
const fidelity = createPvoxFidelityEvidence(
|
|
4164
|
+
const fidelity = createPvoxFidelityEvidence(record2.fidelity);
|
|
4165
4165
|
const diagonalMetres = technicalProfile.maximumPartitionDiagonalMetres;
|
|
4166
4166
|
if (fidelity.canonicalDocumentHash !== pvox.canonicalDocumentHash || fidelity.pvoxBinaryClosureHash !== binaryClosureHash || !approximatelyEqual(fidelity.evaluatedDiagonalMetres, diagonalMetres) || !approximatelyEqual(fidelity.lod0CellSizeMetres, pvox.lods[0].cellSizeMetres)) {
|
|
4167
4167
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must bind the canonical document, PVOX closure, evaluated bounds, and LOD0 cell size.");
|
|
@@ -4170,22 +4170,22 @@ function createModelProcessingManifestV2(input) {
|
|
|
4170
4170
|
if (!approximatelyEqual(fidelity.maximumSurfaceErrorMetres, lod0.maximumSurfaceErrorMetres) || !approximatelyEqual(fidelity.p99SurfaceErrorMetres, lod0.p99SurfaceErrorMetres) || !approximatelyEqual(fidelity.silhouetteIou, lod0.silhouetteIou) || !approximatelyEqual(fidelity.maximumContourDisplacementPx, lod0.maximumContourDisplacementPx) || !approximatelyEqual(fidelity.renderedSsim, lod0.renderedSsim) || !approximatelyEqual(fidelity.p95DeltaE2000, lod0.p95DeltaE2000) || !approximatelyEqual(fidelity.normalizedMaterialError, lod0.normalizedMaterialError)) {
|
|
4171
4171
|
throw new Error("ModelProcessingManifestV2 fidelity evidence must exactly match every governed PVOX LOD0 metric.");
|
|
4172
4172
|
}
|
|
4173
|
-
const childEntries = asDenseArray(
|
|
4173
|
+
const childEntries = asDenseArray(record2.children, "ModelProcessingManifestV2.children", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4174
4174
|
const children = childEntries.map((entry, index) => createAssemblyChild2(entry, index, resolutionId, candidateId));
|
|
4175
|
-
const kind = requireEnum(
|
|
4175
|
+
const kind = requireEnum(record2.kind, ["leaf", "assembly"], "ModelProcessingManifestV2.kind");
|
|
4176
4176
|
if (kind === "leaf" && children.length !== 0) throw new Error("A leaf ModelProcessingManifestV2 cannot contain children.");
|
|
4177
4177
|
if (kind === "leaf" && technicalProfile.partitionCount !== 1) throw new Error("A leaf ModelProcessingManifestV2 must have one partition.");
|
|
4178
4178
|
if (kind === "leaf" && assemblyClosureHash === binaryClosureHash) throw new Error("A leaf ModelProcessingManifestV2 requires a distinct domain-separated empty-child assemblyClosureHash.");
|
|
4179
4179
|
if (kind === "assembly" && children.length === 0) throw new Error("An assembly ModelProcessingManifestV2 requires children.");
|
|
4180
4180
|
if (kind === "assembly") assertAssembly(children);
|
|
4181
|
-
const assemblyClosureAttestation = createHashAttestation(
|
|
4181
|
+
const assemblyClosureAttestation = createHashAttestation(record2.assemblyClosureAttestation, PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN, assemblyClosureHash, "ModelProcessingManifestV2.assemblyClosureAttestation");
|
|
4182
4182
|
const processingClosureAttestation = createHashAttestation(
|
|
4183
|
-
|
|
4183
|
+
record2.processingClosureAttestation,
|
|
4184
4184
|
PVOX_PROCESSING_CLOSURE_HASH_DOMAIN,
|
|
4185
4185
|
processingClosureHash,
|
|
4186
4186
|
"ModelProcessingManifestV2.processingClosureAttestation"
|
|
4187
4187
|
);
|
|
4188
|
-
const processedAt = requireTimestamp(
|
|
4188
|
+
const processedAt = requireTimestamp(record2.processedAt, "ModelProcessingManifestV2.processedAt");
|
|
4189
4189
|
const latestEvidence = Math.max(
|
|
4190
4190
|
Date.parse(converter.compiledAt),
|
|
4191
4191
|
Date.parse(fidelity.evaluatedAt),
|
|
@@ -4199,13 +4199,13 @@ function createModelProcessingManifestV2(input) {
|
|
|
4199
4199
|
return deepFreeze2({
|
|
4200
4200
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4201
4201
|
representation: "pvox",
|
|
4202
|
-
manifestId: requireToken(
|
|
4202
|
+
manifestId: requireToken(record2.manifestId, "ModelProcessingManifestV2.manifestId"),
|
|
4203
4203
|
resolutionId,
|
|
4204
4204
|
candidateId,
|
|
4205
4205
|
kind,
|
|
4206
|
-
requestProfileId: requireToken(
|
|
4206
|
+
requestProfileId: requireToken(record2.requestProfileId, "ModelProcessingManifestV2.requestProfileId"),
|
|
4207
4207
|
requestSemanticProfileHash,
|
|
4208
|
-
capabilityProfileId: requireEnum(
|
|
4208
|
+
capabilityProfileId: requireEnum(record2.capabilityProfileId, PVOX_RUNTIME_CAPABILITY_PROFILE_IDS, "ModelProcessingManifestV2.capabilityProfileId"),
|
|
4209
4209
|
contentHash,
|
|
4210
4210
|
binaryClosureHash,
|
|
4211
4211
|
processingClosureHash,
|
|
@@ -4235,76 +4235,76 @@ function createModelProcessingManifestV2(input) {
|
|
|
4235
4235
|
});
|
|
4236
4236
|
}
|
|
4237
4237
|
function createCandidateAssetRef2(value) {
|
|
4238
|
-
const
|
|
4239
|
-
if (
|
|
4240
|
-
exactKeys(
|
|
4241
|
-
assertDataOnlyTree(
|
|
4242
|
-
const asset = createModelAssetRef(
|
|
4243
|
-
if (
|
|
4238
|
+
const record2 = asRecord(value, "ModelCandidateV2.assetRef");
|
|
4239
|
+
if (record2.disposition === "existing") {
|
|
4240
|
+
exactKeys(record2, ["disposition", "kind", "contentHash", "asset"], "ModelCandidateV2.assetRef");
|
|
4241
|
+
assertDataOnlyTree(record2.asset, "ModelCandidateV2.assetRef.asset");
|
|
4242
|
+
const asset = createModelAssetRef(record2.asset);
|
|
4243
|
+
if (record2.kind !== asset.kind || record2.contentHash !== asset.contentHash) throw new Error("ModelCandidateV2 existing assetRef must match the immutable asset.");
|
|
4244
4244
|
return { disposition: "existing", kind: asset.kind, contentHash: asset.contentHash, asset };
|
|
4245
4245
|
}
|
|
4246
|
-
exactKeys(
|
|
4247
|
-
if (
|
|
4246
|
+
exactKeys(record2, ["disposition", "proposalId", "kind", "contentHash"], "ModelCandidateV2.assetRef");
|
|
4247
|
+
if (record2.disposition !== "proposed") throw new Error("ModelCandidateV2.assetRef disposition is unsupported.");
|
|
4248
4248
|
return {
|
|
4249
4249
|
disposition: "proposed",
|
|
4250
|
-
proposalId: requireToken(
|
|
4251
|
-
kind: requireEnum(
|
|
4252
|
-
contentHash: requireSha256(
|
|
4250
|
+
proposalId: requireToken(record2.proposalId, "ModelCandidateV2.assetRef.proposalId"),
|
|
4251
|
+
kind: requireEnum(record2.kind, ["leaf", "assembly"], "ModelCandidateV2.assetRef.kind"),
|
|
4252
|
+
contentHash: requireSha256(record2.contentHash, "ModelCandidateV2.assetRef.contentHash")
|
|
4253
4253
|
};
|
|
4254
4254
|
}
|
|
4255
4255
|
function createViews(value, resolutionId, candidateId) {
|
|
4256
4256
|
const entries = asDenseArray(value, "ModelCandidateV2.views", 4, 4);
|
|
4257
4257
|
const parsed = entries.map((entry, index) => {
|
|
4258
4258
|
const fieldName = `ModelCandidateV2.views[${index}]`;
|
|
4259
|
-
const
|
|
4260
|
-
exactKeys(
|
|
4261
|
-
const kind = requireEnum(
|
|
4259
|
+
const record2 = asRecord(entry, fieldName);
|
|
4260
|
+
exactKeys(record2, ["kind", "imageUri", "sha256", "contentType", "width", "height"], fieldName);
|
|
4261
|
+
const kind = requireEnum(record2.kind, MODEL_CONFIRMATION_VIEW_KINDS, `${fieldName}.kind`);
|
|
4262
4262
|
const expectedUri = `mcp://models/resolutions/${resolutionId}/candidates/${candidateId}/${kind}.png`;
|
|
4263
|
-
if (
|
|
4264
|
-
if (
|
|
4265
|
-
return { kind, imageUri: expectedUri, sha256: requireSha256(
|
|
4263
|
+
if (record2.imageUri !== expectedUri) throw new Error(`${fieldName}.imageUri must match the candidate view identity.`);
|
|
4264
|
+
if (record2.contentType !== "image/png" || record2.width !== MODEL_CONFIRMATION_VIEW_SIZE_PX || record2.height !== MODEL_CONFIRMATION_VIEW_SIZE_PX) throw new Error(`${fieldName} must be an authenticated 1024x1024 PNG.`);
|
|
4265
|
+
return { kind, imageUri: expectedUri, sha256: requireSha256(record2.sha256, `${fieldName}.sha256`), contentType: "image/png", width: MODEL_CONFIRMATION_VIEW_SIZE_PX, height: MODEL_CONFIRMATION_VIEW_SIZE_PX };
|
|
4266
4266
|
});
|
|
4267
4267
|
if (parsed.some((entry, index) => entry.kind !== MODEL_CONFIRMATION_VIEW_KINDS[index])) throw new Error("ModelCandidateV2.views must be ordered front, left, top, isometric.");
|
|
4268
4268
|
return parsed;
|
|
4269
4269
|
}
|
|
4270
4270
|
function createRenderEvidence2(value) {
|
|
4271
|
-
const
|
|
4272
|
-
exactKeys(
|
|
4273
|
-
if (
|
|
4271
|
+
const record2 = asRecord(value, "ModelCandidateV2.renderEvidence");
|
|
4272
|
+
exactKeys(record2, ["renderId", "representation", "lodLevel", "traversalBackend", "rendererId", "rendererVersion", "rendererQualificationHash", "settingsId", "settingsVersion", "cameraQualificationHash", "processingManifestId", "processingClosureHash", "pvoxContentHash", "pvoxRootHash", "pvoxDirectoryHash", "pvoxPageSetHash", "viewSha256s", "renderedAt", "evidenceHash", "evidenceHashAttestation"], "ModelCandidateV2.renderEvidence");
|
|
4273
|
+
if (record2.representation !== "pvox" || record2.lodLevel !== 0 || record2.traversalBackend !== "native-pvox") {
|
|
4274
4274
|
throw new Error("ModelCandidateV2 render evidence must use native PVOX LOD0 traversal.");
|
|
4275
4275
|
}
|
|
4276
|
-
if (
|
|
4276
|
+
if (record2.rendererId !== PVOX_NATIVE_RENDERER_ID || record2.settingsId !== PVOX_NATIVE_REVIEW_SETTINGS_ID) {
|
|
4277
4277
|
throw new Error("ModelCandidateV2 render evidence must use the governed native renderer and review settings.");
|
|
4278
4278
|
}
|
|
4279
|
-
const evidenceHash = requireSha256(
|
|
4279
|
+
const evidenceHash = requireSha256(record2.evidenceHash, "ModelCandidateV2.renderEvidence.evidenceHash");
|
|
4280
4280
|
return {
|
|
4281
|
-
renderId: requireToken(
|
|
4281
|
+
renderId: requireToken(record2.renderId, "ModelCandidateV2.renderEvidence.renderId"),
|
|
4282
4282
|
representation: "pvox",
|
|
4283
4283
|
lodLevel: 0,
|
|
4284
4284
|
traversalBackend: "native-pvox",
|
|
4285
4285
|
rendererId: PVOX_NATIVE_RENDERER_ID,
|
|
4286
|
-
rendererVersion: assertImmutableAssetVersion(
|
|
4287
|
-
rendererQualificationHash: requireSha256(
|
|
4286
|
+
rendererVersion: assertImmutableAssetVersion(record2.rendererVersion),
|
|
4287
|
+
rendererQualificationHash: requireSha256(record2.rendererQualificationHash, "ModelCandidateV2.renderEvidence.rendererQualificationHash"),
|
|
4288
4288
|
settingsId: PVOX_NATIVE_REVIEW_SETTINGS_ID,
|
|
4289
|
-
settingsVersion: assertImmutableAssetVersion(
|
|
4290
|
-
cameraQualificationHash: requireSha256(
|
|
4291
|
-
processingManifestId: requireToken(
|
|
4292
|
-
processingClosureHash: requireSha256(
|
|
4293
|
-
pvoxContentHash: requireSha256(
|
|
4294
|
-
pvoxRootHash: requireSha256(
|
|
4295
|
-
pvoxDirectoryHash: requireSha256(
|
|
4296
|
-
pvoxPageSetHash: requireSha256(
|
|
4297
|
-
viewSha256s: requireShaTuple(
|
|
4298
|
-
renderedAt: requireTimestamp(
|
|
4289
|
+
settingsVersion: assertImmutableAssetVersion(record2.settingsVersion),
|
|
4290
|
+
cameraQualificationHash: requireSha256(record2.cameraQualificationHash, "ModelCandidateV2.renderEvidence.cameraQualificationHash"),
|
|
4291
|
+
processingManifestId: requireToken(record2.processingManifestId, "ModelCandidateV2.renderEvidence.processingManifestId"),
|
|
4292
|
+
processingClosureHash: requireSha256(record2.processingClosureHash, "ModelCandidateV2.renderEvidence.processingClosureHash"),
|
|
4293
|
+
pvoxContentHash: requireSha256(record2.pvoxContentHash, "ModelCandidateV2.renderEvidence.pvoxContentHash"),
|
|
4294
|
+
pvoxRootHash: requireSha256(record2.pvoxRootHash, "ModelCandidateV2.renderEvidence.pvoxRootHash"),
|
|
4295
|
+
pvoxDirectoryHash: requireSha256(record2.pvoxDirectoryHash, "ModelCandidateV2.renderEvidence.pvoxDirectoryHash"),
|
|
4296
|
+
pvoxPageSetHash: requireSha256(record2.pvoxPageSetHash, "ModelCandidateV2.renderEvidence.pvoxPageSetHash"),
|
|
4297
|
+
viewSha256s: requireShaTuple(record2.viewSha256s, "ModelCandidateV2.renderEvidence.viewSha256s"),
|
|
4298
|
+
renderedAt: requireTimestamp(record2.renderedAt, "ModelCandidateV2.renderEvidence.renderedAt"),
|
|
4299
4299
|
evidenceHash,
|
|
4300
|
-
evidenceHashAttestation: createHashAttestation(
|
|
4300
|
+
evidenceHashAttestation: createHashAttestation(record2.evidenceHashAttestation, PVOX_RENDER_EVIDENCE_HASH_DOMAIN, evidenceHash, "ModelCandidateV2.renderEvidence.evidenceHashAttestation")
|
|
4301
4301
|
};
|
|
4302
4302
|
}
|
|
4303
4303
|
function createPvoxModelMatchAssessmentV2(input) {
|
|
4304
4304
|
const fieldName = "PvoxModelMatchAssessmentV2";
|
|
4305
|
-
const
|
|
4306
|
-
assertDataOnlyTree(
|
|
4307
|
-
exactKeys(
|
|
4305
|
+
const record2 = asRecord(input, fieldName);
|
|
4306
|
+
assertDataOnlyTree(record2, fieldName);
|
|
4307
|
+
exactKeys(record2, [
|
|
4308
4308
|
"contractVersion",
|
|
4309
4309
|
"score",
|
|
4310
4310
|
"assurance",
|
|
@@ -4321,65 +4321,65 @@ function createPvoxModelMatchAssessmentV2(input) {
|
|
|
4321
4321
|
"evidenceHash"
|
|
4322
4322
|
], fieldName);
|
|
4323
4323
|
const assessment = createModelMatchAssessment({
|
|
4324
|
-
...
|
|
4325
|
-
score:
|
|
4326
|
-
...
|
|
4327
|
-
hardConstraintPass:
|
|
4328
|
-
exactMatch:
|
|
4329
|
-
reasonCodes:
|
|
4330
|
-
ranker:
|
|
4331
|
-
fidelityWarnings:
|
|
4332
|
-
request:
|
|
4333
|
-
candidateId:
|
|
4334
|
-
candidateContentHash:
|
|
4324
|
+
...record2.contractVersion === void 0 ? {} : { contractVersion: record2.contractVersion },
|
|
4325
|
+
score: record2.score,
|
|
4326
|
+
...record2.assurance === void 0 ? {} : { assurance: record2.assurance },
|
|
4327
|
+
hardConstraintPass: record2.hardConstraintPass,
|
|
4328
|
+
exactMatch: record2.exactMatch,
|
|
4329
|
+
reasonCodes: record2.reasonCodes,
|
|
4330
|
+
ranker: record2.ranker,
|
|
4331
|
+
fidelityWarnings: record2.fidelityWarnings,
|
|
4332
|
+
request: record2.request,
|
|
4333
|
+
candidateId: record2.candidateId,
|
|
4334
|
+
candidateContentHash: record2.candidateContentHash
|
|
4335
4335
|
});
|
|
4336
4336
|
return deepFreeze2({
|
|
4337
4337
|
...assessment,
|
|
4338
|
-
requestSemanticProfileHash: requireSha256(
|
|
4339
|
-
requestSemanticProfileCanonical: requireString(
|
|
4340
|
-
evidenceHash: requireSha256(
|
|
4338
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, `${fieldName}.requestSemanticProfileHash`),
|
|
4339
|
+
requestSemanticProfileCanonical: requireString(record2.requestSemanticProfileCanonical, `${fieldName}.requestSemanticProfileCanonical`, 65536),
|
|
4340
|
+
evidenceHash: requireSha256(record2.evidenceHash, `${fieldName}.evidenceHash`)
|
|
4341
4341
|
});
|
|
4342
4342
|
}
|
|
4343
4343
|
function createConfirmationBinding(value) {
|
|
4344
|
-
const
|
|
4345
|
-
assertDataOnlyTree(
|
|
4346
|
-
exactKeys(
|
|
4347
|
-
const bindingHash = requireSha256(
|
|
4348
|
-
const hardGateEntries = asDenseArray(
|
|
4344
|
+
const record2 = asRecord(value, "ModelCandidateV2.confirmationBinding");
|
|
4345
|
+
assertDataOnlyTree(record2, "ModelCandidateV2.confirmationBinding");
|
|
4346
|
+
exactKeys(record2, ["bindingHash", "bindingHashAttestation", "resolutionId", "requesterId", "request", "requestSemanticProfileHash", "requestRevision", "candidateId", "candidateContentHash", "candidateAssetRef", "match", "provenance", "rights", "sourceContentHash", "binaryClosureHash", "pvoxRootHash", "pvoxDirectoryHash", "pvoxPageSetHash", "compilationInputHash", "runtimeRequestProfileHash", "processingClosureHash", "assemblyClosureHash", "evaluationClosureHash", "capabilityEvidenceSetHash", "hardGates", "viewSha256s", "rightsDecisionId", "fidelityEvidenceHash", "physicalEvidenceHash", "rendererId", "rendererVersion", "rendererEvidenceHash", "renderEvidence"], "ModelCandidateV2.confirmationBinding");
|
|
4347
|
+
const bindingHash = requireSha256(record2.bindingHash, "ModelCandidateV2.confirmationBinding.bindingHash");
|
|
4348
|
+
const hardGateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.confirmationBinding.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4349
4349
|
return {
|
|
4350
4350
|
bindingHash,
|
|
4351
|
-
bindingHashAttestation: createHashAttestation(
|
|
4352
|
-
resolutionId: requirePathSegment(
|
|
4353
|
-
requesterId: requireToken(
|
|
4354
|
-
request: createModelRequestSpecV2(
|
|
4355
|
-
requestSemanticProfileHash: requireSha256(
|
|
4356
|
-
requestRevision: requireInteger(
|
|
4357
|
-
candidateId: requirePathSegment(
|
|
4358
|
-
candidateContentHash: requireSha256(
|
|
4359
|
-
candidateAssetRef: createCandidateAssetRef2(
|
|
4360
|
-
match: createPvoxModelMatchAssessmentV2(
|
|
4361
|
-
provenance: createModelProvenance(
|
|
4362
|
-
rights: createModelRightsAssessment(
|
|
4363
|
-
sourceContentHash: requireSha256(
|
|
4364
|
-
binaryClosureHash: requireSha256(
|
|
4365
|
-
pvoxRootHash: requireSha256(
|
|
4366
|
-
pvoxDirectoryHash: requireSha256(
|
|
4367
|
-
pvoxPageSetHash: requireSha256(
|
|
4368
|
-
compilationInputHash: requireSha256(
|
|
4369
|
-
runtimeRequestProfileHash: requireSha256(
|
|
4370
|
-
processingClosureHash: requireSha256(
|
|
4371
|
-
assemblyClosureHash: requireSha256(
|
|
4372
|
-
evaluationClosureHash: requireSha256(
|
|
4373
|
-
capabilityEvidenceSetHash: requireSha256(
|
|
4351
|
+
bindingHashAttestation: createHashAttestation(record2.bindingHashAttestation, PVOX_CONFIRMATION_BINDING_HASH_DOMAIN, bindingHash, "ModelCandidateV2.confirmationBinding.bindingHashAttestation"),
|
|
4352
|
+
resolutionId: requirePathSegment(record2.resolutionId, "ModelCandidateV2.confirmationBinding.resolutionId"),
|
|
4353
|
+
requesterId: requireToken(record2.requesterId, "ModelCandidateV2.confirmationBinding.requesterId"),
|
|
4354
|
+
request: createModelRequestSpecV2(record2.request),
|
|
4355
|
+
requestSemanticProfileHash: requireSha256(record2.requestSemanticProfileHash, "ModelCandidateV2.confirmationBinding.requestSemanticProfileHash"),
|
|
4356
|
+
requestRevision: requireInteger(record2.requestRevision, "ModelCandidateV2.confirmationBinding.requestRevision", 0, MODEL_REQUEST_MAX_REVISION),
|
|
4357
|
+
candidateId: requirePathSegment(record2.candidateId, "ModelCandidateV2.confirmationBinding.candidateId"),
|
|
4358
|
+
candidateContentHash: requireSha256(record2.candidateContentHash, "ModelCandidateV2.confirmationBinding.candidateContentHash"),
|
|
4359
|
+
candidateAssetRef: createCandidateAssetRef2(record2.candidateAssetRef),
|
|
4360
|
+
match: createPvoxModelMatchAssessmentV2(record2.match),
|
|
4361
|
+
provenance: createModelProvenance(record2.provenance),
|
|
4362
|
+
rights: createModelRightsAssessment(record2.rights),
|
|
4363
|
+
sourceContentHash: requireSha256(record2.sourceContentHash, "ModelCandidateV2.confirmationBinding.sourceContentHash"),
|
|
4364
|
+
binaryClosureHash: requireSha256(record2.binaryClosureHash, "ModelCandidateV2.confirmationBinding.binaryClosureHash"),
|
|
4365
|
+
pvoxRootHash: requireSha256(record2.pvoxRootHash, "ModelCandidateV2.confirmationBinding.pvoxRootHash"),
|
|
4366
|
+
pvoxDirectoryHash: requireSha256(record2.pvoxDirectoryHash, "ModelCandidateV2.confirmationBinding.pvoxDirectoryHash"),
|
|
4367
|
+
pvoxPageSetHash: requireSha256(record2.pvoxPageSetHash, "ModelCandidateV2.confirmationBinding.pvoxPageSetHash"),
|
|
4368
|
+
compilationInputHash: requireSha256(record2.compilationInputHash, "ModelCandidateV2.confirmationBinding.compilationInputHash"),
|
|
4369
|
+
runtimeRequestProfileHash: requireSha256(record2.runtimeRequestProfileHash, "ModelCandidateV2.confirmationBinding.runtimeRequestProfileHash"),
|
|
4370
|
+
processingClosureHash: requireSha256(record2.processingClosureHash, "ModelCandidateV2.confirmationBinding.processingClosureHash"),
|
|
4371
|
+
assemblyClosureHash: requireSha256(record2.assemblyClosureHash, "ModelCandidateV2.confirmationBinding.assemblyClosureHash"),
|
|
4372
|
+
evaluationClosureHash: requireSha256(record2.evaluationClosureHash, "ModelCandidateV2.confirmationBinding.evaluationClosureHash"),
|
|
4373
|
+
capabilityEvidenceSetHash: requireSha256(record2.capabilityEvidenceSetHash, "ModelCandidateV2.confirmationBinding.capabilityEvidenceSetHash"),
|
|
4374
4374
|
hardGates: hardGateEntries.map(createPvoxGateEvidence),
|
|
4375
|
-
viewSha256s: requireShaTuple(
|
|
4376
|
-
rightsDecisionId: requireToken(
|
|
4377
|
-
fidelityEvidenceHash: requireSha256(
|
|
4378
|
-
physicalEvidenceHash: requireSha256(
|
|
4379
|
-
rendererId: requireToken(
|
|
4380
|
-
rendererVersion: assertImmutableAssetVersion(
|
|
4381
|
-
rendererEvidenceHash: requireSha256(
|
|
4382
|
-
renderEvidence: createRenderEvidence2(
|
|
4375
|
+
viewSha256s: requireShaTuple(record2.viewSha256s, "ModelCandidateV2.confirmationBinding.viewSha256s"),
|
|
4376
|
+
rightsDecisionId: requireToken(record2.rightsDecisionId, "ModelCandidateV2.confirmationBinding.rightsDecisionId"),
|
|
4377
|
+
fidelityEvidenceHash: requireSha256(record2.fidelityEvidenceHash, "ModelCandidateV2.confirmationBinding.fidelityEvidenceHash"),
|
|
4378
|
+
physicalEvidenceHash: requireSha256(record2.physicalEvidenceHash, "ModelCandidateV2.confirmationBinding.physicalEvidenceHash"),
|
|
4379
|
+
rendererId: requireToken(record2.rendererId, "ModelCandidateV2.confirmationBinding.rendererId"),
|
|
4380
|
+
rendererVersion: assertImmutableAssetVersion(record2.rendererVersion),
|
|
4381
|
+
rendererEvidenceHash: requireSha256(record2.rendererEvidenceHash, "ModelCandidateV2.confirmationBinding.rendererEvidenceHash"),
|
|
4382
|
+
renderEvidence: createRenderEvidence2(record2.renderEvidence)
|
|
4383
4383
|
};
|
|
4384
4384
|
}
|
|
4385
4385
|
function assertRequestMatchesProcessing(request, processing) {
|
|
@@ -4414,27 +4414,27 @@ function assertRequestMatchesProcessing(request, processing) {
|
|
|
4414
4414
|
}
|
|
4415
4415
|
}
|
|
4416
4416
|
function createModelCandidateV2(input) {
|
|
4417
|
-
const
|
|
4418
|
-
assertDataOnlyTree(
|
|
4417
|
+
const record2 = asRecord(input, "ModelCandidateV2");
|
|
4418
|
+
assertDataOnlyTree(record2, "ModelCandidateV2");
|
|
4419
4419
|
const commonKeys = ["contractVersion", "resolutionId", "requesterId", "candidateId", "request", "admissionStatus", "assetRef", "match", "provenance", "rights", "processingManifest", "views", "renderEvidence", "evaluationClosureHash", "evaluationClosureAttestation", "hardGates", "confirmationRequired"];
|
|
4420
|
-
const admissionStatus = requireEnum(
|
|
4421
|
-
exactKeys(
|
|
4422
|
-
if (
|
|
4423
|
-
const resolutionId = requirePathSegment(
|
|
4424
|
-
const requesterId = requireToken(
|
|
4425
|
-
const candidateId = requirePathSegment(
|
|
4426
|
-
const request = createModelRequestSpecV2(
|
|
4427
|
-
const assetRef = createCandidateAssetRef2(
|
|
4428
|
-
assertDataOnlyTree(
|
|
4429
|
-
assertDataOnlyTree(
|
|
4430
|
-
assertDataOnlyTree(
|
|
4431
|
-
const match = createPvoxModelMatchAssessmentV2(
|
|
4420
|
+
const admissionStatus = requireEnum(record2.admissionStatus, ["confirmable", "diagnostic"], "ModelCandidateV2.admissionStatus");
|
|
4421
|
+
exactKeys(record2, admissionStatus === "confirmable" ? [...commonKeys, "confirmationBinding", "confirmationToken"] : [...commonKeys, "blockingReasonCodes"], "ModelCandidateV2");
|
|
4422
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelCandidateV2.contractVersion is unsupported.");
|
|
4423
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelCandidateV2.resolutionId");
|
|
4424
|
+
const requesterId = requireToken(record2.requesterId, "ModelCandidateV2.requesterId");
|
|
4425
|
+
const candidateId = requirePathSegment(record2.candidateId, "ModelCandidateV2.candidateId");
|
|
4426
|
+
const request = createModelRequestSpecV2(record2.request);
|
|
4427
|
+
const assetRef = createCandidateAssetRef2(record2.assetRef);
|
|
4428
|
+
assertDataOnlyTree(record2.match, "ModelCandidateV2.match");
|
|
4429
|
+
assertDataOnlyTree(record2.provenance, "ModelCandidateV2.provenance");
|
|
4430
|
+
assertDataOnlyTree(record2.rights, "ModelCandidateV2.rights");
|
|
4431
|
+
const match = createPvoxModelMatchAssessmentV2(record2.match);
|
|
4432
4432
|
assertImmutableAssetVersion(match.ranker.version);
|
|
4433
4433
|
assertImmutableAssetVersion(match.ranker.calibrationVersion);
|
|
4434
|
-
const provenance = createModelProvenance(
|
|
4435
|
-
const rights = createModelRightsAssessment(
|
|
4434
|
+
const provenance = createModelProvenance(record2.provenance);
|
|
4435
|
+
const rights = createModelRightsAssessment(record2.rights);
|
|
4436
4436
|
assertImmutableAssetVersion(rights.policyVersion);
|
|
4437
|
-
const processingManifest = createModelProcessingManifestV2(
|
|
4437
|
+
const processingManifest = createModelProcessingManifestV2(record2.processingManifest);
|
|
4438
4438
|
if (processingManifest.resolutionId !== resolutionId || processingManifest.candidateId !== candidateId || processingManifest.kind !== assetRef.kind || processingManifest.contentHash !== assetRef.contentHash) throw new Error("ModelCandidateV2 processing manifest must match candidate identity and contentHash.");
|
|
4439
4439
|
if (match.candidateId !== candidateId || match.candidateContentHash !== assetRef.contentHash) throw new Error("ModelCandidateV2 match must bind the exact candidate content.");
|
|
4440
4440
|
if (match.requestSemanticProfileHash !== request.requestSemanticProfileHash) throw new Error("ModelCandidateV2 semantic assessment must bind the complete normalized v2 request profile hash.");
|
|
@@ -4450,18 +4450,18 @@ function createModelCandidateV2(input) {
|
|
|
4450
4450
|
if (assetRef.disposition === "existing" && (resourceScope.kind !== "catalog" || resourceScope.assetId !== assetRef.asset.assetId || resourceScope.version !== assetRef.asset.version)) {
|
|
4451
4451
|
throw new Error("ModelCandidateV2 existing asset must prove the same immutable catalog PVOX representation.");
|
|
4452
4452
|
}
|
|
4453
|
-
const views = createViews(
|
|
4454
|
-
const renderEvidence = createRenderEvidence2(
|
|
4453
|
+
const views = createViews(record2.views, resolutionId, candidateId);
|
|
4454
|
+
const renderEvidence = createRenderEvidence2(record2.renderEvidence);
|
|
4455
4455
|
if (renderEvidence.processingManifestId !== processingManifest.manifestId || renderEvidence.processingClosureHash !== processingManifest.processingClosureHash || renderEvidence.pvoxContentHash !== processingManifest.contentHash || renderEvidence.pvoxRootHash !== processingManifest.pvox.rootHash || renderEvidence.pvoxDirectoryHash !== processingManifest.pvox.directoryHash || renderEvidence.pvoxPageSetHash !== processingManifest.pvox.pageSetHash || renderEvidence.viewSha256s.some((digest, index) => digest !== views[index].sha256)) throw new Error("ModelCandidateV2 renderer evidence must bind the PVOX manifest and ordered views.");
|
|
4456
4456
|
if (Date.parse(renderEvidence.renderedAt) < Date.parse(processingManifest.processedAt)) throw new Error("ModelCandidateV2 render evidence must follow processing.");
|
|
4457
|
-
const evaluationClosureHash = requireSha256(
|
|
4457
|
+
const evaluationClosureHash = requireSha256(record2.evaluationClosureHash, "ModelCandidateV2.evaluationClosureHash");
|
|
4458
4458
|
const evaluationClosureAttestation = createHashAttestation(
|
|
4459
|
-
|
|
4459
|
+
record2.evaluationClosureAttestation,
|
|
4460
4460
|
PVOX_EVALUATION_CLOSURE_HASH_DOMAIN,
|
|
4461
4461
|
evaluationClosureHash,
|
|
4462
4462
|
"ModelCandidateV2.evaluationClosureAttestation"
|
|
4463
4463
|
);
|
|
4464
|
-
const gateEntries = asDenseArray(
|
|
4464
|
+
const gateEntries = asDenseArray(record2.hardGates, "ModelCandidateV2.hardGates", PVOX_NON_OVERRIDABLE_GATE_KINDS.length, PVOX_NON_OVERRIDABLE_GATE_KINDS.length);
|
|
4465
4465
|
const gates = gateEntries.map(createPvoxGateEvidence);
|
|
4466
4466
|
const byGate = new Map(gates.map((entry) => [entry.kind, entry]));
|
|
4467
4467
|
if (byGate.size !== PVOX_NON_OVERRIDABLE_GATE_KINDS.length || PVOX_NON_OVERRIDABLE_GATE_KINDS.some((kind) => !byGate.has(kind))) throw new Error("ModelCandidateV2 must contain every non-overridable hard gate exactly once.");
|
|
@@ -4511,16 +4511,16 @@ function createModelCandidateV2(input) {
|
|
|
4511
4511
|
hardGates: orderedGates
|
|
4512
4512
|
};
|
|
4513
4513
|
if (admissionStatus === "diagnostic") {
|
|
4514
|
-
if (
|
|
4515
|
-
if (
|
|
4516
|
-
const suppliedEntries = asDenseArray(
|
|
4514
|
+
if (record2.confirmationRequired !== false) throw new Error("Diagnostic ModelCandidateV2 confirmationRequired must be false.");
|
|
4515
|
+
if (record2.blockingReasonCodes !== void 0) {
|
|
4516
|
+
const suppliedEntries = asDenseArray(record2.blockingReasonCodes, "ModelCandidateV2.blockingReasonCodes", 1, 256);
|
|
4517
4517
|
const suppliedBlockingReasons = suppliedEntries.map((entry, index) => requireReasonCode(entry, `ModelCandidateV2.blockingReasonCodes[${index}]`));
|
|
4518
4518
|
if (JSON.stringify(suppliedBlockingReasons) !== JSON.stringify(blockingReasonCodes)) throw new Error("Diagnostic ModelCandidateV2 blockingReasonCodes must be derived from its evidence.");
|
|
4519
4519
|
}
|
|
4520
4520
|
return deepFreeze2({ ...common, admissionStatus: "diagnostic", blockingReasonCodes, confirmationRequired: false });
|
|
4521
4521
|
}
|
|
4522
|
-
if (
|
|
4523
|
-
const confirmationBinding = createConfirmationBinding(
|
|
4522
|
+
if (record2.confirmationRequired !== true) throw new Error("Confirmable ModelCandidateV2 confirmationRequired must be true.");
|
|
4523
|
+
const confirmationBinding = createConfirmationBinding(record2.confirmationBinding);
|
|
4524
4524
|
const expectedBinding = {
|
|
4525
4525
|
resolutionId,
|
|
4526
4526
|
requesterId,
|
|
@@ -4557,7 +4557,7 @@ function createModelCandidateV2(input) {
|
|
|
4557
4557
|
if (JSON.stringify(confirmationBinding.renderEvidence) !== JSON.stringify(renderEvidence)) throw new Error("ModelCandidateV2 confirmationBinding must bind the complete native renderer evidence.");
|
|
4558
4558
|
if (JSON.stringify(confirmationBinding.hardGates) !== JSON.stringify(orderedGates)) throw new Error("ModelCandidateV2 confirmationBinding must contain the complete ordered hard-gate evidence set.");
|
|
4559
4559
|
if (confirmationBinding.viewSha256s.some((digest, index) => digest !== views[index].sha256)) throw new Error("ModelCandidateV2 confirmationBinding view hashes must match candidate views.");
|
|
4560
|
-
const confirmationToken = requireAttestation(
|
|
4560
|
+
const confirmationToken = requireAttestation(record2.confirmationToken, "ModelCandidateV2.confirmationToken");
|
|
4561
4561
|
if (confirmationBinding.bindingHashAttestation.attestationToken !== confirmationToken) throw new Error("ModelCandidateV2 confirmation token must attest the exact confirmation binding hash.");
|
|
4562
4562
|
return deepFreeze2({
|
|
4563
4563
|
...common,
|
|
@@ -4571,21 +4571,21 @@ function isModelCandidateV2Confirmable(candidate) {
|
|
|
4571
4571
|
return candidate.admissionStatus === "confirmable" && candidate.confirmationRequired && candidate.match.hardConstraintPass && candidate.match.assurance !== "none" && (candidate.rights.status === "allowed" || candidate.rights.status === "attribution-required") && candidate.processingManifest.fidelity.outcome === "passed" && candidate.hardGates.length === PVOX_NON_OVERRIDABLE_GATE_KINDS.length && candidate.hardGates.every(({ outcome }) => outcome === "passed") && candidate.processingManifest.requiredCapabilities.every((name) => candidate.processingManifest.capabilities.find((entry) => entry.capability === name)?.status === "supported");
|
|
4572
4572
|
}
|
|
4573
4573
|
function createModelCandidateConfirmationV2(input, expectedCandidate, expectedResolutionId) {
|
|
4574
|
-
const
|
|
4575
|
-
exactKeys(
|
|
4576
|
-
if (
|
|
4574
|
+
const record2 = asRecord(input, "ModelCandidateConfirmationV2");
|
|
4575
|
+
exactKeys(record2, ["contractVersion", "confirmationId", "resolutionId", "requesterId", "candidateId", "confirmationToken", "confirmationBindingHash", "viewSha256s", "confirmedBy", "confirmedAt", "semanticRiskAccepted"], "ModelCandidateConfirmationV2");
|
|
4576
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelCandidateConfirmationV2.contractVersion is unsupported.");
|
|
4577
4577
|
const candidate = createModelCandidateV2(expectedCandidate);
|
|
4578
4578
|
if (candidate.admissionStatus !== "confirmable") throw new Error("ModelCandidateConfirmationV2 cannot confirm a diagnostic candidate.");
|
|
4579
4579
|
const resolutionId = requirePathSegment(expectedResolutionId, "ModelCandidateConfirmationV2 expected resolutionId");
|
|
4580
|
-
if (candidate.resolutionId !== resolutionId ||
|
|
4581
|
-
if (
|
|
4582
|
-
if (
|
|
4583
|
-
const viewSha256s = requireShaTuple(
|
|
4580
|
+
if (candidate.resolutionId !== resolutionId || record2.resolutionId !== resolutionId || record2.requesterId !== candidate.requesterId || record2.candidateId !== candidate.candidateId) throw new Error("ModelCandidateConfirmationV2 must match the candidate, requester, and resolution.");
|
|
4581
|
+
if (record2.confirmationToken !== candidate.confirmationToken) throw new Error("ModelCandidateConfirmationV2 confirmationToken must match the candidate.");
|
|
4582
|
+
if (record2.confirmationBindingHash !== candidate.confirmationBinding.bindingHash) throw new Error("ModelCandidateConfirmationV2 confirmation binding must match the candidate.");
|
|
4583
|
+
const viewSha256s = requireShaTuple(record2.viewSha256s, "ModelCandidateConfirmationV2.viewSha256s");
|
|
4584
4584
|
if (viewSha256s.some((digest, index) => digest !== candidate.views[index].sha256)) throw new Error("ModelCandidateConfirmationV2 view hashes must match candidate evidence.");
|
|
4585
4585
|
if (!isModelCandidateV2Confirmable(candidate)) throw new Error("ModelCandidateConfirmationV2 candidate is not confirmable because a hard gate is blocked.");
|
|
4586
|
-
const semanticRiskAccepted = requireBoolean(
|
|
4586
|
+
const semanticRiskAccepted = requireBoolean(record2.semanticRiskAccepted, "ModelCandidateConfirmationV2.semanticRiskAccepted");
|
|
4587
4587
|
if (candidate.match.assurance === "low" !== semanticRiskAccepted) throw new Error("Only low semantic assurance requires and permits semantic risk acceptance.");
|
|
4588
|
-
const confirmedAt = requireTimestamp(
|
|
4588
|
+
const confirmedAt = requireTimestamp(record2.confirmedAt, "ModelCandidateConfirmationV2.confirmedAt");
|
|
4589
4589
|
const latestEvidence = Math.max(
|
|
4590
4590
|
Date.parse(candidate.provenance.capturedAt),
|
|
4591
4591
|
Date.parse(candidate.rights.reviewedAt),
|
|
@@ -4594,12 +4594,12 @@ function createModelCandidateConfirmationV2(input, expectedCandidate, expectedRe
|
|
|
4594
4594
|
...candidate.hardGates.map(({ evaluatedAt }) => Date.parse(evaluatedAt))
|
|
4595
4595
|
);
|
|
4596
4596
|
if (Date.parse(confirmedAt) < latestEvidence) throw new Error("ModelCandidateConfirmationV2 confirmation must follow all required evidence.");
|
|
4597
|
-
const confirmedBy = requireString(
|
|
4597
|
+
const confirmedBy = requireString(record2.confirmedBy, "ModelCandidateConfirmationV2.confirmedBy", 256);
|
|
4598
4598
|
if (DIRECT_URL.test(confirmedBy)) throw new Error("ModelCandidateConfirmationV2.confirmedBy must not expose a URL.");
|
|
4599
4599
|
if (confirmedBy !== candidate.requesterId) throw new Error("ModelCandidateConfirmationV2 confirmedBy must be the requester bound to the resolution.");
|
|
4600
4600
|
return deepFreeze2({
|
|
4601
4601
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4602
|
-
confirmationId: requireToken(
|
|
4602
|
+
confirmationId: requireToken(record2.confirmationId, "ModelCandidateConfirmationV2.confirmationId"),
|
|
4603
4603
|
resolutionId,
|
|
4604
4604
|
requesterId: candidate.requesterId,
|
|
4605
4605
|
candidateId: candidate.candidateId,
|
|
@@ -4623,8 +4623,8 @@ function candidatesEquivalent(left, right) {
|
|
|
4623
4623
|
}
|
|
4624
4624
|
function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmation, finalAssetRef) {
|
|
4625
4625
|
if (candidate.assetRef.disposition !== "proposed") throw new Error("ModelPromotionReceiptV2 is only valid for a proposed candidate.");
|
|
4626
|
-
const
|
|
4627
|
-
exactKeys(
|
|
4626
|
+
const record2 = asRecord(input, "ModelPromotionReceiptV2");
|
|
4627
|
+
exactKeys(record2, [
|
|
4628
4628
|
"contractVersion",
|
|
4629
4629
|
"promotionId",
|
|
4630
4630
|
"resolutionId",
|
|
@@ -4655,38 +4655,38 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4655
4655
|
"promotedAt",
|
|
4656
4656
|
"publicationToken"
|
|
4657
4657
|
], "ModelPromotionReceiptV2");
|
|
4658
|
-
if (
|
|
4658
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION || record2.publicationState !== "pointer-last-complete") {
|
|
4659
4659
|
throw new Error("ModelPromotionReceiptV2 must use the v2 contract and prove pointer-last completion.");
|
|
4660
4660
|
}
|
|
4661
|
-
if (
|
|
4661
|
+
if (record2.resolutionId !== resolutionId || record2.requesterId !== candidate.requesterId || record2.candidateId !== candidate.candidateId || record2.proposalId !== candidate.assetRef.proposalId || record2.confirmationId !== confirmation.confirmationId || record2.confirmationBindingHash !== confirmation.confirmationBindingHash || record2.processingManifestId !== candidate.processingManifest.manifestId || record2.processingContentHash !== candidate.processingManifest.contentHash || record2.processingClosureHash !== candidate.processingManifest.processingClosureHash || record2.assemblyClosureHash !== candidate.processingManifest.assemblyClosureHash) {
|
|
4662
4662
|
throw new Error("ModelPromotionReceiptV2 must bind the exact proposal, confirmation, processing content, and PVOX assembly closure.");
|
|
4663
4663
|
}
|
|
4664
|
-
assertDataOnlyTree(
|
|
4665
|
-
const candidateAssetRef = createCandidateAssetRef2(
|
|
4664
|
+
assertDataOnlyTree(record2.candidateAssetRef, "ModelPromotionReceiptV2.candidateAssetRef");
|
|
4665
|
+
const candidateAssetRef = createCandidateAssetRef2(record2.candidateAssetRef);
|
|
4666
4666
|
if (JSON.stringify(candidateAssetRef) !== JSON.stringify(candidate.assetRef)) throw new Error("ModelPromotionReceiptV2 must bind the exact proposed candidate identity.");
|
|
4667
|
-
const childClosureEntries = asDenseArray(
|
|
4667
|
+
const childClosureEntries = asDenseArray(record2.assemblyChildClosureHashes, "ModelPromotionReceiptV2.assemblyChildClosureHashes", 0, PVOX_DEFAULT_LIMITS.maximumAssemblyChildren);
|
|
4668
4668
|
const assemblyChildClosureHashes = childClosureEntries.map((entry, index) => requireSha256(entry, `ModelPromotionReceiptV2.assemblyChildClosureHashes[${index}]`));
|
|
4669
4669
|
const expectedChildClosureHashes = candidate.processingManifest.children.map(({ assetRef }) => assetRef.binaryClosureHash);
|
|
4670
4670
|
if (JSON.stringify(assemblyChildClosureHashes) !== JSON.stringify(expectedChildClosureHashes)) throw new Error("ModelPromotionReceiptV2 child closure sequence must match the canonical assembly order.");
|
|
4671
|
-
assertDataOnlyTree(
|
|
4672
|
-
const receiptAssetRef = createModelAssetRef(
|
|
4671
|
+
assertDataOnlyTree(record2.finalAssetRef, "ModelPromotionReceiptV2.finalAssetRef");
|
|
4672
|
+
const receiptAssetRef = createModelAssetRef(record2.finalAssetRef);
|
|
4673
4673
|
if (JSON.stringify(receiptAssetRef) !== JSON.stringify(finalAssetRef)) throw new Error("ModelPromotionReceiptV2 finalAssetRef must match the completed resolution.");
|
|
4674
|
-
const creditsWrittenAt = requireTimestamp(
|
|
4675
|
-
const catalogRowWrittenAt = requireTimestamp(
|
|
4676
|
-
const indexSnapshotWrittenAt = requireTimestamp(
|
|
4677
|
-
const pointerPublishedAt = requireTimestamp(
|
|
4678
|
-
const promotedAt = requireTimestamp(
|
|
4674
|
+
const creditsWrittenAt = requireTimestamp(record2.creditsWrittenAt, "ModelPromotionReceiptV2.creditsWrittenAt");
|
|
4675
|
+
const catalogRowWrittenAt = requireTimestamp(record2.catalogRowWrittenAt, "ModelPromotionReceiptV2.catalogRowWrittenAt");
|
|
4676
|
+
const indexSnapshotWrittenAt = requireTimestamp(record2.indexSnapshotWrittenAt, "ModelPromotionReceiptV2.indexSnapshotWrittenAt");
|
|
4677
|
+
const pointerPublishedAt = requireTimestamp(record2.pointerPublishedAt, "ModelPromotionReceiptV2.pointerPublishedAt");
|
|
4678
|
+
const promotedAt = requireTimestamp(record2.promotedAt, "ModelPromotionReceiptV2.promotedAt");
|
|
4679
4679
|
const publicationSequence = [confirmation.confirmedAt, creditsWrittenAt, catalogRowWrittenAt, indexSnapshotWrittenAt, pointerPublishedAt, promotedAt].map(Date.parse);
|
|
4680
4680
|
if (publicationSequence.some((time, index) => index > 0 && time < publicationSequence[index - 1])) {
|
|
4681
4681
|
throw new Error("ModelPromotionReceiptV2 must prove credits, catalog row, index snapshot, and pointers were published in pointer-last order.");
|
|
4682
4682
|
}
|
|
4683
|
-
const publicationHash = requireSha256(
|
|
4684
|
-
const publicationHashAttestation = createHashAttestation(
|
|
4685
|
-
const publicationToken = requireAttestation(
|
|
4683
|
+
const publicationHash = requireSha256(record2.publicationHash, "ModelPromotionReceiptV2.publicationHash");
|
|
4684
|
+
const publicationHashAttestation = createHashAttestation(record2.publicationHashAttestation, PVOX_PUBLICATION_HASH_DOMAIN, publicationHash, "ModelPromotionReceiptV2.publicationHashAttestation");
|
|
4685
|
+
const publicationToken = requireAttestation(record2.publicationToken, "ModelPromotionReceiptV2.publicationToken");
|
|
4686
4686
|
if (publicationHashAttestation.attestationToken !== publicationToken) throw new Error("ModelPromotionReceiptV2 publication token must attest the exact publication hash.");
|
|
4687
4687
|
return deepFreeze2({
|
|
4688
4688
|
contractVersion: MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
4689
|
-
promotionId: requireToken(
|
|
4689
|
+
promotionId: requireToken(record2.promotionId, "ModelPromotionReceiptV2.promotionId"),
|
|
4690
4690
|
resolutionId,
|
|
4691
4691
|
requesterId: candidate.requesterId,
|
|
4692
4692
|
candidateId: candidate.candidateId,
|
|
@@ -4700,11 +4700,11 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4700
4700
|
assemblyClosureHash: candidate.processingManifest.assemblyClosureHash,
|
|
4701
4701
|
assemblyChildClosureHashes,
|
|
4702
4702
|
finalAssetRef: receiptAssetRef,
|
|
4703
|
-
creditsRecordHash: requireSha256(
|
|
4704
|
-
catalogRowHash: requireSha256(
|
|
4705
|
-
indexSnapshotHash: requireSha256(
|
|
4706
|
-
catalogPointerEtag: requireToken(
|
|
4707
|
-
indexPointerEtag: requireToken(
|
|
4703
|
+
creditsRecordHash: requireSha256(record2.creditsRecordHash, "ModelPromotionReceiptV2.creditsRecordHash"),
|
|
4704
|
+
catalogRowHash: requireSha256(record2.catalogRowHash, "ModelPromotionReceiptV2.catalogRowHash"),
|
|
4705
|
+
indexSnapshotHash: requireSha256(record2.indexSnapshotHash, "ModelPromotionReceiptV2.indexSnapshotHash"),
|
|
4706
|
+
catalogPointerEtag: requireToken(record2.catalogPointerEtag, "ModelPromotionReceiptV2.catalogPointerEtag"),
|
|
4707
|
+
indexPointerEtag: requireToken(record2.indexPointerEtag, "ModelPromotionReceiptV2.indexPointerEtag"),
|
|
4708
4708
|
publicationState: "pointer-last-complete",
|
|
4709
4709
|
creditsWrittenAt,
|
|
4710
4710
|
catalogRowWrittenAt,
|
|
@@ -4718,43 +4718,43 @@ function createModelPromotionReceiptV2(input, resolutionId, candidate, confirmat
|
|
|
4718
4718
|
}
|
|
4719
4719
|
function createModelRefinementQuestionV2(input, index) {
|
|
4720
4720
|
const fieldName = `ModelResolutionV2.refinementQuestions[${index}]`;
|
|
4721
|
-
const
|
|
4722
|
-
exactKeys(
|
|
4721
|
+
const record2 = asRecord(input, fieldName);
|
|
4722
|
+
exactKeys(record2, ["questionId", "prompt", "reasonCodes"], fieldName);
|
|
4723
4723
|
return deepFreeze2({
|
|
4724
|
-
questionId: requireToken(
|
|
4725
|
-
prompt: requireString(
|
|
4726
|
-
reasonCodes: requireReasonCodes(
|
|
4724
|
+
questionId: requireToken(record2.questionId, `${fieldName}.questionId`),
|
|
4725
|
+
prompt: requireString(record2.prompt, `${fieldName}.prompt`, 500),
|
|
4726
|
+
reasonCodes: requireReasonCodes(record2.reasonCodes, `${fieldName}.reasonCodes`)
|
|
4727
4727
|
});
|
|
4728
4728
|
}
|
|
4729
4729
|
function createModelResolutionV2(input) {
|
|
4730
|
-
const
|
|
4731
|
-
exactKeys(
|
|
4732
|
-
if (
|
|
4733
|
-
const resolutionId = requirePathSegment(
|
|
4734
|
-
const requesterId = requireToken(
|
|
4735
|
-
const request = createModelRequestSpecV2(
|
|
4736
|
-
const state = requireEnum(
|
|
4730
|
+
const record2 = asRecord(input, "ModelResolutionV2");
|
|
4731
|
+
exactKeys(record2, ["contractVersion", "resolutionId", "requesterId", "request", "attempts", "state", "compatibilityState", "candidates", "bestCandidate", "confirmation", "promotionReceipt", "refinementQuestions", "finalAssetRef", "stateReasonCode", "createdAt", "updatedAt"], "ModelResolutionV2");
|
|
4732
|
+
if (record2.contractVersion !== MODEL_RESOLUTION_V2_CONTRACT_VERSION) throw new Error("ModelResolutionV2.contractVersion is unsupported.");
|
|
4733
|
+
const resolutionId = requirePathSegment(record2.resolutionId, "ModelResolutionV2.resolutionId");
|
|
4734
|
+
const requesterId = requireToken(record2.requesterId, "ModelResolutionV2.requesterId");
|
|
4735
|
+
const request = createModelRequestSpecV2(record2.request);
|
|
4736
|
+
const state = requireEnum(record2.state, MODEL_RESOLUTION_V2_STATES, "ModelResolutionV2.state");
|
|
4737
4737
|
const compatibilityState = toLegacyModelResolutionState(state);
|
|
4738
|
-
if (
|
|
4739
|
-
const candidateEntries = asDenseArray(
|
|
4738
|
+
if (record2.compatibilityState !== void 0 && record2.compatibilityState !== compatibilityState) throw new Error("ModelResolutionV2 compatibilityState must match its deterministic legacy projection.");
|
|
4739
|
+
const candidateEntries = asDenseArray(record2.candidates, "ModelResolutionV2.candidates", 0, 20);
|
|
4740
4740
|
const candidates = candidateEntries.map(createModelCandidateV2);
|
|
4741
4741
|
if (new Set(candidates.map(({ candidateId }) => candidateId)).size !== candidates.length) throw new Error("ModelResolutionV2 candidate IDs must be unique.");
|
|
4742
4742
|
if (candidates.some((candidate) => candidate.resolutionId !== resolutionId || candidate.requesterId !== requesterId || JSON.stringify(candidate.request) !== JSON.stringify(request))) throw new Error("ModelResolutionV2 candidates must bind the exact requester, v2 resolution request, and profiles.");
|
|
4743
|
-
const bestCandidate =
|
|
4743
|
+
const bestCandidate = record2.bestCandidate === void 0 ? void 0 : createModelCandidateV2(record2.bestCandidate);
|
|
4744
4744
|
if (bestCandidate !== void 0 && !candidates.some((candidate) => candidatesEquivalent(candidate, bestCandidate))) throw new Error("ModelResolutionV2 bestCandidate must be one exact candidate in the resolution.");
|
|
4745
4745
|
if (state === "awaiting-confirmation" && (bestCandidate === void 0 || !isModelCandidateV2Confirmable(bestCandidate))) throw new Error("ModelResolutionV2 awaiting-confirmation requires one confirmable bestCandidate.");
|
|
4746
|
-
const refinementEntries = asDenseArray(
|
|
4746
|
+
const refinementEntries = asDenseArray(record2.refinementQuestions, "ModelResolutionV2.refinementQuestions", 0, 3);
|
|
4747
4747
|
const refinementQuestions = refinementEntries.map(createModelRefinementQuestionV2);
|
|
4748
4748
|
if (new Set(refinementQuestions.map(({ questionId }) => questionId)).size !== refinementQuestions.length) throw new Error("ModelResolutionV2 refinement question IDs must be unique.");
|
|
4749
4749
|
if (state === "awaiting-confirmation" && bestCandidate?.match.assurance === "low" && refinementQuestions.length === 0) throw new Error("Low-assurance ModelResolutionV2 requires refinement questions.");
|
|
4750
|
-
if (state === "completed" &&
|
|
4751
|
-
const confirmation =
|
|
4750
|
+
if (state === "completed" && record2.finalAssetRef === void 0) throw new Error("ModelResolutionV2 completed state requires finalAssetRef.");
|
|
4751
|
+
const confirmation = record2.confirmation === void 0 ? void 0 : bestCandidate === void 0 ? (() => {
|
|
4752
4752
|
throw new Error("ModelResolutionV2 confirmation requires bestCandidate.");
|
|
4753
|
-
})() : createModelCandidateConfirmationV2(
|
|
4753
|
+
})() : createModelCandidateConfirmationV2(record2.confirmation, bestCandidate, resolutionId);
|
|
4754
4754
|
if (["promoting", "completed"].includes(state) && confirmation === void 0) throw new Error(`ModelResolutionV2 ${state} requires confirmation.`);
|
|
4755
4755
|
if (!["promoting", "completed"].includes(state) && confirmation !== void 0) throw new Error("ModelResolutionV2 confirmation is only allowed while promoting or completed.");
|
|
4756
|
-
if (
|
|
4757
|
-
const finalAssetRef =
|
|
4756
|
+
if (record2.finalAssetRef !== void 0) assertDataOnlyTree(record2.finalAssetRef, "ModelResolutionV2.finalAssetRef");
|
|
4757
|
+
const finalAssetRef = record2.finalAssetRef === void 0 ? void 0 : createModelAssetRef(record2.finalAssetRef);
|
|
4758
4758
|
if (state === "completed" && finalAssetRef === void 0) throw new Error("ModelResolutionV2 completed state requires finalAssetRef.");
|
|
4759
4759
|
if (state !== "completed" && finalAssetRef !== void 0) throw new Error("ModelResolutionV2 finalAssetRef is only allowed for completed state.");
|
|
4760
4760
|
if (finalAssetRef !== void 0 && bestCandidate !== void 0) {
|
|
@@ -4769,21 +4769,21 @@ function createModelResolutionV2(input) {
|
|
|
4769
4769
|
}
|
|
4770
4770
|
}
|
|
4771
4771
|
let promotionReceipt;
|
|
4772
|
-
if (
|
|
4772
|
+
if (record2.promotionReceipt !== void 0) {
|
|
4773
4773
|
if (state !== "completed" || finalAssetRef === void 0 || confirmation === void 0 || bestCandidate?.admissionStatus !== "confirmable" || bestCandidate.assetRef.disposition !== "proposed") {
|
|
4774
4774
|
throw new Error("ModelResolutionV2 promotionReceipt is only allowed for a completed confirmed proposed candidate.");
|
|
4775
4775
|
}
|
|
4776
|
-
promotionReceipt = createModelPromotionReceiptV2(
|
|
4776
|
+
promotionReceipt = createModelPromotionReceiptV2(record2.promotionReceipt, resolutionId, bestCandidate, confirmation, finalAssetRef);
|
|
4777
4777
|
}
|
|
4778
4778
|
if (state === "completed" && bestCandidate?.assetRef.disposition === "proposed" && promotionReceipt === void 0) {
|
|
4779
4779
|
throw new Error("ModelResolutionV2 completed proposed candidates require a pointer-last promotionReceipt.");
|
|
4780
4780
|
}
|
|
4781
4781
|
if (bestCandidate?.assetRef.disposition === "existing" && promotionReceipt !== void 0) throw new Error("ModelResolutionV2 existing selections must not include a promotionReceipt.");
|
|
4782
|
-
const stateReasonCode =
|
|
4782
|
+
const stateReasonCode = record2.stateReasonCode === void 0 ? void 0 : requireReasonCode(record2.stateReasonCode, "ModelResolutionV2.stateReasonCode");
|
|
4783
4783
|
if (["unresolved", "failed", "cancelled"].includes(state) && stateReasonCode === void 0) throw new Error(`ModelResolutionV2 ${state} state requires stateReasonCode.`);
|
|
4784
4784
|
if (!["unresolved", "failed", "cancelled"].includes(state) && stateReasonCode !== void 0) throw new Error("ModelResolutionV2 stateReasonCode is reserved for terminal non-success states.");
|
|
4785
|
-
const createdAt = requireTimestamp(
|
|
4786
|
-
const updatedAt = requireTimestamp(
|
|
4785
|
+
const createdAt = requireTimestamp(record2.createdAt, "ModelResolutionV2.createdAt");
|
|
4786
|
+
const updatedAt = requireTimestamp(record2.updatedAt, "ModelResolutionV2.updatedAt");
|
|
4787
4787
|
if (Date.parse(updatedAt) < Date.parse(createdAt)) throw new Error("ModelResolutionV2.updatedAt must not precede createdAt.");
|
|
4788
4788
|
if (confirmation !== void 0 && Date.parse(confirmation.confirmedAt) > Date.parse(updatedAt)) {
|
|
4789
4789
|
throw new Error("ModelResolutionV2 confirmation confirmedAt must not exceed updatedAt.");
|
|
@@ -4796,7 +4796,7 @@ function createModelResolutionV2(input) {
|
|
|
4796
4796
|
resolutionId,
|
|
4797
4797
|
requesterId,
|
|
4798
4798
|
request,
|
|
4799
|
-
attempts: requireInteger(
|
|
4799
|
+
attempts: requireInteger(record2.attempts, "ModelResolutionV2.attempts", 1, 12),
|
|
4800
4800
|
state,
|
|
4801
4801
|
compatibilityState,
|
|
4802
4802
|
candidates,
|
|
@@ -4926,12 +4926,12 @@ function copyPvoxByteInput(value, fieldName, maximumByteLength) {
|
|
|
4926
4926
|
if (!(value instanceof Uint8Array)) throw new Error(`${fieldName} must be a Uint8Array.`);
|
|
4927
4927
|
let byteLength;
|
|
4928
4928
|
try {
|
|
4929
|
-
const
|
|
4930
|
-
const
|
|
4931
|
-
const
|
|
4932
|
-
if (
|
|
4933
|
-
byteLength =
|
|
4934
|
-
const buffer =
|
|
4929
|
+
const typedArrayPrototype2 = Object.getPrototypeOf(Uint8Array.prototype);
|
|
4930
|
+
const byteLengthGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "byteLength")?.get;
|
|
4931
|
+
const bufferGetter2 = Object.getOwnPropertyDescriptor(typedArrayPrototype2, "buffer")?.get;
|
|
4932
|
+
if (byteLengthGetter2 === void 0 || bufferGetter2 === void 0) throw new Error("Typed-array intrinsics are unavailable.");
|
|
4933
|
+
byteLength = byteLengthGetter2.call(value);
|
|
4934
|
+
const buffer = bufferGetter2.call(value);
|
|
4935
4935
|
if (typeof SharedArrayBuffer !== "undefined" && buffer instanceof SharedArrayBuffer) {
|
|
4936
4936
|
throw new Error(`${fieldName} must not use shared mutable storage.`);
|
|
4937
4937
|
}
|
|
@@ -5137,18 +5137,18 @@ function encodePvoxPageSetHashPreimageV1(records) {
|
|
|
5137
5137
|
preflightPvoxArrayCardinality(records, 1, PVOX_DEFAULT_LIMITS.maximumPages, "PVOX page-set hash input");
|
|
5138
5138
|
assertPvoxPreimageDataOnly(records, "PVOX page-set hash input");
|
|
5139
5139
|
let previousEnd = 0n;
|
|
5140
|
-
const encoded = records.map((
|
|
5141
|
-
assertExactPvoxPreimageKeys(
|
|
5142
|
-
if (
|
|
5143
|
-
const offset = asUnsignedBigInt(
|
|
5144
|
-
const byteLength = assertSafeIntegerInRange(
|
|
5140
|
+
const encoded = records.map((record2, position) => {
|
|
5141
|
+
assertExactPvoxPreimageKeys(record2, ["pageIndex", "byteOffset", "byteLength", "pageSha256"], `records[${position}]`);
|
|
5142
|
+
if (record2.pageIndex !== position) throw new Error("Page-set records must have contiguous page indexes starting at zero.");
|
|
5143
|
+
const offset = asUnsignedBigInt(record2.byteOffset, `records[${position}].byteOffset`);
|
|
5144
|
+
const byteLength = assertSafeIntegerInRange(record2.byteLength, PVOX_PAGE_SIZE_BYTES, PVOX_PAGE_SIZE_BYTES, `records[${position}].byteLength`);
|
|
5145
5145
|
if (offset !== previousEnd || offset !== BigInt(position) * BigInt(PVOX_PAGE_SIZE_BYTES)) throw new Error("Page-set records must exactly cover contiguous 64-KiB PVOX pages.");
|
|
5146
5146
|
previousEnd = offset + BigInt(byteLength);
|
|
5147
5147
|
return concatenateBytes([
|
|
5148
|
-
encodePvoxU32LeV1(
|
|
5148
|
+
encodePvoxU32LeV1(record2.pageIndex),
|
|
5149
5149
|
encodePvoxU64LeV1(offset),
|
|
5150
5150
|
encodePvoxU32LeV1(byteLength),
|
|
5151
|
-
decodePvoxSha256HexV1(
|
|
5151
|
+
decodePvoxSha256HexV1(record2.pageSha256, `records[${position}].pageSha256`)
|
|
5152
5152
|
]);
|
|
5153
5153
|
});
|
|
5154
5154
|
return concatenateBytes([
|
|
@@ -5169,12 +5169,12 @@ function encodeCanonicalJsonRecords(values) {
|
|
|
5169
5169
|
const encoded = [];
|
|
5170
5170
|
let aggregateByteLength = 0;
|
|
5171
5171
|
for (const value of normalized) {
|
|
5172
|
-
const
|
|
5173
|
-
aggregateByteLength +=
|
|
5172
|
+
const record2 = encodeCanonicalJsonRecord(value);
|
|
5173
|
+
aggregateByteLength += record2.byteLength;
|
|
5174
5174
|
if (aggregateByteLength > PVOX_MAX_CANONICAL_HASH_UTF8_BYTES_V1) {
|
|
5175
5175
|
throw new Error("Canonical JSON records exceed the governed PVOX hash-input byte limit.");
|
|
5176
5176
|
}
|
|
5177
|
-
encoded.push(
|
|
5177
|
+
encoded.push(record2);
|
|
5178
5178
|
}
|
|
5179
5179
|
return encoded;
|
|
5180
5180
|
}
|
|
@@ -5241,15 +5241,15 @@ function encodePvoxPhysicalInventoryHashPreimageV1(input) {
|
|
|
5241
5241
|
assertPvoxPreimageDataOnly(input, "PVOX physical-inventory hash input");
|
|
5242
5242
|
assertExactPvoxPreimageKeys(input, ["inventoryVersion", "subjectBinaryClosureHash", "validationEvidenceHash", "entries"], "PVOX physical-inventory hash input");
|
|
5243
5243
|
if (input.entries.length < 1 || input.entries.length > PVOX_DEFAULT_LIMITS.maximumMaterialRegions) throw new Error("Physical inventory must fit the governed material-region limit.");
|
|
5244
|
-
const encoded = input.entries.map((
|
|
5245
|
-
assertExactPvoxPreimageKeys(
|
|
5246
|
-
const regionIndex = assertSafeIntegerInRange(
|
|
5244
|
+
const encoded = input.entries.map((record2, index) => {
|
|
5245
|
+
assertExactPvoxPreimageKeys(record2, ["regionIndex", "physicalPaletteIndex", "regionId", "materialId"], `entries[${index}]`);
|
|
5246
|
+
const regionIndex = assertSafeIntegerInRange(record2.regionIndex, 0, U32_MAXIMUM, `entries[${index}].regionIndex`);
|
|
5247
5247
|
if (regionIndex !== index) throw new Error("Physical inventory records must have contiguous canonical region indexes starting at zero.");
|
|
5248
5248
|
return concatenateBytes([
|
|
5249
5249
|
encodePvoxU32LeV1(regionIndex),
|
|
5250
|
-
encodePvoxU32LeV1(
|
|
5251
|
-
encodePvoxCanonicalUtf8V1(
|
|
5252
|
-
encodePvoxCanonicalUtf8V1(
|
|
5250
|
+
encodePvoxU32LeV1(record2.physicalPaletteIndex),
|
|
5251
|
+
encodePvoxCanonicalUtf8V1(record2.regionId),
|
|
5252
|
+
encodePvoxCanonicalUtf8V1(record2.materialId)
|
|
5253
5253
|
]);
|
|
5254
5254
|
});
|
|
5255
5255
|
return concatenateBytes([
|
|
@@ -5297,12 +5297,12 @@ function encodePvoxPhysicalEvidenceHashPreimageV1(input) {
|
|
|
5297
5297
|
let previousOrder = -1;
|
|
5298
5298
|
for (const [index, evidence] of normalizedEvidence.entries()) {
|
|
5299
5299
|
if (evidence === null || typeof evidence !== "object" || Array.isArray(evidence)) throw new Error(`Physical evidence[${index}] must be an object.`);
|
|
5300
|
-
const
|
|
5301
|
-
if (typeof
|
|
5300
|
+
const record2 = evidence;
|
|
5301
|
+
if (typeof record2.regionId !== "string" || typeof record2.materialId !== "string" || typeof record2.property !== "string") {
|
|
5302
5302
|
throw new Error(`Physical evidence[${index}] must declare regionId, materialId, and property.`);
|
|
5303
5303
|
}
|
|
5304
|
-
const regionIndex = regionOrder.get(`${
|
|
5305
|
-
const propertyIndex = PVOX_PHYSICAL_PROPERTIES.indexOf(
|
|
5304
|
+
const regionIndex = regionOrder.get(`${record2.regionId}\0${record2.materialId}`);
|
|
5305
|
+
const propertyIndex = PVOX_PHYSICAL_PROPERTIES.indexOf(record2.property);
|
|
5306
5306
|
if (regionIndex === void 0 || propertyIndex < 0) throw new Error(`Physical evidence[${index}] is absent from the governed inventory/property registry.`);
|
|
5307
5307
|
const order = regionIndex * PVOX_PHYSICAL_PROPERTIES.length + propertyIndex;
|
|
5308
5308
|
if (order <= previousOrder) throw new Error("Physical evidence must use canonical inventory-region and governed-property order.");
|
|
@@ -5434,33 +5434,33 @@ function encodePvoxEditOverlayRootHashPreimageV1(input) {
|
|
|
5434
5434
|
const pageIndexes = /* @__PURE__ */ new Set();
|
|
5435
5435
|
normalizedPatches.forEach((patch, index) => {
|
|
5436
5436
|
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new Error(`Edit-overlay patch[${index}] must be an object.`);
|
|
5437
|
-
const
|
|
5438
|
-
if (Object.keys(
|
|
5439
|
-
if (
|
|
5440
|
-
const lodLevel = assertSafeIntegerInRange(
|
|
5441
|
-
const partitionIndex = assertSafeIntegerInRange(
|
|
5442
|
-
const hierarchyDepth = assertSafeIntegerInRange(
|
|
5443
|
-
if (typeof
|
|
5444
|
-
const expectedPageIndex = assertSafeIntegerInRange(
|
|
5437
|
+
const record2 = patch;
|
|
5438
|
+
if (Object.keys(record2).length !== requiredKeys.length || requiredKeys.some((key) => !(key in record2))) throw new Error(`Edit-overlay patch[${index}] must contain the complete fixed patch address and transition.`);
|
|
5439
|
+
if (record2.fieldKind !== "render-field" && record2.fieldKind !== "collision-field") throw new Error(`Edit-overlay patch[${index}].fieldKind is invalid.`);
|
|
5440
|
+
const lodLevel = assertSafeIntegerInRange(record2.lodLevel, 0, PVOX_DEFAULT_LIMITS.maximumLodCount - 1, `orderedPatches[${index}].lodLevel`);
|
|
5441
|
+
const partitionIndex = assertSafeIntegerInRange(record2.partitionIndex, 0, PVOX_DEFAULT_LIMITS.maximumPartitions - 1, `orderedPatches[${index}].partitionIndex`);
|
|
5442
|
+
const hierarchyDepth = assertSafeIntegerInRange(record2.hierarchyDepth, 0, PVOX_DEFAULT_LIMITS.maximumHierarchyDepth, `orderedPatches[${index}].hierarchyDepth`);
|
|
5443
|
+
if (typeof record2.mortonCode !== "string" || !/^[0-9a-f]{16}$/u.test(record2.mortonCode)) throw new Error(`Edit-overlay patch[${index}].mortonCode is invalid.`);
|
|
5444
|
+
const expectedPageIndex = assertSafeIntegerInRange(record2.expectedPageIndex, 0, PVOX_DEFAULT_LIMITS.maximumPages - 1, `orderedPatches[${index}].expectedPageIndex`);
|
|
5445
5445
|
if (pageIndexes.has(expectedPageIndex)) throw new Error("Edit-overlay permits only one transition per page.");
|
|
5446
5446
|
pageIndexes.add(expectedPageIndex);
|
|
5447
|
-
const expectedPageHash = decodePvoxSha256HexV1(
|
|
5448
|
-
const resultingPageHash = decodePvoxSha256HexV1(
|
|
5447
|
+
const expectedPageHash = decodePvoxSha256HexV1(record2.expectedPageHash, `orderedPatches[${index}].expectedPageHash`);
|
|
5448
|
+
const resultingPageHash = decodePvoxSha256HexV1(record2.resultingPageHash, `orderedPatches[${index}].resultingPageHash`);
|
|
5449
5449
|
if (expectedPageHash.every((value, offset) => value === resultingPageHash[offset])) throw new Error("Edit-overlay page transitions must change the page hash.");
|
|
5450
|
-
const expectedBrickHash =
|
|
5451
|
-
const resultingBrickHash =
|
|
5450
|
+
const expectedBrickHash = record2.expectedBrickHash;
|
|
5451
|
+
const resultingBrickHash = record2.resultingBrickHash;
|
|
5452
5452
|
decodePvoxSha256HexV1(expectedBrickHash, `orderedPatches[${index}].expectedBrickHash`);
|
|
5453
5453
|
decodePvoxSha256HexV1(resultingBrickHash, `orderedPatches[${index}].resultingBrickHash`);
|
|
5454
|
-
if (
|
|
5454
|
+
if (record2.operation === "insert") {
|
|
5455
5455
|
if (expectedBrickHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH || resultingBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH) throw new Error("Edit-overlay insert must change an empty brick to a non-empty result.");
|
|
5456
|
-
} else if (
|
|
5456
|
+
} else if (record2.operation === "remove") {
|
|
5457
5457
|
if (expectedBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH || resultingBrickHash !== PVOX_EDIT_JOURNAL_GENESIS_HASH) throw new Error("Edit-overlay remove must change a non-empty brick to the empty result.");
|
|
5458
|
-
} else if (
|
|
5458
|
+
} else if (record2.operation === "replace") {
|
|
5459
5459
|
if (expectedBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH || resultingBrickHash === PVOX_EDIT_JOURNAL_GENESIS_HASH || expectedBrickHash === resultingBrickHash) throw new Error("Edit-overlay replace must change one non-empty brick to another.");
|
|
5460
5460
|
} else {
|
|
5461
5461
|
throw new Error(`Edit-overlay patch[${index}].operation is invalid.`);
|
|
5462
5462
|
}
|
|
5463
|
-
const address = [
|
|
5463
|
+
const address = [record2.fieldKind, String(lodLevel).padStart(2, "0"), String(partitionIndex).padStart(6, "0"), String(hierarchyDepth).padStart(2, "0"), record2.mortonCode].join("\0");
|
|
5464
5464
|
if (index > 0 && address <= previousAddress) throw new Error("Edit-overlay patches must be unique and canonically ordered by their complete spatial address.");
|
|
5465
5465
|
previousAddress = address;
|
|
5466
5466
|
});
|
|
@@ -5597,17 +5597,17 @@ function encodePvoxRootHashPreimageV1(exactHeader, orderedSectionHashes) {
|
|
|
5597
5597
|
const sectionCount = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint16(PVOX_ROOT_HEADER_SECTION_COUNT_OFFSET, true);
|
|
5598
5598
|
if (orderedSectionHashes.length !== sectionCount) throw new Error("Root section hashes must match the header sectionCount.");
|
|
5599
5599
|
let previousKey = -1n;
|
|
5600
|
-
const sections = orderedSectionHashes.map((
|
|
5601
|
-
assertExactPvoxPreimageKeys(
|
|
5602
|
-
const sectionType = assertSafeIntegerInRange(
|
|
5603
|
-
const sectionVersion = assertSafeIntegerInRange(
|
|
5600
|
+
const sections = orderedSectionHashes.map((record2, index) => {
|
|
5601
|
+
assertExactPvoxPreimageKeys(record2, ["sectionType", "sectionVersion", "sectionHash"], `orderedSectionHashes[${index}]`);
|
|
5602
|
+
const sectionType = assertSafeIntegerInRange(record2.sectionType, 0, U32_MAXIMUM, `orderedSectionHashes[${index}].sectionType`);
|
|
5603
|
+
const sectionVersion = assertSafeIntegerInRange(record2.sectionVersion, 0, U16_MAXIMUM, `orderedSectionHashes[${index}].sectionVersion`);
|
|
5604
5604
|
const key = BigInt(sectionType) << 16n | BigInt(sectionVersion);
|
|
5605
5605
|
if (key <= previousKey) throw new Error("Root section hashes must be unique and strictly ordered by type then version.");
|
|
5606
5606
|
previousKey = key;
|
|
5607
5607
|
return concatenateBytes([
|
|
5608
5608
|
encodePvoxU32LeV1(sectionType),
|
|
5609
5609
|
encodePvoxU16LeV1(sectionVersion),
|
|
5610
|
-
decodePvoxSha256HexV1(
|
|
5610
|
+
decodePvoxSha256HexV1(record2.sectionHash, `orderedSectionHashes[${index}].sectionHash`)
|
|
5611
5611
|
]);
|
|
5612
5612
|
});
|
|
5613
5613
|
return concatenateBytes([TEXT_ENCODER.encode(PVOX_ROOT_HASH_DOMAIN), header, ...sections]);
|
|
@@ -6642,6 +6642,176 @@ var MODEL_RESOLUTION_V2_JSON_SCHEMAS = deepFreeze3({
|
|
|
6642
6642
|
modelResolution: standaloneSchema("modelResolution", "ModelResolutionV2")
|
|
6643
6643
|
});
|
|
6644
6644
|
|
|
6645
|
+
// src/model-resource-manifest.ts
|
|
6646
|
+
import { computeSha256 } from "@plasius/gpu-shader";
|
|
6647
|
+
var MODEL_RESOURCE_MANIFEST_LIMITS = Object.freeze({
|
|
6648
|
+
files: 16384,
|
|
6649
|
+
fileBytes: 64 * 1024 * 1024,
|
|
6650
|
+
totalBytes: 128 * 1024 * 1024,
|
|
6651
|
+
metadataValues: 262144,
|
|
6652
|
+
metadataDepth: 16,
|
|
6653
|
+
timeoutMs: 6e4
|
|
6654
|
+
});
|
|
6655
|
+
var ModelResourceManifestError = class extends TypeError {
|
|
6656
|
+
constructor(code) {
|
|
6657
|
+
super(`Invalid model resource manifest: ${code}`);
|
|
6658
|
+
this.code = code;
|
|
6659
|
+
this.name = "ModelResourceManifestError";
|
|
6660
|
+
}
|
|
6661
|
+
code;
|
|
6662
|
+
};
|
|
6663
|
+
function fail(code = "invalid-input") {
|
|
6664
|
+
throw new ModelResourceManifestError(code);
|
|
6665
|
+
}
|
|
6666
|
+
function record(value) {
|
|
6667
|
+
if (!value || typeof value !== "object" || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) fail();
|
|
6668
|
+
const output = /* @__PURE__ */ Object.create(null);
|
|
6669
|
+
const keys = Reflect.ownKeys(value);
|
|
6670
|
+
if (keys.length > MODEL_RESOURCE_MANIFEST_LIMITS.files) fail("limit-exceeded");
|
|
6671
|
+
for (const key of keys) {
|
|
6672
|
+
if (typeof key !== "string" || ["__proto__", "constructor", "prototype"].includes(key)) fail();
|
|
6673
|
+
if (key.length > 128) fail("limit-exceeded");
|
|
6674
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
6675
|
+
if (!("value" in property) || !property.enumerable) fail();
|
|
6676
|
+
output[key] = property.value;
|
|
6677
|
+
}
|
|
6678
|
+
return output;
|
|
6679
|
+
}
|
|
6680
|
+
function list(value) {
|
|
6681
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) fail();
|
|
6682
|
+
if (value.length > MODEL_RESOURCE_MANIFEST_LIMITS.files) fail("limit-exceeded");
|
|
6683
|
+
if (Reflect.ownKeys(value).length !== value.length + 1) fail();
|
|
6684
|
+
const output = [];
|
|
6685
|
+
for (let i = 0; i < value.length; i++) {
|
|
6686
|
+
const property = Object.getOwnPropertyDescriptor(value, String(i));
|
|
6687
|
+
if (!property || !("value" in property) || !property.enumerable) fail();
|
|
6688
|
+
output.push(property.value);
|
|
6689
|
+
}
|
|
6690
|
+
return output;
|
|
6691
|
+
}
|
|
6692
|
+
function snapshot(value, budget, depth = 0) {
|
|
6693
|
+
if (--budget.remaining < 0 || depth > MODEL_RESOURCE_MANIFEST_LIMITS.metadataDepth) fail("limit-exceeded");
|
|
6694
|
+
if (value === null || value === void 0 || typeof value === "boolean") return value;
|
|
6695
|
+
if (typeof value === "string") {
|
|
6696
|
+
if (value.length > 2048) fail("limit-exceeded");
|
|
6697
|
+
return value;
|
|
6698
|
+
}
|
|
6699
|
+
if (typeof value === "number") {
|
|
6700
|
+
if (!Number.isFinite(value)) fail();
|
|
6701
|
+
return value;
|
|
6702
|
+
}
|
|
6703
|
+
if (Array.isArray(value)) return Object.freeze(list(value).map((item) => snapshot(item, budget, depth + 1)));
|
|
6704
|
+
const fields = record(value);
|
|
6705
|
+
for (const key of Object.keys(fields)) fields[key] = snapshot(fields[key], budget, depth + 1);
|
|
6706
|
+
return Object.freeze(fields);
|
|
6707
|
+
}
|
|
6708
|
+
function path(value) {
|
|
6709
|
+
if (typeof value !== "string" || value.length > 1024 || !value.length) fail();
|
|
6710
|
+
for (const part of value.split("/")) {
|
|
6711
|
+
if (part.length > 255 || !/^[A-Za-z0-9_][A-Za-z0-9_.-]*$/u.test(part) || part.endsWith(".") || /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\.|$)/iu.test(part)) fail();
|
|
6712
|
+
}
|
|
6713
|
+
return value;
|
|
6714
|
+
}
|
|
6715
|
+
function contentType(value) {
|
|
6716
|
+
if (typeof value !== "string" || value.length > 128 || !/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+(?:; charset=utf-8)?$/u.test(value)) fail();
|
|
6717
|
+
return value;
|
|
6718
|
+
}
|
|
6719
|
+
function uniquePaths(paths) {
|
|
6720
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6721
|
+
for (const value of paths) {
|
|
6722
|
+
const normalized = path(value).toLowerCase();
|
|
6723
|
+
if (seen.has(normalized)) fail("path-conflict");
|
|
6724
|
+
seen.add(normalized);
|
|
6725
|
+
}
|
|
6726
|
+
for (const value of seen) {
|
|
6727
|
+
for (let offset = value.indexOf("/"); offset !== -1; offset = value.indexOf("/", offset + 1)) {
|
|
6728
|
+
if (seen.has(value.slice(0, offset))) fail("path-conflict");
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
}
|
|
6732
|
+
var typedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype);
|
|
6733
|
+
var byteLengthGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "byteLength").get;
|
|
6734
|
+
var bufferGetter = Object.getOwnPropertyDescriptor(typedArrayPrototype, "buffer").get;
|
|
6735
|
+
async function qualifyModelResourceManifest(input) {
|
|
6736
|
+
try {
|
|
6737
|
+
return await qualify(input);
|
|
6738
|
+
} catch (error) {
|
|
6739
|
+
if (error instanceof ModelResourceManifestError) throw error;
|
|
6740
|
+
return fail();
|
|
6741
|
+
}
|
|
6742
|
+
}
|
|
6743
|
+
async function qualify(input) {
|
|
6744
|
+
const started = performance.now();
|
|
6745
|
+
const root = record(input);
|
|
6746
|
+
if (Object.keys(root).some((key) => !["manifest", "packageManifest", "files", "signal", "timeoutMs"].includes(key))) fail();
|
|
6747
|
+
const timeout = root.timeoutMs ?? 1e4;
|
|
6748
|
+
if (typeof timeout !== "number" || !Number.isSafeInteger(timeout) || timeout < 1 || timeout > MODEL_RESOURCE_MANIFEST_LIMITS.timeoutMs) fail();
|
|
6749
|
+
const signal = root.signal;
|
|
6750
|
+
if (signal !== void 0 && !(signal instanceof AbortSignal)) fail();
|
|
6751
|
+
const check = () => {
|
|
6752
|
+
if (signal?.aborted) fail("aborted");
|
|
6753
|
+
if (performance.now() - started >= timeout) fail("deadline-exceeded");
|
|
6754
|
+
};
|
|
6755
|
+
check();
|
|
6756
|
+
const budget = { remaining: MODEL_RESOURCE_MANIFEST_LIMITS.metadataValues };
|
|
6757
|
+
const rawManifest = snapshot(root.manifest, budget);
|
|
6758
|
+
const inventory = record(snapshot(root.packageManifest, budget));
|
|
6759
|
+
if (Object.keys(inventory).some((key) => !["format", "entries"].includes(key)) || !["zip", "tar", "directory-manifest", "inline"].includes(inventory.format)) fail();
|
|
6760
|
+
let manifest;
|
|
6761
|
+
try {
|
|
6762
|
+
manifest = rawManifest.assetKind === "model" ? createModelAssetManifest(rawManifest) : createAssetManifest(rawManifest);
|
|
6763
|
+
} catch {
|
|
6764
|
+
return fail();
|
|
6765
|
+
}
|
|
6766
|
+
uniquePaths(manifest.files.map((file) => file.path));
|
|
6767
|
+
if (!manifest.files.some((file) => file.path === path(manifest.entrypoint))) fail("inventory-mismatch");
|
|
6768
|
+
const descriptors = new Map(manifest.files.map((file) => [file.path, file]));
|
|
6769
|
+
let total = 0;
|
|
6770
|
+
for (const file of manifest.files) {
|
|
6771
|
+
if (!Number.isSafeInteger(file.byteLength)) fail();
|
|
6772
|
+
if (file.byteLength > MODEL_RESOURCE_MANIFEST_LIMITS.fileBytes) fail("limit-exceeded");
|
|
6773
|
+
total += file.byteLength;
|
|
6774
|
+
if (total > MODEL_RESOURCE_MANIFEST_LIMITS.totalBytes) fail("limit-exceeded");
|
|
6775
|
+
contentType(file.contentType);
|
|
6776
|
+
}
|
|
6777
|
+
const entries = list(inventory.entries).map(record);
|
|
6778
|
+
uniquePaths(entries.map((entry) => path(entry.path)));
|
|
6779
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6780
|
+
for (const entry of entries) {
|
|
6781
|
+
if (Object.keys(entry).some((key) => !["id", "path", "role", "contentType"].includes(key)) || typeof entry.id !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(entry.id) || ids.has(entry.id) || !["mesh", "material", "texture", "animation", "auxiliary"].includes(entry.role)) fail();
|
|
6782
|
+
ids.add(entry.id);
|
|
6783
|
+
const descriptor = descriptors.get(entry.path);
|
|
6784
|
+
if (!descriptor) fail("inventory-mismatch");
|
|
6785
|
+
if (entry.contentType !== void 0 && contentType(entry.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
6786
|
+
}
|
|
6787
|
+
if (entries.length !== descriptors.size) fail("inventory-mismatch");
|
|
6788
|
+
const payloads = list(root.files).map((value) => {
|
|
6789
|
+
const file = record(value);
|
|
6790
|
+
if (Object.keys(file).length !== 3 || Object.keys(file).some((key) => !["path", "bytes", "contentType"].includes(key))) fail();
|
|
6791
|
+
const filePath = path(file.path);
|
|
6792
|
+
if (!(file.bytes instanceof Uint8Array) || Object.getPrototypeOf(file.bytes) !== Uint8Array.prototype || !(bufferGetter.call(file.bytes) instanceof ArrayBuffer)) fail();
|
|
6793
|
+
const descriptor = descriptors.get(filePath);
|
|
6794
|
+
if (!descriptor) fail("inventory-mismatch");
|
|
6795
|
+
if (byteLengthGetter.call(file.bytes) !== descriptor.byteLength || contentType(file.contentType) !== descriptor.contentType) fail("content-mismatch");
|
|
6796
|
+
return { path: filePath, bytes: file.bytes, descriptor };
|
|
6797
|
+
});
|
|
6798
|
+
uniquePaths(payloads.map((file) => file.path));
|
|
6799
|
+
if (payloads.length !== descriptors.size) fail("inventory-mismatch");
|
|
6800
|
+
const copies = payloads.map((file) => ({ ...file, bytes: new Uint8Array(file.bytes) }));
|
|
6801
|
+
for (const file of copies) {
|
|
6802
|
+
check();
|
|
6803
|
+
let hash;
|
|
6804
|
+
try {
|
|
6805
|
+
hash = await computeSha256(file.bytes);
|
|
6806
|
+
} catch {
|
|
6807
|
+
return fail("content-mismatch");
|
|
6808
|
+
}
|
|
6809
|
+
check();
|
|
6810
|
+
if (hash !== file.descriptor.sha256) fail("content-mismatch");
|
|
6811
|
+
}
|
|
6812
|
+
return Object.freeze({ ...manifest, files: Object.freeze([...manifest.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)) });
|
|
6813
|
+
}
|
|
6814
|
+
|
|
6645
6815
|
// src/index.ts
|
|
6646
6816
|
var ASSET_CONTRACTS_PACKAGE = "@plasius/asset-contracts";
|
|
6647
6817
|
var UNIFIED_ASSET_PIPELINE_FEATURE_FLAG_ID = "asset.pipeline.unified-ai-assets.enabled";
|
|
@@ -7030,9 +7200,9 @@ async function validateGpuAssetFiles(input) {
|
|
|
7030
7200
|
if (input.files.size !== manifest.files.length) {
|
|
7031
7201
|
throw new Error("GPU asset validation requires exactly the declared file set.");
|
|
7032
7202
|
}
|
|
7033
|
-
for (const
|
|
7034
|
-
if (!manifest.files.some((file) => file.path ===
|
|
7035
|
-
throw new Error(`GPU asset validation received undeclared file ${
|
|
7203
|
+
for (const path2 of input.files.keys()) {
|
|
7204
|
+
if (!manifest.files.some((file) => file.path === path2)) {
|
|
7205
|
+
throw new Error(`GPU asset validation received undeclared file ${path2}.`);
|
|
7036
7206
|
}
|
|
7037
7207
|
}
|
|
7038
7208
|
await Promise.all(manifest.files.map(async (file) => {
|
|
@@ -7043,7 +7213,7 @@ async function validateGpuAssetFiles(input) {
|
|
|
7043
7213
|
if (bytes.byteLength !== file.byteLength) {
|
|
7044
7214
|
throw new Error(`GPU asset file ${file.path} byte length differs from its descriptor.`);
|
|
7045
7215
|
}
|
|
7046
|
-
if (await
|
|
7216
|
+
if (await computeSha2562(bytes) !== file.sha256) {
|
|
7047
7217
|
throw new Error(`GPU asset file ${file.path} digest differs from its descriptor.`);
|
|
7048
7218
|
}
|
|
7049
7219
|
if (file.contentType === ASSET_JSON_CONTENT_TYPE) {
|
|
@@ -7323,7 +7493,7 @@ async function bindDomainManifestBytes(bytes, manifest, fieldName) {
|
|
|
7323
7493
|
if (canonicalizeGpuContract3(parsed) !== canonicalizeGpuContract3(manifest)) {
|
|
7324
7494
|
throw new Error(`${fieldName} manifest bytes differ from the supplied manifest.`);
|
|
7325
7495
|
}
|
|
7326
|
-
return
|
|
7496
|
+
return computeSha2562(bytes);
|
|
7327
7497
|
}
|
|
7328
7498
|
function assertRoleCount(manifest, role, expectedCount) {
|
|
7329
7499
|
if (manifest.files.filter((file) => file.role === role).length !== expectedCount) {
|
|
@@ -7442,8 +7612,10 @@ export {
|
|
|
7442
7612
|
MODEL_RESOLUTION_V2_CONTRACT_VERSION,
|
|
7443
7613
|
MODEL_RESOLUTION_V2_JSON_SCHEMAS,
|
|
7444
7614
|
MODEL_RESOLUTION_V2_STATES,
|
|
7615
|
+
MODEL_RESOURCE_MANIFEST_LIMITS,
|
|
7445
7616
|
MODEL_RIGHTS_STATUSES,
|
|
7446
7617
|
MODEL_TEXT_ONLY_ASSURANCE_CEILING_REASON_CODE,
|
|
7618
|
+
ModelResourceManifestError,
|
|
7447
7619
|
PVOX_ASSEMBLY_CLOSURE_HASH_DOMAIN,
|
|
7448
7620
|
PVOX_ASSET_MANIFEST_VERSION,
|
|
7449
7621
|
PVOX_BINARY_CLOSURE_HASH_DOMAIN,
|
|
@@ -7634,6 +7806,7 @@ export {
|
|
|
7634
7806
|
normalizeAssetId,
|
|
7635
7807
|
normalizePvoxRootHeaderForHashV1,
|
|
7636
7808
|
projectPvoxNamedJsonClosureV1,
|
|
7809
|
+
qualifyModelResourceManifest,
|
|
7637
7810
|
toLegacyModelResolutionState,
|
|
7638
7811
|
validateGpuAssetFiles
|
|
7639
7812
|
};
|